simperby_settlement/
tests.rs

1//! Various test scenarios for the settlement chain communication
2
3use super::*;
4use execution::*;
5use simperby_core::{verify::CommitSequenceVerifier, *};
6use std::time::Duration;
7use tokio::time::sleep;
8
9pub struct ChainInfo {
10    pub chain_name: String,
11    pub last_finalized_header: BlockHeader,
12    pub last_finalization_proof: FinalizationProof,
13    pub reserved_state: ReservedState,
14    /// The private keys of the validators of the next block.
15    ///
16    /// Both governance and consensus sets must be the same.
17    pub validators: Vec<PrivateKey>,
18}
19
20impl ChainInfo {
21    /// Creates Chain info from the standard genesis test suite.
22    ///
23    /// This is useful when you want to test the treasury for the first time.
24    pub fn standard_genesis(chain_name: String) -> Self {
25        let (reserved_state, validators) = test_utils::generate_standard_genesis(4);
26        Self {
27            chain_name,
28            last_finalized_header: reserved_state.genesis_info.header.clone(),
29            last_finalization_proof: reserved_state.genesis_info.genesis_proof.clone(),
30            reserved_state,
31            validators: validators
32                .into_iter()
33                .map(|(_, private_key)| private_key)
34                .collect(),
35        }
36    }
37}
38
39/// A simple transfer scneario.
40///
41/// The treasury contract must be synchronized with the given `chain_info`
42/// and `initial_contract_sequence` as its initial state.
43pub async fn scenario_1(
44    chain_info: ChainInfo,
45    initial_contract_sequence: u128,
46    token_address: HexSerializedVec,
47    temporary_receiver_address: HexSerializedVec,
48    sc: impl SettlementChain,
49    transaction_finalization_wait: Duration,
50) {
51    // Setup the on-chain state
52    let mut csv = CommitSequenceVerifier::new(
53        chain_info.last_finalized_header.clone(),
54        chain_info.reserved_state.clone(),
55    )
56    .unwrap();
57
58    // Query the initial status
59    let initial_balance = sc
60        .get_treasury_fungible_token_balance(token_address.clone())
61        .await
62        .unwrap();
63    let initial_treasury_header = sc.get_light_client_header().await.unwrap();
64    let contract_sequence = sc.get_contract_sequence().await.unwrap();
65    let initial_temporary_receiver_balance = sc
66        .eoa_get_fungible_token_balance(temporary_receiver_address.clone(), token_address.clone())
67        .await
68        .unwrap();
69    assert_eq!(initial_treasury_header, chain_info.last_finalized_header);
70    assert_eq!(contract_sequence, initial_contract_sequence);
71
72    // Apply transactions
73    let mut transactions = Vec::new();
74    for i in 0..10 {
75        let tx = Transaction {
76            author: "doesn't matter".to_owned(),
77            timestamp: 0,
78            head: format!("commit {i}"),
79            body: "".to_owned(),
80            diff: Diff::None,
81        };
82        csv.apply_commit(&Commit::Transaction(tx.clone())).unwrap();
83        transactions.push(tx);
84    }
85    let execute_tx = execution::create_execution_transaction(
86        &Execution {
87            target_chain: chain_info.chain_name,
88            contract_sequence: initial_contract_sequence,
89            message: ExecutionMessage::TransferFungibleToken(TransferFungibleToken {
90                token_address: token_address.clone(),
91                amount: initial_balance,
92                receiver_address: temporary_receiver_address.clone(),
93            }),
94        },
95        "hi".to_owned(),
96        1234,
97    )
98    .unwrap();
99    csv.apply_commit(&Commit::Transaction(execute_tx.clone()))
100        .unwrap();
101    transactions.push(execute_tx.clone());
102    for i in 0..20 {
103        let tx = Transaction {
104            author: "doesn't matter".to_owned(),
105            timestamp: 0,
106            head: format!("commit {i}"),
107            body: "".to_owned(),
108            diff: Diff::None,
109        };
110        csv.apply_commit(&Commit::Transaction(tx.clone())).unwrap();
111        transactions.push(tx);
112    }
113
114    // Complete the block
115    let agenda = Agenda {
116        height: 1,
117        author: chain_info.reserved_state.consensus_leader_order[0].clone(),
118        timestamp: 0,
119        transactions_hash: Agenda::calculate_transactions_hash(&transactions),
120        previous_block_hash: chain_info.last_finalized_header.to_hash256(),
121    };
122    csv.apply_commit(&Commit::Agenda(agenda.clone())).unwrap();
123    csv.apply_commit(&Commit::AgendaProof(AgendaProof {
124        height: 1,
125        agenda_hash: agenda.to_hash256(),
126        proof: chain_info
127            .validators
128            .iter()
129            .map(|private_key| TypedSignature::sign(&agenda, private_key).unwrap())
130            .collect::<Vec<_>>(),
131        timestamp: 0,
132    }))
133    .unwrap();
134    let block_header = BlockHeader {
135        author: chain_info.validators[0].public_key(),
136        prev_block_finalization_proof: chain_info.last_finalization_proof,
137        previous_hash: chain_info.last_finalized_header.to_hash256(),
138        height: 1,
139        timestamp: 0,
140        commit_merkle_root: BlockHeader::calculate_commit_merkle_root(
141            &csv.get_total_commits()[1..],
142        ),
143        repository_merkle_root: Hash256::zero(),
144        validator_set: chain_info.last_finalized_header.validator_set.clone(),
145        version: chain_info.last_finalized_header.version,
146    };
147    csv.apply_commit(&Commit::Block(block_header.clone()))
148        .unwrap();
149    let signatures = chain_info
150        .validators
151        .iter()
152        .map(|private_key| {
153            TypedSignature::sign(
154                &FinalizationSignTarget {
155                    block_hash: block_header.to_hash256(),
156                    round: 0,
157                },
158                private_key,
159            )
160            .unwrap()
161        })
162        .collect::<Vec<_>>();
163    let fp = FinalizationProof {
164        round: 0,
165        signatures,
166    };
167    csv.verify_last_header_finalization(&fp).unwrap();
168
169    // Update light client
170    sc.update_treasury_light_client(block_header.clone(), fp)
171        .await
172        .unwrap();
173    sleep(transaction_finalization_wait).await;
174    assert_eq!(sc.get_light_client_header().await.unwrap(), block_header);
175
176    // Execute transfer
177    let commits = csv.get_total_commits();
178    let merkle_tree = OneshotMerkleTree::create(
179        commits[1..=(commits.len() - 2)]
180            .iter()
181            .map(|c| c.to_hash256())
182            .collect(),
183    );
184    let merkle_proof = merkle_tree
185        .create_merkle_proof(execute_tx.to_hash256())
186        .unwrap();
187    sc.execute(execute_tx, 1, merkle_proof).await.unwrap();
188    sleep(transaction_finalization_wait).await;
189
190    // Check the result
191    assert_eq!(
192        sc.eoa_get_fungible_token_balance(
193            temporary_receiver_address.clone(),
194            token_address.clone()
195        )
196        .await
197        .unwrap(),
198        initial_temporary_receiver_balance + initial_balance
199    );
200}