diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 7b496bc02..0d57c79ea 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -141,6 +141,7 @@ pub enum HostFunctions { StringNewFromLinearMemory, StrKeyToAddr, GetLedgerTimestamp, + GetLedgerSequence, GetCurrentContractAddress, BytesNewFromLinearMemory, BytesLen, @@ -189,6 +190,7 @@ impl HostFunctions { HostFunctions::VecPushBack => "v.6", HostFunctions::StringNewFromLinearMemory => "b.i", HostFunctions::StrKeyToAddr => "a.1", + HostFunctions::GetLedgerSequence => "x.3", HostFunctions::GetLedgerTimestamp => "x.4", HostFunctions::GetCurrentContractAddress => "x.7", HostFunctions::BytesNewFromLinearMemory => "b.3", diff --git a/src/emit/soroban/mod.rs b/src/emit/soroban/mod.rs index dab19fba5..073811af4 100644 --- a/src/emit/soroban/mod.rs +++ b/src/emit/soroban/mod.rs @@ -118,6 +118,7 @@ impl HostFunctions { .fn_type(&[ty.into(), ty.into()], false), HostFunctions::StrKeyToAddr => bin.context.i64_type().fn_type(&[ty.into()], false), HostFunctions::GetLedgerTimestamp => bin.context.i64_type().fn_type(&[], false), + HostFunctions::GetLedgerSequence => bin.context.i64_type().fn_type(&[], false), HostFunctions::GetCurrentContractAddress => bin.context.i64_type().fn_type(&[], false), HostFunctions::ObjToI128Lo64 => bin.context.i64_type().fn_type(&[ty.into()], false), HostFunctions::ObjToI128Hi64 => bin.context.i64_type().fn_type(&[ty.into()], false), @@ -467,6 +468,7 @@ impl SorobanTarget { HostFunctions::StringNewFromLinearMemory, HostFunctions::StrKeyToAddr, HostFunctions::GetLedgerTimestamp, + HostFunctions::GetLedgerSequence, HostFunctions::GetCurrentContractAddress, HostFunctions::BytesNewFromLinearMemory, HostFunctions::BytesCopyToLinearMemory, diff --git a/src/emit/soroban/target.rs b/src/emit/soroban/target.rs index d37527733..8db2707d0 100644 --- a/src/emit/soroban/target.rs +++ b/src/emit/soroban/target.rs @@ -1063,6 +1063,36 @@ impl<'a> TargetRuntime<'a> for SorobanTarget { value.into() } + Expression::Builtin { + loc, + tys, + kind: Builtin::BlockNumber, + args, + } => { + let function_name = HostFunctions::GetLedgerSequence.name(); + let function_value = + runtime_helper(bin, function_name, "reading Soroban ledger sequence number/block.number"); + + let u32_val = bin + .builder + .build_call(function_value, &[], function_name) + .unwrap() + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); + + return bin + .builder + .build_right_shift( + u32_val, + bin.context.i64_type().const_int(32, false), + false, + "block", + ) + .unwrap() + .into(); + } _ => unsupported_soroban(expr.loc(), "this Soroban builtin"), } } diff --git a/tests/soroban_testcases/block_number.rs b/tests/soroban_testcases/block_number.rs new file mode 100644 index 000000000..ea687e051 --- /dev/null +++ b/tests/soroban_testcases/block_number.rs @@ -0,0 +1,25 @@ +use crate::build_solidity; +use soroban_sdk::testutils::Ledger; +use soroban_sdk::{FromVal, Val}; + +#[test] +fn get_ledger_sequence() { + let runtime = build_solidity( + r#" + contract LedgerSequence { + function get_ledger_sequence() public view returns (uint64) { + return block.number; + } + } + "#, + |_| {}, + ); + let addr = runtime.contracts.last().unwrap(); + let samples: Vec = vec![1, 99, 2, 33, 13, 9, 15, 10001, 2 << 16, 2 << 24]; + for number in samples { + runtime.env.ledger().set_sequence_number(number as u32); + let block_number_val: Val = runtime.invoke_contract(addr, "get_ledger_sequence", vec![]); + let block_number_u64: u64 = FromVal::from_val(&runtime.env, &block_number_val); + assert_eq!(number, block_number_u64); + } +} diff --git a/tests/soroban_testcases/mod.rs b/tests/soroban_testcases/mod.rs index e1df43ec2..6530098bf 100644 --- a/tests/soroban_testcases/mod.rs +++ b/tests/soroban_testcases/mod.rs @@ -3,6 +3,7 @@ mod alloc; mod array_args; mod atomic_swap; mod auth; +mod block_number; mod constructor; mod cross_contract_calls; mod events;