pdao_colony_common/lib.rs
1pub mod test_suite;
2
3use pdao_beacon_chain_common::message as pbc_message;
4use pdao_colony_contract_common::*;
5use rust_decimal::prelude::*;
6use serde::{Deserialize, Serialize};
7use serde_tc::*;
8use std::collections::HashMap;
9use thiserror::Error;
10
11/// A contract type.
12#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
13pub enum ContractType {
14 Treasury,
15 LightClient,
16 Custom { name: String, description: String },
17}
18
19/// Information of a contract.
20#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
21pub struct ContractInfo {
22 /// The address of the contract.
23 pub address: String,
24 /// The type of the contract.
25 pub contract_type: ContractType,
26 /// The increasing sequence that is for preventing the replay attack.
27 ///
28 /// A valid message from PBC MUST carry the same number with this,
29 /// in order to succesfully convince the contract. (i.e., this number is something that the consensus should have finalized on-chain).
30 ///
31 /// - Note1: this is totally irrelevant to the account sequence.
32 /// - Note2: the light client contract doesn't need this because the 'block height' provides the same safety guard.
33 pub sequence: u64,
34}
35
36/// An error that can occur when interacting with the contract.
37#[derive(Error, Debug, Serialize, Deserialize, Clone)]
38pub enum Error {
39 /// When there is a problem to access to the full node.
40 #[error("connection error: {0}")]
41 ConnectionError(String),
42 /// When the transaction is rejected by the full node, before it gets to the contract.
43 #[error("transaction rejected: {0}")]
44 TransactionRejected(String),
45 /// When the contract fails to decode the input data.
46 #[error("failed to parse the payload of the transaction")]
47 FailedToParseTransactionPayload,
48 /// When the given proof is invalid.
49 #[error("invalid proof given: got merkle root of {0} but expected {1}")]
50 InvalidProof(String, String),
51 /// When the given message is well decoded and verified, but the message argument is invalid.
52 #[error("invalid message argument given: {0}")]
53 InvalidMessageArgument(String),
54 /// When the relayer account has not enough balance to execute the transaction.
55 #[error("not enough balance: got {0}")]
56 NotEnoughBalance(u64),
57 /// When the account sequence given in the transaction is invalid.
58 #[error("invalid account sequence; expected {0} but got {1}")]
59 InvalidAccountSequence(u64, u64),
60 /// When the contract sequence given in the transaction is invalid.
61 #[error("invalid contract sequence; expected {0} but got {1}")]
62 InvalidContractSequence(u64, u64),
63 /// When the contract fails to execute the transaction with its own error.
64 #[error("internal contract error: {0}")]
65 InternalContractError(String),
66 /// When the contract is missing.
67 #[error("no such contract: {0}")]
68 NoSuchContract(String),
69 /// An unknown error.
70 #[error("unknown error: {0}")]
71 Unknown(String),
72}
73
74/// An abstract information about a block.
75#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
76pub struct Block {
77 /// The height of the block.
78 pub height: u64,
79 /// The UNIX timestamp of the block in seconds.
80 pub timestamp: u64,
81}
82
83/// An abstraction of a colony chain, which is viewed by the relayer and the explorer.
84///
85/// Every colony chains MUST have at least two types of contracts: the light client and the treasury.
86/// This interface directly specifies the actions applicable to the two essential contracts,
87/// whereas it is generalized as an opaque packet for those non-essential, chain-local and application-specific contracts (custom contracts).
88///
89/// One trivial implementation of this trait would carry the address of the full node and
90/// the relayer account used to submit message delivering transactions.
91#[serde_tc_full]
92pub trait ColonyChain: Send + Sync {
93 /// Returns the name of the chain.
94 async fn get_chain_name(&self) -> String;
95
96 /// Checks whether the chain is healthy and the full node is running.
97 async fn check_connection(&self) -> Result<(), Error>;
98
99 /// Getes the latest finalized block on the chain.
100 async fn get_last_block(&self) -> Result<Block, Error>;
101
102 /// Returns the list of the contracts that are available on the chain
103 async fn get_contract_list(&self) -> Result<Vec<ContractInfo>, Error>;
104
105 /// Returns the address and the current balance (which is used to pay the gas fee) of the relayer account in this chain.
106 ///
107 /// Note that there is no authority of the relayer account; it is just a convenient account to pay the gas fee
108 /// (i.e., there is no need to check the transaction submitter by the contract).
109 async fn get_relayer_account_info(&self) -> Result<(String, Decimal), Error>;
110
111 /// Returns the latest header that the light client has verified.
112 async fn get_light_client_header(&self) -> Result<Header, Error>;
113
114 /// Returns the current balance of fungible tokens in the treasury contract.
115 async fn get_treasury_fungible_token_balance(&self) -> Result<HashMap<String, Decimal>, Error>;
116
117 /// Returns the current balance of non-fungible tokens in the treasury contract, identified as `(collection address, token index)`.
118 ///
119 /// Note that the representation of the token index is specific to the chain, so we just provide as a string.
120 async fn get_treasury_non_fungible_token_balance(&self)
121 -> Result<Vec<(String, String)>, Error>;
122
123 /// Updates the light client state by providing the next, valid block header and its proof.
124 ///
125 /// This is one of the message delivery methods; a transaction that carries the given data will be submitted to the chain.
126 async fn update_light_client(
127 &self,
128 header: light_client::Header,
129 proof: light_client::BlockFinalizationProof,
130 ) -> Result<(), Error>;
131
132 /// Transfers a given amount of fungible tokens from the treasury contract to the destination address.
133 ///
134 /// This is one of the message delivery methods; a transaction that carries the given data and the proof will be submitted to the chain.
135 async fn transfer_treasury_fungible_token(
136 &self,
137 message: pbc_message::FungibleTokenTransfer,
138 block_height: u64,
139 proof: MerkleProof,
140 ) -> Result<(), Error>;
141
142 /// Transfers an NFT from the treasury contract to the destination address.
143 ///
144 /// This is one of the message methods; a transaction that carries the given data and the proof will be submitted to the chain.
145 async fn transfer_treasury_non_fungible_token(
146 &self,
147 message: pbc_message::NonFungibleTokenTransfer,
148 block_height: u64,
149 proof: MerkleProof,
150 ) -> Result<(), Error>;
151
152 /// Delivers an arbitrary message, which is an opaque `String`, to one of the 'custom' contracts in this chain except the light client and the treasury,
153 ///
154 /// This is one of the message delivery methods; a transaction that carries the given data and the proof will be submitted to the chain.
155 /// The target contract is the one with the type of `ContractType::Custom` and the name of `contract_name`.
156 async fn deliver_custom_order(
157 &self,
158 contract_name: &str,
159 message: pbc_message::Custom,
160 block_height: u64,
161 proof: MerkleProof,
162 ) -> Result<(), Error>;
163}
164
165impl serde_tc::http::HttpInterface for dyn ColonyChain {}