Skip to content

Commit 9b6f65f

Browse files
Merge 9ff57d2 into 4fa3363
2 parents 4fa3363 + 9ff57d2 commit 9b6f65f

16 files changed

+540
-24
lines changed

Cargo.toml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
[package]
2-
32
name = "minimal-rollup"
43
version = "0.1.0"
54
edition = "2021"
65

6+
[lib]
7+
name = "minimal_rollup"
8+
path = "offchain/lib.rs"
9+
710
[[bin]]
811
name = "signal_slot"
912
path = "offchain/signal_slot.rs"
1013

11-
[[bin]]
12-
name = "utils"
13-
path = "offchain/utils.rs"
14-
1514
[[bin]]
1615
name = "sample_signal_proof"
1716
path = "offchain/sample_signal_proof.rs"
File renamed without changes.

offchain/sample_deposit_proof.rs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
1+
use alloy::primitives::utils::parse_units;
2+
use alloy::sol_types::SolCall;
13
use eyre::Result;
24

35
mod signal_slot;
46
use signal_slot::get_signal_slot;
57

6-
mod utils;
7-
use utils::{deploy_eth_bridge, deploy_signal_service, get_proofs, get_provider, SignalProof};
8+
use minimal_rollup::{
9+
deploy_eth_bridge, deploy_signal_service, get_proofs, get_provider, SignalProof,
10+
};
811

9-
use alloy::hex::decode;
12+
use alloy::hex::{self, decode};
1013
use alloy::primitives::{address, Address, Bytes, FixedBytes, U256};
1114
use std::fs;
1215

16+
use alloy::sol;
17+
18+
sol! {
19+
function somePayableFunction(uint256 someArg) external payable returns (uint256);
20+
function someNonpayableFunction(uint256 someArg) external returns (uint256);
21+
}
22+
1323
fn expand_vector(vec: Vec<Bytes>, name: &str) -> String {
1424
let mut expanded = String::new();
1525
for (i, item) in vec.iter().enumerate() {
@@ -20,7 +30,7 @@ fn expand_vector(vec: Vec<Bytes>, name: &str) -> String {
2030
return expanded;
2131
}
2232

23-
fn create_deposit_call(
33+
pub fn create_deposit_call(
2434
proof: SignalProof,
2535
nonce: usize,
2636
signer: Address,
@@ -69,13 +79,31 @@ fn deposit_specification() -> Vec<DepositSpecification> {
6979
// In this case, the CrossChainDepositExists.sol test case defines makeAddr("recipient");
7080
let recipient = "0x006217c47ffA5Eb3F3c92247ffFE22AD998242c5";
7181
// Use both zero and non-zero amounts (in this case 4 ether)
72-
let amounts = vec![0_u128, 4000000000000000000_u128];
82+
let amounts: Vec<U256> = vec![U256::ZERO, parse_units("4", "ether").unwrap().into()];
83+
7384
// Use different calldata to try different functions and inputs
85+
let valid_payable_function_call = somePayableFunctionCall {
86+
someArg: U256::from(1234),
87+
}
88+
.abi_encode();
89+
let invalid_payable_function_call = somePayableFunctionCall {
90+
someArg: U256::from(1235),
91+
}
92+
.abi_encode();
93+
let valid_nonpayable_function_call = someNonpayableFunctionCall {
94+
someArg: U256::from(1234),
95+
}
96+
.abi_encode();
97+
98+
let valid_payable_encoded = hex::encode(valid_payable_function_call);
99+
let invalid_payable_encoded = hex::encode(invalid_payable_function_call);
100+
let valid_nonpayable_encoded = hex::encode(valid_nonpayable_function_call);
101+
74102
let calldata = vec![
75-
"", // empty
76-
"9b28f6fb00000000000000000000000000000000000000000000000000000000000004d2", // (valid) call to somePayableFunction(1234)
77-
"9b28f6fb00000000000000000000000000000000000000000000000000000000000004d3", // (invalid) call to somePayableFunction(1235)
78-
"5932a71200000000000000000000000000000000000000000000000000000000000004d2", // (valid) call to `someNonPayableFunction(1234)`
103+
"",
104+
&valid_payable_encoded,
105+
&invalid_payable_encoded,
106+
&valid_nonpayable_encoded,
79107
];
80108

81109
// makeAddr("canceler");
@@ -126,6 +154,7 @@ async fn main() -> Result<()> {
126154
let deposits = deposit_specification();
127155
assert!(deposits.len() > 0, "No deposits to prove");
128156
let mut ids: Vec<FixedBytes<32>> = vec![];
157+
129158
// Perform all deposits
130159
for (_i, spec) in deposits.iter().enumerate() {
131160
let tx = eth_bridge
@@ -175,7 +204,7 @@ async fn main() -> Result<()> {
175204
.as_str();
176205
}
177206

178-
let template = fs::read_to_string("offchain/sample_deposit_proof.tmpl")?;
207+
let template = fs::read_to_string("offchain/tmpl/sample_deposit_proof.tmpl")?;
179208
let formatted = template
180209
.replace(
181210
"{signal_service_address}",

offchain/sample_signal_proof.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use eyre::Result;
33
mod signal_slot;
44
use signal_slot::get_signal_slot;
55

6-
mod utils;
7-
use utils::{deploy_signal_service, get_proofs, get_provider};
6+
use minimal_rollup::{deploy_signal_service, get_proofs, get_provider};
87

98
use alloy::primitives::Bytes;
109
use std::fs;
@@ -32,7 +31,7 @@ async fn main() -> Result<()> {
3231
let proof = get_proofs(&provider, slot, &signal_service).await?;
3332

3433

35-
let template = fs::read_to_string("offchain/sample_proof.tmpl")?;
34+
let template = fs::read_to_string("offchain/tmpl/sample_proof.tmpl")?;
3635
let formatted = template
3736
.replace("{signal_service_address}", signal_service.address().to_string().as_str())
3837
.replace("{block_hash}", proof.block_hash.to_string().as_str())
@@ -47,4 +46,3 @@ async fn main() -> Result<()> {
4746
println!("{}", formatted);
4847
Ok(())
4948
}
50-
File renamed without changes.
File renamed without changes.

src/protocol/IMessageRelayer.sol

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ interface IMessageRelayer {
1919
/// @dev Message forwarding failed
2020
error MessageForwardingFailed();
2121

22+
/// @dev Value sent is lower than tip amount
23+
error InsufficientValue();
24+
2225
/// @dev Tip transfer failed
2326
error TipTransferFailed();
2427

src/protocol/taiko_alethia/MessageRelayer.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ contract MessageRelayer is ReentrancyGuardTransient, IMessageRelayer {
9292
require(tipRecipient != address(0), NoTipRecipient());
9393
}
9494

95+
require(msg.value >= tip, InsufficientValue());
9596
uint256 valueToSend = msg.value - tip;
9697
bool forwardMessageSuccess;
9798

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.28;
3+
4+
import {GenericRecipient} from "./GenericRecipient.t.sol";
5+
import {InitialState} from "./InitialState.t.sol";
6+
7+
import {IETHBridge} from "src/protocol/IETHBridge.sol";
8+
import {IMessageRelayer} from "src/protocol/IMessageRelayer.sol";
9+
10+
abstract contract DepositRecipientScenarios is InitialState {}
11+
12+
contract DepositRecipientIsMessageRelayer is DepositRecipientScenarios {
13+
function test_DepositRecipientIsMessageRelayer_relayMessage_shouldInvokeReceiveMessage() public ifRelaySucceeds {
14+
vm.expectCall(address(messageRelayer), ethDeposit.data);
15+
_relayMessage();
16+
}
17+
18+
function test_DepositRecipientIsMessageRelayer_claimDeposit_shouldInvokeReceiveMessage() public ifClaimSucceeds {
19+
vm.expectCall(address(messageRelayer), ethDeposit.data);
20+
_claimDeposit();
21+
}
22+
}
23+
24+
contract DepositRecipientIsNotMessageRelayer is DepositRecipientScenarios {
25+
function setUp() public override {
26+
super.setUp();
27+
// bypass the relayer and send the message directly to the recipient
28+
// do not bother changing the default message encoding (to a `receiveMessage` function)
29+
// because the recipient handles any message
30+
ethDeposit.to = address(to);
31+
}
32+
33+
function test_DepositRecipientIsNotMessageRelayer_relayMessage_shouldInvokeRecipient() public {
34+
vm.expectEmit();
35+
emit GenericRecipient.FunctionCalled();
36+
_relayMessage();
37+
}
38+
39+
function test_DepositRecipientIsNotMessageRelayer_relayMessage_shouldNotInvokeReceiveMessage() public {
40+
vm.expectCall(address(messageRelayer), ethDeposit.data, 0);
41+
_relayMessage();
42+
}
43+
}
44+
45+
// A valid scenario that can be used as a default scenario by unrelated tests.
46+
abstract contract DefaultRecipientScenario is DepositRecipientScenarios {}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.28;
3+
4+
import {GenericRecipient} from "./GenericRecipient.t.sol";
5+
import {DefaultTipRecipientScenario} from "./TipRecipientScenarios.t.sol";
6+
import {IMessageRelayer} from "src/protocol/IMessageRelayer.sol";
7+
8+
import {InitialState} from "./InitialState.t.sol";
9+
import {IETHBridge} from "src/protocol/IETHBridge.sol";
10+
11+
abstract contract FundAmountScenarios is DefaultTipRecipientScenario {
12+
function test_FundAmountScenarios_relayMessage_shouldInvokeRecipient() public ifRelaySucceeds {
13+
vm.expectEmit();
14+
emit GenericRecipient.FunctionCalled();
15+
_relayMessage();
16+
}
17+
18+
function test_FundAmountScenarios_relayMessage_shouldNotRetainFundsInRelayer() public ifRelaySucceeds {
19+
assertEq(address(messageRelayer).balance, 0, "relayer should not have funds");
20+
_relayMessage();
21+
assertEq(address(messageRelayer).balance, 0, "relayer should not retain funds");
22+
}
23+
24+
function test_FundAmountScenarios_relayMessage_shouldSendAmountToRecipient() public ifRelaySucceeds {
25+
uint256 balanceBefore = address(to).balance;
26+
uint256 transferAmount = ethDeposit.amount - tip;
27+
_relayMessage();
28+
assertEq(address(to).balance, balanceBefore + transferAmount, "recipient balance mismatch");
29+
}
30+
31+
function redundant_FundAmountScenarios_relayMessage_shouldSendTipToRecipient() public {
32+
// This test (if it were implemented) would be redundant with the tip recipient scenarios
33+
// It is included for completeness, so this file accounts for all the distributed funds
34+
}
35+
}
36+
37+
contract AmountExceedsTip is FundAmountScenarios {}
38+
39+
contract NoAmountNoTip is FundAmountScenarios {
40+
function setUp() public override {
41+
super.setUp();
42+
ethDeposit.amount = 0;
43+
tip = 0;
44+
_encodeReceiveCall();
45+
}
46+
}
47+
48+
contract NoAmountNonzeroTip is FundAmountScenarios {
49+
function setUp() public override {
50+
super.setUp();
51+
ethDeposit.amount = 0;
52+
relayShouldSucceed = false;
53+
claimShouldSucceed = false;
54+
}
55+
}
56+
57+
contract AmountLessThanTip is FundAmountScenarios {
58+
function setUp() public override {
59+
super.setUp();
60+
ethDeposit.amount = tip - 1 wei;
61+
relayShouldSucceed = false;
62+
claimShouldSucceed = false;
63+
}
64+
}
65+
66+
// A valid scenario that can be used as a default scenario by unrelated tests.
67+
abstract contract DefaultFundAmountScenario is AmountExceedsTip {}

0 commit comments

Comments
 (0)