Skip to content

Commit 271d714

Browse files
authored
Merge pull request #2452 from oasisprotocol/peternose/trivial/read-only-subcall
runtime-sdk/modules/evm/src/precompile/subcall: Support read-only mode
2 parents 6717812 + c34440d commit 271d714

12 files changed

Lines changed: 115 additions & 22 deletions

File tree

runtime-sdk/modules/contracts/src/results.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ fn process_subcalls<Cfg: Config, C: Context>(
138138
body,
139139
max_depth: params.max_subcall_depth,
140140
max_gas,
141+
read_only: false,
141142
},
142143
subcall::AllowAllValidator,
143144
)?;

runtime-sdk/modules/evm/src/precompile/confidential.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -396,8 +396,7 @@ mod test {
396396

397397
#[test]
398398
fn test_x25519_derive() {
399-
let mut rng = OsRng {};
400-
let static_secret = x25519_dalek::StaticSecret::random_from_rng(rng);
399+
let static_secret = x25519_dalek::StaticSecret::random_from_rng(OsRng);
401400
let public = x25519_dalek::PublicKey::from(&static_secret);
402401

403402
let mut blob = [0u8; 64];
@@ -457,8 +456,7 @@ mod test {
457456

458457
#[bench]
459458
fn bench_x25519_derive(b: &mut Bencher) {
460-
let mut rng = OsRng {};
461-
let static_secret = x25519_dalek::StaticSecret::random_from_rng(rng);
459+
let static_secret = x25519_dalek::StaticSecret::random_from_rng(OsRng);
462460
let public = x25519_dalek::PublicKey::from(&static_secret);
463461

464462
let mut blob = [0u8; 64];
@@ -480,8 +478,7 @@ mod test {
480478

481479
#[bench]
482480
fn bench_curve25519_compute_public(b: &mut Bencher) {
483-
let mut rng = OsRng {};
484-
let static_secret = x25519_dalek::StaticSecret::random_from_rng(rng);
481+
let static_secret = x25519_dalek::StaticSecret::random_from_rng(OsRng);
485482

486483
let mut blob = [0u8; 32];
487484
blob[..32].copy_from_slice(&static_secret.to_bytes());

runtime-sdk/modules/evm/src/precompile/subcall.rs

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ pub(super) fn call_subcall<B: EVMBackendExt>(
6363
// Cap maximum amount of gas that can be used.
6464
let max_gas = handle.remaining_gas();
6565

66+
// Ensure that the subcall is read-only and cannot modify state when
67+
// the precompile is called using a static call.
68+
let read_only = handle.is_static();
69+
6670
let result = backend
6771
.subcall(
6872
subcall::SubcallInfo {
@@ -71,6 +75,7 @@ pub(super) fn call_subcall<B: EVMBackendExt>(
7175
body,
7276
max_depth: 8,
7377
max_gas,
78+
read_only,
7479
},
7580
ForbidReentrancy,
7681
)
@@ -150,7 +155,7 @@ mod test {
150155
let dispatch_result = signer.call_evm(
151156
&ctx,
152157
contract_address.into(),
153-
solabi::selector!("test(bytes,bytes)"),
158+
solabi::selector!("test_call(bytes,bytes)"),
154159
&(
155160
solabi::Bytes("accounts.Transfer".as_bytes()),
156161
solabi::Bytes(cbor::to_vec(accounts::types::Transfer {
@@ -182,7 +187,7 @@ mod test {
182187
let dispatch_result = signer.call_evm_opts(
183188
&ctx,
184189
contract_address.into(),
185-
solabi::selector!("test(bytes,bytes)"),
190+
solabi::selector!("test_call(bytes,bytes)"),
186191
&(
187192
solabi::Bytes("accounts.Transfer".as_bytes()),
188193
solabi::Bytes(cbor::to_vec(accounts::types::Transfer {
@@ -233,7 +238,80 @@ mod test {
233238

234239
let events: Vec<GasUsedEvent> = cbor::from_slice(&tags[1].value).unwrap();
235240
assert_eq!(events.len(), 1); // Just one gas used event.
236-
assert_eq!(events[0].amount, 25742);
241+
assert_eq!(events[0].amount, 25738);
242+
}
243+
244+
#[test]
245+
fn test_read_only() {
246+
let mut mock = Mock::default();
247+
let ctx = mock.create_ctx_for_runtime::<TestRuntime>(false);
248+
let mut signer = EvmSigner::new(0, keys::dave::sigspec());
249+
250+
// Create contract.
251+
let contract_address = init_and_deploy_contract(&ctx, &mut signer, TEST_CONTRACT_CODE_HEX);
252+
253+
// Transfer some tokens to the contract.
254+
let dispatch_result = signer.call(
255+
&ctx,
256+
"accounts.Transfer",
257+
accounts::types::Transfer {
258+
to: TestConfig::map_address(contract_address),
259+
amount: BaseUnits::native(2_000),
260+
},
261+
);
262+
assert!(
263+
dispatch_result.result.is_success(),
264+
"transfer should succeed"
265+
);
266+
267+
// Call into the test contract.
268+
let dispatch_result = signer.call_evm_opts(
269+
&ctx,
270+
contract_address.into(),
271+
solabi::selector!("test_staticcall(bytes,bytes)"),
272+
&(
273+
solabi::Bytes("accounts.Transfer".as_bytes()),
274+
solabi::Bytes(cbor::to_vec(accounts::types::Transfer {
275+
to: keys::alice::address(),
276+
amount: BaseUnits::native(1_000),
277+
})),
278+
),
279+
CallOptions {
280+
fee: Fee {
281+
amount: BaseUnits::native(100),
282+
gas: 1_000_000,
283+
..Default::default()
284+
},
285+
..Default::default()
286+
},
287+
);
288+
assert!(
289+
!dispatch_result.result.is_success(),
290+
"call should fail as it is not read-only"
291+
);
292+
293+
// Call into test contract again with a normal call.
294+
let dispatch_result = signer.call_evm_opts(
295+
&ctx,
296+
contract_address.into(),
297+
solabi::selector!("test_call(bytes,bytes)"),
298+
&(
299+
solabi::Bytes("accounts.Transfer".as_bytes()),
300+
solabi::Bytes(cbor::to_vec(accounts::types::Transfer {
301+
to: keys::alice::address(),
302+
amount: BaseUnits::native(1_000),
303+
})),
304+
),
305+
CallOptions {
306+
fee: Fee {
307+
amount: BaseUnits::native(100),
308+
gas: 1_000_000,
309+
..Default::default()
310+
},
311+
..Default::default()
312+
},
313+
);
314+
assert!(dispatch_result.result.is_success(), "call should succeed");
237315
}
238316

239317
#[test]
@@ -285,14 +363,14 @@ mod test {
285363
let dispatch_result = signer.call_evm(
286364
&ctx,
287365
contract_address.into(),
288-
solabi::selector!("test(bytes,bytes)"),
366+
solabi::selector!("test_call(bytes,bytes)"),
289367
&(
290368
solabi::Bytes("evm.Call".as_bytes()),
291369
solabi::Bytes(cbor::to_vec(evm::types::Call {
292370
address: contract_address.into(),
293371
value: 0.into(),
294372
data: solabi::encode_with_selector(
295-
solabi::selector!("test(bytes,bytes)"),
373+
solabi::selector!("test_call(bytes,bytes)"),
296374
&(
297375
solabi::Bytes("accounts.Transfer".as_bytes()),
298376
solabi::Bytes(cbor::to_vec(accounts::types::Transfer {
@@ -339,7 +417,7 @@ mod test {
339417
let dispatch_result = signer.call_evm_opts(
340418
&ctx,
341419
contract_address.into(),
342-
solabi::selector!("test(bytes,bytes)"),
420+
solabi::selector!("test_call(bytes,bytes)"),
343421
&(
344422
solabi::Bytes("accounts.Transfer".as_bytes()),
345423
solabi::Bytes(cbor::to_vec(accounts::types::Transfer {
@@ -365,7 +443,7 @@ mod test {
365443
let dispatch_result = signer.call_evm_opts(
366444
&ctx,
367445
contract_address.into(),
368-
solabi::selector!("test(bytes,bytes)"),
446+
solabi::selector!("test_call(bytes,bytes)"),
369447
&(
370448
solabi::Bytes("accounts.Transfer".as_bytes()),
371449
solabi::Bytes(cbor::to_vec(accounts::types::Transfer {

runtime-sdk/modules/rofl-market/src/payment.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ impl PaymentMethod for Payment {
122122
}),
123123
max_depth: 8,
124124
max_gas: remaining_gas,
125+
read_only: false,
125126
},
126127
ForbidReentrancy,
127128
)?;
@@ -205,6 +206,7 @@ impl PaymentMethod for Payment {
205206
}),
206207
max_depth: 8,
207208
max_gas: remaining_gas,
209+
read_only: false,
208210
},
209211
ForbidReentrancy,
210212
)?;
@@ -300,6 +302,7 @@ impl PaymentMethod for Payment {
300302
}),
301303
max_depth: 8,
302304
max_gas: remaining_gas,
305+
read_only: false,
303306
},
304307
ForbidReentrancy,
305308
)?;

runtime-sdk/src/modules/access/test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,12 @@ impl TestModule {
9393
type Genesis = ();
9494

9595
#[handler(call = "test.FilteredMethod")]
96-
fn filtered_method<C: Context>(_ctx: &C, fail: bool) -> Result<u64, core::Error> {
96+
fn filtered_method<C: Context>(_ctx: &C, _fail: bool) -> Result<u64, core::Error> {
9797
Ok(42)
9898
}
9999

100100
#[handler(call = "test.AllowedMethod")]
101-
fn allowed_method<C: Context>(ctx: &C, _args: ()) -> Result<u64, core::Error> {
101+
fn allowed_method<C: Context>(_ctx: &C, _args: ()) -> Result<u64, core::Error> {
102102
Ok(42)
103103
}
104104
}

runtime-sdk/src/modules/accounts/test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ impl TestModule {
137137
body: cbor::to_value(false),
138138
max_depth: 8,
139139
max_gas,
140+
read_only: false,
140141
},
141142
subcall::AllowAllValidator,
142143
)?;

runtime-sdk/src/modules/core/test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ impl GasWasterModule {
274274

275275
#[handler(call = Self::METHOD_SPECIFIC_GAS_REQUIRED)]
276276
fn specific_gas_required<C: Context>(
277-
ctx: &C,
277+
_ctx: &C,
278278
_args: (),
279279
) -> Result<(), <GasWasterModule as module::Module>::Error> {
280280
// Fails with an error if less than X gas was specified. (doesn't fail with out-of-gas).
@@ -288,7 +288,7 @@ impl GasWasterModule {
288288

289289
#[handler(call = Self::METHOD_SPECIFIC_GAS_REQUIRED_HUGE)]
290290
fn specific_gas_required_huge<C: Context>(
291-
ctx: &C,
291+
_ctx: &C,
292292
_args: (),
293293
) -> Result<(), <GasWasterModule as module::Module>::Error> {
294294
// Fails with an error if less than X gas was specified. (doesn't fail with out-of-gas).

runtime-sdk/src/subcall.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ pub struct SubcallInfo {
4444
pub max_depth: u16,
4545
/// Maximum gas amount that can be consumed.
4646
pub max_gas: u64,
47+
/// Whether the subcall is read-only.
48+
pub read_only: bool,
4749
}
4850

4951
/// Result of dispatching a subcall.
@@ -140,7 +142,7 @@ pub fn call<C: Context, V: Validator + 'static>(
140142
format: transaction::CallFormat::Plain,
141143
method: info.method,
142144
body: info.body,
143-
..Default::default()
145+
read_only: info.read_only,
144146
},
145147
auth_info: transaction::AuthInfo {
146148
signer_info: vec![transaction::SignerInfo {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[{"inputs":[{"internalType":"uint64","name":"code","type":"uint64"},{"internalType":"bytes","name":"module","type":"bytes"}],"name":"SubcallFailed","type":"error"},{"inputs":[{"internalType":"bytes","name":"method","type":"bytes"},{"internalType":"bytes","name":"body","type":"bytes"}],"name":"test","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"test_consensus_round_root","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"method","type":"bytes"},{"internalType":"bytes","name":"body","type":"bytes"}],"name":"test_delegatecall","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"method","type":"bytes"},{"internalType":"bytes","name":"body","type":"bytes"}],"name":"test_spin","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"}]
1+
[{"inputs":[{"internalType":"uint64","name":"code","type":"uint64"},{"internalType":"bytes","name":"module","type":"bytes"}],"name":"SubcallFailed","type":"error"},{"inputs":[{"internalType":"bytes","name":"method","type":"bytes"},{"internalType":"bytes","name":"body","type":"bytes"}],"name":"test_call","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"test_consensus_round_root","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"method","type":"bytes"},{"internalType":"bytes","name":"body","type":"bytes"}],"name":"test_delegatecall","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"method","type":"bytes"},{"internalType":"bytes","name":"body","type":"bytes"}],"name":"test_spin","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"method","type":"bytes"},{"internalType":"bytes","name":"body","type":"bytes"}],"name":"test_staticcall","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"}]

0 commit comments

Comments
 (0)