Skip to content

Commit 9773202

Browse files
authored
Add FLAG_FREE_REJECT: with it, rejecting messages is free of charge. (#4876)
## Motivation In #4875, we make rejecting messages free of charge. ## Proposal Backport this to the testnet. Since it changes execution semantics, it needs to be behind a new flag `FLAG_FREE_REJECT.linera.network`. ## Test Plan CI ## Release Plan - These changes should - be released in a new SDK, - be released in a validator hotfix, - be activated via a resource policy change in a new epoch. ## Links - PR to main: #4875 - [reviewer checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist)
1 parent 5660150 commit 9773202

File tree

4 files changed

+84
-6
lines changed

4 files changed

+84
-6
lines changed

linera-chain/src/block_tracker.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ use linera_base::{
1313
};
1414
use linera_execution::{
1515
execution_state_actor::ExecutionStateActor, ExecutionRuntimeContext, ExecutionStateView,
16-
MessageContext, OperationContext, OutgoingMessage, ResourceController, ResourceTracker,
17-
SystemExecutionStateView, TransactionOutcome, TransactionTracker,
16+
MessageContext, MessageKind, OperationContext, OutgoingMessage, ResourceController,
17+
ResourceTracker, SystemExecutionStateView, TransactionOutcome, TransactionTracker,
18+
FLAG_FREE_REJECT,
1819
};
1920
use linera_views::context::Context;
2021
use tracing::instrument;
@@ -287,6 +288,14 @@ impl<'resources, 'blobs> BlockExecutionTracker<'resources, 'blobs> {
287288
let mut resource_controller = self.resource_controller.with_state(view).await?;
288289

289290
for message_out in &txn_outcome.outgoing_messages {
291+
if message_out.kind == MessageKind::Bouncing
292+
&& resource_controller
293+
.policy()
294+
.http_request_allow_list
295+
.contains(FLAG_FREE_REJECT)
296+
{
297+
continue; // Bouncing messages are free.
298+
}
290299
resource_controller
291300
.track_message(&message_out.message)
292301
.with_execution_context(context)?;

linera-core/src/unit_tests/client_tests.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use linera_chain::{
2525
use linera_execution::{
2626
committee::Committee, system::SystemOperation, ExecutionError, Message, MessageKind, Operation,
2727
QueryOutcome, ResourceControlPolicy, SystemMessage, SystemQuery, SystemResponse,
28+
FLAG_FREE_REJECT,
2829
};
2930
use linera_storage::Storage;
3031
use rand::Rng;
@@ -44,8 +45,8 @@ use crate::test_utils::ScyllaDbStorageBuilder;
4445
use crate::test_utils::ServiceStorageBuilder;
4546
use crate::{
4647
client::{
47-
BlanketMessagePolicy, ChainClient, ChainClientError, ClientOutcome, MessageAction,
48-
MessagePolicy,
48+
BlanketMessagePolicy, ChainClient, ChainClientError, ChainClientOptions, ClientOutcome,
49+
MessageAction, MessagePolicy,
4950
},
5051
local_node::LocalNodeError,
5152
node::{
@@ -2835,3 +2836,51 @@ where
28352836

28362837
Ok(())
28372838
}
2839+
2840+
#[test_case(MemoryStorageBuilder::default(); "memory")]
2841+
#[cfg_attr(feature = "storage-service", test_case(ServiceStorageBuilder::new(); "storage_service"))]
2842+
#[cfg_attr(feature = "rocksdb", test_case(RocksDbStorageBuilder::new().await; "rocks_db"))]
2843+
#[cfg_attr(feature = "dynamodb", test_case(DynamoDbStorageBuilder::default(); "dynamo_db"))]
2844+
#[cfg_attr(feature = "scylladb", test_case(ScyllaDbStorageBuilder::default(); "scylla_db"))]
2845+
#[test_log::test(tokio::test)]
2846+
async fn test_rejected_message_bundles_are_free<B>(storage_builder: B) -> anyhow::Result<()>
2847+
where
2848+
B: StorageBuilder,
2849+
{
2850+
let signer = InMemorySigner::new(None);
2851+
let mut builder = TestBuilder::new(storage_builder, 4, 1, signer)
2852+
.await?
2853+
.with_policy(ResourceControlPolicy {
2854+
http_request_allow_list: BTreeSet::from([FLAG_FREE_REJECT.to_string()]),
2855+
..ResourceControlPolicy::testnet()
2856+
});
2857+
let admin = builder.add_root_chain(1, Amount::from_tokens(2)).await?;
2858+
let user = builder.add_root_chain(1, Amount::ZERO).await?;
2859+
let user_reject = builder
2860+
.make_client_with_options(
2861+
user.chain_id(),
2862+
None,
2863+
BlockHeight::ZERO,
2864+
ChainClientOptions {
2865+
message_policy: MessagePolicy::new(BlanketMessagePolicy::Reject, None),
2866+
..ChainClientOptions::test_default()
2867+
},
2868+
)
2869+
.await?;
2870+
2871+
let recipient = Account::chain(user.chain_id());
2872+
admin
2873+
.transfer(AccountOwner::CHAIN, Amount::ONE, recipient)
2874+
.await
2875+
.unwrap_ok_committed();
2876+
2877+
user_reject.synchronize_from_validators().await?;
2878+
let (certificates, _) = user_reject.process_inbox().await.unwrap();
2879+
assert_eq!(certificates.len(), 1);
2880+
assert_matches!(
2881+
&certificates[0].block().body.transactions[0],
2882+
Transaction::ReceiveMessages(bundle) if bundle.action == MessageAction::Reject
2883+
);
2884+
2885+
Ok(())
2886+
}

linera-core/src/unit_tests/test_utils.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,11 +1029,12 @@ where
10291029
Ok(self.genesis_storage_builder.build(storage).await)
10301030
}
10311031

1032-
pub async fn make_client(
1032+
pub async fn make_client_with_options(
10331033
&mut self,
10341034
chain_id: ChainId,
10351035
block_hash: Option<CryptoHash>,
10361036
block_height: BlockHeight,
1037+
options: ChainClientOptions,
10371038
) -> anyhow::Result<ChainClient<B::Storage>> {
10381039
// Note that new clients are only given the genesis store: they must figure out
10391040
// the rest by asking validators.
@@ -1051,7 +1052,7 @@ where
10511052
format!("Client node for {:.8}", chain_id),
10521053
Duration::from_secs(30),
10531054
Duration::from_secs(1),
1054-
ChainClientOptions::test_default(),
1055+
options,
10551056
crate::client::RequestsSchedulerConfig::default(),
10561057
));
10571058
Ok(client.create_chain_client(
@@ -1064,6 +1065,21 @@ where
10641065
))
10651066
}
10661067

1068+
pub async fn make_client(
1069+
&mut self,
1070+
chain_id: ChainId,
1071+
block_hash: Option<CryptoHash>,
1072+
block_height: BlockHeight,
1073+
) -> anyhow::Result<ChainClient<B::Storage>> {
1074+
self.make_client_with_options(
1075+
chain_id,
1076+
block_hash,
1077+
block_height,
1078+
ChainClientOptions::test_default(),
1079+
)
1080+
.await
1081+
}
1082+
10671083
/// Tries to find a (confirmation) certificate for the given chain_id and block height.
10681084
pub async fn check_that_validators_have_certificate(
10691085
&self,

linera-execution/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ const MAX_STREAM_NAME_LEN: usize = 64;
9090
/// returned to be all zeros.
9191
// Note: testnet-only! This should not survive to mainnet.
9292
pub const FLAG_ZERO_HASH: &str = "FLAG_ZERO_HASH.linera.network";
93+
/// The flag that deactivates charging for bouncing messages. If this is present, outgoing
94+
/// messages are free of charge if they are bouncing, and operation outcomes are counted only
95+
/// by payload size, so that rejecting messages is free.
96+
pub const FLAG_FREE_REJECT: &str = "FLAG_FREE_REJECT.linera.network";
9397

9498
/// An implementation of [`UserContractModule`].
9599
#[derive(Clone)]

0 commit comments

Comments
 (0)