Skip to content

Commit

Permalink
Update Bridge contract to new design (#1064)
Browse files Browse the repository at this point in the history
* bridge contract and test partial upgrade

* Cleanup and add events

* Refactor tx validation and inclusion

* op_return is operatorId change

* kickoff2Address calculation method change

* kickoff2Address merkle tree update

* Add extractKickoffRoot

* changed deposit func, and solved some bugs

* Make deposit amount non constant and remove sig count

* Fix natspec typo

* Internal operator id indexing

* removed output index from declareWithdrawFiller

* newest design

* deposit event update

* Fix input assertion for non-deposit tx bug

* Adapt test suite to new deposit scheme

* Add natspec improvements

* Adapt test suite to new bridge design and general improvements

* Update node tests

* Further node test fixes

* Variable packing in bridge contract

* Test fixes after improving gas costs

* Web3 library fixes

* Code typo fix

* Further web3 fixes

* Further test fixes

* Ethers test fix

* Natspec improvement to deposit

* Fix withdrawalFiller indexing bug

* Change script prefix in initialize call from node

* Sys tx test deposit data update

* Lint fix

* New data for `test_bridge`

* Sys tx test fixes

---------

Co-authored-by: erdemkan <[email protected]>
  • Loading branch information
okkothejawa and erdkocak authored Sep 5, 2024
1 parent 0ed6454 commit ad4e3ba
Show file tree
Hide file tree
Showing 18 changed files with 386 additions and 229 deletions.
18 changes: 9 additions & 9 deletions bin/citrea/tests/e2e/sequencer_behaviour.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ async fn transaction_failing_on_l1_is_removed_from_mempool() -> Result<(), anyho

let random_wallet_address = random_wallet.address();

let second_block_base_fee = 768641461;
let second_block_base_fee = 767970154;

let _pending = seq_test_client
.send_eth(
Expand Down Expand Up @@ -340,7 +340,7 @@ async fn test_gas_limit_too_high() {

let target_gas_limit: u64 = 30_000_000;
let transfer_gas_limit = 21_000;
let system_txs_gas_used = 390434;
let system_txs_gas_used = 300621;
let tx_count = (target_gas_limit - system_txs_gas_used).div_ceil(transfer_gas_limit);
let addr = Address::from_str("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266").unwrap();

Expand Down Expand Up @@ -518,28 +518,28 @@ async fn test_system_tx_effect_on_block_gas_limit() -> Result<(), anyhow::Error>

let seq_port = seq_port_rx.await.unwrap();
let seq_test_client = make_test_client(seq_port).await?;
// sys tx use L1BlockHash(50751 + 80720) + Bridge(261215) = 392686 gas
// sys tx use L1BlockHash(50751 + 80720) + Bridge(169150) = 300621 gas
// the block gas limit is 1_500_000 because the system txs gas limit is 1_500_000 (decided with @eyusufatik and @okkothejawa as bridge init takes 1M gas)

// 1500000 - 392686 = 1107314 gas left in block
// 1107314 / 21000 = 52,72... so 52 ether transfer transactions can be included in the block
// 1500000 - 300621 = 1177464 gas left in block
// 1107314 / 21000 = 57.13... so 57 ether transfer transactions can be included in the block

// send 52 ether transfer transactions
// send 57 ether transfer transactions
let addr = Address::from_str("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266").unwrap();

for _ in 0..51 {
for _ in 0..56 {
let _pending = seq_test_client
.send_eth(addr, None, None, None, 0u128)
.await
.unwrap();
}

// 52th tx should be the last tx in the soft confirmation
// 57th tx should be the last tx in the soft confirmation
let last_in_tx = seq_test_client
.send_eth(addr, None, None, None, 0u128)
.await;

// 53th tx should not be in soft confirmation
// 58th tx should not be in soft confirmation
let not_in_tx = seq_test_client
.send_eth(addr, None, None, None, 0u128)
.await;
Expand Down
66 changes: 42 additions & 24 deletions bin/citrea/tests/evm/ethers_js/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe("RpcTests", function() {
//Makes an initial tx to test for later, used to prevent waiting for a block to mine in each such test
before(async function() {
this.timeout(0);
let tx = await generateTransaction('0.01');
let tx = await generateTransaction('10');
let signer = new ethers.Wallet('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', provider);
tx = await signer.signTransaction(tx);
let tx_response = await provider.broadcastTransaction(tx);
Expand Down Expand Up @@ -49,23 +49,29 @@ describe("RpcTests", function() {
"name": "withdraw",
"inputs": [
{
"name": "bitcoin_address",
"name": "txId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "outputId",
"type": "bytes4",
"internalType": "bytes4"
}
],
"outputs": [],
"stateMutability": "payable"
},
}
];

const privateKey = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
const wallet = new ethers.Wallet(privateKey, provider);
const contractAddress = '0x3100000000000000000000000000000000000002';
const contract = new ethers.Contract(contractAddress, abi, wallet);
const bitcoinAddress = ethers.encodeBytes32String('bc1qa0a0a0a0a0a0a0a0a0a0a0a0');
const txId = ethers.encodeBytes32String('0x1234');
const outputId = ethers.zeroPadBytes(ethers.toUtf8Bytes('0x01'), 4);

let gasEstimate = await contract.withdraw.estimateGas(bitcoinAddress, {value: ethers.parseEther('0.01')});
let gasEstimate = await contract.withdraw.estimateGas(txId, outputId, {value: ethers.parseEther('10')});
expect(gasEstimate > 0n).to.be.true;
});

Expand Down Expand Up @@ -101,7 +107,7 @@ describe("RpcTests", function() {

it("broadcastTransaction publishes a txn and it gets mined", async function() {
this.timeout(0);
let tx = await generateTransaction('0.01');
let tx = await generateTransaction('10');
let signer = new ethers.Wallet('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', provider);
tx = await signer.signTransaction(tx);
let tx_response = await provider.broadcastTransaction(tx);
Expand Down Expand Up @@ -197,34 +203,40 @@ describe("RpcTests", function() {
"type": "function",
"name": "withdraw",
"inputs": [
{
"name": "bitcoin_address",
"type": "bytes32",
"internalType": "bytes32"
}
{
"name": "txId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "outputId",
"type": "bytes4",
"internalType": "bytes4"
}
],
"outputs": [],
"stateMutability": "payable"
},
}
];

const contractAddress = '0x3100000000000000000000000000000000000002';
let wallet = new ethers.Wallet('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', provider);
const contract = new ethers.Contract(contractAddress, abi, wallet);
const bitcoinAddress = ethers.encodeBytes32String('bc1qa0a0a0a0a0a0a0a0a0a0a0a0');
const txId = ethers.encodeBytes32String('0x1234');
const outputId = ethers.zeroPadBytes(ethers.toUtf8Bytes('0x01'), 4);

let tx = {
to: contractAddress,
value: ethers.parseEther('0.9'),
data: contract.interface.encodeFunctionData('withdraw', [bitcoinAddress]),
value: ethers.parseEther('9'),
data: contract.interface.encodeFunctionData('withdraw', [txId, outputId]),
from: wallet.address
};

try {
await provider.call(tx);
expect.fail('Expected an error to be thrown');
} catch (error) {
expect(error.message).to.equal('execution reverted: "Invalid withdraw amount" (action="call", data="0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000017496e76616c696420776974686472617720616d6f756e74000000000000000000", reason="Invalid withdraw amount", transaction={ "data": "0x8e19899e6263317161306130613061306130613061306130613061306130613000000000", "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "to": "0x3100000000000000000000000000000000000002" }, invocation=null, revert={ "args": [ "Invalid withdraw amount" ], "name": "Error", "signature": "Error(string)" }, code=CALL_EXCEPTION, version=6.12.1)');
expect(error.message).to.equal('execution reverted: "Invalid withdraw amount" (action="call", data="0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000017496e76616c696420776974686472617720616d6f756e74000000000000000000", reason="Invalid withdraw amount", transaction={ "data": "0x8786dba730783132333400000000000000000000000000000000000000000000000000003078303100000000000000000000000000000000000000000000000000000000", "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "to": "0x3100000000000000000000000000000000000002" }, invocation=null, revert={ "args": [ "Invalid withdraw amount" ], "name": "Error", "signature": "Error(string)" }, code=CALL_EXCEPTION, version=6.12.1)');
}
});

Expand Down Expand Up @@ -282,27 +294,33 @@ const generateTransaction = async (ether_value) => {
"type": "function",
"name": "withdraw",
"inputs": [
{
"name": "bitcoin_address",
"type": "bytes32",
"internalType": "bytes32"
}
{
"name": "txId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "outputId",
"type": "bytes4",
"internalType": "bytes4"
}
],
"outputs": [],
"stateMutability": "payable"
},
}
];

const privateKey = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
const wallet = new ethers.Wallet(privateKey, provider);
const contractAddress = '0x3100000000000000000000000000000000000002';
const contract = new ethers.Contract(contractAddress, abi, wallet);
const bitcoinAddress = ethers.encodeBytes32String('bc1qa0a0a0a0a0a0a0a0a0a0a0a0');
const txId = ethers.encodeBytes32String('0x1234');
const outputId = ethers.zeroPadBytes(ethers.toUtf8Bytes('0x01'), 4);

let tx = {
to: contractAddress,
value: ethers.parseEther(ether_value),
data: contract.interface.encodeFunctionData('withdraw', [bitcoinAddress]),
data: contract.interface.encodeFunctionData('withdraw', [txId, outputId]),
from: wallet.address,
chainId: 5655,
gasLimit: 1000000,
Expand Down
8 changes: 4 additions & 4 deletions bin/citrea/tests/evm/web3_py/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@ def test_create_access_list(self):
tx = {
'from': "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
'to': "0x3100000000000000000000000000000000000002",
'value': self.web3.to_wei(0.01, 'ether'),
'value': self.web3.to_wei(10, 'ether'),
'gas': 200000,
'gasPrice': self.web3.to_wei(1, 'gwei'),
'data': "0x8e19899e0000000000000000000000000000000000000000000000000000000000000000", # withdraw(bytes32), param is 0x0
'data': "0x8786dba712340000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000", # withdraw(bytes32, bytes4), param is 0x1234, 0x01
'nonce': self.web3.eth.get_transaction_count("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"),
'chainId': 5655
}
Expand Down Expand Up @@ -186,7 +186,7 @@ def test_send_raw_transaction_reverts_correctly(self):
'value': self.web3.to_wei(0.9, 'ether'),
'gas': 200000,
'gasPrice': self.web3.to_wei(1, 'gwei'),
'data': "0x8e19899e0000000000000000000000000000000000000000000000000000000000000000", # withdraw(bytes32), param is 0x0
'data': "0x8786dba712340000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000", # withdraw(bytes32, bytes4), param is 0x1234, 0x01
'nonce': self.web3.eth.get_transaction_count("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"),
'chainId': 5655
}
Expand All @@ -202,7 +202,7 @@ def test_call_errors_correctly_on_withdraw(self):
'value': self.web3.to_wei(0.9, 'ether'),
'gas': 200000,
'gasPrice': self.web3.to_wei(1, 'gwei'),
'data': "0x8e19899e0000000000000000000000000000000000000000000000000000000000000000", # withdraw(bytes32), param is 0x0
'data': "0x8786dba712340000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000", # withdraw(bytes32, bytes4), param is 0x1234, 0x01
'nonce': self.web3.eth.get_transaction_count("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"),
'chainId': 5655
}
Expand Down
27 changes: 10 additions & 17 deletions crates/evm/src/evm/system_contracts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,29 +84,22 @@ impl Bridge {
// This is equal to:
//
// BridgeContract::initializeCall {
// _depositScript: hex!("d2205daf577048c5e5a9a75d0a924ed03e226c3304f4a2f01c65ca1dab73522e6b8bad206228eba653cf1819bcfc1bc858630e5ae373eec1a9924322a5fe8445c5e76027ad201521d65f64be3f71b71ca462220f13c77b251027f6ca443a483353a96fbce222ad200fabeed269694ee83d9b3343a571202e68af65d05feda61dbed0c4bdb256a6eaad2000326d6f721c03dc5f1d8817d8f8ee890a95a2eeda0d4d9a01b1cc9b7b1b724dac00630663697472656114").into(),
// _scriptSuffix: hex!("0800000000000f424068").into(),
// _requiredSigsCount: U256::from(5),
// _scriptPrefix: hex!("4a207c4803421956db53eed29ee45bddbe60d16e66560f918a94270ea5272b2b4e90ac00630663697472656114").into(),
// _scriptSuffix: hex!("08000000003b9aca0068").into(),
// _depositAmount: U256::from(10 ether),
// }
// .abi_encode()
let params = vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 210, 32, 93, 175, 87, 112, 72, 197, 229, 169,
167, 93, 10, 146, 78, 208, 62, 34, 108, 51, 4, 244, 162, 240, 28, 101, 202, 29, 171,
115, 82, 46, 107, 139, 173, 32, 98, 40, 235, 166, 83, 207, 24, 25, 188, 252, 27, 200,
88, 99, 14, 90, 227, 115, 238, 193, 169, 146, 67, 34, 165, 254, 132, 69, 197, 231, 96,
39, 173, 32, 21, 33, 214, 95, 100, 190, 63, 113, 183, 28, 164, 98, 34, 15, 19, 199,
123, 37, 16, 39, 246, 202, 68, 58, 72, 51, 83, 169, 111, 188, 226, 34, 173, 32, 15,
171, 238, 210, 105, 105, 78, 232, 61, 155, 51, 67, 165, 113, 32, 46, 104, 175, 101,
208, 95, 237, 166, 29, 190, 208, 196, 189, 178, 86, 166, 234, 173, 32, 0, 50, 109, 111,
114, 28, 3, 220, 95, 29, 136, 23, 216, 248, 238, 137, 10, 149, 162, 238, 218, 13, 77,
154, 1, 177, 204, 155, 123, 27, 114, 77, 172, 0, 99, 6, 99, 105, 116, 114, 101, 97, 20,
0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 138, 199, 35, 4, 137, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 74, 32, 124, 72, 3, 66, 25, 86, 219,
83, 238, 210, 158, 228, 91, 221, 190, 96, 209, 110, 102, 86, 15, 145, 138, 148, 39, 14,
165, 39, 43, 43, 78, 144, 172, 0, 99, 6, 99, 105, 116, 114, 101, 97, 20, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 8, 0, 0, 0, 0, 0, 15, 66, 64, 104, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 8, 0, 0, 0, 0, 59, 154, 202, 0,
104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];

let mut func_selector = Vec::with_capacity(4 + params.len());
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Loading

0 comments on commit ad4e3ba

Please sign in to comment.