diff --git a/zilliqa/src/cfg.rs b/zilliqa/src/cfg.rs
index d702d8835..8e138d0a0 100644
--- a/zilliqa/src/cfg.rs
+++ b/zilliqa/src/cfg.rs
@@ -433,6 +433,7 @@ impl Default for Forks {
vec![Fork {
at_height: 0,
failed_scilla_call_from_gas_exempt_caller_causes_revert: true,
+ call_mode_1_sets_caller_to_parent_caller: true,
}]
.try_into()
.unwrap()
@@ -460,6 +461,16 @@ pub struct Fork {
/// precompile and the inner Scilla call fails, the entire transaction will revert. If false, the normal EVM
/// semantics apply where the caller can decide how to act based on the success of the inner call.
pub failed_scilla_call_from_gas_exempt_caller_causes_revert: bool,
+ /// If true, if a call is made to the `scilla_call` precompile with `call_mode` / `keep_origin` set to `1`, the
+ /// `_sender` of the inner Scilla call will be set to the caller of the current call-stack. If false, the `_sender`
+ /// will be set to the original transaction signer.
+ ///
+ /// For example:
+ /// A (EOA) -> B (EVM) -> C (EVM) -> D (Scilla)
+ ///
+ /// When this flag is true, `D` will see the `_sender` as `B`. When this flag is false, `D` will see the `_sender`
+ /// as `A`.
+ pub call_mode_1_sets_caller_to_parent_caller: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
diff --git a/zilliqa/src/exec.rs b/zilliqa/src/exec.rs
index ab09823f2..26294ee77 100644
--- a/zilliqa/src/exec.rs
+++ b/zilliqa/src/exec.rs
@@ -429,6 +429,9 @@ pub struct ExternalContext<'a, I> {
pub scilla_call_gas_exempt_addrs: &'a [Address],
// This flag is only used for zq1 whitelisted contracts, and it's used to detect if the entire transaction should be marked as failed
pub enforce_transaction_failure: bool,
+ /// The caller of each call in the call-stack. This is needed because the `scilla_call` precompile needs to peek
+ /// into the call-stack. This will always be non-empty and the first entry will be the transaction signer.
+ pub callers: Vec
,
}
impl> GetInspector for ExternalContext<'_, I> {
@@ -520,6 +523,7 @@ impl State {
fork: self.forks.get(current_block.number),
scilla_call_gas_exempt_addrs: &self.scilla_call_gas_exempt_addrs,
enforce_transaction_failure: false,
+ callers: vec![from_addr],
};
let pending_state = PendingState::new(self.clone());
let mut evm = Evm::builder()
diff --git a/zilliqa/src/precompiles/scilla.rs b/zilliqa/src/precompiles/scilla.rs
index 5c6f0bb37..ac518cc64 100644
--- a/zilliqa/src/precompiles/scilla.rs
+++ b/zilliqa/src/precompiles/scilla.rs
@@ -335,9 +335,48 @@ impl ContextStatefulPrecompile for ScillaRead {
pub fn scilla_call_handle_register(
handler: &mut EvmHandler<'_, ExternalContext, PendingState>,
) {
+ // Create handler
+ let prev_handle = handler.execution.create.clone();
+ handler.execution.create = Arc::new(move |ctx, inputs| {
+ // Reserve enough space to store the caller.
+ ctx.external.callers.reserve(
+ (ctx.evm.journaled_state.depth + 1).saturating_sub(ctx.external.callers.len()),
+ );
+ for _ in ctx.external.callers.len()..(ctx.evm.journaled_state.depth + 1) {
+ ctx.external.callers.push(Address::ZERO);
+ }
+ ctx.external.callers[ctx.evm.journaled_state.depth] = inputs.caller;
+
+ prev_handle(ctx, inputs)
+ });
+
+ // EOF create handler
+ let prev_handle = handler.execution.eofcreate.clone();
+ handler.execution.eofcreate = Arc::new(move |ctx, inputs| {
+ // Reserve enough space to store the caller.
+ ctx.external.callers.reserve(
+ (ctx.evm.journaled_state.depth + 1).saturating_sub(ctx.external.callers.len()),
+ );
+ for _ in ctx.external.callers.len()..(ctx.evm.journaled_state.depth + 1) {
+ ctx.external.callers.push(Address::ZERO);
+ }
+ ctx.external.callers[ctx.evm.journaled_state.depth] = inputs.caller;
+
+ prev_handle(ctx, inputs)
+ });
+
// Call handler
let prev_handle = handler.execution.call.clone();
handler.execution.call = Arc::new(move |ctx, inputs| {
+ // Reserve enough space to store the caller.
+ ctx.external.callers.reserve(
+ (ctx.evm.journaled_state.depth + 1).saturating_sub(ctx.external.callers.len()),
+ );
+ for _ in ctx.external.callers.len()..(ctx.evm.journaled_state.depth + 1) {
+ ctx.external.callers.push(Address::ZERO);
+ }
+ ctx.external.callers[ctx.evm.journaled_state.depth] = inputs.caller;
+
if inputs.bytecode_address != Address::from(*b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0ZIL\x53") {
return prev_handle(ctx, inputs);
}
@@ -502,7 +541,16 @@ fn scilla_call_precompile(
scilla,
evmctx.env.tx.caller,
if keep_origin {
- evmctx.env.tx.caller
+ if external_context
+ .fork
+ .call_mode_1_sets_caller_to_parent_caller
+ {
+ // Use the caller of the parent call-stack.
+ external_context.callers[evmctx.journaled_state.depth - 1]
+ } else {
+ // Use the original transaction signer.
+ evmctx.env.tx.caller
+ }
} else {
input.caller
},
diff --git a/zilliqa/tests/it/contracts/ScillaInterop.sol b/zilliqa/tests/it/contracts/ScillaInterop.sol
index a4f4f8657..5c4707d77 100644
--- a/zilliqa/tests/it/contracts/ScillaInterop.sol
+++ b/zilliqa/tests/it/contracts/ScillaInterop.sol
@@ -7,6 +7,35 @@ library ScillaConnector {
uint private constant SCILLA_CALL_PRECOMPILE_ADDRESS = 0x5a494c53;
uint private constant SCILLA_STATE_READ_PRECOMPILE_ADDRESS = 0x5a494c92;
+ /**
+ * @dev Calls a ZRC2 contract function with two arguments
+ * @param target The address of the ZRC2 contract
+ * @param tran_name The name of the function to call
+ */
+ function call(address target, string memory tran_name) internal {
+ bytes memory encodedArgs = abi.encode(
+ target,
+ tran_name,
+ CALL_SCILLA_WITH_THE_SAME_SENDER
+ );
+ uint256 argsLength = encodedArgs.length;
+
+ assembly {
+ let ok := call(
+ gas(),
+ SCILLA_CALL_PRECOMPILE_ADDRESS,
+ 0,
+ add(encodedArgs, 0x20),
+ argsLength,
+ 0x20,
+ 0
+ )
+ if iszero(ok) {
+ revert(0, 0)
+ }
+ }
+ }
+
/**
* @dev Calls a ZRC2 contract function with two arguments
* @param target The address of the ZRC2 contract
@@ -315,6 +344,13 @@ contract ScillaInterop {
return scillaContract.readNestedMapUint128(varName, key1, key2);
}
+ function callScillaNoArgs(
+ address scillaContract,
+ string memory transitionName
+ ) public {
+ scillaContract.call(transitionName);
+ }
+
function callScilla(
address scillaContract,
string memory transitionName,
diff --git a/zilliqa/tests/it/zil.rs b/zilliqa/tests/it/zil.rs
index 21e47c8eb..452d640c6 100644
--- a/zilliqa/tests/it/zil.rs
+++ b/zilliqa/tests/it/zil.rs
@@ -1098,6 +1098,11 @@ async fn scilla_precompiles(mut network: Network) {
e = {_eventname : "Inserted"; a : a; b : b};
event e
end
+
+ transition LogSender()
+ e = {_eventname : "LogSender"; sender : _sender};
+ event e
+ end
"#;
let data = r#"[
@@ -1129,6 +1134,7 @@ async fn scilla_precompiles(mut network: Network) {
)
.await;
let receipt = wallet.get_transaction_receipt(hash).await.unwrap().unwrap();
+ let evm_contract_address = receipt.contract_address.unwrap();
let read = |fn_name, var_name: &'_ str, keys: &[H160], ty| {
let abi = &abi;
@@ -1201,7 +1207,7 @@ async fn scilla_precompiles(mut network: Network) {
Token::Uint(5.into()),
];
let tx = TransactionRequest::new()
- .to(receipt.contract_address.unwrap())
+ .to(evm_contract_address)
.data(function.encode_input(input).unwrap())
.gas(84_000_000);
@@ -1262,6 +1268,34 @@ async fn scilla_precompiles(mut network: Network) {
.into_uint()
.unwrap();
assert_eq!(val, 5.into());
+
+ // Construct a transaction which logs the `_sender` from the Scilla call.
+ let function = abi.function("callScillaNoArgs").unwrap();
+ let input = &[
+ Token::Address(scilla_contract_address),
+ Token::String("LogSender".to_owned()),
+ ];
+ let tx = TransactionRequest::new()
+ .to(evm_contract_address)
+ .data(function.encode_input(input).unwrap())
+ .gas(84_000_000);
+
+ // Run the transaction.
+ let tx_hash = wallet.send_transaction(tx, None).await.unwrap().tx_hash();
+ let receipt = network.run_until_receipt(&wallet, tx_hash, 100).await;
+ assert_eq!(receipt.logs.len(), 1);
+ let log = &receipt.logs[0];
+ let data = ethabi::decode(&[ParamType::String], &log.data).unwrap()[0]
+ .clone()
+ .into_string()
+ .unwrap();
+ let scilla_log: Value = serde_json::from_str(&data).unwrap();
+ assert_eq!(scilla_log["_eventname"], "LogSender");
+ assert_eq!(scilla_log["params"][0]["vname"], "sender");
+ assert_eq!(
+ scilla_log["params"][0]["value"],
+ format!("{:?}", wallet.address())
+ );
}
#[zilliqa_macros::test(restrict_concurrency)]