Skip to content

Commit 115833f

Browse files
authored
Merge pull request #439 from EspressoSystems/li/integration-nitro
Nitro-Timeboost integration Test
2 parents 3728a81 + 5c54360 commit 115833f

File tree

20 files changed

+619
-395
lines changed

20 files changed

+619
-395
lines changed

.github/workflows/build-and-test.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,40 @@ jobs:
124124
run: just run_demo -l -s /tmp/stamp --ignore-stamp --yapper -k test-configs/local-5.json
125125
- name: Run sailfish demo
126126
run: just run_sailfish_demo
127+
nitro-timeboost-integration:
128+
runs-on: ubuntu-latest
129+
timeout-minutes: 60
130+
steps:
131+
- uses: actions/checkout@v4
132+
with:
133+
submodules: recursive
134+
fetch-depth: 0
135+
- name: Install protobuf compiler
136+
run: |
137+
sudo apt-get update
138+
sudo apt-get install -y protobuf-compiler
139+
- name: Install Just
140+
run: |
141+
wget https://github.com/casey/just/releases/download/1.14.0/just-1.14.0-x86_64-unknown-linux-musl.tar.gz
142+
tar -vxf just-1.14.0-x86_64-unknown-linux-musl.tar.gz just
143+
sudo cp just /usr/bin/just
144+
- name: Clone Espresso Testnode Repository
145+
run: |
146+
git clone --recursive https://github.com/EspressoSystems/decentralized-timeboost-nitro-testnode.git
147+
- name: Run Test Node with Timeboost
148+
run: |
149+
cd decentralized-timeboost-nitro-testnode
150+
chmod +x test-node.bash
151+
./test-node.bash \
152+
--build-dev-nitro \
153+
--batchposters 0 \
154+
--redundantsequencers 1 \
155+
--decentralized-timeboost \
156+
--init &
157+
cd ..
158+
- name: Run test timeboost with nitro sequencer
159+
run: just run_demo -s /tmp/stamp --ignore-stamp -k test-configs/local-2.json --rounds 10000 --yapper --nitro
160+
- name: Verify sequencer blocks
161+
run: |
162+
just verify_blocks
163+

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ rust-version = "1.85.0"
2828
[workspace.dependencies]
2929
aes-gcm = { version = "0.10.3" }
3030
alloy = { version = "1.0", features = ["default", "arbitrary", "k256", "serde", "rlp"] }
31+
alloy-signer = "1.0"
3132
alloy-chains = "0.2"
3233
# derive feature is not exposed via `alloy`, thus has to explicitly declare here
3334
alloy-rlp = { version = "0.3.12", features = ["derive"] }

justfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ mkconfig_docker_full NUM_NODES RPC_URL PARENT_CHAIN_ID PARENT_INBOX_ADDRESS *ARG
109109
--parent-ibox-contr-addr {{PARENT_INBOX_ADDRESS}} \
110110
--mode "increment-address" {{ARGS}} | jq
111111

112+
verify_blocks:
113+
./scripts/verify-blocks
114+
112115
####################
113116
####TEST COMMANDS###
114117
####################

scripts/run-timeboost-demo

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ keyset_file=
1515
tps=1
1616
stamp=
1717
ignore_stamp=false
18-
nitro_node_url=
18+
nitro_enabled=false
1919
namespace=10101
2020
yapper=false
2121

@@ -37,9 +37,9 @@ while [[ $# -gt 0 ]]; do
3737
rounds="$2"
3838
shift 2
3939
;;
40-
-u|--url)
41-
nitro_node_url="$2"
42-
shift 2
40+
--nitro)
41+
nitro_enabled=true
42+
shift
4343
;;
4444
-s|--stamp)
4545
stamp="$2"
@@ -74,12 +74,12 @@ Options:
7474
-t | --tps <number>
7575
number of transactions per second to generate.
7676
77-
-u | --url <URL>
78-
Nitro URL.
79-
8077
-s | --stamp <PATH prefix>
8178
Path prefix for the stamp files.
8279
80+
--nitro
81+
Local Nitro node is running.
82+
8383
--yapper
8484
Run external transaction generator.
8585
@@ -100,12 +100,12 @@ if [ ! $keyset_file ]; then
100100
exit 1
101101
fi
102102

103-
if [ -n "$nitro_node_url" ]; then
104-
docker run --rm --name nitro-dev -p 8547:8547 "offchainlabs/nitro-node:v3.2.1-d81324d" --dev --http.addr 0.0.0.0 --http.api=net,web3,eth,debug &
105-
timeout 30 bash -c 'until curl -sSf http://localhost:8547 -o /dev/null; do sleep 1; done' || exit 1
103+
just build_release --features="until"
104+
105+
if $nitro_enabled; then
106+
timeout 90 bash -c 'until curl -sSf http://localhost:8547 -o /dev/null; do sleep 1; done' || exit 1
106107
fi
107108

108-
just build_release --features="until"
109109

110110
nodes=$(jq '.keyset | length' $keyset_file)
111111

@@ -124,7 +124,7 @@ for (( i=0; i<$nodes; i++ )); do
124124
--until $rounds
125125
--keyset-file $keyset_file
126126
--stamp "${stamp}-$i.sf"
127-
--watchdog-timeout 100
127+
--watchdog-timeout 120
128128
--namespace $namespace)
129129

130130
if $ignore_stamp; then
@@ -135,18 +135,20 @@ for (( i=0; i<$nodes; i++ )); do
135135
cmd+=(--late-start --late-start-node-id 0)
136136
fi
137137

138-
if [ -n "$nitro_node_url" ]; then
139-
cmd+=(--nitro-node-url $nitro_node_url)
140-
fi
141-
142138
echo "${cmd[@]}"
143139
RUST_LOG=$RUST_LOG "${cmd[@]}" &
144140

145141
pids+=($!)
146142
done
147143

148144
if $yapper; then
149-
target/release/yapper --tps $tps --keyset-file $keyset_file &
145+
cmd=(target/release/yapper
146+
--tps $tps
147+
--keyset-file $keyset_file)
148+
if $nitro_enabled; then
149+
cmd+=(--nitro-integration)
150+
fi
151+
"${cmd[@]}" &
150152
fi
151153

152154
wait "${pids[@]}"

scripts/verify-blocks

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
# Configuration
5+
RPC1="http://localhost:8547"
6+
RPC2="http://localhost:8647"
7+
8+
function rpc_call() {
9+
local endpoint=$1
10+
local method=$2
11+
local params=$3
12+
curl -s -X POST \
13+
-H "Content-Type: application/json" \
14+
-d "{\"jsonrpc\":\"2.0\",\"method\":\"$method\",\"params\":$params,\"id\":1}" \
15+
$endpoint
16+
}
17+
18+
function get_latest_block() {
19+
local response=$(rpc_call $RPC1 "eth_blockNumber" "[]")
20+
local block_hex=$(echo "$response" | jq -r .result)
21+
printf "%d" $((block_hex))
22+
}
23+
24+
function verify_block() {
25+
local block_num=$1
26+
local block_hex=$(printf "0x%x" $block_num)
27+
28+
echo -n "Verifying block $block_num (0x${block_hex#0x})..."
29+
echo
30+
31+
block1=$(rpc_call $RPC1 "eth_getBlockByNumber" "[\"$block_hex\", true]")
32+
block2=$(rpc_call $RPC2 "eth_getBlockByNumber" "[\"$block_hex\", true]")
33+
34+
if [ "$(echo "$block1" | jq -r .result)" == "null" ] || [ "$(echo "$block2" | jq -r .result)" == "null" ]; then
35+
echo "❌ Block not found on one or both endpoints!"
36+
return 1
37+
fi
38+
39+
hash1=$(echo "$block1" | jq -r .result.hash)
40+
hash2=$(echo "$block2" | jq -r .result.hash)
41+
42+
if [ "$hash1" != "$hash2" ]; then
43+
echo "❌ Hash mismatch! $RPC1: $hash1, $RPC2: $hash2"
44+
return 1
45+
fi
46+
47+
tx_count=$(echo "$block1" | jq -r '.result.transactions | length')
48+
echo -n " $tx_count tx..."
49+
echo
50+
51+
for ((i=0; i<tx_count; i++)); do
52+
tx_hash1=$(echo "$block1" | jq -r .result.transactions[$i].hash)
53+
tx_hash2=$(echo "$block2" | jq -r .result.transactions[$i].hash)
54+
55+
# Verify transaction order and hash match
56+
if [ "$tx_hash1" != "$tx_hash2" ]; then
57+
echo "❌ Transaction order mismatch at position $i!"
58+
echo " $RPC1: $tx_hash1"
59+
echo " $RPC2: $tx_hash2"
60+
return 1
61+
fi
62+
63+
tx1=$(rpc_call $RPC1 "eth_getTransactionByHash" "[\"$tx_hash1\"]")
64+
tx2=$(rpc_call $RPC2 "eth_getTransactionByHash" "[\"$tx_hash2\"]")
65+
66+
if [ "$tx1" != "$tx2" ]; then
67+
echo "❌ Tx content mismatch for $tx_hash1!"
68+
return 1
69+
fi
70+
printf "✅ Tx content match for $tx_hash1!"
71+
echo
72+
done
73+
echo
74+
return 0
75+
}
76+
77+
echo "=== Starting Chain Verification ==="
78+
latest_block=$(get_latest_block)
79+
echo "Latest block: $latest_block"
80+
81+
for ((block=0; block<=latest_block; block++)); do
82+
if ! verify_block $block; then
83+
echo "❌ Failed for block $block"
84+
exit 1
85+
fi
86+
done
87+
88+
echo "=== Verification Complete ==="
89+
echo "Successfully verified $((latest_block+1)) blocks"

test-configs/local-2.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"keyset": [
3+
{
4+
"sailfish_address": "127.0.0.1:8000",
5+
"decrypt_address": "127.0.0.1:10000",
6+
"certifier_address": "127.0.0.1:11000",
7+
"internal_address": "127.0.0.1:5000",
8+
"signing_key": "uN7ynPoRK7RMWqQLdYbDwTp46Ddx7Eo67sfGuACyzXJL",
9+
"dh_key": "HHf57RWufSGUkxjesR6byyirsTutzSYDD1N4CLr6iXow",
10+
"chain_config": {
11+
"parent_chain_id": 1337,
12+
"parent_chain_rpc_url": "http://localhost:8545",
13+
"parent_ibox_contr_addr": "0xA0f3A1a4E2B2Bcb7b48C8527C28098f207572EC1",
14+
"parent_block_tag": "latest"
15+
},
16+
"enc_key": "8sqKttSTfCVQAMztTivXPnojGRSoLi1SCsVksvG1UeUb5jtFrveGn7gtifndeJ9h8Gf",
17+
"private": {
18+
"signing_key": "CvsiyNPUVMT9z7KK8a5E1qedyctHJbsVDD7vWk2BwenA",
19+
"dh_key": "CZBrm6uQCF7NZH9uhW7hwYr1kwRU83ax4EZsDKi6evBX",
20+
"dec_key": "Akm2Zdo5jxgrjjKsssz1zq544BpQA7HLNwFTmc9R95xgL"
21+
},
22+
"nitro_addr": "localhost:55000"
23+
},
24+
{
25+
"sailfish_address": "127.0.0.1:8001",
26+
"decrypt_address": "127.0.0.1:10001",
27+
"certifier_address": "127.0.0.1:11001",
28+
"internal_address": "127.0.0.1:5001",
29+
"signing_key": "u75WidPfMiDopY1Q8r6YoV6UroXJBQwirVC8TCvSaf3o",
30+
"dh_key": "AZzMix7qg9dNdyXCVGrSsnD6N8Xat3KLuLq3nj6gHQ97",
31+
"chain_config": {
32+
"parent_chain_id": 1337,
33+
"parent_chain_rpc_url": "http://localhost:8545",
34+
"parent_ibox_contr_addr": "0xA0f3A1a4E2B2Bcb7b48C8527C28098f207572EC1",
35+
"parent_block_tag": "latest"
36+
},
37+
"enc_key": "8s4GeqvzzwT7wt92cAE84kByHjaBN6rUX8JZW61SfTQGWtH2N1sT6EMT7z5D1mVuNh6",
38+
"private": {
39+
"signing_key": "HQQFWDXnReUV5cAUNJygcvBvMSJ2R3ud6MuKVCChNqeX",
40+
"dh_key": "5iNXUDM6KfJQPvqmRFx7vHZNQdULSpZU9t585m3HWv9m",
41+
"dec_key": "AgJ3t22a7QbCLhVRCoQxq5caAKmeGCR6JuFEnkwvNXoVQ"
42+
},
43+
"nitro_addr": "localhost:55001"
44+
}
45+
]
46+
}

tests/src/tests/timeboost.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,7 @@ where
108108
"https://theserversroom.com/ethereum/54cmzzhcj1o/"
109109
.parse::<Url>()
110110
.expect("valid url"),
111-
"0x4dbd4fc535ac27206064b68ffcf827b0a60bab3f"
112-
.parse::<alloy::primitives::Address>()
113-
.expect("valid contract"),
111+
alloy::primitives::Address::default(),
114112
BlockNumberOrTag::Finalized,
115113
))
116114
.build();

tests/src/tests/timeboost/handover.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,7 @@ where
156156
"https://theserversroom.com/ethereum/54cmzzhcj1o/"
157157
.parse::<Url>()
158158
.expect("valid url"),
159-
"0x4dbd4fc535ac27206064b68ffcf827b0a60bab3f"
160-
.parse::<alloy::primitives::Address>()
161-
.expect("valid contract"),
159+
alloy::primitives::Address::default(),
162160
BlockNumberOrTag::Finalized,
163161
))
164162
.build()

timeboost-sequencer/src/delayed_inbox.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ use tracing::{error, info, warn};
1414
use crate::queue::BundleQueue;
1515

1616
// Polling interval to check for next delayed inbox index
17-
const INBOX_DELAY: Duration = Duration::from_secs(60);
17+
const INBOX_DELAY: Duration = Duration::from_secs(10);
1818

1919
// Max lookback for `from_block` to `to_block` in our `Filter` request
20-
const MAX_BLOCK_LOOKBACK: u64 = 300;
20+
const MAX_BLOCK_LOOKBACK: u64 = 10000;
2121

2222
sol! {
2323
event InboxMessageDelivered(uint256 indexed messageNum, bytes data);

timeboost-sequencer/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ impl Task {
263263
let mut pending = None;
264264
let mut dkg_bundles = VecDeque::new();
265265
let mut candidates = Candidates::new();
266+
let mut index = DelayedInboxIndex::default();
266267

267268
if !self.sailfish.is_init() {
268269
let actions = self.sailfish.init();
@@ -315,7 +316,8 @@ impl Task {
315316
let timestamp = incl.timestamp();
316317
let delayed_inbox_index = incl.delayed_inbox_index();
317318
let transactions = self.sorter.sort(incl);
318-
if !transactions.is_empty() {
319+
if !transactions.is_empty() || index != delayed_inbox_index {
320+
index = delayed_inbox_index;
319321
let out = Output::Transactions { round, timestamp, transactions, delayed_inbox_index };
320322
self.output.send(out).await.map_err(|_| TimeboostError::ChannelClosed)?;
321323
}

0 commit comments

Comments
 (0)