Skip to content

Commit

Permalink
Test bouncing of tokens rejected by receiver (#1863)
Browse files Browse the repository at this point in the history
* Add a `Block::with_message_rejection` helper

Allow testing the rejection of incoming messages.

* Test rejecting an incoming Fungible Token transfer

The tokens should be bounced back to the sender.
  • Loading branch information
jvff committed Apr 2, 2024
1 parent ead0fe5 commit eb80076
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 6 deletions.
73 changes: 73 additions & 0 deletions examples/fungible/tests/cross_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,76 @@ async fn test_cross_chain_transfer() {
Some(transfer_amount),
);
}

/// Test bouncing some tokens back to the sender.
///
/// Creates the application on a `sender_chain`, initializing it with a single account with some
/// tokens for that chain's owner. Attempts to transfer some tokens to a new `receiver_chain`,
/// but makes the `receiver_chain` reject the transfer message, causing the tokens to be
/// returned back to the sender.
#[tokio::test]
async fn test_bouncing_tokens() {
let initial_amount = Amount::from_tokens(19);
let transfer_amount = Amount::from_tokens(7);

let (validator, bytecode_id) = TestValidator::with_current_bytecode().await;
let mut sender_chain = validator.new_chain().await;
let sender_account = AccountOwner::from(sender_chain.public_key());

let initial_state = InitialStateBuilder::default().with_account(sender_account, initial_amount);
let params = Parameters::new("RET");
let application_id = sender_chain
.create_application::<fungible::FungibleTokenAbi>(
bytecode_id,
params,
initial_state.build(),
vec![],
)
.await;

let receiver_chain = validator.new_chain().await;
let receiver_account = AccountOwner::from(receiver_chain.public_key());

let messages = sender_chain
.add_block(|block| {
block.with_operation(
application_id,
Operation::Transfer {
owner: sender_account,
amount: transfer_amount,
target_account: Account {
chain_id: receiver_chain.id(),
owner: receiver_account,
},
},
);
})
.await;

assert_eq!(
fungible::query_account(application_id, &sender_chain, sender_account).await,
Some(initial_amount.saturating_sub(transfer_amount)),
);

assert_eq!(messages.len(), 2);

receiver_chain
.add_block(move |block| {
block
.with_incoming_message(messages[0])
.with_message_rejection(messages[1]);
})
.await;

assert_eq!(
fungible::query_account(application_id, &receiver_chain, receiver_account).await,
None,
);

sender_chain.handle_received_messages().await;

assert_eq!(
fungible::query_account(application_id, &sender_chain, sender_account).await,
Some(initial_amount),
);
}
29 changes: 23 additions & 6 deletions linera-sdk/src/test/integration/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use linera_base::{
identifiers::{ApplicationId, ChainId, MessageId, Owner},
};
use linera_chain::data_types::{
Block, Certificate, HashedValue, IncomingMessage, LiteVote, SignatureAggregator,
Block, Certificate, HashedValue, IncomingMessage, LiteVote, MessageAction, SignatureAggregator,
};
use linera_execution::{system::SystemOperation, Operation};

Expand All @@ -23,7 +23,7 @@ use crate::ToBcsBytes;
/// [`Certificate`]s using a [`TestValidator`].
pub struct BlockBuilder {
block: Block,
incoming_messages: Vec<MessageId>,
incoming_messages: Vec<(MessageId, MessageAction)>,
validator: TestValidator,
}

Expand Down Expand Up @@ -126,7 +126,18 @@ impl BlockBuilder {
/// The block that produces the message must have already been executed by the test validator,
/// so that the message is already in the inbox of the microchain this block belongs to.
pub fn with_incoming_message(&mut self, message_id: MessageId) -> &mut Self {
self.incoming_messages.push(message_id);
self.incoming_messages
.push((message_id, MessageAction::Accept));
self
}

/// Rejects an incoming message referenced by the [`MessageId`].
///
/// The block that produces the message must have already been executed by the test validator,
/// so that the message is already in the inbox of the microchain this block belongs to.
pub fn with_message_rejection(&mut self, message_id: MessageId) -> &mut Self {
self.incoming_messages
.push((message_id, MessageAction::Reject));
self
}

Expand All @@ -138,7 +149,11 @@ impl BlockBuilder {
&mut self,
message_ids: impl IntoIterator<Item = MessageId>,
) -> &mut Self {
self.incoming_messages.extend(message_ids);
self.incoming_messages.extend(
message_ids
.into_iter()
.map(|message_id| (message_id, MessageAction::Accept)),
);
self
}

Expand Down Expand Up @@ -193,8 +208,8 @@ impl BlockBuilder {
async fn collect_incoming_messages(&mut self) {
let chain_id = self.block.chain_id;

for message_id in mem::take(&mut self.incoming_messages) {
let message = self
for (message_id, action) in mem::take(&mut self.incoming_messages) {
let mut message = self
.validator
.worker()
.await
Expand All @@ -203,6 +218,8 @@ impl BlockBuilder {
.expect("Failed to find message to receive in block")
.expect("Message that block should consume has not been emitted");

message.action = action;

self.block.incoming_messages.push(message);
}
}
Expand Down

0 comments on commit eb80076

Please sign in to comment.