Skip to content

Commit 12c1d23

Browse files
afckdeuszx
andauthored
Remove block fee, disallow empty blocks. (#3850)
## Motivation If all incoming messages are covered by grants, the chain owner should not have any fees left to pay. ## Proposal Remove the base block fee. Disallow empty blocks. ## Test Plan TBD ## Release Plan - Nothing to do / These changes follow the usual release cycle. ## Links - Part of #3841. - [reviewer checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist) --------- Signed-off-by: deuszx <[email protected]> Co-authored-by: deuszx <[email protected]> Co-authored-by: deuszx <[email protected]>
1 parent 436925f commit 12c1d23

File tree

24 files changed

+265
-168
lines changed

24 files changed

+265
-168
lines changed

.github/workflows/rust.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,11 @@ jobs:
6161
mkdir /tmp/local-linera-net
6262
cargo run --features storage-service --bin linera -- net up --storage service:tcp:$LINERA_STORAGE_SERVICE:table --policy-config testnet --path /tmp/local-linera-net --validators 4 --shards 4 &
6363
- name: Create two epochs and run the faucet
64+
# See https://github.com/linera-io/linera-protocol/pull/2835 for details.
6465
run: |
6566
cargo build --bin linera
66-
cargo run --bin linera -- resource-control-policy --block 0.0000001
67-
cargo run --bin linera -- resource-control-policy --block 0.000000
67+
cargo run --bin linera -- resource-control-policy --http-request-timeout-ms 1000
68+
cargo run --bin linera -- resource-control-policy --http-request-timeout-ms 500
6869
cargo run --bin linera -- faucet --amount 1000 --port 8079 &
6970
- name: Run the remote-net tests
7071
run: |

CLI.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,6 @@ View or update the resource control policy
475475

476476
###### **Options:**
477477

478-
* `--block <BLOCK>` — Set the base price for creating a block
479478
* `--wasm-fuel-unit <WASM_FUEL_UNIT>` — Set the price per unit of Wasm fuel
480479
* `--evm-fuel-unit <EVM_FUEL_UNIT>` — Set the price per unit of EVM fuel
481480
* `--read-operation <READ_OPERATION>` — Set the price per read operation
@@ -537,7 +536,6 @@ Create genesis configuration for a Linera deployment. Create initial user chains
537536

538537
Possible values: `no-fees`, `testnet`
539538

540-
* `--block-price <BLOCK_PRICE>` — Set the base price for creating a block. (This will overwrite value from `--policy-config`)
541539
* `--wasm-fuel-unit-price <WASM_FUEL_UNIT_PRICE>` — Set the price per unit of Wasm fuel. (This will overwrite value from `--policy-config`)
542540
* `--evm-fuel-unit-price <EVM_FUEL_UNIT_PRICE>` — Set the price per unit of EVM fuel. (This will overwrite value from `--policy-config`)
543541
* `--read-operation-price <READ_OPERATION_PRICE>` — Set the price per read operation. (This will overwrite value from `--policy-config`)

examples/native-fungible/tests/transfers.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ async fn chain_balance_transfers() {
4343
})
4444
.await;
4545

46-
assert_eq!(recipient_chain.chain_balance().await, transfer_amount);
46+
// At the start, chain has balance of 10 tokens.
47+
// After the transfer, it should have 11 tokens.
48+
let expected_balance = Amount::from_tokens(10) + transfer_amount;
49+
assert_eq!(recipient_chain.chain_balance().await, expected_balance);
4750
assert_balances(&recipient_chain, []).await;
4851
}
4952

linera-base/src/data_types.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,19 @@ impl ApplicationPermissions {
935935
}
936936
}
937937

938+
/// Creates new `ApplicationPermissions` where the given applications are the only ones
939+
/// whose operations are allowed and mandatory, and they can also close the chain.
940+
pub fn new_multiple(app_ids: Vec<ApplicationId>) -> Self {
941+
Self {
942+
execute_operations: Some(app_ids.clone()),
943+
mandatory_applications: app_ids.clone(),
944+
close_chain: app_ids.clone(),
945+
change_application_permissions: app_ids.clone(),
946+
call_service_as_oracle: Some(app_ids.clone()),
947+
make_http_requests: Some(app_ids),
948+
}
949+
}
950+
938951
/// Returns whether operations with the given application ID are allowed on this chain.
939952
pub fn can_execute_operations(&self, app_id: &GenericApplicationId) -> bool {
940953
match (app_id, &self.execute_operations) {

linera-chain/src/chain.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,10 @@ where
708708
);
709709

710710
chain.system.timestamp.set(block.timestamp);
711-
711+
ensure!(
712+
!block.incoming_bundles.is_empty() || !block.operations.is_empty(),
713+
ChainError::EmptyBlock
714+
);
712715
let (_, committee) = chain
713716
.system
714717
.current_committee()

linera-chain/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ pub enum ChainError {
152152
OwnerWeightError(#[from] WeightedError),
153153
#[error("Closed chains cannot have operations, accepted messages or empty blocks")]
154154
ClosedChain,
155+
#[error("Empty blocks are not allowed")]
156+
EmptyBlock,
155157
#[error("All operations on this chain must be from one of the following applications: {0:?}")]
156158
AuthorizedApplications(Vec<ApplicationId>),
157159
#[error("Missing operations or messages from mandatory applications: {0:?}")]

linera-chain/src/test/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ pub trait BlockTestExt: Sized {
7878
/// Returns the block with the specified epoch.
7979
fn with_epoch(self, epoch: impl Into<Epoch>) -> Self;
8080

81+
/// Returns the block with the burn operation appended at the end.
82+
fn with_burn(self, amount: Amount) -> Self;
83+
8184
/// Returns a block proposal in the first round in a default ownership configuration
8285
/// (`Round::MultiLeader(0)`) without any hashed certificate values or validated block.
8386
async fn into_first_proposal(
@@ -121,6 +124,14 @@ impl BlockTestExt for ProposedBlock {
121124
self.with_transfer(AccountOwner::CHAIN, Recipient::chain(chain_id), amount)
122125
}
123126

127+
fn with_burn(self, amount: Amount) -> Self {
128+
self.with_operation(SystemOperation::Transfer {
129+
owner: AccountOwner::CHAIN,
130+
recipient: Recipient::Burn,
131+
amount,
132+
})
133+
}
134+
124135
fn with_incoming_bundle(mut self, incoming_bundle: IncomingBundle) -> Self {
125136
self.incoming_bundles.push(incoming_bundle);
126137
self

linera-chain/src/unit_tests/chain_tests.rs

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ impl TestEnvironment {
109109
fn make_app_description(&self) -> (ApplicationDescription, Blob, Blob) {
110110
let contract = Bytecode::new(b"contract".into());
111111
let service = Bytecode::new(b"service".into());
112+
self.make_app_from_bytecodes(contract, service)
113+
}
114+
115+
fn make_app_from_bytecodes(
116+
&self,
117+
contract: Bytecode,
118+
service: Bytecode,
119+
) -> (ApplicationDescription, Blob, Blob) {
112120
let contract_blob = Blob::new_contract_bytecode(contract.compress());
113121
let service_blob = Blob::new_service_bytecode(service.compress());
114122
let vm_runtime = VmRuntime::Wasm;
@@ -254,8 +262,17 @@ async fn test_application_permissions() -> anyhow::Result<()> {
254262
let application_id = ApplicationId::from(&app_description);
255263
let application = MockApplication::default();
256264

265+
let (another_app, another_contract, another_service) = env.make_app_from_bytecodes(
266+
Bytecode::new(b"contractB".into()),
267+
Bytecode::new(b"serviceB".into()),
268+
);
269+
let another_app_id = ApplicationId::from(&another_app);
270+
257271
let config = InitialChainConfig {
258-
application_permissions: ApplicationPermissions::new_single(application_id),
272+
application_permissions: ApplicationPermissions::new_multiple(vec![
273+
application_id,
274+
another_app_id,
275+
]),
259276
..env.make_open_chain_config()
260277
};
261278
let chain_desc = env.make_child_chain_description_with_config(3, config);
@@ -267,6 +284,10 @@ async fn test_application_permissions() -> anyhow::Result<()> {
267284
extra
268285
.user_contracts()
269286
.insert(application_id, application.clone().into());
287+
extra
288+
.user_contracts()
289+
.insert(another_app_id, application.clone().into());
290+
270291
extra.add_blobs(env.description_blobs()).await?;
271292
extra
272293
.add_blobs([
@@ -275,6 +296,13 @@ async fn test_application_permissions() -> anyhow::Result<()> {
275296
Blob::new_application_description(&app_description),
276297
])
277298
.await?;
299+
extra
300+
.add_blobs([
301+
another_contract,
302+
another_service,
303+
Blob::new_application_description(&another_app),
304+
])
305+
.await?;
278306

279307
// Initialize the chain, with a chain application.
280308
chain.ensure_is_active(time).await?;
@@ -285,20 +313,31 @@ async fn test_application_permissions() -> anyhow::Result<()> {
285313
.execute_block(&invalid_block, time, None, &[], None)
286314
.await;
287315
assert_matches!(result, Err(ChainError::AuthorizedApplications(app_ids))
288-
if app_ids == vec![application_id]
316+
if app_ids == vec![application_id, another_app_id]
289317
);
290318

291319
// After registering, an app operation can already be used in the first block.
292320
application.expect_call(ExpectedCall::execute_operation(|_, _| Ok(vec![])));
293321
application.expect_call(ExpectedCall::default_finalize());
322+
application.expect_call(ExpectedCall::execute_operation(|_, _| Ok(vec![])));
323+
application.expect_call(ExpectedCall::default_finalize());
294324
let app_operation = Operation::User {
295325
application_id,
296326
bytes: b"foo".to_vec(),
297327
};
298-
let valid_block = make_first_block(chain_id).with_operation(app_operation.clone());
328+
let another_app_operation = Operation::User {
329+
application_id: another_app_id,
330+
bytes: b"bar".to_vec(),
331+
};
332+
333+
let valid_block = make_first_block(chain_id)
334+
.with_operation(app_operation.clone())
335+
.with_operation(another_app_operation.clone());
336+
299337
let outcome = chain
300338
.execute_block(&valid_block, time, None, &[], None)
301339
.await?;
340+
302341
let value = ConfirmedBlock::new(outcome.with(valid_block));
303342
chain.apply_confirmed_block(&value, time).await?;
304343

@@ -310,22 +349,24 @@ async fn test_application_permissions() -> anyhow::Result<()> {
310349
.execute_block(&invalid_block, time, None, &[], None)
311350
.await;
312351
assert_matches!(result, Err(ChainError::AuthorizedApplications(app_ids))
313-
if app_ids == vec![application_id]
352+
if app_ids == vec![application_id, another_app_id]
314353
);
315-
316-
// Also, blocks without an application operation or incoming message are forbidden.
317-
let invalid_block = make_child_block(&value);
354+
// Also, blocks without all authorized applications operation, or incoming message, are forbidden.
355+
let invalid_block = make_child_block(&value).with_operation(another_app_operation.clone());
318356
let result = chain
319357
.execute_block(&invalid_block, time, None, &[], None)
320358
.await;
321359
assert_matches!(result, Err(ChainError::MissingMandatoryApplications(app_ids))
322360
if app_ids == vec![application_id]
323361
);
324-
325362
// But app operations continue to work.
326363
application.expect_call(ExpectedCall::execute_operation(|_, _| Ok(vec![])));
327364
application.expect_call(ExpectedCall::default_finalize());
328-
let valid_block = make_child_block(&value).with_operation(app_operation);
365+
application.expect_call(ExpectedCall::execute_operation(|_, _| Ok(vec![])));
366+
application.expect_call(ExpectedCall::default_finalize());
367+
let valid_block = make_child_block(&value)
368+
.with_operation(app_operation.clone())
369+
.with_operation(another_app_operation.clone());
329370
let outcome = chain
330371
.execute_block(&valid_block, time, None, &[], None)
331372
.await?;

linera-client/src/client_options.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,6 @@ pub enum ResourceControlPolicyConfig {
287287
#[cfg(with_testing)]
288288
OnlyFuel,
289289
#[cfg(with_testing)]
290-
FuelAndBlock,
291-
#[cfg(with_testing)]
292290
AllCategories,
293291
}
294292

@@ -300,8 +298,6 @@ impl ResourceControlPolicyConfig {
300298
#[cfg(with_testing)]
301299
ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
302300
#[cfg(with_testing)]
303-
ResourceControlPolicyConfig::FuelAndBlock => ResourceControlPolicy::fuel_and_block(),
304-
#[cfg(with_testing)]
305301
ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
306302
}
307303
}

linera-core/src/client/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2515,6 +2515,13 @@ impl<Env: Environment> ChainClient<Env> {
25152515
owner: AccountOwner,
25162516
) -> Result<(Amount, Option<Amount>), ChainClientError> {
25172517
let incoming_bundles = self.pending_message_bundles().await?;
2518+
// Since we disallow empty blocks, and there is no incoming messages,
2519+
// that could change it, we query for the balance immediately.
2520+
if incoming_bundles.is_empty() {
2521+
let chain_balance = self.local_balance().await?;
2522+
let owner_balance = self.local_owner_balance(owner).await?;
2523+
return Ok((chain_balance, Some(owner_balance)));
2524+
}
25182525
let (previous_block_hash, height, timestamp) = {
25192526
let state = self.state();
25202527
(
@@ -2554,7 +2561,7 @@ impl<Env: Environment> ChainClient<Env> {
25542561
ChainExecutionContext::Block
25552562
) if matches!(
25562563
**execution_error,
2557-
ExecutionError::InsufficientFundingForFees { .. }
2564+
ExecutionError::FeesExceedFunding { .. }
25582565
)
25592566
) =>
25602567
{

0 commit comments

Comments
 (0)