diff --git a/l1-contracts/src/core/FeeJuicePortal.sol b/l1-contracts/src/core/FeeJuicePortal.sol index e84c31ec082..59aad41c5e5 100644 --- a/l1-contracts/src/core/FeeJuicePortal.sol +++ b/l1-contracts/src/core/FeeJuicePortal.sol @@ -59,12 +59,12 @@ contract FeeJuicePortal is IFeeJuicePortal { * @param _to - The aztec address of the recipient * @param _amount - The amount to deposit * @param _secretHash - The hash of the secret consumable message. The hash should be 254 bits (so it can fit in a Field element) - * @return - The key of the entry in the Inbox + * @return - The key of the entry in the Inbox and its leaf index */ function depositToAztecPublic(bytes32 _to, uint256 _amount, bytes32 _secretHash) external override(IFeeJuicePortal) - returns (bytes32) + returns (bytes32, uint256) { // Preamble address rollup = canonicalRollup(); @@ -80,11 +80,11 @@ contract FeeJuicePortal is IFeeJuicePortal { UNDERLYING.safeTransferFrom(msg.sender, address(this), _amount); // Send message to rollup - bytes32 key = inbox.sendL2Message(actor, contentHash, _secretHash); + (bytes32 key, uint256 index) = inbox.sendL2Message(actor, contentHash, _secretHash); - emit DepositToAztecPublic(_to, _amount, _secretHash, key); + emit DepositToAztecPublic(_to, _amount, _secretHash, key, index); - return key; + return (key, index); } /** diff --git a/l1-contracts/src/core/interfaces/IFeeJuicePortal.sol b/l1-contracts/src/core/interfaces/IFeeJuicePortal.sol index 5537127f095..19de1638ac5 100644 --- a/l1-contracts/src/core/interfaces/IFeeJuicePortal.sol +++ b/l1-contracts/src/core/interfaces/IFeeJuicePortal.sol @@ -6,14 +6,16 @@ import {IERC20} from "@oz/token/ERC20/IERC20.sol"; import {IRegistry} from "@aztec/governance/interfaces/IRegistry.sol"; interface IFeeJuicePortal { - event DepositToAztecPublic(bytes32 indexed to, uint256 amount, bytes32 secretHash, bytes32 key); + event DepositToAztecPublic( + bytes32 indexed to, uint256 amount, bytes32 secretHash, bytes32 key, uint256 index + ); event FeesDistributed(address indexed to, uint256 amount); function initialize() external; function distributeFees(address _to, uint256 _amount) external; function depositToAztecPublic(bytes32 _to, uint256 _amount, bytes32 _secretHash) external - returns (bytes32); + returns (bytes32, uint256); function canonicalRollup() external view returns (address); function UNDERLYING() external view returns (IERC20); diff --git a/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol b/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol index b399edb805f..0bceddbc7e8 100644 --- a/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol +++ b/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol @@ -25,13 +25,13 @@ interface IInbox { * @param _recipient - The recipient of the message * @param _content - The content of the message (application specific) * @param _secretHash - The secret hash of the message (make it possible to hide when a specific message is consumed on L2) - * @return The key of the message in the set + * @return The key of the message in the set and its leaf index in the tree */ function sendL2Message( DataStructures.L2Actor memory _recipient, bytes32 _content, bytes32 _secretHash - ) external returns (bytes32); + ) external returns (bytes32, uint256); // docs:end:send_l1_to_l2_message // docs:start:consume diff --git a/l1-contracts/src/core/libraries/DataStructures.sol b/l1-contracts/src/core/libraries/DataStructures.sol index 537f3d6e7b2..61accf40655 100644 --- a/l1-contracts/src/core/libraries/DataStructures.sol +++ b/l1-contracts/src/core/libraries/DataStructures.sol @@ -41,12 +41,14 @@ library DataStructures { * @param recipient - The recipient of the message * @param content - The content of the message (application specific) padded to bytes32 or hashed if larger. * @param secretHash - The secret hash of the message (make it possible to hide when a specific message is consumed on L2). + * @param index - Global leaf index on the L1 to L2 messages tree. */ struct L1ToL2Msg { L1Actor sender; L2Actor recipient; bytes32 content; bytes32 secretHash; + uint256 index; } // docs:end:l1_to_l2_msg diff --git a/l1-contracts/src/core/libraries/crypto/Hash.sol b/l1-contracts/src/core/libraries/crypto/Hash.sol index df3f9467279..766df2b08e0 100644 --- a/l1-contracts/src/core/libraries/crypto/Hash.sol +++ b/l1-contracts/src/core/libraries/crypto/Hash.sol @@ -18,7 +18,9 @@ library Hash { */ function sha256ToField(DataStructures.L1ToL2Msg memory _message) internal pure returns (bytes32) { return sha256ToField( - abi.encode(_message.sender, _message.recipient, _message.content, _message.secretHash) + abi.encode( + _message.sender, _message.recipient, _message.content, _message.secretHash, _message.index + ) ); } diff --git a/l1-contracts/src/core/messagebridge/Inbox.sol b/l1-contracts/src/core/messagebridge/Inbox.sol index e8b32214104..ade70b5f321 100644 --- a/l1-contracts/src/core/messagebridge/Inbox.sol +++ b/l1-contracts/src/core/messagebridge/Inbox.sol @@ -57,13 +57,13 @@ contract Inbox is IInbox { * @param _content - The content of the message (application specific) * @param _secretHash - The secret hash of the message (make it possible to hide when a specific message is consumed on L2) * - * @return Hash of the sent message. + * @return Hash of the sent message and its leaf index in the tree. */ function sendL2Message( DataStructures.L2Actor memory _recipient, bytes32 _content, bytes32 _secretHash - ) external override(IInbox) returns (bytes32) { + ) external override(IInbox) returns (bytes32, uint256) { require( uint256(_recipient.actor) <= Constants.MAX_FIELD_VALUE, Errors.Inbox__ActorTooLarge(_recipient.actor) @@ -81,23 +81,25 @@ contract Inbox is IInbox { currentTree = trees[inProgress]; } + // this is the global leaf index and not index in the l2Block subtree + // such that users can simply use it and don't need access to a node if they are to consume it in public. + // trees are constant size so global index = tree number * size + subtree index + uint256 index = (inProgress - Constants.INITIAL_L2_BLOCK_NUM) * SIZE + currentTree.nextIndex; + DataStructures.L1ToL2Msg memory message = DataStructures.L1ToL2Msg({ sender: DataStructures.L1Actor(msg.sender, block.chainid), recipient: _recipient, content: _content, - secretHash: _secretHash + secretHash: _secretHash, + index: index }); bytes32 leaf = message.sha256ToField(); - // this is the global leaf index and not index in the l2Block subtree - // such that users can simply use it and don't need access to a node if they are to consume it in public. - // trees are constant size so global index = tree number * size + subtree index - uint256 index = - (inProgress - Constants.INITIAL_L2_BLOCK_NUM) * SIZE + currentTree.insertLeaf(leaf); + currentTree.insertLeaf(leaf); totalMessagesInserted++; emit MessageSent(inProgress, index, leaf); - return leaf; + return (leaf, index); } /** diff --git a/l1-contracts/src/mock/MockFeeJuicePortal.sol b/l1-contracts/src/mock/MockFeeJuicePortal.sol index 5227f60717d..ec90fda40f9 100644 --- a/l1-contracts/src/mock/MockFeeJuicePortal.sol +++ b/l1-contracts/src/mock/MockFeeJuicePortal.sol @@ -20,8 +20,13 @@ contract MockFeeJuicePortal is IFeeJuicePortal { function distributeFees(address, uint256) external override {} - function depositToAztecPublic(bytes32, uint256, bytes32) external pure override returns (bytes32) { - return bytes32(0); + function depositToAztecPublic(bytes32, uint256, bytes32) + external + pure + override + returns (bytes32, uint256) + { + return (bytes32(0), 0); } function canonicalRollup() external pure override returns (address) { diff --git a/l1-contracts/test/Inbox.t.sol b/l1-contracts/test/Inbox.t.sol index eb6e278ca42..0447d1dd38e 100644 --- a/l1-contracts/test/Inbox.t.sol +++ b/l1-contracts/test/Inbox.t.sol @@ -38,7 +38,8 @@ contract InboxTest is Test { version: version }), content: 0x2000000000000000000000000000000000000000000000000000000000000000, - secretHash: 0x3000000000000000000000000000000000000000000000000000000000000000 + secretHash: 0x3000000000000000000000000000000000000000000000000000000000000000, + index: 0x01 }); } @@ -46,7 +47,7 @@ contract InboxTest is Test { return (a + b - 1) / b; } - function _boundMessage(DataStructures.L1ToL2Msg memory _message) + function _boundMessage(DataStructures.L1ToL2Msg memory _message, uint256 _globalLeafIndex) internal view returns (DataStructures.L1ToL2Msg memory) @@ -61,6 +62,8 @@ contract InboxTest is Test { _message.secretHash = bytes32(uint256(_message.secretHash) % Constants.P); // update version _message.recipient.version = version; + // set leaf index + _message.index = _globalLeafIndex; return _message; } @@ -84,32 +87,40 @@ contract InboxTest is Test { } function testFuzzInsert(DataStructures.L1ToL2Msg memory _message) public checkInvariant { - DataStructures.L1ToL2Msg memory message = _boundMessage(_message); + uint256 globalLeafIndex = (FIRST_REAL_TREE_NUM - 1) * SIZE; + DataStructures.L1ToL2Msg memory message = _boundMessage(_message, globalLeafIndex); bytes32 leaf = message.sha256ToField(); vm.expectEmit(true, true, true, true); // event we expect - uint256 globalLeafIndex = (FIRST_REAL_TREE_NUM - 1) * SIZE; emit IInbox.MessageSent(FIRST_REAL_TREE_NUM, globalLeafIndex, leaf); // event we will get - bytes32 insertedLeaf = + (bytes32 insertedLeaf, uint256 insertedIndex) = inbox.sendL2Message(message.recipient, message.content, message.secretHash); assertEq(insertedLeaf, leaf); + assertEq(insertedIndex, globalLeafIndex); } function testSendDuplicateL2Messages() public checkInvariant { DataStructures.L1ToL2Msg memory message = _fakeMessage(); - bytes32 leaf1 = inbox.sendL2Message(message.recipient, message.content, message.secretHash); - bytes32 leaf2 = inbox.sendL2Message(message.recipient, message.content, message.secretHash); - bytes32 leaf3 = inbox.sendL2Message(message.recipient, message.content, message.secretHash); + (bytes32 leaf1, uint256 index1) = + inbox.sendL2Message(message.recipient, message.content, message.secretHash); + (bytes32 leaf2, uint256 index2) = + inbox.sendL2Message(message.recipient, message.content, message.secretHash); + (bytes32 leaf3, uint256 index3) = + inbox.sendL2Message(message.recipient, message.content, message.secretHash); // Only 1 tree should be non-zero assertEq(inbox.getNumTrees(), 1); - // All the leaves should be the same - assertEq(leaf1, leaf2); - assertEq(leaf2, leaf3); + // All the leaves should be different since the index gets mixed in + assertNotEq(leaf1, leaf2); + assertNotEq(leaf2, leaf3); + + // Check indices + assertEq(index1 + 1, index2); + assertEq(index1 + 2, index3); } function testRevertIfActorTooLarge() public { @@ -161,7 +172,8 @@ contract InboxTest is Test { // We send the messages and then check that toConsume root did not change. for (uint256 i = 0; i < _messages.length; i++) { - DataStructures.L1ToL2Msg memory message = _boundMessage(_messages[i]); + DataStructures.L1ToL2Msg memory message = + _boundMessage(_messages[i], inbox.getNextMessageIndex()); // We check whether a new tree is correctly initialized when the one in progress is full uint256 numTrees = inbox.getNumTrees(); diff --git a/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol index 21294aababc..b77bdfa3cbc 100644 --- a/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol +++ b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol @@ -77,12 +77,14 @@ contract DepositToAztecPublic is Test { bytes32 to = bytes32(0x0); bytes32 secretHash = bytes32(uint256(0x01)); uint256 amount = 100 ether; + uint256 expectedIndex = 2 ** Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT; DataStructures.L1ToL2Msg memory message = DataStructures.L1ToL2Msg({ sender: DataStructures.L1Actor(address(feeJuicePortal), block.chainid), recipient: DataStructures.L2Actor(feeJuicePortal.L2_TOKEN_ADDRESS(), 1 + numberOfRollups), content: Hash.sha256ToField(abi.encodeWithSignature("claim(bytes32,uint256)", to, amount)), - secretHash: secretHash + secretHash: secretHash, + index: expectedIndex }); bytes32 expectedKey = message.sha256ToField(); @@ -92,16 +94,16 @@ contract DepositToAztecPublic is Test { Inbox inbox = Inbox(address(Rollup(address(registry.getRollup())).INBOX())); assertEq(inbox.totalMessagesInserted(), 0); - uint256 index = 2 ** Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT; vm.expectEmit(true, true, true, true, address(inbox)); - emit IInbox.MessageSent(2, index, expectedKey); + emit IInbox.MessageSent(2, expectedIndex, expectedKey); vm.expectEmit(true, true, true, true, address(feeJuicePortal)); - emit IFeeJuicePortal.DepositToAztecPublic(to, amount, secretHash, expectedKey); + emit IFeeJuicePortal.DepositToAztecPublic(to, amount, secretHash, expectedKey, expectedIndex); - bytes32 key = feeJuicePortal.depositToAztecPublic(to, amount, secretHash); + (bytes32 key, uint256 index) = feeJuicePortal.depositToAztecPublic(to, amount, secretHash); assertEq(inbox.totalMessagesInserted(), 1); assertEq(key, expectedKey); + assertEq(index, expectedIndex); } } diff --git a/l1-contracts/test/fixtures/empty_block_1.json b/l1-contracts/test/fixtures/empty_block_1.json index 990b1e5b8e7..796fc4cd040 100644 --- a/l1-contracts/test/fixtures/empty_block_1.json +++ b/l1-contracts/test/fixtures/empty_block_1.json @@ -8,8 +8,8 @@ "l2ToL1Messages": [] }, "block": { - "archive": "0x18589e53843baf9415aa9a4943bd4d8fa19b318329604f51a4ac0b8e7eb8e155", - "blockHash": "0x2662dae080808aceafe3377c439d8bea968d5ea74c98643adf5c2ea805f72c0c", + "archive": "0x04f48997a6e0472d7919af16c50859f86c041ef9aefceba4be94657943618ac1", + "blockHash": "0x2f3394755802dfe8105c0e7a5a7733970b59d83f6b9bd6eb754317f4d6a73c0f", "body": "0x00000000", "txsEffectsHash": "0x00e994e16b3763fd5039413cf99c2b3c378e2bab939e7992a77bd201b28160d6", "decodedHeader": { @@ -21,12 +21,12 @@ }, "globalVariables": { "blockNumber": 1, - "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000011", + "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000012", "chainId": 31337, - "timestamp": 1728563130, + "timestamp": 1730234580, "version": 1, - "coinbase": "0xd498444361455d5099a036e93f395a584cf085c6", - "feeRecipient": "0x07c66a9b410a7e26824d677a12d8af977c0db64e5fcdfc8ad704c71c0267d649", + "coinbase": "0x2590e3544a7e2e7d736649fefc72ea5ad1d6efc3", + "feeRecipient": "0x2146e005e28b76eedc61edeff431b412b5ece74a5818080051832d286795ce2e", "gasFees": { "feePerDaGas": 0, "feePerL2Gas": 0 @@ -57,8 +57,8 @@ } } }, - "header": "0x1200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e00000001000000000000000000000000000000000000000000000000000000000000000200e994e16b3763fd5039413cf99c2b3c378e2bab939e7992a77bd201b28160d600089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb14f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000100b59baa35b9dc267744f0ccb4e3b0255c1fc512460d91130c6bc19fb2668568d0000008019a8c197c12bb33da6314c4ef4f8f6fcb9e25250c085df8672adf67c8f1e3dbc0000010023c08a6b1297210c5e24c76b9a936250a1ce2721576c26ea797c7ec35f9e46a9000001000000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000006707c7bad498444361455d5099a036e93f395a584cf085c607c66a9b410a7e26824d677a12d8af977c0db64e5fcdfc8ad704c71c0267d649000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "publicInputsHash": "0x00e081da850e07688f3b8eac898caaeb80fabf215e1c87bbecc180d88a19f34e", + "header": "0x1200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e00000001000000000000000000000000000000000000000000000000000000000000000200e994e16b3763fd5039413cf99c2b3c378e2bab939e7992a77bd201b28160d600089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb14f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000100b59baa35b9dc267744f0ccb4e3b0255c1fc512460d91130c6bc19fb2668568d0000008019a8c197c12bb33da6314c4ef4f8f6fcb9e25250c085df8672adf67c8f1e3dbc0000010023c08a6b1297210c5e24c76b9a936250a1ce2721576c26ea797c7ec35f9e46a9000001000000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000672148d42590e3544a7e2e7d736649fefc72ea5ad1d6efc32146e005e28b76eedc61edeff431b412b5ece74a5818080051832d286795ce2e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "publicInputsHash": "0x0086289eca84479d5e34db9c4548bdd156af80fb725c51219650aa4460c80240", "numTxs": 0 } } \ No newline at end of file diff --git a/l1-contracts/test/fixtures/empty_block_2.json b/l1-contracts/test/fixtures/empty_block_2.json index 7d4da40c666..de81f67a810 100644 --- a/l1-contracts/test/fixtures/empty_block_2.json +++ b/l1-contracts/test/fixtures/empty_block_2.json @@ -8,8 +8,8 @@ "l2ToL1Messages": [] }, "block": { - "archive": "0x1d3dc82359e412945750080a10eede5c3ee3cb9360dc3ca35dda2c5f2e296c52", - "blockHash": "0x15fb8c7900432692f9f4e590e6df42d6fff4ce24f0b720514d6bc30ba234d548", + "archive": "0x07d7359b2b2922b8126019bef4f2d5698f25226f5f2270a79d1105503727dd6e", + "blockHash": "0x1836f56b16db44064b3231102c84c6eaf3327d0204a5171f65b3019fc79c2b5e", "body": "0x00000000", "txsEffectsHash": "0x00e994e16b3763fd5039413cf99c2b3c378e2bab939e7992a77bd201b28160d6", "decodedHeader": { @@ -21,12 +21,12 @@ }, "globalVariables": { "blockNumber": 2, - "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000012", + "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000013", "chainId": 31337, - "timestamp": 1728563154, + "timestamp": 1730234604, "version": 1, - "coinbase": "0xd498444361455d5099a036e93f395a584cf085c6", - "feeRecipient": "0x07c66a9b410a7e26824d677a12d8af977c0db64e5fcdfc8ad704c71c0267d649", + "coinbase": "0x2590e3544a7e2e7d736649fefc72ea5ad1d6efc3", + "feeRecipient": "0x2146e005e28b76eedc61edeff431b412b5ece74a5818080051832d286795ce2e", "gasFees": { "feePerDaGas": 0, "feePerL2Gas": 0 @@ -34,7 +34,7 @@ }, "lastArchive": { "nextAvailableLeafIndex": 2, - "root": "0x18589e53843baf9415aa9a4943bd4d8fa19b318329604f51a4ac0b8e7eb8e155" + "root": "0x04f48997a6e0472d7919af16c50859f86c041ef9aefceba4be94657943618ac1" }, "stateReference": { "l1ToL2MessageTree": { @@ -57,8 +57,8 @@ } } }, - "header": "0x18589e53843baf9415aa9a4943bd4d8fa19b318329604f51a4ac0b8e7eb8e15500000002000000000000000000000000000000000000000000000000000000000000000200e994e16b3763fd5039413cf99c2b3c378e2bab939e7992a77bd201b28160d600089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb14f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000200b59baa35b9dc267744f0ccb4e3b0255c1fc512460d91130c6bc19fb2668568d0000010019a8c197c12bb33da6314c4ef4f8f6fcb9e25250c085df8672adf67c8f1e3dbc0000018023c08a6b1297210c5e24c76b9a936250a1ce2721576c26ea797c7ec35f9e46a9000001800000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000006707c7d2d498444361455d5099a036e93f395a584cf085c607c66a9b410a7e26824d677a12d8af977c0db64e5fcdfc8ad704c71c0267d649000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "publicInputsHash": "0x00815a814be4b3e0ac1a671e1a0321c231ac90e23ef7b2b1e411d32a2fb1948d", + "header": "0x04f48997a6e0472d7919af16c50859f86c041ef9aefceba4be94657943618ac100000002000000000000000000000000000000000000000000000000000000000000000200e994e16b3763fd5039413cf99c2b3c378e2bab939e7992a77bd201b28160d600089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb14f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000200b59baa35b9dc267744f0ccb4e3b0255c1fc512460d91130c6bc19fb2668568d0000010019a8c197c12bb33da6314c4ef4f8f6fcb9e25250c085df8672adf67c8f1e3dbc0000018023c08a6b1297210c5e24c76b9a936250a1ce2721576c26ea797c7ec35f9e46a9000001800000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000672148ec2590e3544a7e2e7d736649fefc72ea5ad1d6efc32146e005e28b76eedc61edeff431b412b5ece74a5818080051832d286795ce2e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "publicInputsHash": "0x009727b9d0f6c22ea711c2e31249776bd687ffa0eaf97b54120af68c96da0eda", "numTxs": 0 } } \ No newline at end of file diff --git a/l1-contracts/test/fixtures/mixed_block_1.json b/l1-contracts/test/fixtures/mixed_block_1.json index 6caa935c1d7..29d5521d59b 100644 --- a/l1-contracts/test/fixtures/mixed_block_1.json +++ b/l1-contracts/test/fixtures/mixed_block_1.json @@ -58,8 +58,8 @@ ] }, "block": { - "archive": "0x24c11def12fd4dbe0974daf2b642f44404b96f4bfac4ab9c990c911e7bd96eaa", - "blockHash": "0x2586fea74850f3a459cf291ab83242733b67e885e23aa0cc2a8ca169e78dc97c", + "archive": "0x12e65b031a1821b8ed8d2934a32482f660c0fb9d10557f476efb616c0651c75b", + "blockHash": "0x22a46343d98db8051fed1c42153e8c0a29174978493d65d27b93432506bb8029", "body": "0x00000004000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000001420000000000000000000000000000000000000000000000000000000000000143000000000000000000000000000000000000000000000000000000000000014400000000000000000000000000000000000000000000000000000000000001450000000000000000000000000000000000000000000000000000000000000146000000000000000000000000000000000000000000000000000000000000014700000000000000000000000000000000000000000000000000000000000001480000000000000000000000000000000000000000000000000000000000000149000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014b000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000014d000000000000000000000000000000000000000000000000000000000000014e000000000000000000000000000000000000000000000000000000000000014f0000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000015100000000000000000000000000000000000000000000000000000000000001520000000000000000000000000000000000000000000000000000000000000153000000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000001550000000000000000000000000000000000000000000000000000000000000156000000000000000000000000000000000000000000000000000000000000015700000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000159000000000000000000000000000000000000000000000000000000000000015a000000000000000000000000000000000000000000000000000000000000015b000000000000000000000000000000000000000000000000000000000000015c000000000000000000000000000000000000000000000000000000000000015d000000000000000000000000000000000000000000000000000000000000015e000000000000000000000000000000000000000000000000000000000000015f0000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000001620000000000000000000000000000000000000000000000000000000000000163000000000000000000000000000000000000000000000000000000000000016400000000000000000000000000000000000000000000000000000000000001650000000000000000000000000000000000000000000000000000000000000166000000000000000000000000000000000000000000000000000000000000016700000000000000000000000000000000000000000000000000000000000001680000000000000000000000000000000000000000000000000000000000000169000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016b000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000000016d000000000000000000000000000000000000000000000000000000000000016e000000000000000000000000000000000000000000000000000000000000016f0000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000017100000000000000000000000000000000000000000000000000000000000001720000000000000000000000000000000000000000000000000000000000000173000000000000000000000000000000000000000000000000000000000000017400000000000000000000000000000000000000000000000000000000000001750000000000000000000000000000000000000000000000000000000000000176000000000000000000000000000000000000000000000000000000000000017700000000000000000000000000000000000000000000000000000000000001780000000000000000000000000000000000000000000000000000000000000179000000000000000000000000000000000000000000000000000000000000017a000000000000000000000000000000000000000000000000000000000000017b000000000000000000000000000000000000000000000000000000000000017c000000000000000000000000000000000000000000000000000000000000017d000000000000000000000000000000000000000000000000000000000000017e000000000000000000000000000000000000000000000000000000000000017f3f0000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e000000000000000000000000000000000000000000000000000000000000024f0000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000025100000000000000000000000000000000000000000000000000000000000002520000000000000000000000000000000000000000000000000000000000000253000000000000000000000000000000000000000000000000000000000000025400000000000000000000000000000000000000000000000000000000000002550000000000000000000000000000000000000000000000000000000000000256000000000000000000000000000000000000000000000000000000000000025700000000000000000000000000000000000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000259000000000000000000000000000000000000000000000000000000000000025a000000000000000000000000000000000000000000000000000000000000025b000000000000000000000000000000000000000000000000000000000000025c000000000000000000000000000000000000000000000000000000000000025d000000000000000000000000000000000000000000000000000000000000025e000000000000000000000000000000000000000000000000000000000000025f0000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e000000000000000000000000000000000000000000000000000000000000026f0000000000000000000000000000000000000000000000000000000000000270000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000002720000000000000000000000000000000000000000000000000000000000000273000000000000000000000000000000000000000000000000000000000000027400000000000000000000000000000000000000000000000000000000000002750000000000000000000000000000000000000000000000000000000000000276000000000000000000000000000000000000000000000000000000000000027700000000000000000000000000000000000000000000000000000000000002780000000000000000000000000000000000000000000000000000000000000279000000000000000000000000000000000000000000000000000000000000027a000000000000000000000000000000000000000000000000000000000000027b000000000000000000000000000000000000000000000000000000000000027c000000000000000000000000000000000000000000000000000000000000027d000000000000000000000000000000000000000000000000000000000000027e0800c47667396742a5474f325e2567bff3bb99b7f0bfd2b1a689b635d8b8726cce00284120278895e8d47084ae759f390e9634881e41369a257a36fe99a2369dc800a328b4a4a6ed156325253b4ab2af7ca00e21caf7963f0fba8a88ccdc3512c300705c99df420bface231f6855799db1d0ed7d39419abc47aa8c6efe2ae7aae2009299cc308de6d23788384411e024791a5b2448e455fbdd1d1f28f3ff76631f002d6c03d81ad764de51b0f34584645191cdc2aaae2ca08fb838d142b95d62f5003032f3618b2df0fa335d5fd548d6d85e42b4e7eb5fff9eb687facbbdecb8a60016cab7ddf4d1b440d53d10284c5c82a78b2d4e27dcdb44ef434ef4c6bad6783f0000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000541000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000542000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000543000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000544000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000545000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005460000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000054700000000000000000000000000000000000000000000000000000000000005510000000000000000000000000000000000000000000000000000000000000548000000000000000000000000000000000000000000000000000000000000055200000000000000000000000000000000000000000000000000000000000005490000000000000000000000000000000000000000000000000000000000000553000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000554000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000555000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000556000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000557000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000558000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005590000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000055a0000000000000000000000000000000000000000000000000000000000000551000000000000000000000000000000000000000000000000000000000000055b0000000000000000000000000000000000000000000000000000000000000552000000000000000000000000000000000000000000000000000000000000055c0000000000000000000000000000000000000000000000000000000000000553000000000000000000000000000000000000000000000000000000000000055d0000000000000000000000000000000000000000000000000000000000000554000000000000000000000000000000000000000000000000000000000000055e0000000000000000000000000000000000000000000000000000000000000555000000000000000000000000000000000000000000000000000000000000055f00000000000000000000000000000000000000000000000000000000000005560000000000000000000000000000000000000000000000000000000000000560000000000000000000000000000000000000000000000000000000000000055700000000000000000000000000000000000000000000000000000000000005610000000000000000000000000000000000000000000000000000000000000558000000000000000000000000000000000000000000000000000000000000056200000000000000000000000000000000000000000000000000000000000005590000000000000000000000000000000000000000000000000000000000000563000000000000000000000000000000000000000000000000000000000000055a0000000000000000000000000000000000000000000000000000000000000564000000000000000000000000000000000000000000000000000000000000055b0000000000000000000000000000000000000000000000000000000000000565000000000000000000000000000000000000000000000000000000000000055c0000000000000000000000000000000000000000000000000000000000000566000000000000000000000000000000000000000000000000000000000000055d0000000000000000000000000000000000000000000000000000000000000567000000000000000000000000000000000000000000000000000000000000055e0000000000000000000000000000000000000000000000000000000000000568000000000000000000000000000000000000000000000000000000000000055f00000000000000000000000000000000000000000000000000000000000005690000000000000000000000000000000000000000000000000000000000000560000000000000000000000000000000000000000000000000000000000000056a0000000000000000000000000000000000000000000000000000000000000561000000000000000000000000000000000000000000000000000000000000056b0000000000000000000000000000000000000000000000000000000000000562000000000000000000000000000000000000000000000000000000000000056c0000000000000000000000000000000000000000000000000000000000000563000000000000000000000000000000000000000000000000000000000000056d0000000000000000000000000000000000000000000000000000000000000564000000000000000000000000000000000000000000000000000000000000056e0000000000000000000000000000000000000000000000000000000000000565000000000000000000000000000000000000000000000000000000000000056f00000000000000000000000000000000000000000000000000000000000005660000000000000000000000000000000000000000000000000000000000000570000000000000000000000000000000000000000000000000000000000000056700000000000000000000000000000000000000000000000000000000000005710000000000000000000000000000000000000000000000000000000000000568000000000000000000000000000000000000000000000000000000000000057200000000000000000000000000000000000000000000000000000000000005690000000000000000000000000000000000000000000000000000000000000573000000000000000000000000000000000000000000000000000000000000056a0000000000000000000000000000000000000000000000000000000000000574000000000000000000000000000000000000000000000000000000000000056b0000000000000000000000000000000000000000000000000000000000000575000000000000000000000000000000000000000000000000000000000000056c0000000000000000000000000000000000000000000000000000000000000576000000000000000000000000000000000000000000000000000000000000056d0000000000000000000000000000000000000000000000000000000000000577000000000000000000000000000000000000000000000000000000000000056e0000000000000000000000000000000000000000000000000000000000000578000000000000000000000000000000000000000000000000000000000000056f00000000000000000000000000000000000000000000000000000000000005790000000000000000000000000000000000000000000000000000000000000570000000000000000000000000000000000000000000000000000000000000057a0000000000000000000000000000000000000000000000000000000000000571000000000000000000000000000000000000000000000000000000000000057b0000000000000000000000000000000000000000000000000000000000000572000000000000000000000000000000000000000000000000000000000000057c0000000000000000000000000000000000000000000000000000000000000573000000000000000000000000000000000000000000000000000000000000057d0000000000000000000000000000000000000000000000000000000000000574000000000000000000000000000000000000000000000000000000000000057e0000000000000000000000000000000000000000000000000000000000000575000000000000000000000000000000000000000000000000000000000000057f00000000000000000000000000000000000000000000000000000000000005760000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000057700000000000000000000000000000000000000000000000000000000000005810000000000000000000000000000000000000000000000000000000000000578000000000000000000000000000000000000000000000000000000000000058200000000000000000000000000000000000000000000000000000000000005790000000000000000000000000000000000000000000000000000000000000583000000000000000000000000000000000000000000000000000000000000057a0000000000000000000000000000000000000000000000000000000000000584000000000000000000000000000000000000000000000000000000000000057b0000000000000000000000000000000000000000000000000000000000000585000000000000000000000000000000000000000000000000000000000000057c0000000000000000000000000000000000000000000000000000000000000586000000000000000000000000000000000000000000000000000000000000057d0000000000000000000000000000000000000000000000000000000000000587000000000000000000000000000000000000000000000000000000000000057e0000000000000000000000000000000000000000000000000000000000000588000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000018100000000000000000000000000000000000000000000000000000000000001820000000000000000000000000000000000000000000000000000000000000183000000000000000000000000000000000000000000000000000000000000018400000000000000000000000000000000000000000000000000000000000001850000000000000000000000000000000000000000000000000000000000000186000000000000000000000000000000000000000000000000000000000000018700000000000000000000000000000000000000000000000000000000000001880000000000000000000000000000000000000000000000000000000000000189000000000000000000000000000000000000000000000000000000000000018a000000000000000000000000000000000000000000000000000000000000018b000000000000000000000000000000000000000000000000000000000000018c000000000000000000000000000000000000000000000000000000000000018d000000000000000000000000000000000000000000000000000000000000018e000000000000000000000000000000000000000000000000000000000000018f0000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000019100000000000000000000000000000000000000000000000000000000000001920000000000000000000000000000000000000000000000000000000000000193000000000000000000000000000000000000000000000000000000000000019400000000000000000000000000000000000000000000000000000000000001950000000000000000000000000000000000000000000000000000000000000196000000000000000000000000000000000000000000000000000000000000019700000000000000000000000000000000000000000000000000000000000001980000000000000000000000000000000000000000000000000000000000000199000000000000000000000000000000000000000000000000000000000000019a000000000000000000000000000000000000000000000000000000000000019b000000000000000000000000000000000000000000000000000000000000019c000000000000000000000000000000000000000000000000000000000000019d000000000000000000000000000000000000000000000000000000000000019e000000000000000000000000000000000000000000000000000000000000019f00000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001a100000000000000000000000000000000000000000000000000000000000001a200000000000000000000000000000000000000000000000000000000000001a300000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000001a500000000000000000000000000000000000000000000000000000000000001a600000000000000000000000000000000000000000000000000000000000001a700000000000000000000000000000000000000000000000000000000000001a800000000000000000000000000000000000000000000000000000000000001a900000000000000000000000000000000000000000000000000000000000001aa00000000000000000000000000000000000000000000000000000000000001ab00000000000000000000000000000000000000000000000000000000000001ac00000000000000000000000000000000000000000000000000000000000001ad00000000000000000000000000000000000000000000000000000000000001ae00000000000000000000000000000000000000000000000000000000000001af00000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b100000000000000000000000000000000000000000000000000000000000001b200000000000000000000000000000000000000000000000000000000000001b300000000000000000000000000000000000000000000000000000000000001b400000000000000000000000000000000000000000000000000000000000001b500000000000000000000000000000000000000000000000000000000000001b600000000000000000000000000000000000000000000000000000000000001b700000000000000000000000000000000000000000000000000000000000001b800000000000000000000000000000000000000000000000000000000000001b900000000000000000000000000000000000000000000000000000000000001ba00000000000000000000000000000000000000000000000000000000000001bb00000000000000000000000000000000000000000000000000000000000001bc00000000000000000000000000000000000000000000000000000000000001bd00000000000000000000000000000000000000000000000000000000000001be00000000000000000000000000000000000000000000000000000000000001bf3f0000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e000000000000000000000000000000000000000000000000000000000000028f0000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000000000000000029100000000000000000000000000000000000000000000000000000000000002920000000000000000000000000000000000000000000000000000000000000293000000000000000000000000000000000000000000000000000000000000029400000000000000000000000000000000000000000000000000000000000002950000000000000000000000000000000000000000000000000000000000000296000000000000000000000000000000000000000000000000000000000000029700000000000000000000000000000000000000000000000000000000000002980000000000000000000000000000000000000000000000000000000000000299000000000000000000000000000000000000000000000000000000000000029a000000000000000000000000000000000000000000000000000000000000029b000000000000000000000000000000000000000000000000000000000000029c000000000000000000000000000000000000000000000000000000000000029d000000000000000000000000000000000000000000000000000000000000029e000000000000000000000000000000000000000000000000000000000000029f00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002a100000000000000000000000000000000000000000000000000000000000002a200000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002a400000000000000000000000000000000000000000000000000000000000002a500000000000000000000000000000000000000000000000000000000000002a600000000000000000000000000000000000000000000000000000000000002a700000000000000000000000000000000000000000000000000000000000002a800000000000000000000000000000000000000000000000000000000000002a900000000000000000000000000000000000000000000000000000000000002aa00000000000000000000000000000000000000000000000000000000000002ab00000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000002ad00000000000000000000000000000000000000000000000000000000000002ae00000000000000000000000000000000000000000000000000000000000002af00000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002b100000000000000000000000000000000000000000000000000000000000002b200000000000000000000000000000000000000000000000000000000000002b300000000000000000000000000000000000000000000000000000000000002b400000000000000000000000000000000000000000000000000000000000002b500000000000000000000000000000000000000000000000000000000000002b600000000000000000000000000000000000000000000000000000000000002b700000000000000000000000000000000000000000000000000000000000002b800000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002ba00000000000000000000000000000000000000000000000000000000000002bb00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bd00000000000000000000000000000000000000000000000000000000000002be0800789ff73d7787206612d96dfc2143f2344de21669a3f8cae7fe9a8918631eb00084a17f00bf8793b6851a106e9155543125e0be987ad3c8334456bdda171d0b00a400f8fd336ad84f467465964008238fd1b7f9c51c22912d706cd2b874d24e002c79bdd83c14ff50a46964f838ee207564909e28af79a57fc195810d36f9b20070083c6ef1e4dd88a064e94d2582283b203cf8a2ab1667f4370eda1b4c1fe8005373dffb5b590053d7762efcf9e11280f1486ce82e7996d94ee0f5d7c093bc009eefd90eb40e79c78bac1f71ec78bdc2f8b30041974239bdc765edffed813800ea95742e72792ca7a0f66ce9f55bc47dc09d5ea08c1b9018763102776978303f0000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000581000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000582000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000583000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000584000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000585000000000000000000000000000000000000000000000000000000000000058f00000000000000000000000000000000000000000000000000000000000005860000000000000000000000000000000000000000000000000000000000000590000000000000000000000000000000000000000000000000000000000000058700000000000000000000000000000000000000000000000000000000000005910000000000000000000000000000000000000000000000000000000000000588000000000000000000000000000000000000000000000000000000000000059200000000000000000000000000000000000000000000000000000000000005890000000000000000000000000000000000000000000000000000000000000593000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000594000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000595000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000596000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000597000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000598000000000000000000000000000000000000000000000000000000000000058f00000000000000000000000000000000000000000000000000000000000005990000000000000000000000000000000000000000000000000000000000000590000000000000000000000000000000000000000000000000000000000000059a0000000000000000000000000000000000000000000000000000000000000591000000000000000000000000000000000000000000000000000000000000059b0000000000000000000000000000000000000000000000000000000000000592000000000000000000000000000000000000000000000000000000000000059c0000000000000000000000000000000000000000000000000000000000000593000000000000000000000000000000000000000000000000000000000000059d0000000000000000000000000000000000000000000000000000000000000594000000000000000000000000000000000000000000000000000000000000059e0000000000000000000000000000000000000000000000000000000000000595000000000000000000000000000000000000000000000000000000000000059f000000000000000000000000000000000000000000000000000000000000059600000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000059700000000000000000000000000000000000000000000000000000000000005a1000000000000000000000000000000000000000000000000000000000000059800000000000000000000000000000000000000000000000000000000000005a2000000000000000000000000000000000000000000000000000000000000059900000000000000000000000000000000000000000000000000000000000005a3000000000000000000000000000000000000000000000000000000000000059a00000000000000000000000000000000000000000000000000000000000005a4000000000000000000000000000000000000000000000000000000000000059b00000000000000000000000000000000000000000000000000000000000005a5000000000000000000000000000000000000000000000000000000000000059c00000000000000000000000000000000000000000000000000000000000005a6000000000000000000000000000000000000000000000000000000000000059d00000000000000000000000000000000000000000000000000000000000005a7000000000000000000000000000000000000000000000000000000000000059e00000000000000000000000000000000000000000000000000000000000005a8000000000000000000000000000000000000000000000000000000000000059f00000000000000000000000000000000000000000000000000000000000005a900000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000000000000000005aa00000000000000000000000000000000000000000000000000000000000005a100000000000000000000000000000000000000000000000000000000000005ab00000000000000000000000000000000000000000000000000000000000005a200000000000000000000000000000000000000000000000000000000000005ac00000000000000000000000000000000000000000000000000000000000005a300000000000000000000000000000000000000000000000000000000000005ad00000000000000000000000000000000000000000000000000000000000005a400000000000000000000000000000000000000000000000000000000000005ae00000000000000000000000000000000000000000000000000000000000005a500000000000000000000000000000000000000000000000000000000000005af00000000000000000000000000000000000000000000000000000000000005a600000000000000000000000000000000000000000000000000000000000005b000000000000000000000000000000000000000000000000000000000000005a700000000000000000000000000000000000000000000000000000000000005b100000000000000000000000000000000000000000000000000000000000005a800000000000000000000000000000000000000000000000000000000000005b200000000000000000000000000000000000000000000000000000000000005a900000000000000000000000000000000000000000000000000000000000005b300000000000000000000000000000000000000000000000000000000000005aa00000000000000000000000000000000000000000000000000000000000005b400000000000000000000000000000000000000000000000000000000000005ab00000000000000000000000000000000000000000000000000000000000005b500000000000000000000000000000000000000000000000000000000000005ac00000000000000000000000000000000000000000000000000000000000005b600000000000000000000000000000000000000000000000000000000000005ad00000000000000000000000000000000000000000000000000000000000005b700000000000000000000000000000000000000000000000000000000000005ae00000000000000000000000000000000000000000000000000000000000005b800000000000000000000000000000000000000000000000000000000000005af00000000000000000000000000000000000000000000000000000000000005b900000000000000000000000000000000000000000000000000000000000005b000000000000000000000000000000000000000000000000000000000000005ba00000000000000000000000000000000000000000000000000000000000005b100000000000000000000000000000000000000000000000000000000000005bb00000000000000000000000000000000000000000000000000000000000005b200000000000000000000000000000000000000000000000000000000000005bc00000000000000000000000000000000000000000000000000000000000005b300000000000000000000000000000000000000000000000000000000000005bd00000000000000000000000000000000000000000000000000000000000005b400000000000000000000000000000000000000000000000000000000000005be00000000000000000000000000000000000000000000000000000000000005b500000000000000000000000000000000000000000000000000000000000005bf00000000000000000000000000000000000000000000000000000000000005b600000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000005b700000000000000000000000000000000000000000000000000000000000005c100000000000000000000000000000000000000000000000000000000000005b800000000000000000000000000000000000000000000000000000000000005c200000000000000000000000000000000000000000000000000000000000005b900000000000000000000000000000000000000000000000000000000000005c300000000000000000000000000000000000000000000000000000000000005ba00000000000000000000000000000000000000000000000000000000000005c400000000000000000000000000000000000000000000000000000000000005bb00000000000000000000000000000000000000000000000000000000000005c500000000000000000000000000000000000000000000000000000000000005bc00000000000000000000000000000000000000000000000000000000000005c600000000000000000000000000000000000000000000000000000000000005bd00000000000000000000000000000000000000000000000000000000000005c700000000000000000000000000000000000000000000000000000000000005be00000000000000000000000000000000000000000000000000000000000005c80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c100000000000000000000000000000000000000000000000000000000000001c200000000000000000000000000000000000000000000000000000000000001c300000000000000000000000000000000000000000000000000000000000001c400000000000000000000000000000000000000000000000000000000000001c500000000000000000000000000000000000000000000000000000000000001c600000000000000000000000000000000000000000000000000000000000001c700000000000000000000000000000000000000000000000000000000000001c800000000000000000000000000000000000000000000000000000000000001c900000000000000000000000000000000000000000000000000000000000001ca00000000000000000000000000000000000000000000000000000000000001cb00000000000000000000000000000000000000000000000000000000000001cc00000000000000000000000000000000000000000000000000000000000001cd00000000000000000000000000000000000000000000000000000000000001ce00000000000000000000000000000000000000000000000000000000000001cf00000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001d100000000000000000000000000000000000000000000000000000000000001d200000000000000000000000000000000000000000000000000000000000001d300000000000000000000000000000000000000000000000000000000000001d400000000000000000000000000000000000000000000000000000000000001d500000000000000000000000000000000000000000000000000000000000001d600000000000000000000000000000000000000000000000000000000000001d700000000000000000000000000000000000000000000000000000000000001d800000000000000000000000000000000000000000000000000000000000001d900000000000000000000000000000000000000000000000000000000000001da00000000000000000000000000000000000000000000000000000000000001db00000000000000000000000000000000000000000000000000000000000001dc00000000000000000000000000000000000000000000000000000000000001dd00000000000000000000000000000000000000000000000000000000000001de00000000000000000000000000000000000000000000000000000000000001df00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001e100000000000000000000000000000000000000000000000000000000000001e200000000000000000000000000000000000000000000000000000000000001e300000000000000000000000000000000000000000000000000000000000001e400000000000000000000000000000000000000000000000000000000000001e500000000000000000000000000000000000000000000000000000000000001e600000000000000000000000000000000000000000000000000000000000001e700000000000000000000000000000000000000000000000000000000000001e800000000000000000000000000000000000000000000000000000000000001e900000000000000000000000000000000000000000000000000000000000001ea00000000000000000000000000000000000000000000000000000000000001eb00000000000000000000000000000000000000000000000000000000000001ec00000000000000000000000000000000000000000000000000000000000001ed00000000000000000000000000000000000000000000000000000000000001ee00000000000000000000000000000000000000000000000000000000000001ef00000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000001f100000000000000000000000000000000000000000000000000000000000001f200000000000000000000000000000000000000000000000000000000000001f300000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000001f600000000000000000000000000000000000000000000000000000000000001f700000000000000000000000000000000000000000000000000000000000001f800000000000000000000000000000000000000000000000000000000000001f900000000000000000000000000000000000000000000000000000000000001fa00000000000000000000000000000000000000000000000000000000000001fb00000000000000000000000000000000000000000000000000000000000001fc00000000000000000000000000000000000000000000000000000000000001fd00000000000000000000000000000000000000000000000000000000000001fe00000000000000000000000000000000000000000000000000000000000001ff3f00000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002c100000000000000000000000000000000000000000000000000000000000002c200000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002c400000000000000000000000000000000000000000000000000000000000002c500000000000000000000000000000000000000000000000000000000000002c600000000000000000000000000000000000000000000000000000000000002c700000000000000000000000000000000000000000000000000000000000002c800000000000000000000000000000000000000000000000000000000000002c900000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000002cb00000000000000000000000000000000000000000000000000000000000002cc00000000000000000000000000000000000000000000000000000000000002cd00000000000000000000000000000000000000000000000000000000000002ce00000000000000000000000000000000000000000000000000000000000002cf00000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002d100000000000000000000000000000000000000000000000000000000000002d200000000000000000000000000000000000000000000000000000000000002d300000000000000000000000000000000000000000000000000000000000002d400000000000000000000000000000000000000000000000000000000000002d500000000000000000000000000000000000000000000000000000000000002d600000000000000000000000000000000000000000000000000000000000002d700000000000000000000000000000000000000000000000000000000000002d800000000000000000000000000000000000000000000000000000000000002d900000000000000000000000000000000000000000000000000000000000002da00000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000002dc00000000000000000000000000000000000000000000000000000000000002dd00000000000000000000000000000000000000000000000000000000000002de00000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002e100000000000000000000000000000000000000000000000000000000000002e200000000000000000000000000000000000000000000000000000000000002e300000000000000000000000000000000000000000000000000000000000002e400000000000000000000000000000000000000000000000000000000000002e500000000000000000000000000000000000000000000000000000000000002e600000000000000000000000000000000000000000000000000000000000002e700000000000000000000000000000000000000000000000000000000000002e800000000000000000000000000000000000000000000000000000000000002e900000000000000000000000000000000000000000000000000000000000002ea00000000000000000000000000000000000000000000000000000000000002eb00000000000000000000000000000000000000000000000000000000000002ec00000000000000000000000000000000000000000000000000000000000002ed00000000000000000000000000000000000000000000000000000000000002ee00000000000000000000000000000000000000000000000000000000000002ef00000000000000000000000000000000000000000000000000000000000002f000000000000000000000000000000000000000000000000000000000000002f100000000000000000000000000000000000000000000000000000000000002f200000000000000000000000000000000000000000000000000000000000002f300000000000000000000000000000000000000000000000000000000000002f400000000000000000000000000000000000000000000000000000000000002f500000000000000000000000000000000000000000000000000000000000002f600000000000000000000000000000000000000000000000000000000000002f700000000000000000000000000000000000000000000000000000000000002f800000000000000000000000000000000000000000000000000000000000002f900000000000000000000000000000000000000000000000000000000000002fa00000000000000000000000000000000000000000000000000000000000002fb00000000000000000000000000000000000000000000000000000000000002fc00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000002fe0800a68d2df6e48c8b31f4c76529e586de2cd9d0f2f2fdfbc4c523db9e3a29e9b80016c236e57bf17afe324437acd1060772e3f31d4f9e734ad758d0627c4ba2a200d659011ddde95e32886bdd8c9464da1ca144ccadd539f0992b4abb491d812a00c8025bb9006a976ebd6ad940155f15f89ca0bb7312b53841fc257e7ed689c8004165f2c46b70fb183468b38f31bba7aa9d22ce8dfb61fe721470729ce1c6b100afeb60dd983514ebbaee141b2196f3eb3c9c299f576a0097872cc85a79a43f005f6bfee53d20a474901493558419dbceb3aca40e5e18915d38031498f4d2bb008791217ee341ec5dc11f7d7a31160fb1b1f2cf767062970e9526e5751956253f00000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005c100000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005c200000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005c300000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005c400000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005c500000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005c600000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005c700000000000000000000000000000000000000000000000000000000000005d100000000000000000000000000000000000000000000000000000000000005c800000000000000000000000000000000000000000000000000000000000005d200000000000000000000000000000000000000000000000000000000000005c900000000000000000000000000000000000000000000000000000000000005d300000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005d400000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005d500000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005d600000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005d700000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005d800000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005d900000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005da00000000000000000000000000000000000000000000000000000000000005d100000000000000000000000000000000000000000000000000000000000005db00000000000000000000000000000000000000000000000000000000000005d200000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000005d300000000000000000000000000000000000000000000000000000000000005dd00000000000000000000000000000000000000000000000000000000000005d400000000000000000000000000000000000000000000000000000000000005de00000000000000000000000000000000000000000000000000000000000005d500000000000000000000000000000000000000000000000000000000000005df00000000000000000000000000000000000000000000000000000000000005d600000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000005d700000000000000000000000000000000000000000000000000000000000005e100000000000000000000000000000000000000000000000000000000000005d800000000000000000000000000000000000000000000000000000000000005e200000000000000000000000000000000000000000000000000000000000005d900000000000000000000000000000000000000000000000000000000000005e300000000000000000000000000000000000000000000000000000000000005da00000000000000000000000000000000000000000000000000000000000005e400000000000000000000000000000000000000000000000000000000000005db00000000000000000000000000000000000000000000000000000000000005e500000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000005e600000000000000000000000000000000000000000000000000000000000005dd00000000000000000000000000000000000000000000000000000000000005e700000000000000000000000000000000000000000000000000000000000005de00000000000000000000000000000000000000000000000000000000000005e800000000000000000000000000000000000000000000000000000000000005df00000000000000000000000000000000000000000000000000000000000005e900000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000005ea00000000000000000000000000000000000000000000000000000000000005e100000000000000000000000000000000000000000000000000000000000005eb00000000000000000000000000000000000000000000000000000000000005e200000000000000000000000000000000000000000000000000000000000005ec00000000000000000000000000000000000000000000000000000000000005e300000000000000000000000000000000000000000000000000000000000005ed00000000000000000000000000000000000000000000000000000000000005e400000000000000000000000000000000000000000000000000000000000005ee00000000000000000000000000000000000000000000000000000000000005e500000000000000000000000000000000000000000000000000000000000005ef00000000000000000000000000000000000000000000000000000000000005e600000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000005e700000000000000000000000000000000000000000000000000000000000005f100000000000000000000000000000000000000000000000000000000000005e800000000000000000000000000000000000000000000000000000000000005f200000000000000000000000000000000000000000000000000000000000005e900000000000000000000000000000000000000000000000000000000000005f300000000000000000000000000000000000000000000000000000000000005ea00000000000000000000000000000000000000000000000000000000000005f400000000000000000000000000000000000000000000000000000000000005eb00000000000000000000000000000000000000000000000000000000000005f500000000000000000000000000000000000000000000000000000000000005ec00000000000000000000000000000000000000000000000000000000000005f600000000000000000000000000000000000000000000000000000000000005ed00000000000000000000000000000000000000000000000000000000000005f700000000000000000000000000000000000000000000000000000000000005ee00000000000000000000000000000000000000000000000000000000000005f800000000000000000000000000000000000000000000000000000000000005ef00000000000000000000000000000000000000000000000000000000000005f900000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000005fa00000000000000000000000000000000000000000000000000000000000005f100000000000000000000000000000000000000000000000000000000000005fb00000000000000000000000000000000000000000000000000000000000005f200000000000000000000000000000000000000000000000000000000000005fc00000000000000000000000000000000000000000000000000000000000005f300000000000000000000000000000000000000000000000000000000000005fd00000000000000000000000000000000000000000000000000000000000005f400000000000000000000000000000000000000000000000000000000000005fe00000000000000000000000000000000000000000000000000000000000005f500000000000000000000000000000000000000000000000000000000000005ff00000000000000000000000000000000000000000000000000000000000005f6000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000005f7000000000000000000000000000000000000000000000000000000000000060100000000000000000000000000000000000000000000000000000000000005f8000000000000000000000000000000000000000000000000000000000000060200000000000000000000000000000000000000000000000000000000000005f9000000000000000000000000000000000000000000000000000000000000060300000000000000000000000000000000000000000000000000000000000005fa000000000000000000000000000000000000000000000000000000000000060400000000000000000000000000000000000000000000000000000000000005fb000000000000000000000000000000000000000000000000000000000000060500000000000000000000000000000000000000000000000000000000000005fc000000000000000000000000000000000000000000000000000000000000060600000000000000000000000000000000000000000000000000000000000005fd000000000000000000000000000000000000000000000000000000000000060700000000000000000000000000000000000000000000000000000000000005fe0000000000000000000000000000000000000000000000000000000000000608000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020100000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000000000000000000000203000000000000000000000000000000000000000000000000000000000000020400000000000000000000000000000000000000000000000000000000000002050000000000000000000000000000000000000000000000000000000000000206000000000000000000000000000000000000000000000000000000000000020700000000000000000000000000000000000000000000000000000000000002080000000000000000000000000000000000000000000000000000000000000209000000000000000000000000000000000000000000000000000000000000020a000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000020c000000000000000000000000000000000000000000000000000000000000020d000000000000000000000000000000000000000000000000000000000000020e000000000000000000000000000000000000000000000000000000000000020f0000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000021100000000000000000000000000000000000000000000000000000000000002120000000000000000000000000000000000000000000000000000000000000213000000000000000000000000000000000000000000000000000000000000021400000000000000000000000000000000000000000000000000000000000002150000000000000000000000000000000000000000000000000000000000000216000000000000000000000000000000000000000000000000000000000000021700000000000000000000000000000000000000000000000000000000000002180000000000000000000000000000000000000000000000000000000000000219000000000000000000000000000000000000000000000000000000000000021a000000000000000000000000000000000000000000000000000000000000021b000000000000000000000000000000000000000000000000000000000000021c000000000000000000000000000000000000000000000000000000000000021d000000000000000000000000000000000000000000000000000000000000021e000000000000000000000000000000000000000000000000000000000000021f0000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000022100000000000000000000000000000000000000000000000000000000000002220000000000000000000000000000000000000000000000000000000000000223000000000000000000000000000000000000000000000000000000000000022400000000000000000000000000000000000000000000000000000000000002250000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000022700000000000000000000000000000000000000000000000000000000000002280000000000000000000000000000000000000000000000000000000000000229000000000000000000000000000000000000000000000000000000000000022a000000000000000000000000000000000000000000000000000000000000022b000000000000000000000000000000000000000000000000000000000000022c000000000000000000000000000000000000000000000000000000000000022d000000000000000000000000000000000000000000000000000000000000022e000000000000000000000000000000000000000000000000000000000000022f0000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000023100000000000000000000000000000000000000000000000000000000000002320000000000000000000000000000000000000000000000000000000000000233000000000000000000000000000000000000000000000000000000000000023400000000000000000000000000000000000000000000000000000000000002350000000000000000000000000000000000000000000000000000000000000236000000000000000000000000000000000000000000000000000000000000023700000000000000000000000000000000000000000000000000000000000002380000000000000000000000000000000000000000000000000000000000000239000000000000000000000000000000000000000000000000000000000000023a000000000000000000000000000000000000000000000000000000000000023b000000000000000000000000000000000000000000000000000000000000023c000000000000000000000000000000000000000000000000000000000000023d000000000000000000000000000000000000000000000000000000000000023e000000000000000000000000000000000000000000000000000000000000023f3f0000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030100000000000000000000000000000000000000000000000000000000000003020000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000030400000000000000000000000000000000000000000000000000000000000003050000000000000000000000000000000000000000000000000000000000000306000000000000000000000000000000000000000000000000000000000000030700000000000000000000000000000000000000000000000000000000000003080000000000000000000000000000000000000000000000000000000000000309000000000000000000000000000000000000000000000000000000000000030a000000000000000000000000000000000000000000000000000000000000030b000000000000000000000000000000000000000000000000000000000000030c000000000000000000000000000000000000000000000000000000000000030d000000000000000000000000000000000000000000000000000000000000030e000000000000000000000000000000000000000000000000000000000000030f0000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000031100000000000000000000000000000000000000000000000000000000000003120000000000000000000000000000000000000000000000000000000000000313000000000000000000000000000000000000000000000000000000000000031400000000000000000000000000000000000000000000000000000000000003150000000000000000000000000000000000000000000000000000000000000316000000000000000000000000000000000000000000000000000000000000031700000000000000000000000000000000000000000000000000000000000003180000000000000000000000000000000000000000000000000000000000000319000000000000000000000000000000000000000000000000000000000000031a000000000000000000000000000000000000000000000000000000000000031b000000000000000000000000000000000000000000000000000000000000031c000000000000000000000000000000000000000000000000000000000000031d000000000000000000000000000000000000000000000000000000000000031e000000000000000000000000000000000000000000000000000000000000031f0000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032100000000000000000000000000000000000000000000000000000000000003220000000000000000000000000000000000000000000000000000000000000323000000000000000000000000000000000000000000000000000000000000032400000000000000000000000000000000000000000000000000000000000003250000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000032700000000000000000000000000000000000000000000000000000000000003280000000000000000000000000000000000000000000000000000000000000329000000000000000000000000000000000000000000000000000000000000032a000000000000000000000000000000000000000000000000000000000000032b000000000000000000000000000000000000000000000000000000000000032c000000000000000000000000000000000000000000000000000000000000032d000000000000000000000000000000000000000000000000000000000000032e000000000000000000000000000000000000000000000000000000000000032f0000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033100000000000000000000000000000000000000000000000000000000000003320000000000000000000000000000000000000000000000000000000000000333000000000000000000000000000000000000000000000000000000000000033400000000000000000000000000000000000000000000000000000000000003350000000000000000000000000000000000000000000000000000000000000336000000000000000000000000000000000000000000000000000000000000033700000000000000000000000000000000000000000000000000000000000003380000000000000000000000000000000000000000000000000000000000000339000000000000000000000000000000000000000000000000000000000000033a000000000000000000000000000000000000000000000000000000000000033b000000000000000000000000000000000000000000000000000000000000033c000000000000000000000000000000000000000000000000000000000000033d000000000000000000000000000000000000000000000000000000000000033e0800c064a9343bfaf1345a5ad188e96aa4c5bad0b4a5590da51a9ff8f3c98ade8d008803d57dd0923c95a01b2bfbee4df6d66dc7baee1d2cd2770575feb86d040200ea64eed9489feb7bdf29bf817a6e8772e549da7b291028852d0dd3810cee9500947e8f904d41be8a4e08b146b4e1f0cd88f55722ef987d1d485c4196ab9f71002e5d9ed5d71bc3bea6edf988c4b9ba7fceb0815180d893852ed343c64ab55c0040f7f519ec89d360d83e242b6c0ce049d2b3356b8cfbf1ff3b733ef0f8a089006f5cfe5fc4a7b87c634f9e253a7cea2052229250d0c0e913eb50b5ef3612de00b759fce0eed36bb653b67255cce111b3529c383bd7d2b758f8cb53a4451f273f0000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000601000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000602000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000603000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000604000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000605000000000000000000000000000000000000000000000000000000000000060f00000000000000000000000000000000000000000000000000000000000006060000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000060700000000000000000000000000000000000000000000000000000000000006110000000000000000000000000000000000000000000000000000000000000608000000000000000000000000000000000000000000000000000000000000061200000000000000000000000000000000000000000000000000000000000006090000000000000000000000000000000000000000000000000000000000000613000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000614000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000615000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000616000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000617000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000618000000000000000000000000000000000000000000000000000000000000060f00000000000000000000000000000000000000000000000000000000000006190000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000061a0000000000000000000000000000000000000000000000000000000000000611000000000000000000000000000000000000000000000000000000000000061b0000000000000000000000000000000000000000000000000000000000000612000000000000000000000000000000000000000000000000000000000000061c0000000000000000000000000000000000000000000000000000000000000613000000000000000000000000000000000000000000000000000000000000061d0000000000000000000000000000000000000000000000000000000000000614000000000000000000000000000000000000000000000000000000000000061e0000000000000000000000000000000000000000000000000000000000000615000000000000000000000000000000000000000000000000000000000000061f00000000000000000000000000000000000000000000000000000000000006160000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000061700000000000000000000000000000000000000000000000000000000000006210000000000000000000000000000000000000000000000000000000000000618000000000000000000000000000000000000000000000000000000000000062200000000000000000000000000000000000000000000000000000000000006190000000000000000000000000000000000000000000000000000000000000623000000000000000000000000000000000000000000000000000000000000061a0000000000000000000000000000000000000000000000000000000000000624000000000000000000000000000000000000000000000000000000000000061b0000000000000000000000000000000000000000000000000000000000000625000000000000000000000000000000000000000000000000000000000000061c0000000000000000000000000000000000000000000000000000000000000626000000000000000000000000000000000000000000000000000000000000061d0000000000000000000000000000000000000000000000000000000000000627000000000000000000000000000000000000000000000000000000000000061e0000000000000000000000000000000000000000000000000000000000000628000000000000000000000000000000000000000000000000000000000000061f00000000000000000000000000000000000000000000000000000000000006290000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000062a0000000000000000000000000000000000000000000000000000000000000621000000000000000000000000000000000000000000000000000000000000062b0000000000000000000000000000000000000000000000000000000000000622000000000000000000000000000000000000000000000000000000000000062c0000000000000000000000000000000000000000000000000000000000000623000000000000000000000000000000000000000000000000000000000000062d0000000000000000000000000000000000000000000000000000000000000624000000000000000000000000000000000000000000000000000000000000062e0000000000000000000000000000000000000000000000000000000000000625000000000000000000000000000000000000000000000000000000000000062f00000000000000000000000000000000000000000000000000000000000006260000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000062700000000000000000000000000000000000000000000000000000000000006310000000000000000000000000000000000000000000000000000000000000628000000000000000000000000000000000000000000000000000000000000063200000000000000000000000000000000000000000000000000000000000006290000000000000000000000000000000000000000000000000000000000000633000000000000000000000000000000000000000000000000000000000000062a0000000000000000000000000000000000000000000000000000000000000634000000000000000000000000000000000000000000000000000000000000062b0000000000000000000000000000000000000000000000000000000000000635000000000000000000000000000000000000000000000000000000000000062c0000000000000000000000000000000000000000000000000000000000000636000000000000000000000000000000000000000000000000000000000000062d0000000000000000000000000000000000000000000000000000000000000637000000000000000000000000000000000000000000000000000000000000062e0000000000000000000000000000000000000000000000000000000000000638000000000000000000000000000000000000000000000000000000000000062f00000000000000000000000000000000000000000000000000000000000006390000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000063a0000000000000000000000000000000000000000000000000000000000000631000000000000000000000000000000000000000000000000000000000000063b0000000000000000000000000000000000000000000000000000000000000632000000000000000000000000000000000000000000000000000000000000063c0000000000000000000000000000000000000000000000000000000000000633000000000000000000000000000000000000000000000000000000000000063d0000000000000000000000000000000000000000000000000000000000000634000000000000000000000000000000000000000000000000000000000000063e0000000000000000000000000000000000000000000000000000000000000635000000000000000000000000000000000000000000000000000000000000063f00000000000000000000000000000000000000000000000000000000000006360000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000063700000000000000000000000000000000000000000000000000000000000006410000000000000000000000000000000000000000000000000000000000000638000000000000000000000000000000000000000000000000000000000000064200000000000000000000000000000000000000000000000000000000000006390000000000000000000000000000000000000000000000000000000000000643000000000000000000000000000000000000000000000000000000000000063a0000000000000000000000000000000000000000000000000000000000000644000000000000000000000000000000000000000000000000000000000000063b0000000000000000000000000000000000000000000000000000000000000645000000000000000000000000000000000000000000000000000000000000063c0000000000000000000000000000000000000000000000000000000000000646000000000000000000000000000000000000000000000000000000000000063d0000000000000000000000000000000000000000000000000000000000000647000000000000000000000000000000000000000000000000000000000000063e0000000000000000000000000000000000000000000000000000000000000648000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "txsEffectsHash": "0x00e7daa0660d17d3ae04747bd24c7238da34e77cb04b0b9dd2843dd08f0fd87b", "decodedHeader": { @@ -71,12 +71,12 @@ }, "globalVariables": { "blockNumber": 1, - "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000019", + "slotNumber": "0x000000000000000000000000000000000000000000000000000000000000001a", "chainId": 31337, - "timestamp": 1728562434, + "timestamp": 1730233800, "version": 1, - "coinbase": "0xb1e072764484ad800bc7d95ebdb84ea57ec1d5d3", - "feeRecipient": "0x27f79e3e1124a5869838e688cdf225f421845f3b734541c251652fdeb0f74917", + "coinbase": "0x4b727c2ee4030668748766f0695aebffca7eed1a", + "feeRecipient": "0x17e8c896e28967f5b43e53f229edf504b61881c02885750af6b75a3b5cc9d87c", "gasFees": { "feePerDaGas": 0, "feePerL2Gas": 0 @@ -107,8 +107,8 @@ } } }, - "header": "0x1200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e00000001000000000000000000000000000000000000000000000000000000000000000400e7daa0660d17d3ae04747bd24c7238da34e77cb04b0b9dd2843dd08f0fd87b00089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00d0169cc64b8f1bd695ec8611a5602da48854dc4cc04989c4b63288b339cb1814f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000101a995cda6f326074cf650c6644269e29dbd0532e6a832238345b53ee70c878af000001000deac8396e31bc1196b442ad724bf8f751a245e518147d738cc84b9e1a56b4420000018023866f4c16f3ea1f37dd2ca42d1a635ea909b6c016e45e8434780d3741eb7dbb000001800000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000006707c502b1e072764484ad800bc7d95ebdb84ea57ec1d5d327f79e3e1124a5869838e688cdf225f421845f3b734541c251652fdeb0f74917000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "publicInputsHash": "0x003a792a91c9ec5d6a06b895be65303a3e333e173a643136512d81f4f63e0ceb", + "header": "0x1200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e00000001000000000000000000000000000000000000000000000000000000000000000400e7daa0660d17d3ae04747bd24c7238da34e77cb04b0b9dd2843dd08f0fd87b00089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00d0169cc64b8f1bd695ec8611a5602da48854dc4cc04989c4b63288b339cb1814f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000101a995cda6f326074cf650c6644269e29dbd0532e6a832238345b53ee70c878af000001000deac8396e31bc1196b442ad724bf8f751a245e518147d738cc84b9e1a56b4420000018023866f4c16f3ea1f37dd2ca42d1a635ea909b6c016e45e8434780d3741eb7dbb000001800000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000672145c84b727c2ee4030668748766f0695aebffca7eed1a17e8c896e28967f5b43e53f229edf504b61881c02885750af6b75a3b5cc9d87c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "publicInputsHash": "0x0023cd404ff57c5e39baf1f923c48af8edbd256eb8e183515b60dad7e835189b", "numTxs": 4 } } \ No newline at end of file diff --git a/l1-contracts/test/fixtures/mixed_block_2.json b/l1-contracts/test/fixtures/mixed_block_2.json index a58dd39791b..3f02a33aa7f 100644 --- a/l1-contracts/test/fixtures/mixed_block_2.json +++ b/l1-contracts/test/fixtures/mixed_block_2.json @@ -90,25 +90,25 @@ ] }, "block": { - "archive": "0x217afc0a3ea8a6d6e2297e86269a799cc51d995505d6314e57a4a0f014220c5e", - "blockHash": "0x15a14ac9f21ce7db202b0a41c2fabf60757b961b8b26d91307d6ff3e52cbd356", + "archive": "0x207d7efc92c74a8d0896583fe33ce66b214e2a4d3d48530f2bfa828ec7dca041", + "blockHash": "0x176003b50b6f8005c4cc6c3793ba8a7510283c6e093a15bbaf5fbc7a2c80223c", "body": "0x00000008000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000001420000000000000000000000000000000000000000000000000000000000000143000000000000000000000000000000000000000000000000000000000000014400000000000000000000000000000000000000000000000000000000000001450000000000000000000000000000000000000000000000000000000000000146000000000000000000000000000000000000000000000000000000000000014700000000000000000000000000000000000000000000000000000000000001480000000000000000000000000000000000000000000000000000000000000149000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014b000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000014d000000000000000000000000000000000000000000000000000000000000014e000000000000000000000000000000000000000000000000000000000000014f0000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000015100000000000000000000000000000000000000000000000000000000000001520000000000000000000000000000000000000000000000000000000000000153000000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000001550000000000000000000000000000000000000000000000000000000000000156000000000000000000000000000000000000000000000000000000000000015700000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000159000000000000000000000000000000000000000000000000000000000000015a000000000000000000000000000000000000000000000000000000000000015b000000000000000000000000000000000000000000000000000000000000015c000000000000000000000000000000000000000000000000000000000000015d000000000000000000000000000000000000000000000000000000000000015e000000000000000000000000000000000000000000000000000000000000015f0000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000001620000000000000000000000000000000000000000000000000000000000000163000000000000000000000000000000000000000000000000000000000000016400000000000000000000000000000000000000000000000000000000000001650000000000000000000000000000000000000000000000000000000000000166000000000000000000000000000000000000000000000000000000000000016700000000000000000000000000000000000000000000000000000000000001680000000000000000000000000000000000000000000000000000000000000169000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016b000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000000016d000000000000000000000000000000000000000000000000000000000000016e000000000000000000000000000000000000000000000000000000000000016f0000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000017100000000000000000000000000000000000000000000000000000000000001720000000000000000000000000000000000000000000000000000000000000173000000000000000000000000000000000000000000000000000000000000017400000000000000000000000000000000000000000000000000000000000001750000000000000000000000000000000000000000000000000000000000000176000000000000000000000000000000000000000000000000000000000000017700000000000000000000000000000000000000000000000000000000000001780000000000000000000000000000000000000000000000000000000000000179000000000000000000000000000000000000000000000000000000000000017a000000000000000000000000000000000000000000000000000000000000017b000000000000000000000000000000000000000000000000000000000000017c000000000000000000000000000000000000000000000000000000000000017d000000000000000000000000000000000000000000000000000000000000017e000000000000000000000000000000000000000000000000000000000000017f3f0000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e000000000000000000000000000000000000000000000000000000000000024f0000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000025100000000000000000000000000000000000000000000000000000000000002520000000000000000000000000000000000000000000000000000000000000253000000000000000000000000000000000000000000000000000000000000025400000000000000000000000000000000000000000000000000000000000002550000000000000000000000000000000000000000000000000000000000000256000000000000000000000000000000000000000000000000000000000000025700000000000000000000000000000000000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000259000000000000000000000000000000000000000000000000000000000000025a000000000000000000000000000000000000000000000000000000000000025b000000000000000000000000000000000000000000000000000000000000025c000000000000000000000000000000000000000000000000000000000000025d000000000000000000000000000000000000000000000000000000000000025e000000000000000000000000000000000000000000000000000000000000025f0000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e000000000000000000000000000000000000000000000000000000000000026f0000000000000000000000000000000000000000000000000000000000000270000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000002720000000000000000000000000000000000000000000000000000000000000273000000000000000000000000000000000000000000000000000000000000027400000000000000000000000000000000000000000000000000000000000002750000000000000000000000000000000000000000000000000000000000000276000000000000000000000000000000000000000000000000000000000000027700000000000000000000000000000000000000000000000000000000000002780000000000000000000000000000000000000000000000000000000000000279000000000000000000000000000000000000000000000000000000000000027a000000000000000000000000000000000000000000000000000000000000027b000000000000000000000000000000000000000000000000000000000000027c000000000000000000000000000000000000000000000000000000000000027d000000000000000000000000000000000000000000000000000000000000027e0800c47667396742a5474f325e2567bff3bb99b7f0bfd2b1a689b635d8b8726cce00284120278895e8d47084ae759f390e9634881e41369a257a36fe99a2369dc800a328b4a4a6ed156325253b4ab2af7ca00e21caf7963f0fba8a88ccdc3512c300705c99df420bface231f6855799db1d0ed7d39419abc47aa8c6efe2ae7aae2009299cc308de6d23788384411e024791a5b2448e455fbdd1d1f28f3ff76631f002d6c03d81ad764de51b0f34584645191cdc2aaae2ca08fb838d142b95d62f5003032f3618b2df0fa335d5fd548d6d85e42b4e7eb5fff9eb687facbbdecb8a60016cab7ddf4d1b440d53d10284c5c82a78b2d4e27dcdb44ef434ef4c6bad6783f0000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000541000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000542000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000543000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000544000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000545000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005460000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000054700000000000000000000000000000000000000000000000000000000000005510000000000000000000000000000000000000000000000000000000000000548000000000000000000000000000000000000000000000000000000000000055200000000000000000000000000000000000000000000000000000000000005490000000000000000000000000000000000000000000000000000000000000553000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000554000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000555000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000556000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000557000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000558000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005590000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000055a0000000000000000000000000000000000000000000000000000000000000551000000000000000000000000000000000000000000000000000000000000055b0000000000000000000000000000000000000000000000000000000000000552000000000000000000000000000000000000000000000000000000000000055c0000000000000000000000000000000000000000000000000000000000000553000000000000000000000000000000000000000000000000000000000000055d0000000000000000000000000000000000000000000000000000000000000554000000000000000000000000000000000000000000000000000000000000055e0000000000000000000000000000000000000000000000000000000000000555000000000000000000000000000000000000000000000000000000000000055f00000000000000000000000000000000000000000000000000000000000005560000000000000000000000000000000000000000000000000000000000000560000000000000000000000000000000000000000000000000000000000000055700000000000000000000000000000000000000000000000000000000000005610000000000000000000000000000000000000000000000000000000000000558000000000000000000000000000000000000000000000000000000000000056200000000000000000000000000000000000000000000000000000000000005590000000000000000000000000000000000000000000000000000000000000563000000000000000000000000000000000000000000000000000000000000055a0000000000000000000000000000000000000000000000000000000000000564000000000000000000000000000000000000000000000000000000000000055b0000000000000000000000000000000000000000000000000000000000000565000000000000000000000000000000000000000000000000000000000000055c0000000000000000000000000000000000000000000000000000000000000566000000000000000000000000000000000000000000000000000000000000055d0000000000000000000000000000000000000000000000000000000000000567000000000000000000000000000000000000000000000000000000000000055e0000000000000000000000000000000000000000000000000000000000000568000000000000000000000000000000000000000000000000000000000000055f00000000000000000000000000000000000000000000000000000000000005690000000000000000000000000000000000000000000000000000000000000560000000000000000000000000000000000000000000000000000000000000056a0000000000000000000000000000000000000000000000000000000000000561000000000000000000000000000000000000000000000000000000000000056b0000000000000000000000000000000000000000000000000000000000000562000000000000000000000000000000000000000000000000000000000000056c0000000000000000000000000000000000000000000000000000000000000563000000000000000000000000000000000000000000000000000000000000056d0000000000000000000000000000000000000000000000000000000000000564000000000000000000000000000000000000000000000000000000000000056e0000000000000000000000000000000000000000000000000000000000000565000000000000000000000000000000000000000000000000000000000000056f00000000000000000000000000000000000000000000000000000000000005660000000000000000000000000000000000000000000000000000000000000570000000000000000000000000000000000000000000000000000000000000056700000000000000000000000000000000000000000000000000000000000005710000000000000000000000000000000000000000000000000000000000000568000000000000000000000000000000000000000000000000000000000000057200000000000000000000000000000000000000000000000000000000000005690000000000000000000000000000000000000000000000000000000000000573000000000000000000000000000000000000000000000000000000000000056a0000000000000000000000000000000000000000000000000000000000000574000000000000000000000000000000000000000000000000000000000000056b0000000000000000000000000000000000000000000000000000000000000575000000000000000000000000000000000000000000000000000000000000056c0000000000000000000000000000000000000000000000000000000000000576000000000000000000000000000000000000000000000000000000000000056d0000000000000000000000000000000000000000000000000000000000000577000000000000000000000000000000000000000000000000000000000000056e0000000000000000000000000000000000000000000000000000000000000578000000000000000000000000000000000000000000000000000000000000056f00000000000000000000000000000000000000000000000000000000000005790000000000000000000000000000000000000000000000000000000000000570000000000000000000000000000000000000000000000000000000000000057a0000000000000000000000000000000000000000000000000000000000000571000000000000000000000000000000000000000000000000000000000000057b0000000000000000000000000000000000000000000000000000000000000572000000000000000000000000000000000000000000000000000000000000057c0000000000000000000000000000000000000000000000000000000000000573000000000000000000000000000000000000000000000000000000000000057d0000000000000000000000000000000000000000000000000000000000000574000000000000000000000000000000000000000000000000000000000000057e0000000000000000000000000000000000000000000000000000000000000575000000000000000000000000000000000000000000000000000000000000057f00000000000000000000000000000000000000000000000000000000000005760000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000057700000000000000000000000000000000000000000000000000000000000005810000000000000000000000000000000000000000000000000000000000000578000000000000000000000000000000000000000000000000000000000000058200000000000000000000000000000000000000000000000000000000000005790000000000000000000000000000000000000000000000000000000000000583000000000000000000000000000000000000000000000000000000000000057a0000000000000000000000000000000000000000000000000000000000000584000000000000000000000000000000000000000000000000000000000000057b0000000000000000000000000000000000000000000000000000000000000585000000000000000000000000000000000000000000000000000000000000057c0000000000000000000000000000000000000000000000000000000000000586000000000000000000000000000000000000000000000000000000000000057d0000000000000000000000000000000000000000000000000000000000000587000000000000000000000000000000000000000000000000000000000000057e0000000000000000000000000000000000000000000000000000000000000588000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000018100000000000000000000000000000000000000000000000000000000000001820000000000000000000000000000000000000000000000000000000000000183000000000000000000000000000000000000000000000000000000000000018400000000000000000000000000000000000000000000000000000000000001850000000000000000000000000000000000000000000000000000000000000186000000000000000000000000000000000000000000000000000000000000018700000000000000000000000000000000000000000000000000000000000001880000000000000000000000000000000000000000000000000000000000000189000000000000000000000000000000000000000000000000000000000000018a000000000000000000000000000000000000000000000000000000000000018b000000000000000000000000000000000000000000000000000000000000018c000000000000000000000000000000000000000000000000000000000000018d000000000000000000000000000000000000000000000000000000000000018e000000000000000000000000000000000000000000000000000000000000018f0000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000019100000000000000000000000000000000000000000000000000000000000001920000000000000000000000000000000000000000000000000000000000000193000000000000000000000000000000000000000000000000000000000000019400000000000000000000000000000000000000000000000000000000000001950000000000000000000000000000000000000000000000000000000000000196000000000000000000000000000000000000000000000000000000000000019700000000000000000000000000000000000000000000000000000000000001980000000000000000000000000000000000000000000000000000000000000199000000000000000000000000000000000000000000000000000000000000019a000000000000000000000000000000000000000000000000000000000000019b000000000000000000000000000000000000000000000000000000000000019c000000000000000000000000000000000000000000000000000000000000019d000000000000000000000000000000000000000000000000000000000000019e000000000000000000000000000000000000000000000000000000000000019f00000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001a100000000000000000000000000000000000000000000000000000000000001a200000000000000000000000000000000000000000000000000000000000001a300000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000001a500000000000000000000000000000000000000000000000000000000000001a600000000000000000000000000000000000000000000000000000000000001a700000000000000000000000000000000000000000000000000000000000001a800000000000000000000000000000000000000000000000000000000000001a900000000000000000000000000000000000000000000000000000000000001aa00000000000000000000000000000000000000000000000000000000000001ab00000000000000000000000000000000000000000000000000000000000001ac00000000000000000000000000000000000000000000000000000000000001ad00000000000000000000000000000000000000000000000000000000000001ae00000000000000000000000000000000000000000000000000000000000001af00000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b100000000000000000000000000000000000000000000000000000000000001b200000000000000000000000000000000000000000000000000000000000001b300000000000000000000000000000000000000000000000000000000000001b400000000000000000000000000000000000000000000000000000000000001b500000000000000000000000000000000000000000000000000000000000001b600000000000000000000000000000000000000000000000000000000000001b700000000000000000000000000000000000000000000000000000000000001b800000000000000000000000000000000000000000000000000000000000001b900000000000000000000000000000000000000000000000000000000000001ba00000000000000000000000000000000000000000000000000000000000001bb00000000000000000000000000000000000000000000000000000000000001bc00000000000000000000000000000000000000000000000000000000000001bd00000000000000000000000000000000000000000000000000000000000001be00000000000000000000000000000000000000000000000000000000000001bf3f0000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e000000000000000000000000000000000000000000000000000000000000028f0000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000000000000000029100000000000000000000000000000000000000000000000000000000000002920000000000000000000000000000000000000000000000000000000000000293000000000000000000000000000000000000000000000000000000000000029400000000000000000000000000000000000000000000000000000000000002950000000000000000000000000000000000000000000000000000000000000296000000000000000000000000000000000000000000000000000000000000029700000000000000000000000000000000000000000000000000000000000002980000000000000000000000000000000000000000000000000000000000000299000000000000000000000000000000000000000000000000000000000000029a000000000000000000000000000000000000000000000000000000000000029b000000000000000000000000000000000000000000000000000000000000029c000000000000000000000000000000000000000000000000000000000000029d000000000000000000000000000000000000000000000000000000000000029e000000000000000000000000000000000000000000000000000000000000029f00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002a100000000000000000000000000000000000000000000000000000000000002a200000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002a400000000000000000000000000000000000000000000000000000000000002a500000000000000000000000000000000000000000000000000000000000002a600000000000000000000000000000000000000000000000000000000000002a700000000000000000000000000000000000000000000000000000000000002a800000000000000000000000000000000000000000000000000000000000002a900000000000000000000000000000000000000000000000000000000000002aa00000000000000000000000000000000000000000000000000000000000002ab00000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000002ad00000000000000000000000000000000000000000000000000000000000002ae00000000000000000000000000000000000000000000000000000000000002af00000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002b100000000000000000000000000000000000000000000000000000000000002b200000000000000000000000000000000000000000000000000000000000002b300000000000000000000000000000000000000000000000000000000000002b400000000000000000000000000000000000000000000000000000000000002b500000000000000000000000000000000000000000000000000000000000002b600000000000000000000000000000000000000000000000000000000000002b700000000000000000000000000000000000000000000000000000000000002b800000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002ba00000000000000000000000000000000000000000000000000000000000002bb00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bd00000000000000000000000000000000000000000000000000000000000002be0800789ff73d7787206612d96dfc2143f2344de21669a3f8cae7fe9a8918631eb00084a17f00bf8793b6851a106e9155543125e0be987ad3c8334456bdda171d0b00a400f8fd336ad84f467465964008238fd1b7f9c51c22912d706cd2b874d24e002c79bdd83c14ff50a46964f838ee207564909e28af79a57fc195810d36f9b20070083c6ef1e4dd88a064e94d2582283b203cf8a2ab1667f4370eda1b4c1fe8005373dffb5b590053d7762efcf9e11280f1486ce82e7996d94ee0f5d7c093bc009eefd90eb40e79c78bac1f71ec78bdc2f8b30041974239bdc765edffed813800ea95742e72792ca7a0f66ce9f55bc47dc09d5ea08c1b9018763102776978303f0000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000581000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000582000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000583000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000584000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000585000000000000000000000000000000000000000000000000000000000000058f00000000000000000000000000000000000000000000000000000000000005860000000000000000000000000000000000000000000000000000000000000590000000000000000000000000000000000000000000000000000000000000058700000000000000000000000000000000000000000000000000000000000005910000000000000000000000000000000000000000000000000000000000000588000000000000000000000000000000000000000000000000000000000000059200000000000000000000000000000000000000000000000000000000000005890000000000000000000000000000000000000000000000000000000000000593000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000594000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000595000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000596000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000597000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000598000000000000000000000000000000000000000000000000000000000000058f00000000000000000000000000000000000000000000000000000000000005990000000000000000000000000000000000000000000000000000000000000590000000000000000000000000000000000000000000000000000000000000059a0000000000000000000000000000000000000000000000000000000000000591000000000000000000000000000000000000000000000000000000000000059b0000000000000000000000000000000000000000000000000000000000000592000000000000000000000000000000000000000000000000000000000000059c0000000000000000000000000000000000000000000000000000000000000593000000000000000000000000000000000000000000000000000000000000059d0000000000000000000000000000000000000000000000000000000000000594000000000000000000000000000000000000000000000000000000000000059e0000000000000000000000000000000000000000000000000000000000000595000000000000000000000000000000000000000000000000000000000000059f000000000000000000000000000000000000000000000000000000000000059600000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000059700000000000000000000000000000000000000000000000000000000000005a1000000000000000000000000000000000000000000000000000000000000059800000000000000000000000000000000000000000000000000000000000005a2000000000000000000000000000000000000000000000000000000000000059900000000000000000000000000000000000000000000000000000000000005a3000000000000000000000000000000000000000000000000000000000000059a00000000000000000000000000000000000000000000000000000000000005a4000000000000000000000000000000000000000000000000000000000000059b00000000000000000000000000000000000000000000000000000000000005a5000000000000000000000000000000000000000000000000000000000000059c00000000000000000000000000000000000000000000000000000000000005a6000000000000000000000000000000000000000000000000000000000000059d00000000000000000000000000000000000000000000000000000000000005a7000000000000000000000000000000000000000000000000000000000000059e00000000000000000000000000000000000000000000000000000000000005a8000000000000000000000000000000000000000000000000000000000000059f00000000000000000000000000000000000000000000000000000000000005a900000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000000000000000005aa00000000000000000000000000000000000000000000000000000000000005a100000000000000000000000000000000000000000000000000000000000005ab00000000000000000000000000000000000000000000000000000000000005a200000000000000000000000000000000000000000000000000000000000005ac00000000000000000000000000000000000000000000000000000000000005a300000000000000000000000000000000000000000000000000000000000005ad00000000000000000000000000000000000000000000000000000000000005a400000000000000000000000000000000000000000000000000000000000005ae00000000000000000000000000000000000000000000000000000000000005a500000000000000000000000000000000000000000000000000000000000005af00000000000000000000000000000000000000000000000000000000000005a600000000000000000000000000000000000000000000000000000000000005b000000000000000000000000000000000000000000000000000000000000005a700000000000000000000000000000000000000000000000000000000000005b100000000000000000000000000000000000000000000000000000000000005a800000000000000000000000000000000000000000000000000000000000005b200000000000000000000000000000000000000000000000000000000000005a900000000000000000000000000000000000000000000000000000000000005b300000000000000000000000000000000000000000000000000000000000005aa00000000000000000000000000000000000000000000000000000000000005b400000000000000000000000000000000000000000000000000000000000005ab00000000000000000000000000000000000000000000000000000000000005b500000000000000000000000000000000000000000000000000000000000005ac00000000000000000000000000000000000000000000000000000000000005b600000000000000000000000000000000000000000000000000000000000005ad00000000000000000000000000000000000000000000000000000000000005b700000000000000000000000000000000000000000000000000000000000005ae00000000000000000000000000000000000000000000000000000000000005b800000000000000000000000000000000000000000000000000000000000005af00000000000000000000000000000000000000000000000000000000000005b900000000000000000000000000000000000000000000000000000000000005b000000000000000000000000000000000000000000000000000000000000005ba00000000000000000000000000000000000000000000000000000000000005b100000000000000000000000000000000000000000000000000000000000005bb00000000000000000000000000000000000000000000000000000000000005b200000000000000000000000000000000000000000000000000000000000005bc00000000000000000000000000000000000000000000000000000000000005b300000000000000000000000000000000000000000000000000000000000005bd00000000000000000000000000000000000000000000000000000000000005b400000000000000000000000000000000000000000000000000000000000005be00000000000000000000000000000000000000000000000000000000000005b500000000000000000000000000000000000000000000000000000000000005bf00000000000000000000000000000000000000000000000000000000000005b600000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000005b700000000000000000000000000000000000000000000000000000000000005c100000000000000000000000000000000000000000000000000000000000005b800000000000000000000000000000000000000000000000000000000000005c200000000000000000000000000000000000000000000000000000000000005b900000000000000000000000000000000000000000000000000000000000005c300000000000000000000000000000000000000000000000000000000000005ba00000000000000000000000000000000000000000000000000000000000005c400000000000000000000000000000000000000000000000000000000000005bb00000000000000000000000000000000000000000000000000000000000005c500000000000000000000000000000000000000000000000000000000000005bc00000000000000000000000000000000000000000000000000000000000005c600000000000000000000000000000000000000000000000000000000000005bd00000000000000000000000000000000000000000000000000000000000005c700000000000000000000000000000000000000000000000000000000000005be00000000000000000000000000000000000000000000000000000000000005c80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c100000000000000000000000000000000000000000000000000000000000001c200000000000000000000000000000000000000000000000000000000000001c300000000000000000000000000000000000000000000000000000000000001c400000000000000000000000000000000000000000000000000000000000001c500000000000000000000000000000000000000000000000000000000000001c600000000000000000000000000000000000000000000000000000000000001c700000000000000000000000000000000000000000000000000000000000001c800000000000000000000000000000000000000000000000000000000000001c900000000000000000000000000000000000000000000000000000000000001ca00000000000000000000000000000000000000000000000000000000000001cb00000000000000000000000000000000000000000000000000000000000001cc00000000000000000000000000000000000000000000000000000000000001cd00000000000000000000000000000000000000000000000000000000000001ce00000000000000000000000000000000000000000000000000000000000001cf00000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001d100000000000000000000000000000000000000000000000000000000000001d200000000000000000000000000000000000000000000000000000000000001d300000000000000000000000000000000000000000000000000000000000001d400000000000000000000000000000000000000000000000000000000000001d500000000000000000000000000000000000000000000000000000000000001d600000000000000000000000000000000000000000000000000000000000001d700000000000000000000000000000000000000000000000000000000000001d800000000000000000000000000000000000000000000000000000000000001d900000000000000000000000000000000000000000000000000000000000001da00000000000000000000000000000000000000000000000000000000000001db00000000000000000000000000000000000000000000000000000000000001dc00000000000000000000000000000000000000000000000000000000000001dd00000000000000000000000000000000000000000000000000000000000001de00000000000000000000000000000000000000000000000000000000000001df00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001e100000000000000000000000000000000000000000000000000000000000001e200000000000000000000000000000000000000000000000000000000000001e300000000000000000000000000000000000000000000000000000000000001e400000000000000000000000000000000000000000000000000000000000001e500000000000000000000000000000000000000000000000000000000000001e600000000000000000000000000000000000000000000000000000000000001e700000000000000000000000000000000000000000000000000000000000001e800000000000000000000000000000000000000000000000000000000000001e900000000000000000000000000000000000000000000000000000000000001ea00000000000000000000000000000000000000000000000000000000000001eb00000000000000000000000000000000000000000000000000000000000001ec00000000000000000000000000000000000000000000000000000000000001ed00000000000000000000000000000000000000000000000000000000000001ee00000000000000000000000000000000000000000000000000000000000001ef00000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000001f100000000000000000000000000000000000000000000000000000000000001f200000000000000000000000000000000000000000000000000000000000001f300000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000001f600000000000000000000000000000000000000000000000000000000000001f700000000000000000000000000000000000000000000000000000000000001f800000000000000000000000000000000000000000000000000000000000001f900000000000000000000000000000000000000000000000000000000000001fa00000000000000000000000000000000000000000000000000000000000001fb00000000000000000000000000000000000000000000000000000000000001fc00000000000000000000000000000000000000000000000000000000000001fd00000000000000000000000000000000000000000000000000000000000001fe00000000000000000000000000000000000000000000000000000000000001ff3f00000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002c100000000000000000000000000000000000000000000000000000000000002c200000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002c400000000000000000000000000000000000000000000000000000000000002c500000000000000000000000000000000000000000000000000000000000002c600000000000000000000000000000000000000000000000000000000000002c700000000000000000000000000000000000000000000000000000000000002c800000000000000000000000000000000000000000000000000000000000002c900000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000002cb00000000000000000000000000000000000000000000000000000000000002cc00000000000000000000000000000000000000000000000000000000000002cd00000000000000000000000000000000000000000000000000000000000002ce00000000000000000000000000000000000000000000000000000000000002cf00000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002d100000000000000000000000000000000000000000000000000000000000002d200000000000000000000000000000000000000000000000000000000000002d300000000000000000000000000000000000000000000000000000000000002d400000000000000000000000000000000000000000000000000000000000002d500000000000000000000000000000000000000000000000000000000000002d600000000000000000000000000000000000000000000000000000000000002d700000000000000000000000000000000000000000000000000000000000002d800000000000000000000000000000000000000000000000000000000000002d900000000000000000000000000000000000000000000000000000000000002da00000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000002dc00000000000000000000000000000000000000000000000000000000000002dd00000000000000000000000000000000000000000000000000000000000002de00000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002e100000000000000000000000000000000000000000000000000000000000002e200000000000000000000000000000000000000000000000000000000000002e300000000000000000000000000000000000000000000000000000000000002e400000000000000000000000000000000000000000000000000000000000002e500000000000000000000000000000000000000000000000000000000000002e600000000000000000000000000000000000000000000000000000000000002e700000000000000000000000000000000000000000000000000000000000002e800000000000000000000000000000000000000000000000000000000000002e900000000000000000000000000000000000000000000000000000000000002ea00000000000000000000000000000000000000000000000000000000000002eb00000000000000000000000000000000000000000000000000000000000002ec00000000000000000000000000000000000000000000000000000000000002ed00000000000000000000000000000000000000000000000000000000000002ee00000000000000000000000000000000000000000000000000000000000002ef00000000000000000000000000000000000000000000000000000000000002f000000000000000000000000000000000000000000000000000000000000002f100000000000000000000000000000000000000000000000000000000000002f200000000000000000000000000000000000000000000000000000000000002f300000000000000000000000000000000000000000000000000000000000002f400000000000000000000000000000000000000000000000000000000000002f500000000000000000000000000000000000000000000000000000000000002f600000000000000000000000000000000000000000000000000000000000002f700000000000000000000000000000000000000000000000000000000000002f800000000000000000000000000000000000000000000000000000000000002f900000000000000000000000000000000000000000000000000000000000002fa00000000000000000000000000000000000000000000000000000000000002fb00000000000000000000000000000000000000000000000000000000000002fc00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000002fe0800a68d2df6e48c8b31f4c76529e586de2cd9d0f2f2fdfbc4c523db9e3a29e9b80016c236e57bf17afe324437acd1060772e3f31d4f9e734ad758d0627c4ba2a200d659011ddde95e32886bdd8c9464da1ca144ccadd539f0992b4abb491d812a00c8025bb9006a976ebd6ad940155f15f89ca0bb7312b53841fc257e7ed689c8004165f2c46b70fb183468b38f31bba7aa9d22ce8dfb61fe721470729ce1c6b100afeb60dd983514ebbaee141b2196f3eb3c9c299f576a0097872cc85a79a43f005f6bfee53d20a474901493558419dbceb3aca40e5e18915d38031498f4d2bb008791217ee341ec5dc11f7d7a31160fb1b1f2cf767062970e9526e5751956253f00000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005c100000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005c200000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005c300000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005c400000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005c500000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005c600000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005c700000000000000000000000000000000000000000000000000000000000005d100000000000000000000000000000000000000000000000000000000000005c800000000000000000000000000000000000000000000000000000000000005d200000000000000000000000000000000000000000000000000000000000005c900000000000000000000000000000000000000000000000000000000000005d300000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005d400000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005d500000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005d600000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005d700000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005d800000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005d900000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005da00000000000000000000000000000000000000000000000000000000000005d100000000000000000000000000000000000000000000000000000000000005db00000000000000000000000000000000000000000000000000000000000005d200000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000005d300000000000000000000000000000000000000000000000000000000000005dd00000000000000000000000000000000000000000000000000000000000005d400000000000000000000000000000000000000000000000000000000000005de00000000000000000000000000000000000000000000000000000000000005d500000000000000000000000000000000000000000000000000000000000005df00000000000000000000000000000000000000000000000000000000000005d600000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000005d700000000000000000000000000000000000000000000000000000000000005e100000000000000000000000000000000000000000000000000000000000005d800000000000000000000000000000000000000000000000000000000000005e200000000000000000000000000000000000000000000000000000000000005d900000000000000000000000000000000000000000000000000000000000005e300000000000000000000000000000000000000000000000000000000000005da00000000000000000000000000000000000000000000000000000000000005e400000000000000000000000000000000000000000000000000000000000005db00000000000000000000000000000000000000000000000000000000000005e500000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000005e600000000000000000000000000000000000000000000000000000000000005dd00000000000000000000000000000000000000000000000000000000000005e700000000000000000000000000000000000000000000000000000000000005de00000000000000000000000000000000000000000000000000000000000005e800000000000000000000000000000000000000000000000000000000000005df00000000000000000000000000000000000000000000000000000000000005e900000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000005ea00000000000000000000000000000000000000000000000000000000000005e100000000000000000000000000000000000000000000000000000000000005eb00000000000000000000000000000000000000000000000000000000000005e200000000000000000000000000000000000000000000000000000000000005ec00000000000000000000000000000000000000000000000000000000000005e300000000000000000000000000000000000000000000000000000000000005ed00000000000000000000000000000000000000000000000000000000000005e400000000000000000000000000000000000000000000000000000000000005ee00000000000000000000000000000000000000000000000000000000000005e500000000000000000000000000000000000000000000000000000000000005ef00000000000000000000000000000000000000000000000000000000000005e600000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000005e700000000000000000000000000000000000000000000000000000000000005f100000000000000000000000000000000000000000000000000000000000005e800000000000000000000000000000000000000000000000000000000000005f200000000000000000000000000000000000000000000000000000000000005e900000000000000000000000000000000000000000000000000000000000005f300000000000000000000000000000000000000000000000000000000000005ea00000000000000000000000000000000000000000000000000000000000005f400000000000000000000000000000000000000000000000000000000000005eb00000000000000000000000000000000000000000000000000000000000005f500000000000000000000000000000000000000000000000000000000000005ec00000000000000000000000000000000000000000000000000000000000005f600000000000000000000000000000000000000000000000000000000000005ed00000000000000000000000000000000000000000000000000000000000005f700000000000000000000000000000000000000000000000000000000000005ee00000000000000000000000000000000000000000000000000000000000005f800000000000000000000000000000000000000000000000000000000000005ef00000000000000000000000000000000000000000000000000000000000005f900000000000000000000000000000000000000000000000000000000000005f000000000000000000000000000000000000000000000000000000000000005fa00000000000000000000000000000000000000000000000000000000000005f100000000000000000000000000000000000000000000000000000000000005fb00000000000000000000000000000000000000000000000000000000000005f200000000000000000000000000000000000000000000000000000000000005fc00000000000000000000000000000000000000000000000000000000000005f300000000000000000000000000000000000000000000000000000000000005fd00000000000000000000000000000000000000000000000000000000000005f400000000000000000000000000000000000000000000000000000000000005fe00000000000000000000000000000000000000000000000000000000000005f500000000000000000000000000000000000000000000000000000000000005ff00000000000000000000000000000000000000000000000000000000000005f6000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000005f7000000000000000000000000000000000000000000000000000000000000060100000000000000000000000000000000000000000000000000000000000005f8000000000000000000000000000000000000000000000000000000000000060200000000000000000000000000000000000000000000000000000000000005f9000000000000000000000000000000000000000000000000000000000000060300000000000000000000000000000000000000000000000000000000000005fa000000000000000000000000000000000000000000000000000000000000060400000000000000000000000000000000000000000000000000000000000005fb000000000000000000000000000000000000000000000000000000000000060500000000000000000000000000000000000000000000000000000000000005fc000000000000000000000000000000000000000000000000000000000000060600000000000000000000000000000000000000000000000000000000000005fd000000000000000000000000000000000000000000000000000000000000060700000000000000000000000000000000000000000000000000000000000005fe0000000000000000000000000000000000000000000000000000000000000608000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020100000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000000000000000000000203000000000000000000000000000000000000000000000000000000000000020400000000000000000000000000000000000000000000000000000000000002050000000000000000000000000000000000000000000000000000000000000206000000000000000000000000000000000000000000000000000000000000020700000000000000000000000000000000000000000000000000000000000002080000000000000000000000000000000000000000000000000000000000000209000000000000000000000000000000000000000000000000000000000000020a000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000020c000000000000000000000000000000000000000000000000000000000000020d000000000000000000000000000000000000000000000000000000000000020e000000000000000000000000000000000000000000000000000000000000020f0000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000021100000000000000000000000000000000000000000000000000000000000002120000000000000000000000000000000000000000000000000000000000000213000000000000000000000000000000000000000000000000000000000000021400000000000000000000000000000000000000000000000000000000000002150000000000000000000000000000000000000000000000000000000000000216000000000000000000000000000000000000000000000000000000000000021700000000000000000000000000000000000000000000000000000000000002180000000000000000000000000000000000000000000000000000000000000219000000000000000000000000000000000000000000000000000000000000021a000000000000000000000000000000000000000000000000000000000000021b000000000000000000000000000000000000000000000000000000000000021c000000000000000000000000000000000000000000000000000000000000021d000000000000000000000000000000000000000000000000000000000000021e000000000000000000000000000000000000000000000000000000000000021f0000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000022100000000000000000000000000000000000000000000000000000000000002220000000000000000000000000000000000000000000000000000000000000223000000000000000000000000000000000000000000000000000000000000022400000000000000000000000000000000000000000000000000000000000002250000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000022700000000000000000000000000000000000000000000000000000000000002280000000000000000000000000000000000000000000000000000000000000229000000000000000000000000000000000000000000000000000000000000022a000000000000000000000000000000000000000000000000000000000000022b000000000000000000000000000000000000000000000000000000000000022c000000000000000000000000000000000000000000000000000000000000022d000000000000000000000000000000000000000000000000000000000000022e000000000000000000000000000000000000000000000000000000000000022f0000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000023100000000000000000000000000000000000000000000000000000000000002320000000000000000000000000000000000000000000000000000000000000233000000000000000000000000000000000000000000000000000000000000023400000000000000000000000000000000000000000000000000000000000002350000000000000000000000000000000000000000000000000000000000000236000000000000000000000000000000000000000000000000000000000000023700000000000000000000000000000000000000000000000000000000000002380000000000000000000000000000000000000000000000000000000000000239000000000000000000000000000000000000000000000000000000000000023a000000000000000000000000000000000000000000000000000000000000023b000000000000000000000000000000000000000000000000000000000000023c000000000000000000000000000000000000000000000000000000000000023d000000000000000000000000000000000000000000000000000000000000023e000000000000000000000000000000000000000000000000000000000000023f3f0000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030100000000000000000000000000000000000000000000000000000000000003020000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000030400000000000000000000000000000000000000000000000000000000000003050000000000000000000000000000000000000000000000000000000000000306000000000000000000000000000000000000000000000000000000000000030700000000000000000000000000000000000000000000000000000000000003080000000000000000000000000000000000000000000000000000000000000309000000000000000000000000000000000000000000000000000000000000030a000000000000000000000000000000000000000000000000000000000000030b000000000000000000000000000000000000000000000000000000000000030c000000000000000000000000000000000000000000000000000000000000030d000000000000000000000000000000000000000000000000000000000000030e000000000000000000000000000000000000000000000000000000000000030f0000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000031100000000000000000000000000000000000000000000000000000000000003120000000000000000000000000000000000000000000000000000000000000313000000000000000000000000000000000000000000000000000000000000031400000000000000000000000000000000000000000000000000000000000003150000000000000000000000000000000000000000000000000000000000000316000000000000000000000000000000000000000000000000000000000000031700000000000000000000000000000000000000000000000000000000000003180000000000000000000000000000000000000000000000000000000000000319000000000000000000000000000000000000000000000000000000000000031a000000000000000000000000000000000000000000000000000000000000031b000000000000000000000000000000000000000000000000000000000000031c000000000000000000000000000000000000000000000000000000000000031d000000000000000000000000000000000000000000000000000000000000031e000000000000000000000000000000000000000000000000000000000000031f0000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032100000000000000000000000000000000000000000000000000000000000003220000000000000000000000000000000000000000000000000000000000000323000000000000000000000000000000000000000000000000000000000000032400000000000000000000000000000000000000000000000000000000000003250000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000032700000000000000000000000000000000000000000000000000000000000003280000000000000000000000000000000000000000000000000000000000000329000000000000000000000000000000000000000000000000000000000000032a000000000000000000000000000000000000000000000000000000000000032b000000000000000000000000000000000000000000000000000000000000032c000000000000000000000000000000000000000000000000000000000000032d000000000000000000000000000000000000000000000000000000000000032e000000000000000000000000000000000000000000000000000000000000032f0000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033100000000000000000000000000000000000000000000000000000000000003320000000000000000000000000000000000000000000000000000000000000333000000000000000000000000000000000000000000000000000000000000033400000000000000000000000000000000000000000000000000000000000003350000000000000000000000000000000000000000000000000000000000000336000000000000000000000000000000000000000000000000000000000000033700000000000000000000000000000000000000000000000000000000000003380000000000000000000000000000000000000000000000000000000000000339000000000000000000000000000000000000000000000000000000000000033a000000000000000000000000000000000000000000000000000000000000033b000000000000000000000000000000000000000000000000000000000000033c000000000000000000000000000000000000000000000000000000000000033d000000000000000000000000000000000000000000000000000000000000033e0800c064a9343bfaf1345a5ad188e96aa4c5bad0b4a5590da51a9ff8f3c98ade8d008803d57dd0923c95a01b2bfbee4df6d66dc7baee1d2cd2770575feb86d040200ea64eed9489feb7bdf29bf817a6e8772e549da7b291028852d0dd3810cee9500947e8f904d41be8a4e08b146b4e1f0cd88f55722ef987d1d485c4196ab9f71002e5d9ed5d71bc3bea6edf988c4b9ba7fceb0815180d893852ed343c64ab55c0040f7f519ec89d360d83e242b6c0ce049d2b3356b8cfbf1ff3b733ef0f8a089006f5cfe5fc4a7b87c634f9e253a7cea2052229250d0c0e913eb50b5ef3612de00b759fce0eed36bb653b67255cce111b3529c383bd7d2b758f8cb53a4451f273f0000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000601000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000602000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000603000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000604000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000605000000000000000000000000000000000000000000000000000000000000060f00000000000000000000000000000000000000000000000000000000000006060000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000060700000000000000000000000000000000000000000000000000000000000006110000000000000000000000000000000000000000000000000000000000000608000000000000000000000000000000000000000000000000000000000000061200000000000000000000000000000000000000000000000000000000000006090000000000000000000000000000000000000000000000000000000000000613000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000614000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000615000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000616000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000617000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000618000000000000000000000000000000000000000000000000000000000000060f00000000000000000000000000000000000000000000000000000000000006190000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000061a0000000000000000000000000000000000000000000000000000000000000611000000000000000000000000000000000000000000000000000000000000061b0000000000000000000000000000000000000000000000000000000000000612000000000000000000000000000000000000000000000000000000000000061c0000000000000000000000000000000000000000000000000000000000000613000000000000000000000000000000000000000000000000000000000000061d0000000000000000000000000000000000000000000000000000000000000614000000000000000000000000000000000000000000000000000000000000061e0000000000000000000000000000000000000000000000000000000000000615000000000000000000000000000000000000000000000000000000000000061f00000000000000000000000000000000000000000000000000000000000006160000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000061700000000000000000000000000000000000000000000000000000000000006210000000000000000000000000000000000000000000000000000000000000618000000000000000000000000000000000000000000000000000000000000062200000000000000000000000000000000000000000000000000000000000006190000000000000000000000000000000000000000000000000000000000000623000000000000000000000000000000000000000000000000000000000000061a0000000000000000000000000000000000000000000000000000000000000624000000000000000000000000000000000000000000000000000000000000061b0000000000000000000000000000000000000000000000000000000000000625000000000000000000000000000000000000000000000000000000000000061c0000000000000000000000000000000000000000000000000000000000000626000000000000000000000000000000000000000000000000000000000000061d0000000000000000000000000000000000000000000000000000000000000627000000000000000000000000000000000000000000000000000000000000061e0000000000000000000000000000000000000000000000000000000000000628000000000000000000000000000000000000000000000000000000000000061f00000000000000000000000000000000000000000000000000000000000006290000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000062a0000000000000000000000000000000000000000000000000000000000000621000000000000000000000000000000000000000000000000000000000000062b0000000000000000000000000000000000000000000000000000000000000622000000000000000000000000000000000000000000000000000000000000062c0000000000000000000000000000000000000000000000000000000000000623000000000000000000000000000000000000000000000000000000000000062d0000000000000000000000000000000000000000000000000000000000000624000000000000000000000000000000000000000000000000000000000000062e0000000000000000000000000000000000000000000000000000000000000625000000000000000000000000000000000000000000000000000000000000062f00000000000000000000000000000000000000000000000000000000000006260000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000062700000000000000000000000000000000000000000000000000000000000006310000000000000000000000000000000000000000000000000000000000000628000000000000000000000000000000000000000000000000000000000000063200000000000000000000000000000000000000000000000000000000000006290000000000000000000000000000000000000000000000000000000000000633000000000000000000000000000000000000000000000000000000000000062a0000000000000000000000000000000000000000000000000000000000000634000000000000000000000000000000000000000000000000000000000000062b0000000000000000000000000000000000000000000000000000000000000635000000000000000000000000000000000000000000000000000000000000062c0000000000000000000000000000000000000000000000000000000000000636000000000000000000000000000000000000000000000000000000000000062d0000000000000000000000000000000000000000000000000000000000000637000000000000000000000000000000000000000000000000000000000000062e0000000000000000000000000000000000000000000000000000000000000638000000000000000000000000000000000000000000000000000000000000062f00000000000000000000000000000000000000000000000000000000000006390000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000063a0000000000000000000000000000000000000000000000000000000000000631000000000000000000000000000000000000000000000000000000000000063b0000000000000000000000000000000000000000000000000000000000000632000000000000000000000000000000000000000000000000000000000000063c0000000000000000000000000000000000000000000000000000000000000633000000000000000000000000000000000000000000000000000000000000063d0000000000000000000000000000000000000000000000000000000000000634000000000000000000000000000000000000000000000000000000000000063e0000000000000000000000000000000000000000000000000000000000000635000000000000000000000000000000000000000000000000000000000000063f00000000000000000000000000000000000000000000000000000000000006360000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000063700000000000000000000000000000000000000000000000000000000000006410000000000000000000000000000000000000000000000000000000000000638000000000000000000000000000000000000000000000000000000000000064200000000000000000000000000000000000000000000000000000000000006390000000000000000000000000000000000000000000000000000000000000643000000000000000000000000000000000000000000000000000000000000063a0000000000000000000000000000000000000000000000000000000000000644000000000000000000000000000000000000000000000000000000000000063b0000000000000000000000000000000000000000000000000000000000000645000000000000000000000000000000000000000000000000000000000000063c0000000000000000000000000000000000000000000000000000000000000646000000000000000000000000000000000000000000000000000000000000063d0000000000000000000000000000000000000000000000000000000000000647000000000000000000000000000000000000000000000000000000000000063e0000000000000000000000000000000000000000000000000000000000000648000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e000000000000000000000000000000000000000000000000000000000000024f0000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000025100000000000000000000000000000000000000000000000000000000000002520000000000000000000000000000000000000000000000000000000000000253000000000000000000000000000000000000000000000000000000000000025400000000000000000000000000000000000000000000000000000000000002550000000000000000000000000000000000000000000000000000000000000256000000000000000000000000000000000000000000000000000000000000025700000000000000000000000000000000000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000259000000000000000000000000000000000000000000000000000000000000025a000000000000000000000000000000000000000000000000000000000000025b000000000000000000000000000000000000000000000000000000000000025c000000000000000000000000000000000000000000000000000000000000025d000000000000000000000000000000000000000000000000000000000000025e000000000000000000000000000000000000000000000000000000000000025f0000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e000000000000000000000000000000000000000000000000000000000000026f0000000000000000000000000000000000000000000000000000000000000270000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000002720000000000000000000000000000000000000000000000000000000000000273000000000000000000000000000000000000000000000000000000000000027400000000000000000000000000000000000000000000000000000000000002750000000000000000000000000000000000000000000000000000000000000276000000000000000000000000000000000000000000000000000000000000027700000000000000000000000000000000000000000000000000000000000002780000000000000000000000000000000000000000000000000000000000000279000000000000000000000000000000000000000000000000000000000000027a000000000000000000000000000000000000000000000000000000000000027b000000000000000000000000000000000000000000000000000000000000027c000000000000000000000000000000000000000000000000000000000000027d000000000000000000000000000000000000000000000000000000000000027e000000000000000000000000000000000000000000000000000000000000027f3f0000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000003420000000000000000000000000000000000000000000000000000000000000343000000000000000000000000000000000000000000000000000000000000034400000000000000000000000000000000000000000000000000000000000003450000000000000000000000000000000000000000000000000000000000000346000000000000000000000000000000000000000000000000000000000000034700000000000000000000000000000000000000000000000000000000000003480000000000000000000000000000000000000000000000000000000000000349000000000000000000000000000000000000000000000000000000000000034a000000000000000000000000000000000000000000000000000000000000034b000000000000000000000000000000000000000000000000000000000000034c000000000000000000000000000000000000000000000000000000000000034d000000000000000000000000000000000000000000000000000000000000034e000000000000000000000000000000000000000000000000000000000000034f0000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000035100000000000000000000000000000000000000000000000000000000000003520000000000000000000000000000000000000000000000000000000000000353000000000000000000000000000000000000000000000000000000000000035400000000000000000000000000000000000000000000000000000000000003550000000000000000000000000000000000000000000000000000000000000356000000000000000000000000000000000000000000000000000000000000035700000000000000000000000000000000000000000000000000000000000003580000000000000000000000000000000000000000000000000000000000000359000000000000000000000000000000000000000000000000000000000000035a000000000000000000000000000000000000000000000000000000000000035b000000000000000000000000000000000000000000000000000000000000035c000000000000000000000000000000000000000000000000000000000000035d000000000000000000000000000000000000000000000000000000000000035e000000000000000000000000000000000000000000000000000000000000035f0000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000036100000000000000000000000000000000000000000000000000000000000003620000000000000000000000000000000000000000000000000000000000000363000000000000000000000000000000000000000000000000000000000000036400000000000000000000000000000000000000000000000000000000000003650000000000000000000000000000000000000000000000000000000000000366000000000000000000000000000000000000000000000000000000000000036700000000000000000000000000000000000000000000000000000000000003680000000000000000000000000000000000000000000000000000000000000369000000000000000000000000000000000000000000000000000000000000036a000000000000000000000000000000000000000000000000000000000000036b000000000000000000000000000000000000000000000000000000000000036c000000000000000000000000000000000000000000000000000000000000036d000000000000000000000000000000000000000000000000000000000000036e000000000000000000000000000000000000000000000000000000000000036f0000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000037100000000000000000000000000000000000000000000000000000000000003720000000000000000000000000000000000000000000000000000000000000373000000000000000000000000000000000000000000000000000000000000037400000000000000000000000000000000000000000000000000000000000003750000000000000000000000000000000000000000000000000000000000000376000000000000000000000000000000000000000000000000000000000000037700000000000000000000000000000000000000000000000000000000000003780000000000000000000000000000000000000000000000000000000000000379000000000000000000000000000000000000000000000000000000000000037a000000000000000000000000000000000000000000000000000000000000037b000000000000000000000000000000000000000000000000000000000000037c000000000000000000000000000000000000000000000000000000000000037d000000000000000000000000000000000000000000000000000000000000037e08003bbb1177505f3433bb062787fcac6585d30fa1a7e0b809ca5eef1cecc56cd0002d5b86280022d106b72e4425ea49f927ca2b8138fc13b3d9feeaf36ae61fb100adab9d66b73ae23a6e9127ebe0bcd963ef4312dd66878a6be131d39a7ee01c00d48f30dbb82c96c734c8abe33f4f9f75e6d0ba4462ee07d73c5335e04a02e900f31a3a4e7333ac6043a929fca12f5347e9e8bf1d67f5993860a774f10b77bc00230e3132bfa5df23e2e018b20cd4e0c75555825ee7924da73f017a279d39cd0095e2affd6b67c6ecbf77fa1e638139498e1642abb468c58ca8ce275260ea6100362e0941035fd171fab926caf55d18b22f7de99d60ac6f454386a9cce4a1ff3f0000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064a0000000000000000000000000000000000000000000000000000000000000641000000000000000000000000000000000000000000000000000000000000064b0000000000000000000000000000000000000000000000000000000000000642000000000000000000000000000000000000000000000000000000000000064c0000000000000000000000000000000000000000000000000000000000000643000000000000000000000000000000000000000000000000000000000000064d0000000000000000000000000000000000000000000000000000000000000644000000000000000000000000000000000000000000000000000000000000064e0000000000000000000000000000000000000000000000000000000000000645000000000000000000000000000000000000000000000000000000000000064f00000000000000000000000000000000000000000000000000000000000006460000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000064700000000000000000000000000000000000000000000000000000000000006510000000000000000000000000000000000000000000000000000000000000648000000000000000000000000000000000000000000000000000000000000065200000000000000000000000000000000000000000000000000000000000006490000000000000000000000000000000000000000000000000000000000000653000000000000000000000000000000000000000000000000000000000000064a0000000000000000000000000000000000000000000000000000000000000654000000000000000000000000000000000000000000000000000000000000064b0000000000000000000000000000000000000000000000000000000000000655000000000000000000000000000000000000000000000000000000000000064c0000000000000000000000000000000000000000000000000000000000000656000000000000000000000000000000000000000000000000000000000000064d0000000000000000000000000000000000000000000000000000000000000657000000000000000000000000000000000000000000000000000000000000064e0000000000000000000000000000000000000000000000000000000000000658000000000000000000000000000000000000000000000000000000000000064f00000000000000000000000000000000000000000000000000000000000006590000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000065a0000000000000000000000000000000000000000000000000000000000000651000000000000000000000000000000000000000000000000000000000000065b0000000000000000000000000000000000000000000000000000000000000652000000000000000000000000000000000000000000000000000000000000065c0000000000000000000000000000000000000000000000000000000000000653000000000000000000000000000000000000000000000000000000000000065d0000000000000000000000000000000000000000000000000000000000000654000000000000000000000000000000000000000000000000000000000000065e0000000000000000000000000000000000000000000000000000000000000655000000000000000000000000000000000000000000000000000000000000065f00000000000000000000000000000000000000000000000000000000000006560000000000000000000000000000000000000000000000000000000000000660000000000000000000000000000000000000000000000000000000000000065700000000000000000000000000000000000000000000000000000000000006610000000000000000000000000000000000000000000000000000000000000658000000000000000000000000000000000000000000000000000000000000066200000000000000000000000000000000000000000000000000000000000006590000000000000000000000000000000000000000000000000000000000000663000000000000000000000000000000000000000000000000000000000000065a0000000000000000000000000000000000000000000000000000000000000664000000000000000000000000000000000000000000000000000000000000065b0000000000000000000000000000000000000000000000000000000000000665000000000000000000000000000000000000000000000000000000000000065c0000000000000000000000000000000000000000000000000000000000000666000000000000000000000000000000000000000000000000000000000000065d0000000000000000000000000000000000000000000000000000000000000667000000000000000000000000000000000000000000000000000000000000065e0000000000000000000000000000000000000000000000000000000000000668000000000000000000000000000000000000000000000000000000000000065f00000000000000000000000000000000000000000000000000000000000006690000000000000000000000000000000000000000000000000000000000000660000000000000000000000000000000000000000000000000000000000000066a0000000000000000000000000000000000000000000000000000000000000661000000000000000000000000000000000000000000000000000000000000066b0000000000000000000000000000000000000000000000000000000000000662000000000000000000000000000000000000000000000000000000000000066c0000000000000000000000000000000000000000000000000000000000000663000000000000000000000000000000000000000000000000000000000000066d0000000000000000000000000000000000000000000000000000000000000664000000000000000000000000000000000000000000000000000000000000066e0000000000000000000000000000000000000000000000000000000000000665000000000000000000000000000000000000000000000000000000000000066f00000000000000000000000000000000000000000000000000000000000006660000000000000000000000000000000000000000000000000000000000000670000000000000000000000000000000000000000000000000000000000000066700000000000000000000000000000000000000000000000000000000000006710000000000000000000000000000000000000000000000000000000000000668000000000000000000000000000000000000000000000000000000000000067200000000000000000000000000000000000000000000000000000000000006690000000000000000000000000000000000000000000000000000000000000673000000000000000000000000000000000000000000000000000000000000066a0000000000000000000000000000000000000000000000000000000000000674000000000000000000000000000000000000000000000000000000000000066b0000000000000000000000000000000000000000000000000000000000000675000000000000000000000000000000000000000000000000000000000000066c0000000000000000000000000000000000000000000000000000000000000676000000000000000000000000000000000000000000000000000000000000066d0000000000000000000000000000000000000000000000000000000000000677000000000000000000000000000000000000000000000000000000000000066e0000000000000000000000000000000000000000000000000000000000000678000000000000000000000000000000000000000000000000000000000000066f00000000000000000000000000000000000000000000000000000000000006790000000000000000000000000000000000000000000000000000000000000670000000000000000000000000000000000000000000000000000000000000067a0000000000000000000000000000000000000000000000000000000000000671000000000000000000000000000000000000000000000000000000000000067b0000000000000000000000000000000000000000000000000000000000000672000000000000000000000000000000000000000000000000000000000000067c0000000000000000000000000000000000000000000000000000000000000673000000000000000000000000000000000000000000000000000000000000067d0000000000000000000000000000000000000000000000000000000000000674000000000000000000000000000000000000000000000000000000000000067e0000000000000000000000000000000000000000000000000000000000000675000000000000000000000000000000000000000000000000000000000000067f00000000000000000000000000000000000000000000000000000000000006760000000000000000000000000000000000000000000000000000000000000680000000000000000000000000000000000000000000000000000000000000067700000000000000000000000000000000000000000000000000000000000006810000000000000000000000000000000000000000000000000000000000000678000000000000000000000000000000000000000000000000000000000000068200000000000000000000000000000000000000000000000000000000000006790000000000000000000000000000000000000000000000000000000000000683000000000000000000000000000000000000000000000000000000000000067a0000000000000000000000000000000000000000000000000000000000000684000000000000000000000000000000000000000000000000000000000000067b0000000000000000000000000000000000000000000000000000000000000685000000000000000000000000000000000000000000000000000000000000067c0000000000000000000000000000000000000000000000000000000000000686000000000000000000000000000000000000000000000000000000000000067d0000000000000000000000000000000000000000000000000000000000000687000000000000000000000000000000000000000000000000000000000000067e0000000000000000000000000000000000000000000000000000000000000688000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e000000000000000000000000000000000000000000000000000000000000028f0000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000000000000000029100000000000000000000000000000000000000000000000000000000000002920000000000000000000000000000000000000000000000000000000000000293000000000000000000000000000000000000000000000000000000000000029400000000000000000000000000000000000000000000000000000000000002950000000000000000000000000000000000000000000000000000000000000296000000000000000000000000000000000000000000000000000000000000029700000000000000000000000000000000000000000000000000000000000002980000000000000000000000000000000000000000000000000000000000000299000000000000000000000000000000000000000000000000000000000000029a000000000000000000000000000000000000000000000000000000000000029b000000000000000000000000000000000000000000000000000000000000029c000000000000000000000000000000000000000000000000000000000000029d000000000000000000000000000000000000000000000000000000000000029e000000000000000000000000000000000000000000000000000000000000029f00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002a100000000000000000000000000000000000000000000000000000000000002a200000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002a400000000000000000000000000000000000000000000000000000000000002a500000000000000000000000000000000000000000000000000000000000002a600000000000000000000000000000000000000000000000000000000000002a700000000000000000000000000000000000000000000000000000000000002a800000000000000000000000000000000000000000000000000000000000002a900000000000000000000000000000000000000000000000000000000000002aa00000000000000000000000000000000000000000000000000000000000002ab00000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000002ad00000000000000000000000000000000000000000000000000000000000002ae00000000000000000000000000000000000000000000000000000000000002af00000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002b100000000000000000000000000000000000000000000000000000000000002b200000000000000000000000000000000000000000000000000000000000002b300000000000000000000000000000000000000000000000000000000000002b400000000000000000000000000000000000000000000000000000000000002b500000000000000000000000000000000000000000000000000000000000002b600000000000000000000000000000000000000000000000000000000000002b700000000000000000000000000000000000000000000000000000000000002b800000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002ba00000000000000000000000000000000000000000000000000000000000002bb00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bd00000000000000000000000000000000000000000000000000000000000002be00000000000000000000000000000000000000000000000000000000000002bf3f0000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000038100000000000000000000000000000000000000000000000000000000000003820000000000000000000000000000000000000000000000000000000000000383000000000000000000000000000000000000000000000000000000000000038400000000000000000000000000000000000000000000000000000000000003850000000000000000000000000000000000000000000000000000000000000386000000000000000000000000000000000000000000000000000000000000038700000000000000000000000000000000000000000000000000000000000003880000000000000000000000000000000000000000000000000000000000000389000000000000000000000000000000000000000000000000000000000000038a000000000000000000000000000000000000000000000000000000000000038b000000000000000000000000000000000000000000000000000000000000038c000000000000000000000000000000000000000000000000000000000000038d000000000000000000000000000000000000000000000000000000000000038e000000000000000000000000000000000000000000000000000000000000038f0000000000000000000000000000000000000000000000000000000000000390000000000000000000000000000000000000000000000000000000000000039100000000000000000000000000000000000000000000000000000000000003920000000000000000000000000000000000000000000000000000000000000393000000000000000000000000000000000000000000000000000000000000039400000000000000000000000000000000000000000000000000000000000003950000000000000000000000000000000000000000000000000000000000000396000000000000000000000000000000000000000000000000000000000000039700000000000000000000000000000000000000000000000000000000000003980000000000000000000000000000000000000000000000000000000000000399000000000000000000000000000000000000000000000000000000000000039a000000000000000000000000000000000000000000000000000000000000039b000000000000000000000000000000000000000000000000000000000000039c000000000000000000000000000000000000000000000000000000000000039d000000000000000000000000000000000000000000000000000000000000039e000000000000000000000000000000000000000000000000000000000000039f00000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003a100000000000000000000000000000000000000000000000000000000000003a200000000000000000000000000000000000000000000000000000000000003a300000000000000000000000000000000000000000000000000000000000003a400000000000000000000000000000000000000000000000000000000000003a500000000000000000000000000000000000000000000000000000000000003a600000000000000000000000000000000000000000000000000000000000003a700000000000000000000000000000000000000000000000000000000000003a800000000000000000000000000000000000000000000000000000000000003a900000000000000000000000000000000000000000000000000000000000003aa00000000000000000000000000000000000000000000000000000000000003ab00000000000000000000000000000000000000000000000000000000000003ac00000000000000000000000000000000000000000000000000000000000003ad00000000000000000000000000000000000000000000000000000000000003ae00000000000000000000000000000000000000000000000000000000000003af00000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003b100000000000000000000000000000000000000000000000000000000000003b200000000000000000000000000000000000000000000000000000000000003b300000000000000000000000000000000000000000000000000000000000003b400000000000000000000000000000000000000000000000000000000000003b500000000000000000000000000000000000000000000000000000000000003b600000000000000000000000000000000000000000000000000000000000003b700000000000000000000000000000000000000000000000000000000000003b800000000000000000000000000000000000000000000000000000000000003b900000000000000000000000000000000000000000000000000000000000003ba00000000000000000000000000000000000000000000000000000000000003bb00000000000000000000000000000000000000000000000000000000000003bc00000000000000000000000000000000000000000000000000000000000003bd00000000000000000000000000000000000000000000000000000000000003be08004a32502b5d2a0cf5d776c92ef74de61a28e7882bdadeb3b0530f472704604300808c2412e2725ecaa6a6bd77e3159349e238dc7817f906ba32afd40b3cb3cb00d7d4e3b313c4cce9bd98c317ea715847a92d795b82a6f8b8144b7c61bee64200b76f07678c289f41378029b1353a73e1afbe241612a97c23e7fc059099f49d00c72046b39a9dc902cee36db5214c72315636bd824abfabdf80b1c5e6a01fd7006e0a74fec166a12f5a62954776784f01cba3be7ab876eef2e49a2ab7a5b0f900c83ddd8f6b91086bc83485adbe8056212b4d33b91840cd3dc649f9736881460022441e76225010acce7f429dc754fb3260ae1d387937978f69c67a879ed0733f0000000000000000000000000000000000000000000000000000000000000680000000000000000000000000000000000000000000000000000000000000068a0000000000000000000000000000000000000000000000000000000000000681000000000000000000000000000000000000000000000000000000000000068b0000000000000000000000000000000000000000000000000000000000000682000000000000000000000000000000000000000000000000000000000000068c0000000000000000000000000000000000000000000000000000000000000683000000000000000000000000000000000000000000000000000000000000068d0000000000000000000000000000000000000000000000000000000000000684000000000000000000000000000000000000000000000000000000000000068e0000000000000000000000000000000000000000000000000000000000000685000000000000000000000000000000000000000000000000000000000000068f00000000000000000000000000000000000000000000000000000000000006860000000000000000000000000000000000000000000000000000000000000690000000000000000000000000000000000000000000000000000000000000068700000000000000000000000000000000000000000000000000000000000006910000000000000000000000000000000000000000000000000000000000000688000000000000000000000000000000000000000000000000000000000000069200000000000000000000000000000000000000000000000000000000000006890000000000000000000000000000000000000000000000000000000000000693000000000000000000000000000000000000000000000000000000000000068a0000000000000000000000000000000000000000000000000000000000000694000000000000000000000000000000000000000000000000000000000000068b0000000000000000000000000000000000000000000000000000000000000695000000000000000000000000000000000000000000000000000000000000068c0000000000000000000000000000000000000000000000000000000000000696000000000000000000000000000000000000000000000000000000000000068d0000000000000000000000000000000000000000000000000000000000000697000000000000000000000000000000000000000000000000000000000000068e0000000000000000000000000000000000000000000000000000000000000698000000000000000000000000000000000000000000000000000000000000068f00000000000000000000000000000000000000000000000000000000000006990000000000000000000000000000000000000000000000000000000000000690000000000000000000000000000000000000000000000000000000000000069a0000000000000000000000000000000000000000000000000000000000000691000000000000000000000000000000000000000000000000000000000000069b0000000000000000000000000000000000000000000000000000000000000692000000000000000000000000000000000000000000000000000000000000069c0000000000000000000000000000000000000000000000000000000000000693000000000000000000000000000000000000000000000000000000000000069d0000000000000000000000000000000000000000000000000000000000000694000000000000000000000000000000000000000000000000000000000000069e0000000000000000000000000000000000000000000000000000000000000695000000000000000000000000000000000000000000000000000000000000069f000000000000000000000000000000000000000000000000000000000000069600000000000000000000000000000000000000000000000000000000000006a0000000000000000000000000000000000000000000000000000000000000069700000000000000000000000000000000000000000000000000000000000006a1000000000000000000000000000000000000000000000000000000000000069800000000000000000000000000000000000000000000000000000000000006a2000000000000000000000000000000000000000000000000000000000000069900000000000000000000000000000000000000000000000000000000000006a3000000000000000000000000000000000000000000000000000000000000069a00000000000000000000000000000000000000000000000000000000000006a4000000000000000000000000000000000000000000000000000000000000069b00000000000000000000000000000000000000000000000000000000000006a5000000000000000000000000000000000000000000000000000000000000069c00000000000000000000000000000000000000000000000000000000000006a6000000000000000000000000000000000000000000000000000000000000069d00000000000000000000000000000000000000000000000000000000000006a7000000000000000000000000000000000000000000000000000000000000069e00000000000000000000000000000000000000000000000000000000000006a8000000000000000000000000000000000000000000000000000000000000069f00000000000000000000000000000000000000000000000000000000000006a900000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000006aa00000000000000000000000000000000000000000000000000000000000006a100000000000000000000000000000000000000000000000000000000000006ab00000000000000000000000000000000000000000000000000000000000006a200000000000000000000000000000000000000000000000000000000000006ac00000000000000000000000000000000000000000000000000000000000006a300000000000000000000000000000000000000000000000000000000000006ad00000000000000000000000000000000000000000000000000000000000006a400000000000000000000000000000000000000000000000000000000000006ae00000000000000000000000000000000000000000000000000000000000006a500000000000000000000000000000000000000000000000000000000000006af00000000000000000000000000000000000000000000000000000000000006a600000000000000000000000000000000000000000000000000000000000006b000000000000000000000000000000000000000000000000000000000000006a700000000000000000000000000000000000000000000000000000000000006b100000000000000000000000000000000000000000000000000000000000006a800000000000000000000000000000000000000000000000000000000000006b200000000000000000000000000000000000000000000000000000000000006a900000000000000000000000000000000000000000000000000000000000006b300000000000000000000000000000000000000000000000000000000000006aa00000000000000000000000000000000000000000000000000000000000006b400000000000000000000000000000000000000000000000000000000000006ab00000000000000000000000000000000000000000000000000000000000006b500000000000000000000000000000000000000000000000000000000000006ac00000000000000000000000000000000000000000000000000000000000006b600000000000000000000000000000000000000000000000000000000000006ad00000000000000000000000000000000000000000000000000000000000006b700000000000000000000000000000000000000000000000000000000000006ae00000000000000000000000000000000000000000000000000000000000006b800000000000000000000000000000000000000000000000000000000000006af00000000000000000000000000000000000000000000000000000000000006b900000000000000000000000000000000000000000000000000000000000006b000000000000000000000000000000000000000000000000000000000000006ba00000000000000000000000000000000000000000000000000000000000006b100000000000000000000000000000000000000000000000000000000000006bb00000000000000000000000000000000000000000000000000000000000006b200000000000000000000000000000000000000000000000000000000000006bc00000000000000000000000000000000000000000000000000000000000006b300000000000000000000000000000000000000000000000000000000000006bd00000000000000000000000000000000000000000000000000000000000006b400000000000000000000000000000000000000000000000000000000000006be00000000000000000000000000000000000000000000000000000000000006b500000000000000000000000000000000000000000000000000000000000006bf00000000000000000000000000000000000000000000000000000000000006b600000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000006b700000000000000000000000000000000000000000000000000000000000006c100000000000000000000000000000000000000000000000000000000000006b800000000000000000000000000000000000000000000000000000000000006c200000000000000000000000000000000000000000000000000000000000006b900000000000000000000000000000000000000000000000000000000000006c300000000000000000000000000000000000000000000000000000000000006ba00000000000000000000000000000000000000000000000000000000000006c400000000000000000000000000000000000000000000000000000000000006bb00000000000000000000000000000000000000000000000000000000000006c500000000000000000000000000000000000000000000000000000000000006bc00000000000000000000000000000000000000000000000000000000000006c600000000000000000000000000000000000000000000000000000000000006bd00000000000000000000000000000000000000000000000000000000000006c700000000000000000000000000000000000000000000000000000000000006be00000000000000000000000000000000000000000000000000000000000006c80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002c100000000000000000000000000000000000000000000000000000000000002c200000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002c400000000000000000000000000000000000000000000000000000000000002c500000000000000000000000000000000000000000000000000000000000002c600000000000000000000000000000000000000000000000000000000000002c700000000000000000000000000000000000000000000000000000000000002c800000000000000000000000000000000000000000000000000000000000002c900000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000002cb00000000000000000000000000000000000000000000000000000000000002cc00000000000000000000000000000000000000000000000000000000000002cd00000000000000000000000000000000000000000000000000000000000002ce00000000000000000000000000000000000000000000000000000000000002cf00000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002d100000000000000000000000000000000000000000000000000000000000002d200000000000000000000000000000000000000000000000000000000000002d300000000000000000000000000000000000000000000000000000000000002d400000000000000000000000000000000000000000000000000000000000002d500000000000000000000000000000000000000000000000000000000000002d600000000000000000000000000000000000000000000000000000000000002d700000000000000000000000000000000000000000000000000000000000002d800000000000000000000000000000000000000000000000000000000000002d900000000000000000000000000000000000000000000000000000000000002da00000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000002dc00000000000000000000000000000000000000000000000000000000000002dd00000000000000000000000000000000000000000000000000000000000002de00000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002e100000000000000000000000000000000000000000000000000000000000002e200000000000000000000000000000000000000000000000000000000000002e300000000000000000000000000000000000000000000000000000000000002e400000000000000000000000000000000000000000000000000000000000002e500000000000000000000000000000000000000000000000000000000000002e600000000000000000000000000000000000000000000000000000000000002e700000000000000000000000000000000000000000000000000000000000002e800000000000000000000000000000000000000000000000000000000000002e900000000000000000000000000000000000000000000000000000000000002ea00000000000000000000000000000000000000000000000000000000000002eb00000000000000000000000000000000000000000000000000000000000002ec00000000000000000000000000000000000000000000000000000000000002ed00000000000000000000000000000000000000000000000000000000000002ee00000000000000000000000000000000000000000000000000000000000002ef00000000000000000000000000000000000000000000000000000000000002f000000000000000000000000000000000000000000000000000000000000002f100000000000000000000000000000000000000000000000000000000000002f200000000000000000000000000000000000000000000000000000000000002f300000000000000000000000000000000000000000000000000000000000002f400000000000000000000000000000000000000000000000000000000000002f500000000000000000000000000000000000000000000000000000000000002f600000000000000000000000000000000000000000000000000000000000002f700000000000000000000000000000000000000000000000000000000000002f800000000000000000000000000000000000000000000000000000000000002f900000000000000000000000000000000000000000000000000000000000002fa00000000000000000000000000000000000000000000000000000000000002fb00000000000000000000000000000000000000000000000000000000000002fc00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000002ff3f00000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003c100000000000000000000000000000000000000000000000000000000000003c200000000000000000000000000000000000000000000000000000000000003c300000000000000000000000000000000000000000000000000000000000003c400000000000000000000000000000000000000000000000000000000000003c500000000000000000000000000000000000000000000000000000000000003c600000000000000000000000000000000000000000000000000000000000003c700000000000000000000000000000000000000000000000000000000000003c800000000000000000000000000000000000000000000000000000000000003c900000000000000000000000000000000000000000000000000000000000003ca00000000000000000000000000000000000000000000000000000000000003cb00000000000000000000000000000000000000000000000000000000000003cc00000000000000000000000000000000000000000000000000000000000003cd00000000000000000000000000000000000000000000000000000000000003ce00000000000000000000000000000000000000000000000000000000000003cf00000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003d100000000000000000000000000000000000000000000000000000000000003d200000000000000000000000000000000000000000000000000000000000003d300000000000000000000000000000000000000000000000000000000000003d400000000000000000000000000000000000000000000000000000000000003d500000000000000000000000000000000000000000000000000000000000003d600000000000000000000000000000000000000000000000000000000000003d700000000000000000000000000000000000000000000000000000000000003d800000000000000000000000000000000000000000000000000000000000003d900000000000000000000000000000000000000000000000000000000000003da00000000000000000000000000000000000000000000000000000000000003db00000000000000000000000000000000000000000000000000000000000003dc00000000000000000000000000000000000000000000000000000000000003dd00000000000000000000000000000000000000000000000000000000000003de00000000000000000000000000000000000000000000000000000000000003df00000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003e100000000000000000000000000000000000000000000000000000000000003e200000000000000000000000000000000000000000000000000000000000003e300000000000000000000000000000000000000000000000000000000000003e400000000000000000000000000000000000000000000000000000000000003e500000000000000000000000000000000000000000000000000000000000003e600000000000000000000000000000000000000000000000000000000000003e700000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e900000000000000000000000000000000000000000000000000000000000003ea00000000000000000000000000000000000000000000000000000000000003eb00000000000000000000000000000000000000000000000000000000000003ec00000000000000000000000000000000000000000000000000000000000003ed00000000000000000000000000000000000000000000000000000000000003ee00000000000000000000000000000000000000000000000000000000000003ef00000000000000000000000000000000000000000000000000000000000003f000000000000000000000000000000000000000000000000000000000000003f100000000000000000000000000000000000000000000000000000000000003f200000000000000000000000000000000000000000000000000000000000003f300000000000000000000000000000000000000000000000000000000000003f400000000000000000000000000000000000000000000000000000000000003f500000000000000000000000000000000000000000000000000000000000003f600000000000000000000000000000000000000000000000000000000000003f700000000000000000000000000000000000000000000000000000000000003f800000000000000000000000000000000000000000000000000000000000003f900000000000000000000000000000000000000000000000000000000000003fa00000000000000000000000000000000000000000000000000000000000003fb00000000000000000000000000000000000000000000000000000000000003fc00000000000000000000000000000000000000000000000000000000000003fd00000000000000000000000000000000000000000000000000000000000003fe0800bb07f923e24c36227d717557d58d32b99370b81f7a70d1835becc1114f7d7700624291eff6ab801e5668bc6619a3df132f8b392b063f09dfe8a69e38224eb200bd6b195f8716ab3dc93c44037cac579d25d0e77c2782b5aa62534ee25d36bc008c99d470d2c53624a8c5bedfeffdcc5356f6da89ee0e372b3ea3fa4f8cd652009944e00a3f9a7d2e8a535d3a210c3271d5732518037704d67ef5d42a8b82c200523014cb6eabe4366c6e599ba96534fc15ecc804a13fbaaacf8717e1b0a8d20005621252c4b36c113f21ad6c13b99e5cef514bdd98ef0aae4075b5cb5a000a00d80cb2a60aae6c3d2e7d27fb1519a708a18bb5b5085f78684bdee7512856a93f00000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000006ca00000000000000000000000000000000000000000000000000000000000006c100000000000000000000000000000000000000000000000000000000000006cb00000000000000000000000000000000000000000000000000000000000006c200000000000000000000000000000000000000000000000000000000000006cc00000000000000000000000000000000000000000000000000000000000006c300000000000000000000000000000000000000000000000000000000000006cd00000000000000000000000000000000000000000000000000000000000006c400000000000000000000000000000000000000000000000000000000000006ce00000000000000000000000000000000000000000000000000000000000006c500000000000000000000000000000000000000000000000000000000000006cf00000000000000000000000000000000000000000000000000000000000006c600000000000000000000000000000000000000000000000000000000000006d000000000000000000000000000000000000000000000000000000000000006c700000000000000000000000000000000000000000000000000000000000006d100000000000000000000000000000000000000000000000000000000000006c800000000000000000000000000000000000000000000000000000000000006d200000000000000000000000000000000000000000000000000000000000006c900000000000000000000000000000000000000000000000000000000000006d300000000000000000000000000000000000000000000000000000000000006ca00000000000000000000000000000000000000000000000000000000000006d400000000000000000000000000000000000000000000000000000000000006cb00000000000000000000000000000000000000000000000000000000000006d500000000000000000000000000000000000000000000000000000000000006cc00000000000000000000000000000000000000000000000000000000000006d600000000000000000000000000000000000000000000000000000000000006cd00000000000000000000000000000000000000000000000000000000000006d700000000000000000000000000000000000000000000000000000000000006ce00000000000000000000000000000000000000000000000000000000000006d800000000000000000000000000000000000000000000000000000000000006cf00000000000000000000000000000000000000000000000000000000000006d900000000000000000000000000000000000000000000000000000000000006d000000000000000000000000000000000000000000000000000000000000006da00000000000000000000000000000000000000000000000000000000000006d100000000000000000000000000000000000000000000000000000000000006db00000000000000000000000000000000000000000000000000000000000006d200000000000000000000000000000000000000000000000000000000000006dc00000000000000000000000000000000000000000000000000000000000006d300000000000000000000000000000000000000000000000000000000000006dd00000000000000000000000000000000000000000000000000000000000006d400000000000000000000000000000000000000000000000000000000000006de00000000000000000000000000000000000000000000000000000000000006d500000000000000000000000000000000000000000000000000000000000006df00000000000000000000000000000000000000000000000000000000000006d600000000000000000000000000000000000000000000000000000000000006e000000000000000000000000000000000000000000000000000000000000006d700000000000000000000000000000000000000000000000000000000000006e100000000000000000000000000000000000000000000000000000000000006d800000000000000000000000000000000000000000000000000000000000006e200000000000000000000000000000000000000000000000000000000000006d900000000000000000000000000000000000000000000000000000000000006e300000000000000000000000000000000000000000000000000000000000006da00000000000000000000000000000000000000000000000000000000000006e400000000000000000000000000000000000000000000000000000000000006db00000000000000000000000000000000000000000000000000000000000006e500000000000000000000000000000000000000000000000000000000000006dc00000000000000000000000000000000000000000000000000000000000006e600000000000000000000000000000000000000000000000000000000000006dd00000000000000000000000000000000000000000000000000000000000006e700000000000000000000000000000000000000000000000000000000000006de00000000000000000000000000000000000000000000000000000000000006e800000000000000000000000000000000000000000000000000000000000006df00000000000000000000000000000000000000000000000000000000000006e900000000000000000000000000000000000000000000000000000000000006e000000000000000000000000000000000000000000000000000000000000006ea00000000000000000000000000000000000000000000000000000000000006e100000000000000000000000000000000000000000000000000000000000006eb00000000000000000000000000000000000000000000000000000000000006e200000000000000000000000000000000000000000000000000000000000006ec00000000000000000000000000000000000000000000000000000000000006e300000000000000000000000000000000000000000000000000000000000006ed00000000000000000000000000000000000000000000000000000000000006e400000000000000000000000000000000000000000000000000000000000006ee00000000000000000000000000000000000000000000000000000000000006e500000000000000000000000000000000000000000000000000000000000006ef00000000000000000000000000000000000000000000000000000000000006e600000000000000000000000000000000000000000000000000000000000006f000000000000000000000000000000000000000000000000000000000000006e700000000000000000000000000000000000000000000000000000000000006f100000000000000000000000000000000000000000000000000000000000006e800000000000000000000000000000000000000000000000000000000000006f200000000000000000000000000000000000000000000000000000000000006e900000000000000000000000000000000000000000000000000000000000006f300000000000000000000000000000000000000000000000000000000000006ea00000000000000000000000000000000000000000000000000000000000006f400000000000000000000000000000000000000000000000000000000000006eb00000000000000000000000000000000000000000000000000000000000006f500000000000000000000000000000000000000000000000000000000000006ec00000000000000000000000000000000000000000000000000000000000006f600000000000000000000000000000000000000000000000000000000000006ed00000000000000000000000000000000000000000000000000000000000006f700000000000000000000000000000000000000000000000000000000000006ee00000000000000000000000000000000000000000000000000000000000006f800000000000000000000000000000000000000000000000000000000000006ef00000000000000000000000000000000000000000000000000000000000006f900000000000000000000000000000000000000000000000000000000000006f000000000000000000000000000000000000000000000000000000000000006fa00000000000000000000000000000000000000000000000000000000000006f100000000000000000000000000000000000000000000000000000000000006fb00000000000000000000000000000000000000000000000000000000000006f200000000000000000000000000000000000000000000000000000000000006fc00000000000000000000000000000000000000000000000000000000000006f300000000000000000000000000000000000000000000000000000000000006fd00000000000000000000000000000000000000000000000000000000000006f400000000000000000000000000000000000000000000000000000000000006fe00000000000000000000000000000000000000000000000000000000000006f500000000000000000000000000000000000000000000000000000000000006ff00000000000000000000000000000000000000000000000000000000000006f6000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000006f7000000000000000000000000000000000000000000000000000000000000070100000000000000000000000000000000000000000000000000000000000006f8000000000000000000000000000000000000000000000000000000000000070200000000000000000000000000000000000000000000000000000000000006f9000000000000000000000000000000000000000000000000000000000000070300000000000000000000000000000000000000000000000000000000000006fa000000000000000000000000000000000000000000000000000000000000070400000000000000000000000000000000000000000000000000000000000006fb000000000000000000000000000000000000000000000000000000000000070500000000000000000000000000000000000000000000000000000000000006fc000000000000000000000000000000000000000000000000000000000000070600000000000000000000000000000000000000000000000000000000000006fd000000000000000000000000000000000000000000000000000000000000070700000000000000000000000000000000000000000000000000000000000006fe0000000000000000000000000000000000000000000000000000000000000708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030100000000000000000000000000000000000000000000000000000000000003020000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000030400000000000000000000000000000000000000000000000000000000000003050000000000000000000000000000000000000000000000000000000000000306000000000000000000000000000000000000000000000000000000000000030700000000000000000000000000000000000000000000000000000000000003080000000000000000000000000000000000000000000000000000000000000309000000000000000000000000000000000000000000000000000000000000030a000000000000000000000000000000000000000000000000000000000000030b000000000000000000000000000000000000000000000000000000000000030c000000000000000000000000000000000000000000000000000000000000030d000000000000000000000000000000000000000000000000000000000000030e000000000000000000000000000000000000000000000000000000000000030f0000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000031100000000000000000000000000000000000000000000000000000000000003120000000000000000000000000000000000000000000000000000000000000313000000000000000000000000000000000000000000000000000000000000031400000000000000000000000000000000000000000000000000000000000003150000000000000000000000000000000000000000000000000000000000000316000000000000000000000000000000000000000000000000000000000000031700000000000000000000000000000000000000000000000000000000000003180000000000000000000000000000000000000000000000000000000000000319000000000000000000000000000000000000000000000000000000000000031a000000000000000000000000000000000000000000000000000000000000031b000000000000000000000000000000000000000000000000000000000000031c000000000000000000000000000000000000000000000000000000000000031d000000000000000000000000000000000000000000000000000000000000031e000000000000000000000000000000000000000000000000000000000000031f0000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032100000000000000000000000000000000000000000000000000000000000003220000000000000000000000000000000000000000000000000000000000000323000000000000000000000000000000000000000000000000000000000000032400000000000000000000000000000000000000000000000000000000000003250000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000032700000000000000000000000000000000000000000000000000000000000003280000000000000000000000000000000000000000000000000000000000000329000000000000000000000000000000000000000000000000000000000000032a000000000000000000000000000000000000000000000000000000000000032b000000000000000000000000000000000000000000000000000000000000032c000000000000000000000000000000000000000000000000000000000000032d000000000000000000000000000000000000000000000000000000000000032e000000000000000000000000000000000000000000000000000000000000032f0000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033100000000000000000000000000000000000000000000000000000000000003320000000000000000000000000000000000000000000000000000000000000333000000000000000000000000000000000000000000000000000000000000033400000000000000000000000000000000000000000000000000000000000003350000000000000000000000000000000000000000000000000000000000000336000000000000000000000000000000000000000000000000000000000000033700000000000000000000000000000000000000000000000000000000000003380000000000000000000000000000000000000000000000000000000000000339000000000000000000000000000000000000000000000000000000000000033a000000000000000000000000000000000000000000000000000000000000033b000000000000000000000000000000000000000000000000000000000000033c000000000000000000000000000000000000000000000000000000000000033d000000000000000000000000000000000000000000000000000000000000033e000000000000000000000000000000000000000000000000000000000000033f3f0000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040100000000000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000403000000000000000000000000000000000000000000000000000000000000040400000000000000000000000000000000000000000000000000000000000004050000000000000000000000000000000000000000000000000000000000000406000000000000000000000000000000000000000000000000000000000000040700000000000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000409000000000000000000000000000000000000000000000000000000000000040a000000000000000000000000000000000000000000000000000000000000040b000000000000000000000000000000000000000000000000000000000000040c000000000000000000000000000000000000000000000000000000000000040d000000000000000000000000000000000000000000000000000000000000040e000000000000000000000000000000000000000000000000000000000000040f0000000000000000000000000000000000000000000000000000000000000410000000000000000000000000000000000000000000000000000000000000041100000000000000000000000000000000000000000000000000000000000004120000000000000000000000000000000000000000000000000000000000000413000000000000000000000000000000000000000000000000000000000000041400000000000000000000000000000000000000000000000000000000000004150000000000000000000000000000000000000000000000000000000000000416000000000000000000000000000000000000000000000000000000000000041700000000000000000000000000000000000000000000000000000000000004180000000000000000000000000000000000000000000000000000000000000419000000000000000000000000000000000000000000000000000000000000041a000000000000000000000000000000000000000000000000000000000000041b000000000000000000000000000000000000000000000000000000000000041c000000000000000000000000000000000000000000000000000000000000041d000000000000000000000000000000000000000000000000000000000000041e000000000000000000000000000000000000000000000000000000000000041f0000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000042100000000000000000000000000000000000000000000000000000000000004220000000000000000000000000000000000000000000000000000000000000423000000000000000000000000000000000000000000000000000000000000042400000000000000000000000000000000000000000000000000000000000004250000000000000000000000000000000000000000000000000000000000000426000000000000000000000000000000000000000000000000000000000000042700000000000000000000000000000000000000000000000000000000000004280000000000000000000000000000000000000000000000000000000000000429000000000000000000000000000000000000000000000000000000000000042a000000000000000000000000000000000000000000000000000000000000042b000000000000000000000000000000000000000000000000000000000000042c000000000000000000000000000000000000000000000000000000000000042d000000000000000000000000000000000000000000000000000000000000042e000000000000000000000000000000000000000000000000000000000000042f0000000000000000000000000000000000000000000000000000000000000430000000000000000000000000000000000000000000000000000000000000043100000000000000000000000000000000000000000000000000000000000004320000000000000000000000000000000000000000000000000000000000000433000000000000000000000000000000000000000000000000000000000000043400000000000000000000000000000000000000000000000000000000000004350000000000000000000000000000000000000000000000000000000000000436000000000000000000000000000000000000000000000000000000000000043700000000000000000000000000000000000000000000000000000000000004380000000000000000000000000000000000000000000000000000000000000439000000000000000000000000000000000000000000000000000000000000043a000000000000000000000000000000000000000000000000000000000000043b000000000000000000000000000000000000000000000000000000000000043c000000000000000000000000000000000000000000000000000000000000043d000000000000000000000000000000000000000000000000000000000000043e0800a38df4d53fe5da3d48c97dd0f58e7a52faade40c8f50bc0f6408a2b5b3829800a3d4b994758c6630518cce3116391874b9b3b8a1bbcb36485d2702f05af359004c06e9dd14418667d7c66384699665f9222ed08be87c67293a2f1b6b4c41aa006b77b11703f0e3d21aa68c9f6d2ae8ad94ecafdb56be0c4605a0c6723e9ddc0049a087e215b640d8360f8dd98e07c77e4d2025a0dcdcaf78bc00bd6fde09680014741a52013a10622de90b1a7854f1203af5c97121a4b3774c249fba6e0dba00c783bd01c4a4cafe83fd3b4d1ce0c555be4b6d1f772cccccf86049f9af0bbe002ff3ac4d7e2f1caf1fb30e1d20eba5be2b6a42dd54690dee988a934026491e3f0000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000070a0000000000000000000000000000000000000000000000000000000000000701000000000000000000000000000000000000000000000000000000000000070b0000000000000000000000000000000000000000000000000000000000000702000000000000000000000000000000000000000000000000000000000000070c0000000000000000000000000000000000000000000000000000000000000703000000000000000000000000000000000000000000000000000000000000070d0000000000000000000000000000000000000000000000000000000000000704000000000000000000000000000000000000000000000000000000000000070e0000000000000000000000000000000000000000000000000000000000000705000000000000000000000000000000000000000000000000000000000000070f00000000000000000000000000000000000000000000000000000000000007060000000000000000000000000000000000000000000000000000000000000710000000000000000000000000000000000000000000000000000000000000070700000000000000000000000000000000000000000000000000000000000007110000000000000000000000000000000000000000000000000000000000000708000000000000000000000000000000000000000000000000000000000000071200000000000000000000000000000000000000000000000000000000000007090000000000000000000000000000000000000000000000000000000000000713000000000000000000000000000000000000000000000000000000000000070a0000000000000000000000000000000000000000000000000000000000000714000000000000000000000000000000000000000000000000000000000000070b0000000000000000000000000000000000000000000000000000000000000715000000000000000000000000000000000000000000000000000000000000070c0000000000000000000000000000000000000000000000000000000000000716000000000000000000000000000000000000000000000000000000000000070d0000000000000000000000000000000000000000000000000000000000000717000000000000000000000000000000000000000000000000000000000000070e0000000000000000000000000000000000000000000000000000000000000718000000000000000000000000000000000000000000000000000000000000070f00000000000000000000000000000000000000000000000000000000000007190000000000000000000000000000000000000000000000000000000000000710000000000000000000000000000000000000000000000000000000000000071a0000000000000000000000000000000000000000000000000000000000000711000000000000000000000000000000000000000000000000000000000000071b0000000000000000000000000000000000000000000000000000000000000712000000000000000000000000000000000000000000000000000000000000071c0000000000000000000000000000000000000000000000000000000000000713000000000000000000000000000000000000000000000000000000000000071d0000000000000000000000000000000000000000000000000000000000000714000000000000000000000000000000000000000000000000000000000000071e0000000000000000000000000000000000000000000000000000000000000715000000000000000000000000000000000000000000000000000000000000071f00000000000000000000000000000000000000000000000000000000000007160000000000000000000000000000000000000000000000000000000000000720000000000000000000000000000000000000000000000000000000000000071700000000000000000000000000000000000000000000000000000000000007210000000000000000000000000000000000000000000000000000000000000718000000000000000000000000000000000000000000000000000000000000072200000000000000000000000000000000000000000000000000000000000007190000000000000000000000000000000000000000000000000000000000000723000000000000000000000000000000000000000000000000000000000000071a0000000000000000000000000000000000000000000000000000000000000724000000000000000000000000000000000000000000000000000000000000071b0000000000000000000000000000000000000000000000000000000000000725000000000000000000000000000000000000000000000000000000000000071c0000000000000000000000000000000000000000000000000000000000000726000000000000000000000000000000000000000000000000000000000000071d0000000000000000000000000000000000000000000000000000000000000727000000000000000000000000000000000000000000000000000000000000071e0000000000000000000000000000000000000000000000000000000000000728000000000000000000000000000000000000000000000000000000000000071f00000000000000000000000000000000000000000000000000000000000007290000000000000000000000000000000000000000000000000000000000000720000000000000000000000000000000000000000000000000000000000000072a0000000000000000000000000000000000000000000000000000000000000721000000000000000000000000000000000000000000000000000000000000072b0000000000000000000000000000000000000000000000000000000000000722000000000000000000000000000000000000000000000000000000000000072c0000000000000000000000000000000000000000000000000000000000000723000000000000000000000000000000000000000000000000000000000000072d0000000000000000000000000000000000000000000000000000000000000724000000000000000000000000000000000000000000000000000000000000072e0000000000000000000000000000000000000000000000000000000000000725000000000000000000000000000000000000000000000000000000000000072f00000000000000000000000000000000000000000000000000000000000007260000000000000000000000000000000000000000000000000000000000000730000000000000000000000000000000000000000000000000000000000000072700000000000000000000000000000000000000000000000000000000000007310000000000000000000000000000000000000000000000000000000000000728000000000000000000000000000000000000000000000000000000000000073200000000000000000000000000000000000000000000000000000000000007290000000000000000000000000000000000000000000000000000000000000733000000000000000000000000000000000000000000000000000000000000072a0000000000000000000000000000000000000000000000000000000000000734000000000000000000000000000000000000000000000000000000000000072b0000000000000000000000000000000000000000000000000000000000000735000000000000000000000000000000000000000000000000000000000000072c0000000000000000000000000000000000000000000000000000000000000736000000000000000000000000000000000000000000000000000000000000072d0000000000000000000000000000000000000000000000000000000000000737000000000000000000000000000000000000000000000000000000000000072e0000000000000000000000000000000000000000000000000000000000000738000000000000000000000000000000000000000000000000000000000000072f00000000000000000000000000000000000000000000000000000000000007390000000000000000000000000000000000000000000000000000000000000730000000000000000000000000000000000000000000000000000000000000073a0000000000000000000000000000000000000000000000000000000000000731000000000000000000000000000000000000000000000000000000000000073b0000000000000000000000000000000000000000000000000000000000000732000000000000000000000000000000000000000000000000000000000000073c0000000000000000000000000000000000000000000000000000000000000733000000000000000000000000000000000000000000000000000000000000073d0000000000000000000000000000000000000000000000000000000000000734000000000000000000000000000000000000000000000000000000000000073e0000000000000000000000000000000000000000000000000000000000000735000000000000000000000000000000000000000000000000000000000000073f00000000000000000000000000000000000000000000000000000000000007360000000000000000000000000000000000000000000000000000000000000740000000000000000000000000000000000000000000000000000000000000073700000000000000000000000000000000000000000000000000000000000007410000000000000000000000000000000000000000000000000000000000000738000000000000000000000000000000000000000000000000000000000000074200000000000000000000000000000000000000000000000000000000000007390000000000000000000000000000000000000000000000000000000000000743000000000000000000000000000000000000000000000000000000000000073a0000000000000000000000000000000000000000000000000000000000000744000000000000000000000000000000000000000000000000000000000000073b0000000000000000000000000000000000000000000000000000000000000745000000000000000000000000000000000000000000000000000000000000073c0000000000000000000000000000000000000000000000000000000000000746000000000000000000000000000000000000000000000000000000000000073d0000000000000000000000000000000000000000000000000000000000000747000000000000000000000000000000000000000000000000000000000000073e0000000000000000000000000000000000000000000000000000000000000748000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "txsEffectsHash": "0x00e73bbc5444fa184c4c8bdd7f3ae7e3060c0b9a1a0ad96fe7472a7854786778", "decodedHeader": { "contentCommitment": { - "inHash": "0x00212ff46db74e06c26240f9a92fb6fea84709380935d657361bbd5bcb891937", + "inHash": "0x00e1371045bd7d2c3e1f19cba5f536f0e82042ba4bc257d4ba19c146215e8242", "outHash": "0x00b581181fdd29a9e20363313973f1545a94d0157e542d9b116ff7ae3f58a428", "numTxs": 8, "txsEffectsHash": "0x00e73bbc5444fa184c4c8bdd7f3ae7e3060c0b9a1a0ad96fe7472a7854786778" }, "globalVariables": { "blockNumber": 2, - "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000022", + "slotNumber": "0x0000000000000000000000000000000000000000000000000000000000000023", "chainId": 31337, - "timestamp": 1728562650, + "timestamp": 1730234016, "version": 1, - "coinbase": "0xb1e072764484ad800bc7d95ebdb84ea57ec1d5d3", - "feeRecipient": "0x27f79e3e1124a5869838e688cdf225f421845f3b734541c251652fdeb0f74917", + "coinbase": "0x4b727c2ee4030668748766f0695aebffca7eed1a", + "feeRecipient": "0x17e8c896e28967f5b43e53f229edf504b61881c02885750af6b75a3b5cc9d87c", "gasFees": { "feePerDaGas": 0, "feePerL2Gas": 0 @@ -116,12 +116,12 @@ }, "lastArchive": { "nextAvailableLeafIndex": 2, - "root": "0x24c11def12fd4dbe0974daf2b642f44404b96f4bfac4ab9c990c911e7bd96eaa" + "root": "0x12e65b031a1821b8ed8d2934a32482f660c0fb9d10557f476efb616c0651c75b" }, "stateReference": { "l1ToL2MessageTree": { "nextAvailableLeafIndex": 32, - "root": "0x224c43ed89fb9404e06e7382170d1e279a53211bab61876f38d8a4180390b7ad" + "root": "0x2cd1079160767e99ff3e43a4e56a0b79c7d28239245ac3240935dfb98a4eee29" }, "partialStateReference": { "noteHashTree": { @@ -139,8 +139,8 @@ } } }, - "header": "0x24c11def12fd4dbe0974daf2b642f44404b96f4bfac4ab9c990c911e7bd96eaa00000002000000000000000000000000000000000000000000000000000000000000000800e73bbc5444fa184c4c8bdd7f3ae7e3060c0b9a1a0ad96fe7472a785478677800212ff46db74e06c26240f9a92fb6fea84709380935d657361bbd5bcb89193700b581181fdd29a9e20363313973f1545a94d0157e542d9b116ff7ae3f58a428224c43ed89fb9404e06e7382170d1e279a53211bab61876f38d8a4180390b7ad0000002017752a4346cf34b18277458ace73be4895316cb1c3cbce628d573d5d10cde7ce00000200152db065a479b5630768d6c5250bb6233e71729f857c16cffa98569acf90a2bf000002800a020b31737a919cbd6b0c0fe25d466a11e2186eb8038cd63a5e7d2900473d53000002800000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000006707c5dab1e072764484ad800bc7d95ebdb84ea57ec1d5d327f79e3e1124a5869838e688cdf225f421845f3b734541c251652fdeb0f74917000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "publicInputsHash": "0x00e844cf75bddf47717e37193790124c73cba7b5c35d80841aacaad877ef579d", + "header": "0x12e65b031a1821b8ed8d2934a32482f660c0fb9d10557f476efb616c0651c75b00000002000000000000000000000000000000000000000000000000000000000000000800e73bbc5444fa184c4c8bdd7f3ae7e3060c0b9a1a0ad96fe7472a785478677800e1371045bd7d2c3e1f19cba5f536f0e82042ba4bc257d4ba19c146215e824200b581181fdd29a9e20363313973f1545a94d0157e542d9b116ff7ae3f58a4282cd1079160767e99ff3e43a4e56a0b79c7d28239245ac3240935dfb98a4eee290000002017752a4346cf34b18277458ace73be4895316cb1c3cbce628d573d5d10cde7ce00000200152db065a479b5630768d6c5250bb6233e71729f857c16cffa98569acf90a2bf000002800a020b31737a919cbd6b0c0fe25d466a11e2186eb8038cd63a5e7d2900473d53000002800000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002300000000000000000000000000000000000000000000000000000000672146a04b727c2ee4030668748766f0695aebffca7eed1a17e8c896e28967f5b43e53f229edf504b61881c02885750af6b75a3b5cc9d87c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "publicInputsHash": "0x009c999658e0df4745b03a4e5e099524ad8709803c0e49e233ecccf2b1c7daa0", "numTxs": 8 } } \ No newline at end of file diff --git a/l1-contracts/test/harnesses/InboxHarness.sol b/l1-contracts/test/harnesses/InboxHarness.sol index 420960467c2..f88f266270d 100644 --- a/l1-contracts/test/harnesses/InboxHarness.sol +++ b/l1-contracts/test/harnesses/InboxHarness.sol @@ -36,4 +36,10 @@ contract InboxHarness is Inbox { // -INITIAL_L2_BLOCK_NUM because tree number INITIAL_L2_BLOCK_NUM is not real return inProgress - Constants.INITIAL_L2_BLOCK_NUM; } + + function getNextMessageIndex() external view returns (uint256) { + FrontierLib.Tree storage currentTree = trees[inProgress]; + uint256 index = (inProgress - Constants.INITIAL_L2_BLOCK_NUM) * SIZE + currentTree.nextIndex; + return index; + } } diff --git a/l1-contracts/test/portals/TokenPortal.sol b/l1-contracts/test/portals/TokenPortal.sol index b6d0a503e14..674b007ce95 100644 --- a/l1-contracts/test/portals/TokenPortal.sol +++ b/l1-contracts/test/portals/TokenPortal.sol @@ -17,6 +17,18 @@ import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; contract TokenPortal { using SafeERC20 for IERC20; + event DepositToAztecPublic( + bytes32 to, uint256 amount, bytes32 secretHash, bytes32 key, uint256 index + ); + + event DepositToAztecPrivate( + bytes32 secretHashForRedeemingMintedNotes, + uint256 amount, + bytes32 secretHashForL2MessageConsumption, + bytes32 key, + uint256 index + ); + IRegistry public registry; IERC20 public underlying; bytes32 public l2Bridge; @@ -34,11 +46,11 @@ contract TokenPortal { * @param _to - The aztec address of the recipient * @param _amount - The amount to deposit * @param _secretHash - The hash of the secret consumable message. The hash should be 254 bits (so it can fit in a Field element) - * @return The key of the entry in the Inbox + * @return The key of the entry in the Inbox and its leaf index */ function depositToAztecPublic(bytes32 _to, uint256 _amount, bytes32 _secretHash) external - returns (bytes32) + returns (bytes32, uint256) { // Preamble IInbox inbox = IRollup(registry.getRollup()).INBOX(); @@ -52,7 +64,12 @@ contract TokenPortal { underlying.safeTransferFrom(msg.sender, address(this), _amount); // Send message to rollup - return inbox.sendL2Message(actor, contentHash, _secretHash); + (bytes32 key, uint256 index) = inbox.sendL2Message(actor, contentHash, _secretHash); + + // Emit event + emit DepositToAztecPublic(_to, _amount, _secretHash, key, index); + + return (key, index); } // docs:end:deposit_public @@ -62,13 +79,13 @@ contract TokenPortal { * @param _secretHashForRedeemingMintedNotes - The hash of the secret to redeem minted notes privately on Aztec. The hash should be 254 bits (so it can fit in a Field element) * @param _amount - The amount to deposit * @param _secretHashForL2MessageConsumption - The hash of the secret consumable L1 to L2 message. The hash should be 254 bits (so it can fit in a Field element) - * @return The key of the entry in the Inbox + * @return The key of the entry in the Inbox and its leaf index */ function depositToAztecPrivate( bytes32 _secretHashForRedeemingMintedNotes, uint256 _amount, bytes32 _secretHashForL2MessageConsumption - ) external returns (bytes32) { + ) external returns (bytes32, uint256) { // Preamble IInbox inbox = IRollup(registry.getRollup()).INBOX(); DataStructures.L2Actor memory actor = DataStructures.L2Actor(l2Bridge, 1); @@ -84,7 +101,15 @@ contract TokenPortal { underlying.safeTransferFrom(msg.sender, address(this), _amount); // Send message to rollup - return inbox.sendL2Message(actor, contentHash, _secretHashForL2MessageConsumption); + (bytes32 key, uint256 index) = + inbox.sendL2Message(actor, contentHash, _secretHashForL2MessageConsumption); + + // Emit event + emit DepositToAztecPrivate( + _secretHashForRedeemingMintedNotes, _amount, _secretHashForL2MessageConsumption, key, index + ); + + return (key, index); } // docs:end:deposit_private diff --git a/l1-contracts/test/portals/TokenPortal.t.sol b/l1-contracts/test/portals/TokenPortal.t.sol index 60672b8e081..d462f0e16aa 100644 --- a/l1-contracts/test/portals/TokenPortal.t.sol +++ b/l1-contracts/test/portals/TokenPortal.t.sol @@ -81,7 +81,7 @@ contract TokenPortalTest is Test { vm.deal(address(this), 100 ether); } - function _createExpectedMintPrivateL1ToL2Message() + function _createExpectedMintPrivateL1ToL2Message(uint256 _index) internal view returns (DataStructures.L1ToL2Msg memory) @@ -94,11 +94,12 @@ contract TokenPortalTest is Test { "mint_private(bytes32,uint256)", secretHashForRedeemingMintedNotes, amount ) ), - secretHash: secretHashForL2MessageConsumption + secretHash: secretHashForL2MessageConsumption, + index: _index }); } - function _createExpectedMintPublicL1ToL2Message() + function _createExpectedMintPublicL1ToL2Message(uint256 _index) internal view returns (DataStructures.L1ToL2Msg memory) @@ -107,7 +108,8 @@ contract TokenPortalTest is Test { sender: DataStructures.L1Actor(address(tokenPortal), block.chainid), recipient: DataStructures.L2Actor(l2TokenAddress, 1), content: Hash.sha256ToField(abi.encodeWithSignature("mint_public(bytes32,uint256)", to, amount)), - secretHash: secretHashForL2MessageConsumption + secretHash: secretHashForL2MessageConsumption, + index: _index }); } @@ -117,23 +119,25 @@ contract TokenPortalTest is Test { testERC20.approve(address(tokenPortal), mintAmount); // Check for the expected message - DataStructures.L1ToL2Msg memory expectedMessage = _createExpectedMintPrivateL1ToL2Message(); + uint256 expectedIndex = (FIRST_REAL_TREE_NUM - 1) * L1_TO_L2_MSG_SUBTREE_SIZE; + DataStructures.L1ToL2Msg memory expectedMessage = + _createExpectedMintPrivateL1ToL2Message(expectedIndex); bytes32 expectedLeaf = expectedMessage.sha256ToField(); // Check the event was emitted vm.expectEmit(true, true, true, true); // event we expect - uint256 globalLeafIndex = (FIRST_REAL_TREE_NUM - 1) * L1_TO_L2_MSG_SUBTREE_SIZE; - emit IInbox.MessageSent(FIRST_REAL_TREE_NUM, globalLeafIndex, expectedLeaf); + emit IInbox.MessageSent(FIRST_REAL_TREE_NUM, expectedIndex, expectedLeaf); // event we will get // Perform op - bytes32 leaf = tokenPortal.depositToAztecPrivate( + (bytes32 leaf, uint256 index) = tokenPortal.depositToAztecPrivate( secretHashForRedeemingMintedNotes, amount, secretHashForL2MessageConsumption ); assertEq(leaf, expectedLeaf, "returned leaf and calculated leaf should match"); + assertEq(index, expectedIndex, "returned index and calculated index should match"); return leaf; } @@ -144,19 +148,22 @@ contract TokenPortalTest is Test { testERC20.approve(address(tokenPortal), mintAmount); // Check for the expected message - DataStructures.L1ToL2Msg memory expectedMessage = _createExpectedMintPublicL1ToL2Message(); + uint256 expectedIndex = (FIRST_REAL_TREE_NUM - 1) * L1_TO_L2_MSG_SUBTREE_SIZE; + DataStructures.L1ToL2Msg memory expectedMessage = + _createExpectedMintPublicL1ToL2Message(expectedIndex); bytes32 expectedLeaf = expectedMessage.sha256ToField(); // Check the event was emitted vm.expectEmit(true, true, true, true); // event we expect - uint256 globalLeafIndex = (FIRST_REAL_TREE_NUM - 1) * L1_TO_L2_MSG_SUBTREE_SIZE; - emit IInbox.MessageSent(FIRST_REAL_TREE_NUM, globalLeafIndex, expectedLeaf); + emit IInbox.MessageSent(FIRST_REAL_TREE_NUM, expectedIndex, expectedLeaf); // Perform op - bytes32 leaf = tokenPortal.depositToAztecPublic(to, amount, secretHashForL2MessageConsumption); + (bytes32 leaf, uint256 index) = + tokenPortal.depositToAztecPublic(to, amount, secretHashForL2MessageConsumption); assertEq(leaf, expectedLeaf, "returned leaf and calculated leaf should match"); + assertEq(index, expectedIndex, "returned index and calculated index should match"); return leaf; } diff --git a/l1-contracts/test/portals/UniswapPortal.sol b/l1-contracts/test/portals/UniswapPortal.sol index 9d8688b255e..fc6bebd74a4 100644 --- a/l1-contracts/test/portals/UniswapPortal.sol +++ b/l1-contracts/test/portals/UniswapPortal.sol @@ -67,7 +67,7 @@ contract UniswapPortal { bool _withCaller, // Avoiding stack too deep PortalDataStructures.OutboxMessageMetadata[2] calldata _outboxMessageMetadata - ) public returns (bytes32) { + ) public returns (bytes32, uint256) { LocalSwapVars memory vars; vars.inputAsset = TokenPortal(_inputTokenPortal).underlying(); @@ -174,7 +174,7 @@ contract UniswapPortal { bool _withCaller, // Avoiding stack too deep PortalDataStructures.OutboxMessageMetadata[2] calldata _outboxMessageMetadata - ) public returns (bytes32) { + ) public returns (bytes32, uint256) { LocalSwapVars memory vars; vars.inputAsset = TokenPortal(_inputTokenPortal).underlying(); diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 1465b61e8c6..b637afe6464 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -276,7 +276,13 @@ impl PrivateContext { // docs:start:context_consume_l1_to_l2_message // docs:start:consume_l1_to_l2_message - pub fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress) { + pub fn consume_l1_to_l2_message( + &mut self, + content: Field, + secret: Field, + sender: EthAddress, + leaf_index: Field, + ) { // docs:end:context_consume_l1_to_l2_message let nullifier = process_l1_to_l2_message( self.historical_header.state.l1_to_l2_message_tree.root, @@ -286,6 +292,7 @@ impl PrivateContext { self.version(), content, secret, + leaf_index, ); // Push nullifier (and the "commitment" corresponding to this can be "empty") diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index e7b55e86299..9730495b753 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -1,5 +1,7 @@ use crate::context::gas::GasOpts; -use crate::hash::{compute_message_hash, compute_message_nullifier, compute_secret_hash}; +use crate::hash::{ + compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_secret_hash, +}; use dep::protocol_types::abis::function_selector::FunctionSelector; use dep::protocol_types::address::{AztecAddress, EthAddress}; use dep::protocol_types::constants::{MAX_FIELD_VALUE, PUBLIC_DISPATCH_SELECTOR}; @@ -42,7 +44,7 @@ impl PublicContext { leaf_index: Field, ) { let secret_hash = compute_secret_hash(secret); - let message_hash = compute_message_hash( + let message_hash = compute_l1_to_l2_message_hash( sender, self.chain_id(), /*recipient=*/ @@ -50,8 +52,9 @@ impl PublicContext { self.version(), content, secret_hash, + leaf_index, ); - let nullifier = compute_message_nullifier(message_hash, secret, leaf_index); + let nullifier = compute_l1_to_l2_message_nullifier(message_hash, secret); assert( !self.nullifier_exists(nullifier, self.this_address()), diff --git a/noir-projects/aztec-nr/aztec/src/hash.nr b/noir-projects/aztec-nr/aztec/src/hash.nr index 4e5b9111463..e60eb7d229c 100644 --- a/noir-projects/aztec-nr/aztec/src/hash.nr +++ b/noir-projects/aztec-nr/aztec/src/hash.nr @@ -41,21 +41,23 @@ pub fn compute_unencrypted_log_hash( sha256_to_field(hash_bytes) } -pub fn compute_message_hash( +pub fn compute_l1_to_l2_message_hash( sender: EthAddress, chain_id: Field, recipient: AztecAddress, version: Field, content: Field, secret_hash: Field, + leaf_index: Field, ) -> Field { - let mut hash_bytes = [0 as u8; 192]; + let mut hash_bytes = [0 as u8; 224]; let sender_bytes: [u8; 32] = sender.to_field().to_be_bytes(); let chain_id_bytes: [u8; 32] = chain_id.to_be_bytes(); let recipient_bytes: [u8; 32] = recipient.to_field().to_be_bytes(); let version_bytes: [u8; 32] = version.to_be_bytes(); let content_bytes: [u8; 32] = content.to_be_bytes(); let secret_hash_bytes: [u8; 32] = secret_hash.to_be_bytes(); + let leaf_index_bytes: [u8; 32] = leaf_index.to_be_bytes(); for i in 0..32 { hash_bytes[i] = sender_bytes[i]; @@ -64,18 +66,15 @@ pub fn compute_message_hash( hash_bytes[i + 96] = version_bytes[i]; hash_bytes[i + 128] = content_bytes[i]; hash_bytes[i + 160] = secret_hash_bytes[i]; + hash_bytes[i + 192] = leaf_index_bytes[i]; } sha256_to_field(hash_bytes) } -// The nullifier of a l1 to l2 message is the hash of the message salted with the secret and index of the message hash -// in the L1 to L2 message tree -pub fn compute_message_nullifier(message_hash: Field, secret: Field, leaf_index: Field) -> Field { - poseidon2_hash_with_separator( - [message_hash, secret, leaf_index], - GENERATOR_INDEX__MESSAGE_NULLIFIER, - ) +// The nullifier of a l1 to l2 message is the hash of the message salted with the secret +pub fn compute_l1_to_l2_message_nullifier(message_hash: Field, secret: Field) -> Field { + poseidon2_hash_with_separator([message_hash, secret], GENERATOR_INDEX__MESSAGE_NULLIFIER) } pub struct ArgsHasher { diff --git a/noir-projects/aztec-nr/aztec/src/messaging.nr b/noir-projects/aztec-nr/aztec/src/messaging.nr index 6679fe0671e..98a4110e628 100644 --- a/noir-projects/aztec-nr/aztec/src/messaging.nr +++ b/noir-projects/aztec-nr/aztec/src/messaging.nr @@ -1,5 +1,5 @@ use crate::{ - hash::{compute_message_hash, compute_message_nullifier, compute_secret_hash}, + hash::{compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_secret_hash}, oracle::get_l1_to_l2_membership_witness::get_l1_to_l2_membership_witness, }; @@ -16,24 +16,26 @@ pub fn process_l1_to_l2_message( version: Field, content: Field, secret: Field, + leaf_index: Field, ) -> Field { let secret_hash = compute_secret_hash(secret); - let message_hash = compute_message_hash( + let message_hash = compute_l1_to_l2_message_hash( portal_contract_address, chain_id, contract_address, version, content, secret_hash, + leaf_index, ); // We prove that `message_hash` is in the tree by showing the derivation of the tree root, using a merkle path we // get from an oracle. - let (leaf_index, sibling_path) = + let (_leaf_index, sibling_path) = unsafe { get_l1_to_l2_membership_witness(contract_address, message_hash, secret) }; let root = root_from_sibling_path(message_hash, leaf_index, sibling_path); assert(root == l1_to_l2_root, "Message not in state"); - compute_message_nullifier(message_hash, secret, leaf_index) + compute_l1_to_l2_message_nullifier(message_hash, secret) } diff --git a/noir-projects/noir-contracts/contracts/fee_juice_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fee_juice_contract/src/main.nr index 3f0cdbaa78e..065427f87d1 100644 --- a/noir-projects/noir-contracts/contracts/fee_juice_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fee_juice_contract/src/main.nr @@ -48,13 +48,13 @@ contract FeeJuice { } #[private] - fn claim(to: AztecAddress, amount: Field, secret: Field) { + fn claim(to: AztecAddress, amount: Field, secret: Field, message_leaf_index: Field) { let content_hash = get_bridge_gas_msg_hash(to, amount); let portal_address = storage.portal_address.read_private(); assert(!portal_address.is_zero()); // Consume message and emit nullifier - context.consume_l1_to_l2_message(content_hash, secret, portal_address); + context.consume_l1_to_l2_message(content_hash, secret, portal_address, message_leaf_index); // TODO(palla/gas) Emit an unencrypted log to announce which L1 to L2 message has been claimed // Otherwise, we cannot trace L1 deposits to their corresponding claims on L2 diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr index 1305b464478..f6565b2a80f 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -386,6 +386,7 @@ contract Test { amount: Field, secret_for_L1_to_L2_message_consumption: Field, portal_address: EthAddress, + message_leaf_index: Field, ) { // Consume L1 to L2 message and emit nullifier let content_hash = @@ -394,6 +395,7 @@ contract Test { content_hash, secret_for_L1_to_L2_message_consumption, portal_address, + message_leaf_index, ); } @@ -413,9 +415,10 @@ contract Test { content: Field, secret: Field, sender: EthAddress, + message_leaf_index: Field, ) { // Consume message and emit nullifier - context.consume_l1_to_l2_message(content, secret, sender); + context.consume_l1_to_l2_message(content, secret, sender, message_leaf_index); } #[private] diff --git a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr index ef94712166a..8443c54e0f1 100644 --- a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr @@ -98,6 +98,7 @@ contract TokenBridge { secret_hash_for_redeeming_minted_notes: Field, // secret hash used to redeem minted notes at a later time. This enables anyone to call this function and mint tokens to a user on their behalf amount: Field, secret_for_L1_to_L2_message_consumption: Field, // secret used to consume the L1 to L2 message + message_leaf_index: Field, ) { // Consume L1 to L2 message and emit nullifier let content_hash = @@ -106,6 +107,7 @@ contract TokenBridge { content_hash, secret_for_L1_to_L2_message_consumption, storage.portal_address.read_private(), + message_leaf_index, ); // Mint tokens on L2 diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index 8e6523c3283..52e1a1a1886 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -686,13 +686,12 @@ export class Archiver implements ArchiveSource { } /** - * Gets the first L1 to L2 message index in the L1 to L2 message tree which is greater than or equal to `startIndex`. + * Gets the L1 to L2 message index in the L1 to L2 message tree. * @param l1ToL2Message - The L1 to L2 message. - * @param startIndex - The index to start searching from. * @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found). */ - getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise { - return this.store.getL1ToL2MessageIndex(l1ToL2Message, startIndex); + getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise { + return this.store.getL1ToL2MessageIndex(l1ToL2Message); } getContractClassIds(): Promise { @@ -925,8 +924,8 @@ class ArchiverStoreHelper getL1ToL2Messages(blockNumber: bigint): Promise { return this.store.getL1ToL2Messages(blockNumber); } - getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise { - return this.store.getL1ToL2MessageIndex(l1ToL2Message, startIndex); + getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise { + return this.store.getL1ToL2MessageIndex(l1ToL2Message); } getLogs( from: number, diff --git a/yarn-project/archiver/src/archiver/archiver_store.ts b/yarn-project/archiver/src/archiver/archiver_store.ts index 8a1cc1559c9..9128b33db44 100644 --- a/yarn-project/archiver/src/archiver/archiver_store.ts +++ b/yarn-project/archiver/src/archiver/archiver_store.ts @@ -111,12 +111,11 @@ export interface ArchiverDataStore { getL1ToL2Messages(blockNumber: bigint): Promise; /** - * Gets the first L1 to L2 message index in the L1 to L2 message tree which is greater than or equal to `startIndex`. + * Gets the L1 to L2 message index in the L1 to L2 message tree. * @param l1ToL2Message - The L1 to L2 message. - * @param startIndex - The index to start searching from. * @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found). */ - getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise; + getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise; /** * Get the total number of L1 to L2 messages diff --git a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts index 4bea2246fb7..a41f01ab6e9 100644 --- a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts +++ b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts @@ -257,20 +257,6 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch await store.getL1ToL2Messages(l2BlockNumber); }).rejects.toThrow(`L1 to L2 message gap found in block ${l2BlockNumber}`); }); - - it('correctly handles duplicate messages', async () => { - const messageHash = Fr.random(); - const msgs = [new InboxLeaf(0n, messageHash), new InboxLeaf(16n, messageHash)]; - - await store.addL1ToL2Messages({ lastProcessedL1BlockNumber: 100n, retrievedData: msgs }); - - const index1 = (await store.getL1ToL2MessageIndex(messageHash, 0n))!; - expect(index1).toBe(0n); - const index2 = await store.getL1ToL2MessageIndex(messageHash, index1 + 1n); - expect(index2).toBeDefined(); - expect(index2).toBeGreaterThan(index1); - expect(index2).toBe(16n); - }); }); describe('contractInstances', () => { diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts index b0328eae595..0a1949f0a11 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts @@ -199,13 +199,12 @@ export class KVArchiverDataStore implements ArchiverDataStore { } /** - * Gets the first L1 to L2 message index in the L1 to L2 message tree which is greater than or equal to `startIndex`. + * Gets the L1 to L2 message index in the L1 to L2 message tree. * @param l1ToL2Message - The L1 to L2 message. - * @param startIndex - The index to start searching from. * @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found). */ - getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise { - return Promise.resolve(this.#messageStore.getL1ToL2MessageIndex(l1ToL2Message, startIndex)); + getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise { + return Promise.resolve(this.#messageStore.getL1ToL2MessageIndex(l1ToL2Message)); } /** diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/message_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/message_store.ts index da3964b6da2..6e522948b50 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/message_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/message_store.ts @@ -10,7 +10,7 @@ import { type DataRetrieval } from '../structs/data_retrieval.js'; */ export class MessageStore { #l1ToL2Messages: AztecMap; - #l1ToL2MessageIndices: AztecMap; // We store array of bigints here because there can be duplicate messages + #l1ToL2MessageIndices: AztecMap; #lastSynchedL1Block: AztecSingleton; #totalMessageCount: AztecSingleton; @@ -57,11 +57,8 @@ export class MessageStore { for (const message of messages.retrievedData) { const key = `${message.index}`; - void this.#l1ToL2Messages.setIfNotExists(key, message.leaf.toBuffer()); - - const indices = this.#l1ToL2MessageIndices.get(message.leaf.toString()) ?? []; - indices.push(message.index); - void this.#l1ToL2MessageIndices.set(message.leaf.toString(), indices); + void this.#l1ToL2Messages.set(key, message.leaf.toBuffer()); + void this.#l1ToL2MessageIndices.set(message.leaf.toString(), message.index); } const lastTotalMessageCount = this.getTotalL1ToL2MessageCount(); @@ -72,15 +69,12 @@ export class MessageStore { } /** - * Gets the first L1 to L2 message index in the L1 to L2 message tree which is greater than or equal to `startIndex`. + * Gets the L1 to L2 message index in the L1 to L2 message tree. * @param l1ToL2Message - The L1 to L2 message. - * @param startIndex - The index to start searching from. * @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found). */ - getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise { - const indices = this.#l1ToL2MessageIndices.get(l1ToL2Message.toString()) ?? []; - const index = indices.find(i => i >= startIndex); - return Promise.resolve(index); + getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise { + return Promise.resolve(this.#l1ToL2MessageIndices.get(l1ToL2Message.toString())); } getL1ToL2Messages(blockNumber: bigint): Fr[] { diff --git a/yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.test.ts b/yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.test.ts index f4006e81af1..ace75482382 100644 --- a/yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.test.ts +++ b/yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.test.ts @@ -25,22 +25,9 @@ describe('l1_to_l2_message_store', () => { expect(retrievedMsgs.length).toEqual(10); const msg = msgs[4]; - const expectedIndex = store.getMessageIndex(msg.leaf, 0n)!; + const expectedIndex = store.getMessageIndex(msg.leaf); expect(expectedIndex).toEqual( (blockNumber - BigInt(INITIAL_L2_BLOCK_NUM)) * BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP) + 4n, ); }); - - it('correctly handles duplicate messages', () => { - const messageHash = Fr.random(); - - store.addMessage(new InboxLeaf(0n, messageHash)); // l2 block 1 - store.addMessage(new InboxLeaf(16n, messageHash)); // l2 block 2 - - const index1 = store.getMessageIndex(messageHash, 0n)!; - const index2 = store.getMessageIndex(messageHash, index1 + 1n); - - expect(index2).toBeDefined(); - expect(index2).toBeGreaterThan(index1); - }); }); diff --git a/yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts b/yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts index 563458f4667..c33c841d8da 100644 --- a/yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +++ b/yarn-project/archiver/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts @@ -46,19 +46,14 @@ export class L1ToL2MessageStore { } /** - * Gets the first L1 to L2 message index in the L1 to L2 message tree which is greater than or equal to `startIndex`. + * Gets the L1 to L2 message index in the L1 to L2 message tree. * @param l1ToL2Message - The L1 to L2 message. - * @param startIndex - The index to start searching from. * @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found). */ - getMessageIndex(l1ToL2Message: Fr, startIndex: bigint): bigint | undefined { + getMessageIndex(l1ToL2Message: Fr): bigint | undefined { for (const [key, message] of this.store.entries()) { if (message.equals(l1ToL2Message)) { - const indexInTheWholeTree = BigInt(key); - if (indexInTheWholeTree < startIndex) { - continue; - } - return indexInTheWholeTree; + return BigInt(key); } } return undefined; diff --git a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts index 4a57dbffb6d..4d3e887c072 100644 --- a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts +++ b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts @@ -281,13 +281,12 @@ export class MemoryArchiverStore implements ArchiverDataStore { } /** - * Gets the first L1 to L2 message index in the L1 to L2 message tree which is greater than or equal to `startIndex`. + * Gets the L1 to L2 message index in the L1 to L2 message tree. * @param l1ToL2Message - The L1 to L2 message. - * @param startIndex - The index to start searching from. * @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found). */ - getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise { - return Promise.resolve(this.l1ToL2Messages.getMessageIndex(l1ToL2Message, startIndex)); + getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise { + return Promise.resolve(this.l1ToL2Messages.getMessageIndex(l1ToL2Message)); } /** diff --git a/yarn-project/archiver/src/test/mock_archiver.ts b/yarn-project/archiver/src/test/mock_archiver.ts index 5cfb7424551..a31e7bbd872 100644 --- a/yarn-project/archiver/src/test/mock_archiver.ts +++ b/yarn-project/archiver/src/test/mock_archiver.ts @@ -18,8 +18,8 @@ export class MockArchiver extends MockL2BlockSource implements L2BlockSource, L1 return this.messageSource.getL1ToL2Messages(blockNumber); } - getL1ToL2MessageIndex(_l1ToL2Message: Fr, _startIndex: bigint): Promise { - return this.messageSource.getL1ToL2MessageIndex(_l1ToL2Message, _startIndex); + getL1ToL2MessageIndex(_l1ToL2Message: Fr): Promise { + return this.messageSource.getL1ToL2MessageIndex(_l1ToL2Message); } } diff --git a/yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts b/yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts index cac4157d6ae..48055cec0ce 100644 --- a/yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts +++ b/yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts @@ -21,7 +21,7 @@ export class MockL1ToL2MessageSource implements L1ToL2MessageSource { return Promise.resolve(this.messagesPerBlock.get(Number(blockNumber)) ?? []); } - getL1ToL2MessageIndex(_l1ToL2Message: Fr, _startIndex: bigint): Promise { + getL1ToL2MessageIndex(_l1ToL2Message: Fr): Promise { throw new Error('Method not implemented.'); } diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index cc0173596b6..a22e5ce6f8a 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -44,7 +44,6 @@ import { type L1_TO_L2_MSG_TREE_HEIGHT, type NOTE_HASH_TREE_HEIGHT, type NULLIFIER_TREE_HEIGHT, - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, type NullifierLeafPreimage, type PUBLIC_DATA_TREE_HEIGHT, type ProtocolContractAddresses, @@ -448,15 +447,13 @@ export class AztecNodeService implements AztecNode { * Returns the index and a sibling path for a leaf in the committed l1 to l2 data tree. * @param blockNumber - The block number at which to get the data. * @param l1ToL2Message - The l1ToL2Message to get the index / sibling path for. - * @param startIndex - The index to start searching from (used when skipping nullified messages) * @returns A tuple of the index and the sibling path of the L1ToL2Message (undefined if not found). */ public async getL1ToL2MessageMembershipWitness( blockNumber: L2BlockNumber, l1ToL2Message: Fr, - startIndex = 0n, ): Promise<[bigint, SiblingPath] | undefined> { - const index = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message, startIndex); + const index = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message); if (index === undefined) { return undefined; } @@ -471,15 +468,10 @@ export class AztecNodeService implements AztecNode { /** * Returns whether an L1 to L2 message is synced by archiver and if it's ready to be included in a block. * @param l1ToL2Message - The L1 to L2 message to check. - * @param startL2BlockNumber - The block number after which we are interested in checking if the message was - * included. - * @remarks We pass in the minL2BlockNumber because there can be duplicate messages and the block number allow us - * to skip the duplicates (we know after which block a given message is to be included). * @returns Whether the message is synced and ready to be included in a block. */ - public async isL1ToL2MessageSynced(l1ToL2Message: Fr, startL2BlockNumber = INITIAL_L2_BLOCK_NUM): Promise { - const startIndex = BigInt(startL2BlockNumber - INITIAL_L2_BLOCK_NUM) * BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP); - return (await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message, startIndex)) !== undefined; + public async isL1ToL2MessageSynced(l1ToL2Message: Fr): Promise { + return (await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message)) !== undefined; } /** diff --git a/yarn-project/aztec.js/src/fee/fee_juice_payment_method_with_claim.ts b/yarn-project/aztec.js/src/fee/fee_juice_payment_method_with_claim.ts index a11f8617792..2c3fa87bb59 100644 --- a/yarn-project/aztec.js/src/fee/fee_juice_payment_method_with_claim.ts +++ b/yarn-project/aztec.js/src/fee/fee_juice_payment_method_with_claim.ts @@ -1,15 +1,19 @@ import { type FunctionCall } from '@aztec/circuit-types'; import { type AztecAddress, Fr, FunctionSelector } from '@aztec/circuits.js'; import { FunctionType } from '@aztec/foundation/abi'; -import { ProtocolContractAddress } from '@aztec/protocol-contracts'; +import { ProtocolContractAddress, ProtocolContractArtifact } from '@aztec/protocol-contracts'; +import { type L2AmountClaim } from '../utils/portal_manager.js'; import { FeeJuicePaymentMethod } from './fee_juice_payment_method.js'; /** * Pay fee directly with Fee Juice claimed on the same tx. */ export class FeeJuicePaymentMethodWithClaim extends FeeJuicePaymentMethod { - constructor(sender: AztecAddress, private claimAmount: bigint | Fr, private claimSecret: Fr) { + constructor( + sender: AztecAddress, + private claim: Pick, + ) { super(sender); } @@ -18,13 +22,17 @@ export class FeeJuicePaymentMethodWithClaim extends FeeJuicePaymentMethod { * @returns A function call */ override getFunctionCalls(): Promise { + const selector = FunctionSelector.fromNameAndParameters( + ProtocolContractArtifact.FeeJuice.functions.find(f => f.name === 'claim')!, + ); + return Promise.resolve([ { to: ProtocolContractAddress.FeeJuice, name: 'claim', - selector: FunctionSelector.fromSignature('claim((Field),Field,Field)'), + selector, isStatic: false, - args: [this.sender, new Fr(this.claimAmount), this.claimSecret], + args: [this.sender, this.claim.claimAmount, this.claim.claimSecret, new Fr(this.claim.messageLeafIndex)], returnTypes: [], type: FunctionType.PRIVATE, }, diff --git a/yarn-project/aztec.js/src/index.ts b/yarn-project/aztec.js/src/index.ts index 9192fce979f..0cd90c9fd60 100644 --- a/yarn-project/aztec.js/src/index.ts +++ b/yarn-project/aztec.js/src/index.ts @@ -24,42 +24,50 @@ export { Contract, ContractBase, ContractFunctionInteraction, + DefaultWaitOpts, + DeployMethod, + DeploySentTx, + SentTx, type ContractMethod, type ContractNotes, type ContractStorageLayout, - DefaultWaitOpts, - DeployMethod, type DeployOptions, - DeploySentTx, type SendMethodOptions, - SentTx, type WaitOpts, } from './contract/index.js'; export { ContractDeployer } from './deployment/index.js'; export { - type AztecAddressLike, AnvilTestWatcher, CheatCodes, - type EthAddressLike, EthCheatCodes, - type EventSelectorLike, - type FieldLike, - type FunctionSelectorLike, - type WrappedFieldLike, + L1FeeJuicePortalManager, + L1ToL2TokenPortalManager, + L1TokenManager, + L1TokenPortalManager, computeAuthWitMessageHash, - computeInnerAuthWitHashFromAction, computeInnerAuthWitHash, + computeInnerAuthWitHashFromAction, + generateClaimSecret, generatePublicKey, readFieldCompressedString, waitForAccountSynch, waitForPXE, + type AztecAddressLike, + type EthAddressLike, + type EventSelectorLike, + type FieldLike, + type FunctionSelectorLike, + type L2AmountClaim, + type L2Claim, + type L2RedeemableAmountClaim, + type WrappedFieldLike, } from './utils/index.js'; export { NoteSelector } from '@aztec/foundation/abi'; -export { createPXEClient, createCompatibleClient } from './rpc_clients/index.js'; +export { createCompatibleClient, createPXEClient } from './rpc_clients/index.js'; export { type AuthWitnessProvider } from './account/index.js'; @@ -72,19 +80,19 @@ export { AccountWallet, AccountWalletWithSecretKey, SignerlessWallet, type Walle // // here once the issue is resolved. export { AztecAddress, + ContractClassWithId, + ContractInstanceWithAddress, EthAddress, - PublicKeys, Fq, Fr, GlobalVariables, GrumpkinScalar, INITIAL_L2_BLOCK_NUM, + NodeInfo, Point, + PublicKeys, getContractClassFromArtifact, getContractInstanceFromDeployParams, - ContractClassWithId, - ContractInstanceWithAddress, - NodeInfo, } from '@aztec/circuits.js'; export { computeSecretHash } from '@aztec/circuits.js/hash'; @@ -100,32 +108,30 @@ export { Grumpkin, Schnorr } from '@aztec/circuits.js/barretenberg'; export { AuthWitness, - type AztecNode, Body, Comparator, CompleteAddress, EncryptedL2BlockL2Logs, + EncryptedLogPayload, + EncryptedNoteL2BlockL2Logs, + EpochProofQuote, + EpochProofQuotePayload, EventType, ExtendedNote, - UniqueNote, FunctionCall, L1Actor, + L1EventPayload, + L1NotePayload, L1ToL2Message, L2Actor, L2Block, L2BlockL2Logs, - EncryptedNoteL2BlockL2Logs, - type LogFilter, LogId, LogType, MerkleTreeId, Note, - type PXE, PackedValues, - type PartialAddress, - type PublicKey, SiblingPath, - type SyncStatus, Tx, TxExecutionRequest, TxHash, @@ -133,25 +139,27 @@ export { TxStatus, UnencryptedL2BlockL2Logs, UnencryptedL2Log, + UniqueNote, createAztecNodeClient, merkleTreeIds, - mockTx, mockEpochProofQuote, - EncryptedLogPayload, - L1NotePayload, - L1EventPayload, - EpochProofQuote, - EpochProofQuotePayload, + mockTx, + type AztecNode, + type LogFilter, + type PXE, + type PartialAddress, + type PublicKey, + type SyncStatus, } from '@aztec/circuit-types'; // TODO: These kinds of things have no place on our public api. // External devs will almost certainly have their own methods of doing these things. // If we want to use them in our own "aztec.js consuming code", import them from foundation as needed. -export { encodeArguments, decodeFromAbi, type AbiType } from '@aztec/foundation/abi'; +export { decodeFromAbi, encodeArguments, type AbiType } from '@aztec/foundation/abi'; export { toBigIntBE } from '@aztec/foundation/bigint-buffer'; export { sha256 } from '@aztec/foundation/crypto'; export { makeFetch } from '@aztec/foundation/json-rpc/client'; -export { type DebugLogger, createDebugLogger, onLog } from '@aztec/foundation/log'; +export { createDebugLogger, onLog, type DebugLogger } from '@aztec/foundation/log'; export { retry, retryUntil } from '@aztec/foundation/retry'; export { to2Fields, toBigInt } from '@aztec/foundation/serialize'; export { sleep } from '@aztec/foundation/sleep'; @@ -159,7 +167,7 @@ export { elapsed } from '@aztec/foundation/timer'; export { type FieldsOf } from '@aztec/foundation/types'; export { fileURLToPath } from '@aztec/foundation/url'; -export { type DeployL1Contracts, deployL1Contract, deployL1Contracts } from '@aztec/ethereum'; +export { deployL1Contract, deployL1Contracts, type DeployL1Contracts } from '@aztec/ethereum'; // Start of section that exports public api via granular api. // Here you *can* do `export *` as the granular api defacto exports things explicitly. diff --git a/yarn-project/aztec.js/src/utils/index.ts b/yarn-project/aztec.js/src/utils/index.ts index 6691a934a10..4d9c7dc3969 100644 --- a/yarn-project/aztec.js/src/utils/index.ts +++ b/yarn-project/aztec.js/src/utils/index.ts @@ -7,3 +7,4 @@ export * from './pxe.js'; export * from './account.js'; export * from './anvil_test_watcher.js'; export * from './field_compressed_string.js'; +export * from './portal_manager.js'; diff --git a/yarn-project/aztec.js/src/utils/portal_manager.ts b/yarn-project/aztec.js/src/utils/portal_manager.ts new file mode 100644 index 00000000000..3953f32f54e --- /dev/null +++ b/yarn-project/aztec.js/src/utils/portal_manager.ts @@ -0,0 +1,423 @@ +import { + type AztecAddress, + type DebugLogger, + EthAddress, + Fr, + type PXE, + type SiblingPath, + computeSecretHash, +} from '@aztec/aztec.js'; +import { extractEvent } from '@aztec/ethereum'; +import { sha256ToField } from '@aztec/foundation/crypto'; +import { FeeJuicePortalAbi, OutboxAbi, TestERC20Abi, TokenPortalAbi } from '@aztec/l1-artifacts'; + +import { + type Account, + type Chain, + type GetContractReturnType, + type Hex, + type HttpTransport, + type PublicClient, + type WalletClient, + getContract, + toFunctionSelector, +} from 'viem'; + +/** L1 to L2 message info to claim it on L2. */ +export type L2Claim = { + /** Secret for claiming. */ + claimSecret: Fr; + /** Hash of the secret for claiming. */ + claimSecretHash: Fr; + /** Hash of the message. */ + messageHash: Hex; + /** Leaf index in the L1 to L2 message tree. */ + messageLeafIndex: bigint; +}; + +/** L1 to L2 message info that corresponds to an amount to claim. */ +export type L2AmountClaim = L2Claim & { /** Amount to claim */ claimAmount: Fr }; + +/** L1 to L2 message info that corresponds to an amount to claim with associated notes to be redeemed. */ +export type L2RedeemableAmountClaim = L2AmountClaim & { + /** Secret for redeeming the minted notes */ redeemSecret: Fr; + /** Hash of the redeem secret*/ redeemSecretHash: Fr; +}; + +/** Stringifies an eth address for logging. */ +function stringifyEthAddress(address: EthAddress | Hex, name?: string) { + return name ? `${name} (${address.toString()})` : address.toString(); +} + +/** Generates a pair secret and secret hash */ +export function generateClaimSecret(logger?: DebugLogger): [Fr, Fr] { + const secret = Fr.random(); + const secretHash = computeSecretHash(secret); + logger?.verbose(`Generated claim secret=${secret.toString()} hash=${secretHash.toString()}`); + return [secret, secretHash]; +} + +/** Helper for managing an ERC20 on L1. */ +export class L1TokenManager { + private contract: GetContractReturnType>; + + public constructor( + /** Address of the ERC20 contract. */ + public readonly address: EthAddress, + private publicClient: PublicClient, + private walletClient: WalletClient, + private logger: DebugLogger, + ) { + this.contract = getContract({ + address: this.address.toString(), + abi: TestERC20Abi, + client: this.walletClient, + }); + } + + /** + * Returns the balance of the given address. + * @param address - Address to get the balance of. + */ + public async getL1TokenBalance(address: Hex) { + return await this.contract.read.balanceOf([address]); + } + + /** + * Mints tokens for the given address. Returns once the tx has been mined. + * @param amount - Amount to mint. + * @param address - Address to mint the tokens for. + * @param addressName - Optional name of the address for logging. + */ + public async mint(amount: bigint, address: Hex, addressName?: string) { + this.logger.info(`Minting ${amount} tokens for ${stringifyEthAddress(address, addressName)}`); + await this.publicClient.waitForTransactionReceipt({ + hash: await this.contract.write.mint([address, amount]), + }); + } + + /** + * Approves tokens for the given address. Returns once the tx has been mined. + * @param amount - Amount to approve. + * @param address - Address to approve the tokens for. + * @param addressName - Optional name of the address for logging. + */ + public async approve(amount: bigint, address: Hex, addressName = '') { + this.logger.info(`Approving ${amount} tokens for ${stringifyEthAddress(address, addressName)}`); + await this.publicClient.waitForTransactionReceipt({ + hash: await this.contract.write.approve([address, amount]), + }); + } +} + +/** Helper for interacting with the FeeJuicePortal on L1. */ +export class L1FeeJuicePortalManager { + private readonly tokenManager: L1TokenManager; + private readonly contract: GetContractReturnType< + typeof FeeJuicePortalAbi, + WalletClient + >; + + constructor( + portalAddress: EthAddress, + tokenAddress: EthAddress, + private readonly publicClient: PublicClient, + private readonly walletClient: WalletClient, + private readonly logger: DebugLogger, + ) { + this.tokenManager = new L1TokenManager(tokenAddress, publicClient, walletClient, logger); + this.contract = getContract({ + address: portalAddress.toString(), + abi: FeeJuicePortalAbi, + client: this.walletClient, + }); + } + + /** Returns the associated token manager for the L1 ERC20. */ + public getTokenManager() { + return this.tokenManager; + } + + /** + * Bridges fee juice from L1 to L2 publicly. Handles L1 ERC20 approvals. Returns once the tx has been mined. + * @param to - Address to send the tokens to on L2. + * @param amount - Amount of tokens to send. + * @param mint - Whether to mint the tokens before sending (only during testing). + */ + public async bridgeTokensPublic(to: AztecAddress, amount: bigint, mint = false): Promise { + const [claimSecret, claimSecretHash] = generateClaimSecret(); + if (mint) { + await this.tokenManager.mint(amount, this.walletClient.account.address); + } + + await this.tokenManager.approve(amount, this.contract.address, 'FeeJuice Portal'); + + this.logger.info('Sending L1 Fee Juice to L2 to be claimed publicly'); + const args = [to.toString(), amount, claimSecretHash.toString()] as const; + + await this.contract.simulate.depositToAztecPublic(args); + + const txReceipt = await this.publicClient.waitForTransactionReceipt({ + hash: await this.contract.write.depositToAztecPublic(args), + }); + + const log = extractEvent( + txReceipt.logs, + this.contract.address, + this.contract.abi, + 'DepositToAztecPublic', + log => + log.args.secretHash === claimSecretHash.toString() && + log.args.amount === amount && + log.args.to === to.toString(), + this.logger, + ); + + return { + claimAmount: new Fr(amount), + claimSecret, + claimSecretHash, + messageHash: log.args.key, + messageLeafIndex: log.args.index, + }; + } + + /** + * Creates a new instance + * @param pxe - PXE client used for retrieving the L1 contract addresses. + * @param publicClient - L1 public client. + * @param walletClient - L1 wallet client. + * @param logger - Logger. + */ + public static async new( + pxe: PXE, + publicClient: PublicClient, + walletClient: WalletClient, + logger: DebugLogger, + ): Promise { + const { + l1ContractAddresses: { feeJuiceAddress, feeJuicePortalAddress }, + } = await pxe.getNodeInfo(); + + if (feeJuiceAddress.isZero() || feeJuicePortalAddress.isZero()) { + throw new Error('Portal or token not deployed on L1'); + } + + return new L1FeeJuicePortalManager(feeJuicePortalAddress, feeJuiceAddress, publicClient, walletClient, logger); + } +} + +/** Helper for interacting with a test TokenPortal on L1 for sending tokens to L2. */ +export class L1ToL2TokenPortalManager { + protected readonly portal: GetContractReturnType>; + protected readonly tokenManager: L1TokenManager; + + constructor( + portalAddress: EthAddress, + tokenAddress: EthAddress, + protected publicClient: PublicClient, + protected walletClient: WalletClient, + protected logger: DebugLogger, + ) { + this.tokenManager = new L1TokenManager(tokenAddress, publicClient, walletClient, logger); + this.portal = getContract({ + address: portalAddress.toString(), + abi: TokenPortalAbi, + client: this.walletClient, + }); + } + + /** Returns the token manager for the underlying L1 token. */ + public getTokenManager() { + return this.tokenManager; + } + + /** + * Bridges tokens from L1 to L2. Handles token approvals. Returns once the tx has been mined. + * @param to - Address to send the tokens to on L2. + * @param amount - Amount of tokens to send. + * @param mint - Whether to mint the tokens before sending (only during testing). + */ + public async bridgeTokensPublic(to: AztecAddress, amount: bigint, mint = false): Promise { + const [claimSecret, claimSecretHash] = await this.bridgeSetup(amount, mint); + + this.logger.info('Sending L1 tokens to L2 to be claimed publicly'); + const { request } = await this.portal.simulate.depositToAztecPublic([ + to.toString(), + amount, + claimSecretHash.toString(), + ]); + + const txReceipt = await this.publicClient.waitForTransactionReceipt({ + hash: await this.walletClient.writeContract(request), + }); + + const log = extractEvent( + txReceipt.logs, + this.portal.address, + this.portal.abi, + 'DepositToAztecPublic', + log => + log.args.secretHash === claimSecretHash.toString() && + log.args.amount === amount && + log.args.to === to.toString(), + this.logger, + ); + + return { + claimAmount: new Fr(amount), + claimSecret, + claimSecretHash, + messageHash: log.args.key, + messageLeafIndex: log.args.index, + }; + } + + /** + * Bridges tokens from L1 to L2 privately. Handles token approvals. Returns once the tx has been mined. + * @param to - Address to send the tokens to on L2. + * @param amount - Amount of tokens to send. + * @param mint - Whether to mint the tokens before sending (only during testing). + */ + public async bridgeTokensPrivate(to: AztecAddress, amount: bigint, mint = false): Promise { + const [claimSecret, claimSecretHash] = await this.bridgeSetup(amount, mint); + + const redeemSecret = Fr.random(); + const redeemSecretHash = computeSecretHash(redeemSecret); + this.logger.info('Sending L1 tokens to L2 to be claimed privately'); + const { request } = await this.portal.simulate.depositToAztecPrivate([ + redeemSecretHash.toString(), + amount, + claimSecretHash.toString(), + ]); + + const txReceipt = await this.publicClient.waitForTransactionReceipt({ + hash: await this.walletClient.writeContract(request), + }); + + const log = extractEvent( + txReceipt.logs, + this.portal.address, + this.portal.abi, + 'DepositToAztecPrivate', + log => + log.args.secretHashForRedeemingMintedNotes === redeemSecretHash.toString() && + log.args.amount === amount && + log.args.secretHashForL2MessageConsumption === claimSecretHash.toString(), + this.logger, + ); + + this.logger.info(`Redeem shield secret: ${redeemSecret.toString()}, secret hash: ${redeemSecretHash.toString()}`); + + return { + claimAmount: new Fr(amount), + claimSecret, + claimSecretHash, + redeemSecret, + redeemSecretHash, + messageHash: log.args.key, + messageLeafIndex: log.args.index, + }; + } + + private async bridgeSetup(amount: bigint, mint: boolean) { + if (mint) { + await this.tokenManager.mint(amount, this.walletClient.account.address); + } + await this.tokenManager.approve(amount, this.portal.address, 'TokenPortal'); + return generateClaimSecret(); + } +} + +/** Helper for interacting with a test TokenPortal on L1 for both withdrawing from and briding to L2. */ +export class L1TokenPortalManager extends L1ToL2TokenPortalManager { + private readonly outbox: GetContractReturnType>; + + constructor( + portalAddress: EthAddress, + tokenAddress: EthAddress, + outboxAddress: EthAddress, + publicClient: PublicClient, + walletClient: WalletClient, + logger: DebugLogger, + ) { + super(portalAddress, tokenAddress, publicClient, walletClient, logger); + this.outbox = getContract({ + address: outboxAddress.toString(), + abi: OutboxAbi, + client: walletClient, + }); + } + + /** + * Withdraws funds from the portal by consuming an L2 to L1 message. Returns once the tx is mined on L1. + * @param amount - Amount to withdraw. + * @param recipient - Who will receive the funds. + * @param blockNumber - L2 block number of the message. + * @param messageIndex - Index of the message. + * @param siblingPath - Sibling path of the message. + */ + public async withdrawFunds( + amount: bigint, + recipient: EthAddress, + blockNumber: bigint, + messageIndex: bigint, + siblingPath: SiblingPath, + ) { + this.logger.info( + `Sending L1 tx to consume message at block ${blockNumber} index ${messageIndex} to withdraw ${amount}`, + ); + + const isConsumedBefore = await this.outbox.read.hasMessageBeenConsumedAtBlockAndIndex([blockNumber, messageIndex]); + if (isConsumedBefore) { + throw new Error(`L1 to L2 message at block ${blockNumber} index ${messageIndex} has already been consumed`); + } + + // Call function on L1 contract to consume the message + const { request: withdrawRequest } = await this.portal.simulate.withdraw([ + recipient.toString(), + amount, + false, + BigInt(blockNumber), + messageIndex, + siblingPath.toBufferArray().map((buf: Buffer): Hex => `0x${buf.toString('hex')}`), + ]); + + await this.publicClient.waitForTransactionReceipt({ hash: await this.walletClient.writeContract(withdrawRequest) }); + + const isConsumedAfter = await this.outbox.read.hasMessageBeenConsumedAtBlockAndIndex([blockNumber, messageIndex]); + if (!isConsumedAfter) { + throw new Error(`L1 to L2 message at block ${blockNumber} index ${messageIndex} not consumed after withdrawal`); + } + } + + /** + * Computes the L2 to L1 message leaf for the given parameters. + * @param amount - Amount to bridge. + * @param recipient - Recipient on L1. + * @param l2Bridge - Address of the L2 bridge. + * @param callerOnL1 - Caller address on L1. + */ + public getL2ToL1MessageLeaf( + amount: bigint, + recipient: EthAddress, + l2Bridge: AztecAddress, + callerOnL1: EthAddress = EthAddress.ZERO, + ): Fr { + const content = sha256ToField([ + Buffer.from(toFunctionSelector('withdraw(address,uint256,address)').substring(2), 'hex'), + recipient.toBuffer32(), + new Fr(amount).toBuffer(), + callerOnL1.toBuffer32(), + ]); + const leaf = sha256ToField([ + l2Bridge.toBuffer(), + new Fr(1).toBuffer(), // aztec version + EthAddress.fromString(this.portal.address).toBuffer32() ?? Buffer.alloc(32, 0), + new Fr(this.publicClient.chain.id).toBuffer(), // chain id + content.toBuffer(), + ]); + + return leaf; + } +} diff --git a/yarn-project/circuit-types/src/interfaces/aztec-node.ts b/yarn-project/circuit-types/src/interfaces/aztec-node.ts index e7c79fda0ce..e12578d7c44 100644 --- a/yarn-project/circuit-types/src/interfaces/aztec-node.ts +++ b/yarn-project/circuit-types/src/interfaces/aztec-node.ts @@ -75,13 +75,11 @@ export interface AztecNode extends ProverCoordination { * Returns the index and a sibling path for a leaf in the committed l1 to l2 data tree. * @param blockNumber - The block number at which to get the data. * @param l1ToL2Message - The l1ToL2Message to get the index / sibling path for. - * @param startIndex - The index to start searching from. * @returns A tuple of the index and the sibling path of the L1ToL2Message (undefined if not found). */ getL1ToL2MessageMembershipWitness( blockNumber: L2BlockNumber, l1ToL2Message: Fr, - startIndex: bigint, ): Promise<[bigint, SiblingPath] | undefined>; /** diff --git a/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts b/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts index 56e457f62f1..6d8ada947b9 100644 --- a/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts +++ b/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts @@ -16,22 +16,16 @@ import { L2Actor } from './l2_actor.js'; */ export class L1ToL2Message { constructor( - /** - * The sender of the message on L1. - */ + /** The sender of the message on L1. */ public readonly sender: L1Actor, - /** - * The recipient of the message on L2. - */ + /** The recipient of the message on L2. */ public readonly recipient: L2Actor, - /** - * The message content. - */ + /** The message content. */ public readonly content: Fr, - /** - * The hash of the spending secret. - */ + /** The hash of the spending secret. */ public readonly secretHash: Fr, + /** Global index of this message on the tree. */ + public readonly index: Fr, ) {} /** @@ -39,11 +33,11 @@ export class L1ToL2Message { * @returns The message as an array of fields (in order). */ toFields(): Fr[] { - return [...this.sender.toFields(), ...this.recipient.toFields(), this.content, this.secretHash]; + return [...this.sender.toFields(), ...this.recipient.toFields(), this.content, this.secretHash, this.index]; } toBuffer(): Buffer { - return serializeToBuffer(this.sender, this.recipient, this.content, this.secretHash); + return serializeToBuffer(this.sender, this.recipient, this.content, this.secretHash, this.index); } hash(): Fr { @@ -56,7 +50,8 @@ export class L1ToL2Message { const recipient = reader.readObject(L2Actor); const content = Fr.fromBuffer(reader); const secretHash = Fr.fromBuffer(reader); - return new L1ToL2Message(sender, recipient, content, secretHash); + const index = Fr.fromBuffer(reader); + return new L1ToL2Message(sender, recipient, content, secretHash, index); } toString(): string { @@ -69,11 +64,11 @@ export class L1ToL2Message { } static empty(): L1ToL2Message { - return new L1ToL2Message(L1Actor.empty(), L2Actor.empty(), Fr.ZERO, Fr.ZERO); + return new L1ToL2Message(L1Actor.empty(), L2Actor.empty(), Fr.ZERO, Fr.ZERO, Fr.ZERO); } static random(): L1ToL2Message { - return new L1ToL2Message(L1Actor.random(), L2Actor.random(), Fr.random(), Fr.random()); + return new L1ToL2Message(L1Actor.random(), L2Actor.random(), Fr.random(), Fr.random(), Fr.random()); } } @@ -84,26 +79,18 @@ export async function getNonNullifiedL1ToL2MessageWitness( messageHash: Fr, secret: Fr, ): Promise<[bigint, SiblingPath]> { - let nullifierIndex: bigint | undefined; - let messageIndex = 0n; - let startIndex = 0n; - let siblingPath: SiblingPath; - - // We iterate over messages until we find one whose nullifier is not in the nullifier tree --> we need to check - // for nullifiers because messages can have duplicates. - do { - const response = await node.getL1ToL2MessageMembershipWitness('latest', messageHash, startIndex); - if (!response) { - throw new Error(`No non-nullified L1 to L2 message found for message hash ${messageHash.toString()}`); - } - [messageIndex, siblingPath] = response; - - const messageNullifier = computeL1ToL2MessageNullifier(contractAddress, messageHash, secret, messageIndex); + const response = await node.getL1ToL2MessageMembershipWitness('latest', messageHash); + if (!response) { + throw new Error(`No L1 to L2 message found for message hash ${messageHash.toString()}`); + } + const [messageIndex, siblingPath] = response; - nullifierIndex = await node.findLeafIndex('latest', MerkleTreeId.NULLIFIER_TREE, messageNullifier); + const messageNullifier = computeL1ToL2MessageNullifier(contractAddress, messageHash, secret); - startIndex = messageIndex + 1n; - } while (nullifierIndex !== undefined); + const nullifierIndex = await node.findLeafIndex('latest', MerkleTreeId.NULLIFIER_TREE, messageNullifier); + if (nullifierIndex !== undefined) { + throw new Error(`No non-nullified L1 to L2 message found for message hash ${messageHash.toString()}`); + } return [messageIndex, siblingPath]; } diff --git a/yarn-project/circuit-types/src/messaging/l1_to_l2_message_source.ts b/yarn-project/circuit-types/src/messaging/l1_to_l2_message_source.ts index 1bd0efab89d..d31cb84e258 100644 --- a/yarn-project/circuit-types/src/messaging/l1_to_l2_message_source.ts +++ b/yarn-project/circuit-types/src/messaging/l1_to_l2_message_source.ts @@ -12,12 +12,11 @@ export interface L1ToL2MessageSource { getL1ToL2Messages(blockNumber: bigint): Promise; /** - * Gets the first L1 to L2 message index in the L1 to L2 message tree which is greater than or equal to `startIndex`. + * Gets the L1 to L2 message index in the L1 to L2 message tree. * @param l1ToL2Message - The L1 to L2 message. - * @param startIndex - The index to start searching from. * @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found). */ - getL1ToL2MessageIndex(l1ToL2Message: Fr, startIndex: bigint): Promise; + getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise; /** * Gets the number of the latest L2 block processed by the implementation. diff --git a/yarn-project/circuits.js/src/hash/hash.ts b/yarn-project/circuits.js/src/hash/hash.ts index eab83b28f0b..932fbcf54a9 100644 --- a/yarn-project/circuits.js/src/hash/hash.ts +++ b/yarn-project/circuits.js/src/hash/hash.ts @@ -115,16 +115,8 @@ export function computeSecretHash(secret: Fr) { return poseidon2HashWithSeparator([secret], GeneratorIndex.SECRET_HASH); } -export function computeL1ToL2MessageNullifier( - contract: AztecAddress, - messageHash: Fr, - secret: Fr, - messageIndex: bigint, -) { - const innerMessageNullifier = poseidon2HashWithSeparator( - [messageHash, secret, messageIndex], - GeneratorIndex.MESSAGE_NULLIFIER, - ); +export function computeL1ToL2MessageNullifier(contract: AztecAddress, messageHash: Fr, secret: Fr) { + const innerMessageNullifier = poseidon2HashWithSeparator([messageHash, secret], GeneratorIndex.MESSAGE_NULLIFIER); return siloNullifier(contract, innerMessageNullifier); } diff --git a/yarn-project/cli-wallet/src/cmds/bridge_fee_juice.ts b/yarn-project/cli-wallet/src/cmds/bridge_fee_juice.ts index 414b6a67eb0..12daf7172c3 100644 --- a/yarn-project/cli-wallet/src/cmds/bridge_fee_juice.ts +++ b/yarn-project/cli-wallet/src/cmds/bridge_fee_juice.ts @@ -1,5 +1,5 @@ -import { createCompatibleClient } from '@aztec/aztec.js'; -import { FeeJuicePortalManager, prettyPrintJSON } from '@aztec/cli/utils'; +import { L1FeeJuicePortalManager, createCompatibleClient } from '@aztec/aztec.js'; +import { prettyPrintJSON } from '@aztec/cli/utils'; import { createEthereumChain, createL1Clients } from '@aztec/ethereum'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; @@ -32,13 +32,18 @@ export async function bridgeL1FeeJuice( } = await client.getPXEInfo(); // Setup portal manager - const portal = await FeeJuicePortalManager.new(client, publicClient, walletClient, debugLogger); - const { claimAmount, claimSecret, messageHash } = await portal.bridgeTokensPublic(recipient, amount, mint); + const portal = await L1FeeJuicePortalManager.new(client, publicClient, walletClient, debugLogger); + const { claimAmount, claimSecret, messageHash, messageLeafIndex } = await portal.bridgeTokensPublic( + recipient, + amount, + mint, + ); if (json) { const out = { claimAmount, claimSecret, + messageLeafIndex, }; log(prettyPrintJSON(out)); } else { @@ -47,7 +52,9 @@ export async function bridgeL1FeeJuice( } else { log(`Bridged ${claimAmount} fee juice to L2 portal`); } - log(`claimAmount=${claimAmount},claimSecret=${claimSecret},messageHash=${messageHash}\n`); + log( + `claimAmount=${claimAmount},claimSecret=${claimSecret},messageHash=${messageHash},messageLeafIndex=${messageLeafIndex}\n`, + ); log(`Note: You need to wait for two L2 blocks before pulling them from the L2 side`); if (wait) { log( @@ -82,5 +89,5 @@ export async function bridgeL1FeeJuice( } } - return claimSecret; + return [claimSecret, messageLeafIndex] as const; } diff --git a/yarn-project/cli-wallet/src/cmds/index.ts b/yarn-project/cli-wallet/src/cmds/index.ts index 2cc1e08cf98..7484d4413e4 100644 --- a/yarn-project/cli-wallet/src/cmds/index.ts +++ b/yarn-project/cli-wallet/src/cmds/index.ts @@ -341,7 +341,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL .action(async (amount, recipient, options) => { const { bridgeL1FeeJuice } = await import('./bridge_fee_juice.js'); const { rpcUrl, l1RpcUrl, l1ChainId, l1PrivateKey, mnemonic, mint, json, wait, interval: intervalS } = options; - const secret = await bridgeL1FeeJuice( + const [secret, messageLeafIndex] = await bridgeL1FeeJuice( amount, recipient, rpcUrl, @@ -357,7 +357,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL debugLogger, ); if (db) { - await db.pushBridgedFeeJuice(recipient, secret, amount, log); + await db.pushBridgedFeeJuice(recipient, secret, amount, messageLeafIndex, log); } }); diff --git a/yarn-project/cli-wallet/src/storage/wallet_db.ts b/yarn-project/cli-wallet/src/storage/wallet_db.ts index 629f94b7764..aeaf2f40cf4 100644 --- a/yarn-project/cli-wallet/src/storage/wallet_db.ts +++ b/yarn-project/cli-wallet/src/storage/wallet_db.ts @@ -32,12 +32,12 @@ export class WalletDB { this.#transactions = store.openMap('transactions'); } - async pushBridgedFeeJuice(recipient: AztecAddress, secret: Fr, amount: bigint, log: LogFn) { + async pushBridgedFeeJuice(recipient: AztecAddress, secret: Fr, amount: bigint, leafIndex: bigint, log: LogFn) { let stackPointer = this.#bridgedFeeJuice.get(`${recipient.toString()}:stackPointer`)?.readInt8() || 0; stackPointer++; await this.#bridgedFeeJuice.set( `${recipient.toString()}:${stackPointer}`, - Buffer.from(`${amount.toString()}:${secret.toString()}`), + Buffer.from(`${amount.toString()}:${secret.toString()}:${leafIndex.toString()}`), ); await this.#bridgedFeeJuice.set(`${recipient.toString()}:stackPointer`, Buffer.from([stackPointer])); log(`Pushed ${amount} fee juice for recipient ${recipient.toString()}. Stack pointer ${stackPointer}`); @@ -51,10 +51,10 @@ export class WalletDB { `No stored fee juice available for recipient ${recipient.toString()}. Please provide claim amount and secret. Stack pointer ${stackPointer}`, ); } - const [amountStr, secretStr] = result.toString().split(':'); + const [amountStr, secretStr, leafIndexStr] = result.toString().split(':'); await this.#bridgedFeeJuice.set(`${recipient.toString()}:stackPointer`, Buffer.from([--stackPointer])); log(`Retrieved ${amountStr} fee juice for recipient ${recipient.toString()}. Stack pointer ${stackPointer}`); - return { amount: BigInt(amountStr), secret: secretStr }; + return { amount: BigInt(amountStr), secret: secretStr, leafIndex: BigInt(leafIndexStr) }; } async storeAccount( diff --git a/yarn-project/cli-wallet/src/utils/options/fees.ts b/yarn-project/cli-wallet/src/utils/options/fees.ts index 47af6863666..ba976a60962 100644 --- a/yarn-project/cli-wallet/src/utils/options/fees.ts +++ b/yarn-project/cli-wallet/src/utils/options/fees.ts @@ -153,19 +153,23 @@ export function parsePaymentMethod( log('Using no fee payment'); return new NoFeePaymentMethod(); case 'native': - if (parsed.claim || (parsed.claimSecret && parsed.claimAmount)) { - let claimAmount, claimSecret; + if (parsed.claim || (parsed.claimSecret && parsed.claimAmount && parsed.messageLeafIndex)) { + let claimAmount, claimSecret, messageLeafIndex; if (parsed.claim && db) { - ({ amount: claimAmount, secret: claimSecret } = await db.popBridgedFeeJuice(sender.getAddress(), log)); + ({ + amount: claimAmount, + secret: claimSecret, + leafIndex: messageLeafIndex, + } = await db.popBridgedFeeJuice(sender.getAddress(), log)); } else { - ({ claimAmount, claimSecret } = parsed); + ({ claimAmount, claimSecret, messageLeafIndex } = parsed); } log(`Using Fee Juice for fee payments with claim for ${claimAmount} tokens`); - return new FeeJuicePaymentMethodWithClaim( - sender.getAddress(), - BigInt(claimAmount), - Fr.fromString(claimSecret), - ); + return new FeeJuicePaymentMethodWithClaim(sender.getAddress(), { + claimAmount: typeof claimAmount === 'string' ? Fr.fromString(claimAmount) : new Fr(claimAmount), + claimSecret: Fr.fromString(claimSecret), + messageLeafIndex: BigInt(messageLeafIndex), + }); } else { log(`Using Fee Juice for fee payment`); return new FeeJuicePaymentMethod(sender.getAddress()); diff --git a/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts b/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts index 55b3d945513..dc25a095182 100644 --- a/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts +++ b/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts @@ -1,5 +1,6 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; import { BatchCall, type PXE, type Wallet, createCompatibleClient } from '@aztec/aztec.js'; +import { L1FeeJuicePortalManager } from '@aztec/aztec.js'; import { type AztecAddress, type EthAddress, Fq, Fr } from '@aztec/circuits.js'; import { type ContractArtifacts, @@ -13,8 +14,6 @@ import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { getContract } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; -import { FeeJuicePortalManager } from '../../utils/portal_manager.js'; - type ContractDeploymentInfo = { address: AztecAddress; initHash: Fr; @@ -241,7 +240,7 @@ async function fundFPC( const feeJuiceContract = await FeeJuiceContract.at(feeJuice, wallet); - const feeJuicePortal = await FeeJuicePortalManager.new( + const feeJuicePortal = await L1FeeJuicePortalManager.new( wallet, l1Clients.publicClient, l1Clients.walletClient, @@ -249,7 +248,11 @@ async function fundFPC( ); const amount = 10n ** 21n; - const { claimAmount, claimSecret } = await feeJuicePortal.bridgeTokensPublic(fpcAddress, amount, true); + const { claimAmount, claimSecret, messageLeafIndex } = await feeJuicePortal.bridgeTokensPublic( + fpcAddress, + amount, + true, + ); const counter = await CounterContract.at(counterAddress, wallet); @@ -265,7 +268,7 @@ async function fundFPC( .wait({ proven: true, provenTimeout: 600 }); await feeJuiceContract.methods - .claim(fpcAddress, claimAmount, claimSecret) + .claim(fpcAddress, claimAmount, claimSecret, messageLeafIndex) .send() .wait({ proven: true, provenTimeout: 600 }); } diff --git a/yarn-project/cli/src/cmds/l1/bridge_erc20.ts b/yarn-project/cli/src/cmds/l1/bridge_erc20.ts index 877535faad1..393c315f6a4 100644 --- a/yarn-project/cli/src/cmds/l1/bridge_erc20.ts +++ b/yarn-project/cli/src/cmds/l1/bridge_erc20.ts @@ -1,9 +1,9 @@ +import { L1ToL2TokenPortalManager } from '@aztec/aztec.js'; import { type AztecAddress, type EthAddress, type Fr } from '@aztec/circuits.js'; import { createEthereumChain, createL1Clients } from '@aztec/ethereum'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { prettyPrintJSON } from '../../utils/commands.js'; -import { L1PortalManager } from '../../utils/portal_manager.js'; export async function bridgeERC20( amount: bigint, @@ -25,7 +25,7 @@ export async function bridgeERC20( const { publicClient, walletClient } = createL1Clients(chain.rpcUrl, privateKey ?? mnemonic, chain.chainInfo); // Setup portal manager - const manager = new L1PortalManager(portalAddress, tokenAddress, publicClient, walletClient, debugLogger); + const manager = new L1ToL2TokenPortalManager(portalAddress, tokenAddress, publicClient, walletClient, debugLogger); let claimSecret: Fr; let messageHash: `0x${string}`; if (privateTransfer) { diff --git a/yarn-project/cli/src/utils/index.ts b/yarn-project/cli/src/utils/index.ts index 0c0dbffaef8..1e4c577e271 100644 --- a/yarn-project/cli/src/utils/index.ts +++ b/yarn-project/cli/src/utils/index.ts @@ -2,5 +2,4 @@ export * from './commands.js'; export * from './aztec.js'; export * from './encoding.js'; export * from './github.js'; -export * from './portal_manager.js'; export * from './inspect.js'; diff --git a/yarn-project/cli/src/utils/portal_manager.ts b/yarn-project/cli/src/utils/portal_manager.ts deleted file mode 100644 index 265ca2f5f95..00000000000 --- a/yarn-project/cli/src/utils/portal_manager.ts +++ /dev/null @@ -1,210 +0,0 @@ -// REFACTOR: This file has been shamelessly copied from yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts -// We should make this a shared utility in the aztec.js package. -import { type AztecAddress, type DebugLogger, type EthAddress, Fr, type PXE, computeSecretHash } from '@aztec/aztec.js'; -import { FeeJuicePortalAbi, TestERC20Abi, TokenPortalAbi } from '@aztec/l1-artifacts'; - -import { - type Account, - type Chain, - type GetContractReturnType, - type Hex, - type HttpTransport, - type PublicClient, - type WalletClient, - getContract, -} from 'viem'; - -export interface L2Claim { - claimSecret: Fr; - claimAmount: Fr; - messageHash: `0x${string}`; -} - -function stringifyEthAddress(address: EthAddress | Hex, name?: string) { - return name ? `${name} (${address.toString()})` : address.toString(); -} - -function generateClaimSecret(): [Fr, Fr] { - const secret = Fr.random(); - const secretHash = computeSecretHash(secret); - return [secret, secretHash]; -} - -class L1TokenManager { - private contract: GetContractReturnType>; - - public constructor( - public readonly address: EthAddress, - private publicClient: PublicClient, - private walletClient: WalletClient, - private logger: DebugLogger, - ) { - this.contract = getContract({ - address: this.address.toString(), - abi: TestERC20Abi, - client: this.walletClient, - }); - } - - public async getL1TokenBalance(address: Hex) { - return await this.contract.read.balanceOf([address]); - } - - public async mint(amount: bigint, address: Hex, addressName = '') { - this.logger.info(`Minting ${amount} tokens for ${stringifyEthAddress(address, addressName)}`); - await this.publicClient.waitForTransactionReceipt({ - hash: await this.contract.write.mint([address, amount]), - }); - } - - public async approve(amount: bigint, address: Hex, addressName = '') { - this.logger.info(`Approving ${amount} tokens for ${stringifyEthAddress(address, addressName)}`); - await this.publicClient.waitForTransactionReceipt({ - hash: await this.contract.write.approve([address, amount]), - }); - } -} - -export class FeeJuicePortalManager { - tokenManager: L1TokenManager; - contract: GetContractReturnType>; - - constructor( - portalAddress: EthAddress, - tokenAddress: EthAddress, - private publicClient: PublicClient, - private walletClient: WalletClient, - /** Logger. */ - private logger: DebugLogger, - ) { - this.tokenManager = new L1TokenManager(tokenAddress, publicClient, walletClient, logger); - this.contract = getContract({ - address: portalAddress.toString(), - abi: FeeJuicePortalAbi, - client: this.walletClient, - }); - } - - public async bridgeTokensPublic(to: AztecAddress, amount: bigint, mint = false): Promise { - const [claimSecret, claimSecretHash] = generateClaimSecret(); - if (mint) { - await this.tokenManager.mint(amount, this.walletClient.account.address); - } - - await this.tokenManager.approve(amount, this.contract.address, 'FeeJuice Portal'); - - this.logger.info('Sending L1 Fee Juice to L2 to be claimed publicly'); - const args = [to.toString(), amount, claimSecretHash.toString()] as const; - - const { result: messageHash } = await this.contract.simulate.depositToAztecPublic(args); - - await this.publicClient.waitForTransactionReceipt({ - hash: await this.contract.write.depositToAztecPublic(args), - }); - - return { - claimAmount: new Fr(amount), - claimSecret, - messageHash, - }; - } - - public static async new( - pxe: PXE, - publicClient: PublicClient, - walletClient: WalletClient, - logger: DebugLogger, - ): Promise { - const { - l1ContractAddresses: { feeJuiceAddress, feeJuicePortalAddress }, - } = await pxe.getNodeInfo(); - - if (feeJuiceAddress.isZero() || feeJuicePortalAddress.isZero()) { - throw new Error('Portal or token not deployed on L1'); - } - - return new FeeJuicePortalManager(feeJuicePortalAddress, feeJuiceAddress, publicClient, walletClient, logger); - } -} - -export class L1PortalManager { - contract: GetContractReturnType>; - private tokenManager: L1TokenManager; - - constructor( - portalAddress: EthAddress, - tokenAddress: EthAddress, - private publicClient: PublicClient, - private walletClient: WalletClient, - private logger: DebugLogger, - ) { - this.tokenManager = new L1TokenManager(tokenAddress, publicClient, walletClient, logger); - this.contract = getContract({ - address: portalAddress.toString(), - abi: TokenPortalAbi, - client: this.walletClient, - }); - } - - public bridgeTokensPublic(to: AztecAddress, amount: bigint, mint = false): Promise { - return this.bridgeTokens(to, amount, mint, /* privateTransfer */ false); - } - - public bridgeTokensPrivate(to: AztecAddress, amount: bigint, mint = false): Promise { - return this.bridgeTokens(to, amount, mint, /* privateTransfer */ true); - } - - private async bridgeTokens( - to: AztecAddress, - amount: bigint, - mint: boolean, - privateTransfer: boolean, - ): Promise { - const [claimSecret, claimSecretHash] = generateClaimSecret(); - - if (mint) { - await this.tokenManager.mint(amount, this.walletClient.account.address); - } - - await this.tokenManager.approve(amount, this.contract.address, 'TokenPortal'); - - let messageHash: `0x${string}`; - - if (privateTransfer) { - const secret = Fr.random(); - const secretHash = computeSecretHash(secret); - this.logger.info('Sending L1 tokens to L2 to be claimed privately'); - ({ result: messageHash } = await this.contract.simulate.depositToAztecPrivate([ - secretHash.toString(), - amount, - claimSecretHash.toString(), - ])); - - await this.publicClient.waitForTransactionReceipt({ - hash: await this.contract.write.depositToAztecPrivate([ - secretHash.toString(), - amount, - claimSecretHash.toString(), - ]), - }); - this.logger.info(`Redeem shield secret: ${secret.toString()}, secret hash: ${secretHash.toString()}`); - } else { - this.logger.info('Sending L1 tokens to L2 to be claimed publicly'); - ({ result: messageHash } = await this.contract.simulate.depositToAztecPublic([ - to.toString(), - amount, - claimSecretHash.toString(), - ])); - - await this.publicClient.waitForTransactionReceipt({ - hash: await this.contract.write.depositToAztecPublic([to.toString(), amount, claimSecretHash.toString()]), - }); - } - - return { - claimAmount: new Fr(amount), - claimSecret, - messageHash, - }; - } -} diff --git a/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts b/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts index fb9ca25c781..45e7ce812c0 100644 --- a/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts +++ b/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts @@ -100,14 +100,13 @@ describe('benchmarks/proving', () => { logger: ctx.logger, }); - const { secret } = await feeJuiceBridgeTestHarness.prepareTokensOnL1( - 1_000_000_000_000n, + const { claimSecret, messageLeafIndex } = await feeJuiceBridgeTestHarness.prepareTokensOnL1( 1_000_000_000_000n, initialFpContract.address, ); await Promise.all([ - initialGasContract.methods.claim(initialFpContract.address, 1e12, secret).send().wait(), + initialGasContract.methods.claim(initialFpContract.address, 1e12, claimSecret, messageLeafIndex).send().wait(), initialTokenContract.methods.mint_public(initialSchnorrWallet.getAddress(), 1e12).send().wait(), initialTokenContract.methods.privately_mint_private_note(1e12).send().wait(), ]); diff --git a/yarn-project/end-to-end/src/benchmarks/bench_tx_size_fees.test.ts b/yarn-project/end-to-end/src/benchmarks/bench_tx_size_fees.test.ts index fb2f9e53fca..e294bd3de89 100644 --- a/yarn-project/end-to-end/src/benchmarks/bench_tx_size_fees.test.ts +++ b/yarn-project/end-to-end/src/benchmarks/bench_tx_size_fees.test.ts @@ -61,20 +61,15 @@ describe('benchmarks/tx_size_fees', () => { logger: ctx.logger, }); - const { secret: fpcSecret } = await feeJuiceBridgeTestHarness.prepareTokensOnL1( - 100_000_000_000n, - 100_000_000_000n, - fpc.address, - ); - const { secret: aliceSecret } = await feeJuiceBridgeTestHarness.prepareTokensOnL1( - 100_000_000_000n, - 100_000_000_000n, - aliceWallet.getAddress(), - ); + const { claimSecret: fpcSecret, messageLeafIndex: fpcLeafIndex } = + await feeJuiceBridgeTestHarness.prepareTokensOnL1(100_000_000_000n, fpc.address); + + const { claimSecret: aliceSecret, messageLeafIndex: aliceLeafIndex } = + await feeJuiceBridgeTestHarness.prepareTokensOnL1(100_000_000_000n, aliceWallet.getAddress()); await Promise.all([ - feeJuice.methods.claim(fpc.address, 100e9, fpcSecret).send().wait(), - feeJuice.methods.claim(aliceWallet.getAddress(), 100e9, aliceSecret).send().wait(), + feeJuice.methods.claim(fpc.address, 100e9, fpcSecret, fpcLeafIndex).send().wait(), + feeJuice.methods.claim(aliceWallet.getAddress(), 100e9, aliceSecret, aliceLeafIndex).send().wait(), ]); await token.methods.privately_mint_private_note(100e9).send().wait(); await token.methods.mint_public(aliceWallet.getAddress(), 100e9).send().wait(); diff --git a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts index 347c5250b92..bb2ad9bb0af 100644 --- a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts @@ -79,6 +79,7 @@ const deployerPK = '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092 const logger = createDebugLogger('aztec:integration_l1_publisher'); const config = getConfigEnvVars(); +config.l1RpcUrl = config.l1RpcUrl || 'http://localhost:8545'; const numberOfConsecutiveBlocks = 2; diff --git a/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts b/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts index 848f09e9b00..522fc09ff09 100644 --- a/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts +++ b/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts @@ -155,17 +155,17 @@ describe('End-to-end tests for devnet', () => { await expect(getL1Balance(l1Account.address, feeJuiceL1)).resolves.toBeGreaterThan(0n); const amount = 1_000_000_000_000n; - const { claimAmount, claimSecret } = await cli<{ claimAmount: string; claimSecret: { value: string } }>( - 'bridge-fee-juice', - [amount, l2Account.getAddress()], - { - 'l1-rpc-url': ETHEREUM_HOST!, - 'l1-chain-id': l1ChainId.toString(), - 'l1-private-key': l1Account.privateKey, - 'rpc-url': pxeUrl, - mint: true, - }, - ); + const { claimAmount, claimSecret, messageLeafIndex } = await cli<{ + claimAmount: string; + claimSecret: { value: string }; + messageLeafIndex: string; + }>('bridge-fee-juice', [amount, l2Account.getAddress()], { + 'l1-rpc-url': ETHEREUM_HOST!, + 'l1-chain-id': l1ChainId.toString(), + 'l1-private-key': l1Account.privateKey, + 'rpc-url': pxeUrl, + mint: true, + }); if (['1', 'true', 'yes'].includes(USE_EMPTY_BLOCKS)) { await advanceChainWithEmptyBlocks(pxe); @@ -177,11 +177,11 @@ describe('End-to-end tests for devnet', () => { .deploy({ fee: { gasSettings: GasSettings.default(), - paymentMethod: new FeeJuicePaymentMethodWithClaim( - l2Account.getAddress(), - BigInt(claimAmount), - Fr.fromString(claimSecret.value), - ), + paymentMethod: new FeeJuicePaymentMethodWithClaim(l2Account.getAddress(), { + claimAmount: Fr.fromString(claimAmount), + claimSecret: Fr.fromString(claimSecret.value), + messageLeafIndex: BigInt(messageLeafIndex), + }), }, }) .wait(waitOpts); diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts index 168aa7fe72c..887d1c9609c 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts @@ -11,7 +11,7 @@ import { createDebugLogger, } from '@aztec/aztec.js'; import { createL1Clients } from '@aztec/ethereum'; -import { InboxAbi, OutboxAbi, RollupAbi, TestERC20Abi, TokenPortalAbi } from '@aztec/l1-artifacts'; +import { InboxAbi, OutboxAbi, RollupAbi } from '@aztec/l1-artifacts'; import { TokenBridgeContract, TokenContract } from '@aztec/noir-contracts.js'; import { type Chain, type HttpTransport, type PublicClient, getContract } from 'viem'; @@ -151,17 +151,6 @@ export class CrossChainMessagingTest { client: walletClient, }); - const tokenPortal = getContract({ - address: tokenPortalAddress.toString(), - abi: TokenPortalAbi, - client: walletClient, - }); - const underlyingERC20 = getContract({ - address: crossChainContext.underlying.toString(), - abi: TestERC20Abi, - client: walletClient, - }); - this.crossChainTestHarness = new CrossChainTestHarness( this.aztecNode, this.pxe, @@ -170,13 +159,9 @@ export class CrossChainMessagingTest { this.l2Bridge, this.ethAccount, tokenPortalAddress, - tokenPortal, - underlyingERC20, - inbox, - outbox, + crossChainContext.underlying, publicClient, walletClient, - this.ownerAddress, this.aztecNodeConfig.l1Contracts, this.user1Wallet, ); @@ -192,12 +177,12 @@ export class CrossChainMessagingTest { return { l2Token: this.crossChainTestHarness.l2Token.address, l2Bridge: this.crossChainTestHarness.l2Bridge.address, - tokenPortal: this.crossChainTestHarness.tokenPortal.address, - underlying: EthAddress.fromString(this.crossChainTestHarness.underlyingERC20.address), + tokenPortal: this.crossChainTestHarness.tokenPortalAddress, + underlying: this.crossChainTestHarness.underlyingERC20Address, ethAccount: this.crossChainTestHarness.ethAccount, ownerAddress: this.crossChainTestHarness.ownerAddress, - inbox: EthAddress.fromString(this.crossChainTestHarness.inbox.address), - outbox: EthAddress.fromString(this.crossChainTestHarness.outbox.address), + inbox: this.crossChainTestHarness.l1ContractAddresses.inboxAddress, + outbox: this.crossChainTestHarness.l1ContractAddresses.outboxAddress, }; } } diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts index 554900d2318..7ff83ac3156 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts @@ -1,12 +1,4 @@ -import { - type EthAddressLike, - type FieldLike, - Fr, - L1Actor, - L1ToL2Message, - L2Actor, - computeSecretHash, -} from '@aztec/aztec.js'; +import { type AztecAddress, Fr, generateClaimSecret } from '@aztec/aztec.js'; import { TestContract } from '@aztec/noir-contracts.js'; import { sendL1ToL2Message } from '../fixtures/l1_to_l2_messaging.js'; @@ -38,38 +30,26 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { const testContract = await TestContract.deploy(user1Wallet).send().deployed(); const consumeMethod = isPrivate - ? (content: FieldLike, secret: FieldLike, sender: EthAddressLike, _leafIndex: FieldLike) => - testContract.methods.consume_message_from_arbitrary_sender_private(content, secret, sender) + ? testContract.methods.consume_message_from_arbitrary_sender_private : testContract.methods.consume_message_from_arbitrary_sender_public; - const secret = Fr.random(); + const [secret, secretHash] = generateClaimSecret(); - const message = new L1ToL2Message( - new L1Actor(crossChainTestHarness.ethAccount, crossChainTestHarness.publicClient.chain.id), - new L2Actor(testContract.address, 1), - Fr.random(), // content - computeSecretHash(secret), // secretHash - ); + const message = { recipient: testContract.address, content: Fr.random(), secretHash }; + const [message1Hash, actualMessage1Index] = await sendL2Message(message); - const actualMessage1Index = await sendL2Message(message); - - const [message1Index, _1] = (await aztecNode.getL1ToL2MessageMembershipWitness('latest', message.hash(), 0n))!; + const [message1Index] = (await aztecNode.getL1ToL2MessageMembershipWitness('latest', message1Hash))!; expect(actualMessage1Index.toBigInt()).toBe(message1Index); // Finally, we consume the L1 -> L2 message using the test contract either from private or public - await consumeMethod(message.content, secret, message.sender.sender, message1Index).send().wait(); + await consumeMethod(message.content, secret, crossChainTestHarness.ethAccount, message1Index).send().wait(); // We send and consume the exact same message the second time to test that oracles correctly return the new // non-nullified message - const actualMessage2Index = await sendL2Message(message); + const [message2Hash, actualMessage2Index] = await sendL2Message(message); - // We check that the duplicate message was correctly inserted by checking that its message index is defined and - // larger than the previous message index - const [message2Index, _2] = (await aztecNode.getL1ToL2MessageMembershipWitness( - 'latest', - message.hash(), - message1Index + 1n, - ))!; + // We check that the duplicate message was correctly inserted by checking that its message index is defined + const [message2Index] = (await aztecNode.getL1ToL2MessageMembershipWitness('latest', message2Hash))!; expect(message2Index).toBeDefined(); expect(message2Index).toBeGreaterThan(message1Index); @@ -77,14 +57,14 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { // Now we consume the message again. Everything should pass because oracle should return the duplicate message // which is not nullified - await consumeMethod(message.content, secret, message.sender.sender, message2Index).send().wait(); + await consumeMethod(message.content, secret, crossChainTestHarness.ethAccount, message2Index).send().wait(); }, 120_000, ); - const sendL2Message = async (message: L1ToL2Message) => { + const sendL2Message = async (message: { recipient: AztecAddress; content: Fr; secretHash: Fr }) => { const [msgHash, globalLeafIndex] = await sendL1ToL2Message(message, crossChainTestHarness); await crossChainTestHarness.makeMessageConsumable(msgHash); - return globalLeafIndex; + return [msgHash, globalLeafIndex]; }; }); diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts index 782df6f0e9c..60eb205af6e 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts @@ -3,7 +3,7 @@ import { sha256ToField } from '@aztec/foundation/crypto'; import { OutboxAbi } from '@aztec/l1-artifacts'; import { TestContract } from '@aztec/noir-contracts.js'; -import { type Hex, decodeEventLog } from 'viem'; +import { type Hex, decodeEventLog, getContract } from 'viem'; import { CrossChainMessagingTest } from './cross_chain_messaging_test.js'; @@ -20,7 +20,11 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { aztecNode = crossChainTestHarness.aztecNode; - outbox = crossChainTestHarness.outbox; + outbox = getContract({ + address: crossChainTestHarness.l1ContractAddresses.outboxAddress.toString(), + abi: OutboxAbi, + client: crossChainTestHarness.walletClient, + }); }, 300_000); afterAll(async () => { diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_failure_cases.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_failure_cases.test.ts index afc868a2086..f0a0e64c90b 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_failure_cases.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_failure_cases.test.ts @@ -9,7 +9,7 @@ import { CrossChainMessagingTest } from './cross_chain_messaging_test.js'; describe('e2e_cross_chain_messaging token_bridge_failure_cases', () => { const t = new CrossChainMessagingTest('token_bridge_failure_cases'); - let { crossChainTestHarness, ethAccount, l2Bridge, user1Wallet, user2Wallet, aztecNode, ownerAddress } = t; + let { crossChainTestHarness, ethAccount, l2Bridge, user1Wallet, user2Wallet, ownerAddress } = t; beforeAll(async () => { await t.applyBaseSnapshots(); @@ -18,7 +18,6 @@ describe('e2e_cross_chain_messaging token_bridge_failure_cases', () => { ({ crossChainTestHarness, user1Wallet, user2Wallet } = t); ethAccount = crossChainTestHarness.ethAccount; l2Bridge = crossChainTestHarness.l2Bridge; - aztecNode = crossChainTestHarness.aztecNode; ownerAddress = crossChainTestHarness.ownerAddress; }, 300_000); @@ -43,30 +42,34 @@ describe('e2e_cross_chain_messaging token_bridge_failure_cases', () => { it("Can't claim funds privately which were intended for public deposit from the token portal", async () => { const bridgeAmount = 100n; - const [secret, secretHash] = crossChainTestHarness.generateClaimSecret(); await crossChainTestHarness.mintTokensOnL1(bridgeAmount); - const msgHash = await crossChainTestHarness.sendTokensToPortalPublic(bridgeAmount, secretHash); + const claim = await crossChainTestHarness.sendTokensToPortalPublic(bridgeAmount); expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(0n); - await crossChainTestHarness.makeMessageConsumable(msgHash); + await crossChainTestHarness.makeMessageConsumable(claim.messageHash); // Wrong message hash const content = sha256ToField([ Buffer.from(toFunctionSelector('mint_private(bytes32,uint256)').substring(2), 'hex'), - secretHash, + claim.claimSecretHash, new Fr(bridgeAmount), ]); + const wrongMessage = new L1ToL2Message( new L1Actor(crossChainTestHarness.tokenPortalAddress, crossChainTestHarness.publicClient.chain.id), new L2Actor(l2Bridge.address, 1), content, - secretHash, + claim.claimSecretHash, + new Fr(claim.messageLeafIndex), ); await expect( - l2Bridge.withWallet(user2Wallet).methods.claim_private(secretHash, bridgeAmount, secret).prove(), - ).rejects.toThrow(`No non-nullified L1 to L2 message found for message hash ${wrongMessage.hash().toString()}`); + l2Bridge + .withWallet(user2Wallet) + .methods.claim_private(claim.claimSecretHash, bridgeAmount, claim.claimSecret, claim.messageLeafIndex) + .prove(), + ).rejects.toThrow(`No L1 to L2 message found for message hash ${wrongMessage.hash().toString()}`); }, 60_000); it("Can't claim funds publicly which were intended for private deposit from the token portal", async () => { @@ -75,29 +78,17 @@ describe('e2e_cross_chain_messaging token_bridge_failure_cases', () => { await crossChainTestHarness.mintTokensOnL1(bridgeAmount); // 2. Deposit tokens to the TokenPortal privately - const [secretForL2MessageConsumption, secretHashForL2MessageConsumption] = - crossChainTestHarness.generateClaimSecret(); - - const msgHash = await crossChainTestHarness.sendTokensToPortalPrivate( - Fr.random(), - bridgeAmount, - secretHashForL2MessageConsumption, - ); + const claim = await crossChainTestHarness.sendTokensToPortalPrivate(bridgeAmount); expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(0n); // Wait for the message to be available for consumption - await crossChainTestHarness.makeMessageConsumable(msgHash); - - // get message leaf index, needed for claiming in public - const maybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash, 0n); - expect(maybeIndexAndPath).toBeDefined(); - const messageLeafIndex = maybeIndexAndPath![0]; + await crossChainTestHarness.makeMessageConsumable(claim.messageHash); // 3. Consume L1 -> L2 message and try to mint publicly on L2 - should fail await expect( l2Bridge .withWallet(user2Wallet) - .methods.claim_public(ownerAddress, bridgeAmount, secretForL2MessageConsumption, messageLeafIndex) + .methods.claim_public(ownerAddress, bridgeAmount, Fr.random(), claim.messageLeafIndex) .prove(), ).rejects.toThrow(NO_L1_TO_L2_MSG_ERROR); }); diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts index e9a525a3da4..4ba54c6760d 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts @@ -52,32 +52,19 @@ describe('e2e_cross_chain_messaging token_bridge_private', () => { const l1TokenBalance = 1000000n; const bridgeAmount = 100n; - const [secretForL2MessageConsumption, secretHashForL2MessageConsumption] = - crossChainTestHarness.generateClaimSecret(); - const [secretForRedeemingMintedNotes, secretHashForRedeemingMintedNotes] = - crossChainTestHarness.generateClaimSecret(); - // 1. Mint tokens on L1 await crossChainTestHarness.mintTokensOnL1(l1TokenBalance); // 2. Deposit tokens to the TokenPortal - const msgHash = await crossChainTestHarness.sendTokensToPortalPrivate( - secretHashForRedeemingMintedNotes, - bridgeAmount, - secretHashForL2MessageConsumption, - ); + const claim = await crossChainTestHarness.sendTokensToPortalPrivate(bridgeAmount); expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(l1TokenBalance - bridgeAmount); - await crossChainTestHarness.makeMessageConsumable(msgHash); + await crossChainTestHarness.makeMessageConsumable(claim.messageHash); // 3. Consume L1 -> L2 message and mint private tokens on L2 - await crossChainTestHarness.consumeMessageOnAztecAndMintPrivately( - secretHashForRedeemingMintedNotes, - bridgeAmount, - secretForL2MessageConsumption, - ); + await crossChainTestHarness.consumeMessageOnAztecAndMintPrivately(claim); // tokens were minted privately in a TransparentNote which the owner (person who knows the secret) must redeem: - await crossChainTestHarness.redeemShieldPrivatelyOnL2(bridgeAmount, secretForRedeemingMintedNotes); + await crossChainTestHarness.redeemShieldPrivatelyOnL2(bridgeAmount, claim.redeemSecret); await crossChainTestHarness.expectPrivateBalanceOnL2(ownerAddress, bridgeAmount); // time to withdraw the funds again! @@ -121,57 +108,51 @@ describe('e2e_cross_chain_messaging token_bridge_private', () => { it('Someone else can mint funds to me on my behalf (privately)', async () => { const l1TokenBalance = 1000000n; const bridgeAmount = 100n; - const [secretForL2MessageConsumption, secretHashForL2MessageConsumption] = - crossChainTestHarness.generateClaimSecret(); - const [secretForRedeemingMintedNotes, secretHashForRedeemingMintedNotes] = - crossChainTestHarness.generateClaimSecret(); await crossChainTestHarness.mintTokensOnL1(l1TokenBalance); - const msgHash = await crossChainTestHarness.sendTokensToPortalPrivate( - secretHashForRedeemingMintedNotes, - bridgeAmount, - secretHashForL2MessageConsumption, - ); + const claim = await crossChainTestHarness.sendTokensToPortalPrivate(bridgeAmount); expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(l1TokenBalance - bridgeAmount); // Wait for the message to be available for consumption - await crossChainTestHarness.makeMessageConsumable(msgHash); + await crossChainTestHarness.makeMessageConsumable(claim.messageHash); // 3. Consume L1 -> L2 message and mint private tokens on L2 const content = sha256ToField([ Buffer.from(toFunctionSelector('mint_private(bytes32,uint256)').substring(2), 'hex'), - secretHashForL2MessageConsumption, + claim.claimSecretHash, new Fr(bridgeAmount), ]); + const wrongMessage = new L1ToL2Message( new L1Actor(crossChainTestHarness.tokenPortalAddress, crossChainTestHarness.publicClient.chain.id), new L2Actor(l2Bridge.address, 1), content, - secretHashForL2MessageConsumption, + claim.claimSecretHash, + new Fr(claim.messageLeafIndex), ); // Sending wrong secret hashes should fail: await expect( l2Bridge .withWallet(user2Wallet) - .methods.claim_private(secretHashForL2MessageConsumption, bridgeAmount, secretForL2MessageConsumption) + .methods.claim_private(claim.claimSecretHash, bridgeAmount, claim.claimSecret, claim.messageLeafIndex) .prove(), - ).rejects.toThrow(`No non-nullified L1 to L2 message found for message hash ${wrongMessage.hash().toString()}`); + ).rejects.toThrow(`No L1 to L2 message found for message hash ${wrongMessage.hash().toString()}`); // send the right one - const consumptionReceipt = await l2Bridge .withWallet(user2Wallet) - .methods.claim_private(secretHashForRedeemingMintedNotes, bridgeAmount, secretForL2MessageConsumption) + .methods.claim_private(claim.redeemSecretHash, bridgeAmount, claim.claimSecret, claim.messageLeafIndex) .send() .wait(); // Now user1 can claim the notes that user2 minted on their behalf. await crossChainTestHarness.addPendingShieldNoteToPXE( bridgeAmount, - secretHashForRedeemingMintedNotes, + claim.redeemSecretHash, consumptionReceipt.txHash, ); - await crossChainTestHarness.redeemShieldPrivatelyOnL2(bridgeAmount, secretForRedeemingMintedNotes); + await crossChainTestHarness.redeemShieldPrivatelyOnL2(bridgeAmount, claim.redeemSecret); await crossChainTestHarness.expectPrivateBalanceOnL2(ownerAddress, bridgeAmount); }), 90_000; diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts index 22d44a573e9..e8eff976fbb 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts @@ -38,37 +38,36 @@ describe('e2e_cross_chain_messaging token_bridge_public', () => { // docs:start:e2e_public_cross_chain it('Publicly deposit funds from L1 -> L2 and withdraw back to L1', async () => { - // Generate a claim secret using pedersen const l1TokenBalance = 1000000n; const bridgeAmount = 100n; - const [secret, secretHash] = crossChainTestHarness.generateClaimSecret(); - // 1. Mint tokens on L1 logger.verbose(`1. Mint tokens on L1`); await crossChainTestHarness.mintTokensOnL1(l1TokenBalance); // 2. Deposit tokens to the TokenPortal logger.verbose(`2. Deposit tokens to the TokenPortal`); - const msgHash = await crossChainTestHarness.sendTokensToPortalPublic(bridgeAmount, secretHash); + const claim = await crossChainTestHarness.sendTokensToPortalPublic(bridgeAmount); + const msgHash = Fr.fromString(claim.messageHash); expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(l1TokenBalance - bridgeAmount); // Wait for the message to be available for consumption logger.verbose(`Wait for the message to be available for consumption`); await crossChainTestHarness.makeMessageConsumable(msgHash); - // Get message leaf index, needed for claiming in public - const maybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash, 0n); + // Check message leaf index matches + const maybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash); expect(maybeIndexAndPath).toBeDefined(); const messageLeafIndex = maybeIndexAndPath![0]; + expect(messageLeafIndex).toEqual(claim.messageLeafIndex); // 3. Consume L1 -> L2 message and mint public tokens on L2 logger.verbose('3. Consume L1 -> L2 message and mint public tokens on L2'); - await crossChainTestHarness.consumeMessageOnAztecAndMintPublicly(bridgeAmount, secret, messageLeafIndex); + await crossChainTestHarness.consumeMessageOnAztecAndMintPublicly(claim); await crossChainTestHarness.expectPublicBalanceOnL2(ownerAddress, bridgeAmount); const afterBalance = bridgeAmount; - // time to withdraw the funds again! + // Time to withdraw the funds again! logger.info('Withdrawing funds from L2'); // 4. Give approval to bridge to burn owner's funds: @@ -112,28 +111,27 @@ describe('e2e_cross_chain_messaging token_bridge_public', () => { // docs:end:e2e_public_cross_chain it('Someone else can mint funds to me on my behalf (publicly)', async () => { - // Generate a claim secret using pedersen const l1TokenBalance = 1000000n; const bridgeAmount = 100n; - const [secret, secretHash] = crossChainTestHarness.generateClaimSecret(); - await crossChainTestHarness.mintTokensOnL1(l1TokenBalance); - const msgHash = await crossChainTestHarness.sendTokensToPortalPublic(bridgeAmount, secretHash); + const claim = await crossChainTestHarness.sendTokensToPortalPublic(bridgeAmount); + const msgHash = Fr.fromString(claim.messageHash); expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(l1TokenBalance - bridgeAmount); await crossChainTestHarness.makeMessageConsumable(msgHash); - // get message leaf index, needed for claiming in public - const maybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash, 0n); + // Check message leaf index matches + const maybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash); expect(maybeIndexAndPath).toBeDefined(); const messageLeafIndex = maybeIndexAndPath![0]; + expect(messageLeafIndex).toEqual(claim.messageLeafIndex); // user2 tries to consume this message and minting to itself -> should fail since the message is intended to be consumed only by owner. await expect( l2Bridge .withWallet(user2Wallet) - .methods.claim_public(user2Wallet.getAddress(), bridgeAmount, secret, messageLeafIndex) + .methods.claim_public(user2Wallet.getAddress(), bridgeAmount, claim.claimSecret, messageLeafIndex) .prove(), ).rejects.toThrow(NO_L1_TO_L2_MSG_ERROR); @@ -141,9 +139,10 @@ describe('e2e_cross_chain_messaging token_bridge_public', () => { logger.info("user2 consumes owner's message on L2 Publicly"); await l2Bridge .withWallet(user2Wallet) - .methods.claim_public(ownerAddress, bridgeAmount, secret, messageLeafIndex) + .methods.claim_public(ownerAddress, bridgeAmount, claim.claimSecret, messageLeafIndex) .send() .wait(); + // ensure funds are gone to owner and not user2. await crossChainTestHarness.expectPublicBalanceOnL2(ownerAddress, bridgeAmount); await crossChainTestHarness.expectPublicBalanceOnL2(user2Wallet.getAddress(), 0n); diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public_to_private.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public_to_private.test.ts index 5b1086c095c..60e71effabe 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public_to_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public_to_private.test.ts @@ -1,10 +1,11 @@ +import { Fr } from '@aztec/circuits.js'; + import { CrossChainMessagingTest } from './cross_chain_messaging_test.js'; describe('e2e_cross_chain_messaging token_bridge_public_to_private', () => { const t = new CrossChainMessagingTest('token_bridge_public_to_private'); let { crossChainTestHarness, ethAccount, aztecNode, ownerAddress } = t; - let underlyingERC20: any; beforeEach(async () => { await t.applyBaseSnapshots(); @@ -15,41 +16,38 @@ describe('e2e_cross_chain_messaging token_bridge_public_to_private', () => { ethAccount = crossChainTestHarness.ethAccount; aztecNode = crossChainTestHarness.aztecNode; ownerAddress = crossChainTestHarness.ownerAddress; - underlyingERC20 = crossChainTestHarness.underlyingERC20; }, 300_000); afterEach(async () => { await t.teardown(); }); - // Moved from e2e_public_to_private_messaging.test.ts it('Milestone 5.4: Should be able to create a commitment in a public function and spend in a private function', async () => { - // Generate a claim secret using pedersen const l1TokenBalance = 1000000n; const bridgeAmount = 100n; const shieldAmount = 50n; - const [secret, secretHash] = crossChainTestHarness.generateClaimSecret(); - await crossChainTestHarness.mintTokensOnL1(l1TokenBalance); - const msgHash = await crossChainTestHarness.sendTokensToPortalPublic(bridgeAmount, secretHash); - expect(await underlyingERC20.read.balanceOf([ethAccount.toString()])).toBe(l1TokenBalance - bridgeAmount); + const claim = await crossChainTestHarness.sendTokensToPortalPublic(bridgeAmount); + const msgHash = Fr.fromString(claim.messageHash); + expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toEqual(l1TokenBalance - bridgeAmount); await crossChainTestHarness.makeMessageConsumable(msgHash); - // get message leaf index, needed for claiming in public - const maybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash, 0n); + // Check message leaf index matches + const maybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash); expect(maybeIndexAndPath).toBeDefined(); const messageLeafIndex = maybeIndexAndPath![0]; + expect(messageLeafIndex).toEqual(claim.messageLeafIndex); - await crossChainTestHarness.consumeMessageOnAztecAndMintPublicly(bridgeAmount, secret, messageLeafIndex); + await crossChainTestHarness.consumeMessageOnAztecAndMintPublicly(claim); await crossChainTestHarness.expectPublicBalanceOnL2(ownerAddress, bridgeAmount); // Create the commitment to be spent in the private domain - await crossChainTestHarness.shieldFundsOnL2(shieldAmount, secretHash); + await crossChainTestHarness.shieldFundsOnL2(shieldAmount, claim.claimSecretHash); // Create the transaction spending the commitment - await crossChainTestHarness.redeemShieldPrivatelyOnL2(shieldAmount, secret); + await crossChainTestHarness.redeemShieldPrivatelyOnL2(shieldAmount, claim.claimSecret); await crossChainTestHarness.expectPublicBalanceOnL2(ownerAddress, bridgeAmount - shieldAmount); await crossChainTestHarness.expectPrivateBalanceOnL2(ownerAddress, shieldAmount); diff --git a/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts b/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts index 4cd12d6eeec..53b0b18e510 100644 --- a/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts @@ -97,13 +97,8 @@ describe('e2e_fees account_init', () => { }); it('pays natively in the Fee Juice by bridging funds themselves', async () => { - const { secret } = await t.feeJuiceBridgeTestHarness.prepareTokensOnL1( - t.INITIAL_GAS_BALANCE, - t.INITIAL_GAS_BALANCE, - bobsAddress, - ); - - const paymentMethod = new FeeJuicePaymentMethodWithClaim(bobsAddress, t.INITIAL_GAS_BALANCE, secret); + const claim = await t.feeJuiceBridgeTestHarness.prepareTokensOnL1(t.INITIAL_GAS_BALANCE, bobsAddress); + const paymentMethod = new FeeJuicePaymentMethodWithClaim(bobsAddress, claim); const tx = await bobsAccountManager.deploy({ fee: { gasSettings, paymentMethod } }).wait(); expect(tx.transactionFee!).toBeGreaterThan(0n); await expect(t.getGasBalanceFn(bobsAddress)).resolves.toEqual([t.INITIAL_GAS_BALANCE - tx.transactionFee!]); diff --git a/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts index b87d2348949..6956bcf24a9 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts @@ -50,12 +50,8 @@ describe('e2e_fees Fee Juice payments', () => { }); it('claims bridged funds and pays with them on the same tx', async () => { - const { secret } = await t.feeJuiceBridgeTestHarness.prepareTokensOnL1( - t.INITIAL_GAS_BALANCE, - t.INITIAL_GAS_BALANCE, - aliceAddress, - ); - const paymentMethod = new FeeJuicePaymentMethodWithClaim(aliceAddress, t.INITIAL_GAS_BALANCE, secret); + const claim = await t.feeJuiceBridgeTestHarness.prepareTokensOnL1(t.INITIAL_GAS_BALANCE, aliceAddress); + const paymentMethod = new FeeJuicePaymentMethodWithClaim(aliceAddress, claim); const receipt = await bananaCoin.methods .transfer_public(aliceAddress, bobAddress, 1n, 0n) .send({ fee: { gasSettings, paymentMethod } }) diff --git a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts index 0290a13bcfe..e2b6c4f01f9 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts @@ -122,9 +122,9 @@ export class FeesTest { } async mintAndBridgeFeeJuice(address: AztecAddress, amount: bigint) { - const { secret } = await this.feeJuiceBridgeTestHarness.prepareTokensOnL1(amount, amount, address); - - await this.feeJuiceContract.methods.claim(address, amount, secret).send().wait(); + const claim = await this.feeJuiceBridgeTestHarness.prepareTokensOnL1(amount, address); + const { claimSecret: secret, messageLeafIndex: index } = claim; + await this.feeJuiceContract.methods.claim(address, amount, secret, index).send().wait(); } /** Alice mints bananaCoin tokens privately to the target address and redeems them. */ @@ -265,7 +265,7 @@ export class FeesTest { 'token_and_private_fpc', async context => { // Deploy token/fpc flavors for private refunds - const feeJuiceContract = this.feeJuiceBridgeTestHarness.l2Token; + const feeJuiceContract = this.feeJuiceBridgeTestHarness.feeJuice; expect(await context.pxe.isContractPubliclyDeployed(feeJuiceContract.address)).toBe(true); const token = await TokenContract.deploy(this.aliceWallet, this.aliceAddress, 'PVT', 'PVT', 18n) @@ -282,11 +282,7 @@ export class FeesTest { const privateFPC = await privateFPCSent.deployed(); this.logger.info(`PrivateFPC deployed at ${privateFPC.address}`); - await this.feeJuiceBridgeTestHarness.bridgeFromL1ToL2( - this.INITIAL_GAS_BALANCE, - this.INITIAL_GAS_BALANCE, - privateFPC.address, - ); + await this.feeJuiceBridgeTestHarness.bridgeFromL1ToL2(this.INITIAL_GAS_BALANCE, privateFPC.address); return { tokenAddress: token.address, @@ -307,7 +303,7 @@ export class FeesTest { await this.snapshotManager.snapshot( 'fpc_setup', async context => { - const feeJuiceContract = this.feeJuiceBridgeTestHarness.l2Token; + const feeJuiceContract = this.feeJuiceBridgeTestHarness.feeJuice; expect(await context.pxe.isContractPubliclyDeployed(feeJuiceContract.address)).toBe(true); const bananaCoin = this.bananaCoin; @@ -315,11 +311,7 @@ export class FeesTest { this.logger.info(`BananaPay deployed at ${bananaFPC.address}`); - await this.feeJuiceBridgeTestHarness.bridgeFromL1ToL2( - this.INITIAL_GAS_BALANCE, - this.INITIAL_GAS_BALANCE, - bananaFPC.address, - ); + await this.feeJuiceBridgeTestHarness.bridgeFromL1ToL2(this.INITIAL_GAS_BALANCE, bananaFPC.address); return { bananaFPCAddress: bananaFPC.address, diff --git a/yarn-project/end-to-end/src/fixtures/l1_to_l2_messaging.ts b/yarn-project/end-to-end/src/fixtures/l1_to_l2_messaging.ts index 403838f5769..b4f7c44e5da 100644 --- a/yarn-project/end-to-end/src/fixtures/l1_to_l2_messaging.ts +++ b/yarn-project/end-to-end/src/fixtures/l1_to_l2_messaging.ts @@ -1,16 +1,23 @@ -import { type L1ToL2Message } from '@aztec/aztec.js'; import { type AztecAddress, Fr } from '@aztec/circuits.js'; import { type L1ContractAddresses } from '@aztec/ethereum'; import { InboxAbi } from '@aztec/l1-artifacts'; import { expect } from '@jest/globals'; -import { type Hex, type PublicClient, type WalletClient, decodeEventLog, getContract } from 'viem'; +import { + type Account, + type Chain, + type HttpTransport, + type PublicClient, + type WalletClient, + decodeEventLog, + getContract, +} from 'viem'; export async function sendL1ToL2Message( - message: L1ToL2Message | { recipient: AztecAddress; content: Fr; secretHash: Fr }, + message: { recipient: AztecAddress; content: Fr; secretHash: Fr }, ctx: { - walletClient: WalletClient; - publicClient: PublicClient; + walletClient: WalletClient; + publicClient: PublicClient; l1ContractAddresses: Pick; }, ) { @@ -20,18 +27,15 @@ export async function sendL1ToL2Message( client: ctx.walletClient, }); - const recipient = 'recipient' in message.recipient ? message.recipient.recipient : message.recipient; - const version = 'version' in message.recipient ? message.recipient.version : 1; + const { recipient, content, secretHash } = message; + const version = 1; // We inject the message to Inbox - const txHash = await inbox.write.sendL2Message( - [ - { actor: recipient.toString() as Hex, version: BigInt(version) }, - message.content.toString() as Hex, - message.secretHash.toString() as Hex, - ] as const, - {} as any, - ); + const txHash = await inbox.write.sendL2Message([ + { actor: recipient.toString(), version: BigInt(version) }, + content.toString(), + secretHash.toString(), + ]); // We check that the message was correctly injected by checking the emitted event const txReceipt = await ctx.publicClient.waitForTransactionReceipt({ hash: txHash }); @@ -49,11 +53,5 @@ export async function sendL1ToL2Message( const receivedMsgHash = topics.args.hash; const receivedGlobalLeafIndex = topics.args.index; - // We check that the leaf inserted into the subtree matches the expected message hash - if ('hash' in message) { - const msgHash = message.hash(); - expect(receivedMsgHash).toBe(msgHash.toString()); - } - return [Fr.fromString(receivedMsgHash), new Fr(receivedGlobalLeafIndex)]; } diff --git a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts index bbb2b206b2d..3b104a99cf7 100644 --- a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts @@ -7,38 +7,32 @@ import { ExtendedNote, type FieldsOf, Fr, + type L1TokenManager, + L1TokenPortalManager, + type L2AmountClaim, + type L2RedeemableAmountClaim, Note, type PXE, type SiblingPath, type TxHash, type TxReceipt, type Wallet, - computeSecretHash, deployL1Contract, retryUntil, } from '@aztec/aztec.js'; import { type L1ContractAddresses } from '@aztec/ethereum'; -import { sha256ToField } from '@aztec/foundation/crypto'; -import { - InboxAbi, - OutboxAbi, - TestERC20Abi, - TestERC20Bytecode, - TokenPortalAbi, - TokenPortalBytecode, -} from '@aztec/l1-artifacts'; +import { TestERC20Abi, TestERC20Bytecode, TokenPortalAbi, TokenPortalBytecode } from '@aztec/l1-artifacts'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { TokenBridgeContract } from '@aztec/noir-contracts.js/TokenBridge'; import { type Account, type Chain, - type GetContractReturnType, + type Hex, type HttpTransport, type PublicClient, type WalletClient, getContract, - toFunctionSelector, } from 'viem'; // docs:start:deployAndInitializeTokenAndBridgeContracts @@ -150,32 +144,18 @@ export class CrossChainTestHarness { underlyingERC20Address?: EthAddress, ): Promise { const ethAccount = EthAddress.fromString((await walletClient.getAddresses())[0]); - const owner = wallet.getCompleteAddress(); const l1ContractAddresses = (await pxeService.getNodeInfo()).l1ContractAddresses; - const inbox = getContract({ - address: l1ContractAddresses.inboxAddress.toString(), - abi: InboxAbi, - client: walletClient, - }); - - const outbox = getContract({ - address: l1ContractAddresses.outboxAddress.toString(), - abi: OutboxAbi, - client: walletClient, - }); - // Deploy and initialize all required contracts logger.info('Deploying and initializing token, portal and its bridge...'); - const { token, bridge, tokenPortalAddress, tokenPortal, underlyingERC20 } = - await deployAndInitializeTokenAndBridgeContracts( - wallet, - walletClient, - publicClient, - l1ContractAddresses.registryAddress, - owner.address, - underlyingERC20Address, - ); + const { token, bridge, tokenPortalAddress, underlyingERC20 } = await deployAndInitializeTokenAndBridgeContracts( + wallet, + walletClient, + publicClient, + l1ContractAddresses.registryAddress, + wallet.getAddress(), + underlyingERC20Address, + ); logger.info('Deployed and initialized token, portal and its bridge.'); return new CrossChainTestHarness( @@ -186,18 +166,19 @@ export class CrossChainTestHarness { bridge, ethAccount, tokenPortalAddress, - tokenPortal, - underlyingERC20, - inbox, - outbox, + underlyingERC20.address, publicClient, walletClient, - owner.address, l1ContractAddresses, wallet, ); } + private readonly l1TokenManager: L1TokenManager; + private readonly l1TokenPortalManager: L1TokenPortalManager; + + public readonly ownerAddress: AztecAddress; + constructor( /** Aztec node instance. */ public aztecNode: AztecNode, @@ -216,96 +197,46 @@ export class CrossChainTestHarness { /** Portal address. */ public tokenPortalAddress: EthAddress, - /** Token portal instance. */ - public tokenPortal: any, /** Underlying token for portal tests. */ - public underlyingERC20: any, - /** Message Bridge Inbox. */ - public inbox: GetContractReturnType>, - /** Message Bridge Outbox. */ - public outbox: GetContractReturnType>, + public underlyingERC20Address: EthAddress, /** Viem Public client instance. */ public publicClient: PublicClient, /** Viem Wallet Client instance. */ - public walletClient: any, - - /** Aztec address to use in tests. */ - public ownerAddress: AztecAddress, + public walletClient: WalletClient, /** Deployment addresses for all L1 contracts */ public readonly l1ContractAddresses: L1ContractAddresses, /** Wallet of the owner. */ public readonly ownerWallet: Wallet, - ) {} - - /** - * Used to generate a claim secret using pedersen's hash function. - * @dev Used for both L1 to L2 messages and transparent note (pending shields) secrets. - * @returns A tuple of the secret and its hash. - */ - generateClaimSecret(): [Fr, Fr] { - this.logger.debug("Generating a claim secret using pedersen's hash function"); - const secret = Fr.random(); - const secretHash = computeSecretHash(secret); - this.logger.info('Generated claim secret: ' + secretHash.toString()); - return [secret, secretHash]; + ) { + this.l1TokenPortalManager = new L1TokenPortalManager( + this.tokenPortalAddress, + this.underlyingERC20Address, + this.l1ContractAddresses.outboxAddress, + this.publicClient, + this.walletClient, + this.logger, + ); + this.l1TokenManager = this.l1TokenPortalManager.getTokenManager(); + this.ownerAddress = this.ownerWallet.getAddress(); } async mintTokensOnL1(amount: bigint) { - this.logger.info('Minting tokens on L1'); - const txHash = await this.underlyingERC20.write.mint([this.ethAccount.toString(), amount], {} as any); - await this.publicClient.waitForTransactionReceipt({ hash: txHash }); - expect(await this.underlyingERC20.read.balanceOf([this.ethAccount.toString()])).toBe(amount); + await this.l1TokenManager.mint(amount, this.ethAccount.toString()); + expect(await this.l1TokenManager.getL1TokenBalance(this.ethAccount.toString())).toEqual(amount); } - async getL1BalanceOf(address: EthAddress) { - return await this.underlyingERC20.read.balanceOf([address.toString()]); + getL1BalanceOf(address: EthAddress) { + return this.l1TokenManager.getL1TokenBalance(address.toString()); } - async sendTokensToPortalPublic(bridgeAmount: bigint, secretHash: Fr) { - const txHash1 = await this.underlyingERC20.write.approve( - [this.tokenPortalAddress.toString(), bridgeAmount], - {} as any, - ); - await this.publicClient.waitForTransactionReceipt({ hash: txHash1 }); - - // Deposit tokens to the TokenPortal - this.logger.info('Sending messages to L1 portal to be consumed publicly'); - const args = [this.ownerAddress.toString(), bridgeAmount, secretHash.toString()] as const; - const { result: messageHash } = await this.tokenPortal.simulate.depositToAztecPublic(args, { - account: this.ethAccount.toString(), - } as any); - const txHash2 = await this.tokenPortal.write.depositToAztecPublic(args, {} as any); - await this.publicClient.waitForTransactionReceipt({ hash: txHash2 }); - - return Fr.fromString(messageHash); + sendTokensToPortalPublic(bridgeAmount: bigint, mint = false) { + return this.l1TokenPortalManager.bridgeTokensPublic(this.ownerAddress, bridgeAmount, mint); } - async sendTokensToPortalPrivate( - secretHashForRedeemingMintedNotes: Fr, - bridgeAmount: bigint, - secretHashForL2MessageConsumption: Fr, - ) { - const txHash1 = await this.underlyingERC20.write.approve( - [this.tokenPortalAddress.toString(), bridgeAmount], - {} as any, - ); - await this.publicClient.waitForTransactionReceipt({ hash: txHash1 }); - // Deposit tokens to the TokenPortal - this.logger.info('Sending messages to L1 portal to be consumed privately'); - const args = [ - secretHashForRedeemingMintedNotes.toString(), - bridgeAmount, - secretHashForL2MessageConsumption.toString(), - ] as const; - const { result: messageHash } = await this.tokenPortal.simulate.depositToAztecPrivate(args, { - account: this.ethAccount.toString(), - } as any); - const txHash2 = await this.tokenPortal.write.depositToAztecPrivate(args, {} as any); - await this.publicClient.waitForTransactionReceipt({ hash: txHash2 }); - - return Fr.fromString(messageHash); + sendTokensToPortalPrivate(bridgeAmount: bigint, mint = false) { + return this.l1TokenPortalManager.bridgeTokensPrivate(this.ownerAddress, bridgeAmount, mint); } async mintTokensPublicOnL2(amount: bigint) { @@ -318,30 +249,33 @@ export class CrossChainTestHarness { await this.addPendingShieldNoteToPXE(amount, secretHash, receipt.txHash); } - async performL2Transfer(transferAmount: bigint, receiverAddress: AztecAddress) { + async sendL2PublicTransfer(transferAmount: bigint, receiverAddress: AztecAddress) { // send a transfer tx to force through rollup with the message included await this.l2Token.methods.transfer_public(this.ownerAddress, receiverAddress, transferAmount, 0).send().wait(); } async consumeMessageOnAztecAndMintPrivately( - secretHashForRedeemingMintedNotes: Fr, - bridgeAmount: bigint, - secretForL2MessageConsumption: Fr, + claim: Pick, ) { this.logger.info('Consuming messages on L2 privately'); - // Call the mint tokens function on the Aztec.nr contract + const { claimAmount, claimSecret: secretForL2MessageConsumption, messageLeafIndex, redeemSecretHash } = claim; const consumptionReceipt = await this.l2Bridge.methods - .claim_private(secretHashForRedeemingMintedNotes, bridgeAmount, secretForL2MessageConsumption) + .claim_private(redeemSecretHash, claimAmount, secretForL2MessageConsumption, messageLeafIndex) .send() .wait(); - await this.addPendingShieldNoteToPXE(bridgeAmount, secretHashForRedeemingMintedNotes, consumptionReceipt.txHash); + await this.addPendingShieldNoteToPXE(claimAmount.toBigInt(), redeemSecretHash, consumptionReceipt.txHash); } - async consumeMessageOnAztecAndMintPublicly(bridgeAmount: bigint, secret: Fr, leafIndex: bigint) { + async consumeMessageOnAztecAndMintPublicly( + claim: Pick, + ) { this.logger.info('Consuming messages on L2 Publicly'); - // Call the mint tokens function on the Aztec.nr contract - await this.l2Bridge.methods.claim_public(this.ownerAddress, bridgeAmount, secret, leafIndex).send().wait(); + const { claimAmount, claimSecret, messageLeafIndex } = claim; + await this.l2Bridge.methods + .claim_public(this.ownerAddress, claimAmount, claimSecret, messageLeafIndex) + .send() + .wait(); } async withdrawPrivateFromAztecToL1(withdrawAmount: bigint, nonce: Fr = Fr.ZERO): Promise> { @@ -382,52 +316,27 @@ export class CrossChainTestHarness { } getL2ToL1MessageLeaf(withdrawAmount: bigint, callerOnL1: EthAddress = EthAddress.ZERO): Fr { - const content = sha256ToField([ - Buffer.from(toFunctionSelector('withdraw(address,uint256,address)').substring(2), 'hex'), - this.ethAccount.toBuffer32(), - new Fr(withdrawAmount).toBuffer(), - callerOnL1.toBuffer32(), - ]); - const leaf = sha256ToField([ - this.l2Bridge.address.toBuffer(), - new Fr(1).toBuffer(), // aztec version - this.tokenPortalAddress.toBuffer32() ?? Buffer.alloc(32, 0), - new Fr(this.publicClient.chain.id).toBuffer(), // chain id - content.toBuffer(), - ]); - - return leaf; + return this.l1TokenPortalManager.getL2ToL1MessageLeaf( + withdrawAmount, + this.ethAccount, + this.l2Bridge.address, + callerOnL1, + ); } - async withdrawFundsFromBridgeOnL1( - withdrawAmount: bigint, - blockNumber: number, + withdrawFundsFromBridgeOnL1( + amount: bigint, + blockNumber: number | bigint, messageIndex: bigint, siblingPath: SiblingPath, ) { - this.logger.info('Send L1 tx to consume message and withdraw funds'); - // Call function on L1 contract to consume the message - const { request: withdrawRequest } = await this.tokenPortal.simulate.withdraw([ - this.ethAccount.toString(), - withdrawAmount, - false, + return this.l1TokenPortalManager.withdrawFunds( + amount, + this.ethAccount, BigInt(blockNumber), messageIndex, - siblingPath.toBufferArray().map((buf: Buffer) => `0x${buf.toString('hex')}`) as readonly `0x${string}`[], - ]); - - expect( - await this.outbox.read.hasMessageBeenConsumedAtBlockAndIndex([BigInt(blockNumber), BigInt(messageIndex)], {}), - ).toBe(false); - - await this.walletClient.writeContract(withdrawRequest); - await expect(async () => { - await this.walletClient.writeContract(withdrawRequest); - }).rejects.toThrow(); - - expect( - await this.outbox.read.hasMessageBeenConsumedAtBlockAndIndex([BigInt(blockNumber), BigInt(messageIndex)], {}), - ).toBe(true); + siblingPath, + ); } async shieldFundsOnL2(shieldAmount: bigint, secretHash: Fr) { @@ -471,11 +380,12 @@ export class CrossChainTestHarness { * the message is sent to Inbox and when the subtree containing the message is included in the block and then when * it's included it becomes available for consumption in the next block because the l1 to l2 message tree. */ - async makeMessageConsumable(msgHash: Fr) { + async makeMessageConsumable(msgHash: Fr | Hex) { + const frMsgHash = typeof msgHash === 'string' ? Fr.fromString(msgHash) : msgHash; const currentL2BlockNumber = await this.aztecNode.getBlockNumber(); // We poll isL1ToL2MessageSynced endpoint until the message is available await retryUntil( - async () => await this.aztecNode.isL1ToL2MessageSynced(msgHash, currentL2BlockNumber), + async () => await this.aztecNode.isL1ToL2MessageSynced(frMsgHash, currentL2BlockNumber), 'message sync', 10, ); diff --git a/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts b/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts index 7e955b742d4..458dede26ed 100644 --- a/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts @@ -3,34 +3,22 @@ import { type AztecNode, type DebugLogger, EthAddress, - Fr, + L1FeeJuicePortalManager, + type L1TokenManager, + type L2AmountClaim, type PXE, type Wallet, - computeSecretHash, } from '@aztec/aztec.js'; -import { FeeJuicePortalAbi, OutboxAbi, TestERC20Abi } from '@aztec/l1-artifacts'; import { FeeJuiceContract } from '@aztec/noir-contracts.js'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; -import { - type Account, - type Chain, - type GetContractReturnType, - type HttpTransport, - type PublicClient, - type WalletClient, - getContract, -} from 'viem'; +import { type Account, type Chain, type HttpTransport, type PublicClient, type WalletClient } from 'viem'; export interface IGasBridgingTestHarness { getL1FeeJuiceBalance(address: EthAddress): Promise; - prepareTokensOnL1( - l1TokenBalance: bigint, - bridgeAmount: bigint, - owner: AztecAddress, - ): Promise<{ secret: Fr; secretHash: Fr; msgHash: Fr }>; - bridgeFromL1ToL2(l1TokenBalance: bigint, bridgeAmount: bigint, owner: AztecAddress): Promise; - l2Token: FeeJuiceContract; + prepareTokensOnL1(bridgeAmount: bigint, owner: AztecAddress): Promise; + bridgeFromL1ToL2(bridgeAmount: bigint, owner: AztecAddress): Promise; + feeJuice: FeeJuiceContract; l1FeeJuiceAddress: EthAddress; } @@ -60,24 +48,6 @@ export class FeeJuicePortalTestingHarnessFactory { throw new Error('Fee Juice portal not deployed on L1'); } - const outbox = getContract({ - address: l1ContractAddresses.outboxAddress.toString(), - abi: OutboxAbi, - client: walletClient, - }); - - const gasL1 = getContract({ - address: feeJuiceAddress.toString(), - abi: TestERC20Abi, - client: walletClient, - }); - - const feeJuicePortal = getContract({ - address: feeJuicePortalAddress.toString(), - abi: FeeJuicePortalAbi, - client: walletClient, - }); - const gasL2 = await FeeJuiceContract.at(ProtocolContractAddress.FeeJuice, wallet); return new GasBridgingTestHarness( @@ -87,9 +57,7 @@ export class FeeJuicePortalTestingHarnessFactory { gasL2, ethAccount, feeJuicePortalAddress, - feeJuicePortal, - gasL1, - outbox, + feeJuiceAddress, publicClient, walletClient, ); @@ -106,6 +74,9 @@ export class FeeJuicePortalTestingHarnessFactory { * shared between cross chain tests. */ export class GasBridgingTestHarness implements IGasBridgingTestHarness { + private readonly l1TokenManager: L1TokenManager; + private readonly feeJuicePortalManager: L1FeeJuicePortalManager; + constructor( /** Aztec node */ public aztecNode: AztecNode, @@ -115,76 +86,53 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness { public logger: DebugLogger, /** L2 Token/Bridge contract. */ - public l2Token: FeeJuiceContract, + public feeJuice: FeeJuiceContract, /** Eth account to interact with. */ public ethAccount: EthAddress, /** Portal address. */ - public tokenPortalAddress: EthAddress, - /** Token portal instance. */ - public tokenPortal: GetContractReturnType>, + public feeJuicePortalAddress: EthAddress, /** Underlying token for portal tests. */ - public underlyingERC20: GetContractReturnType>, - /** Message Bridge Outbox. */ - public outbox: GetContractReturnType>, + public l1FeeJuiceAddress: EthAddress, /** Viem Public client instance. */ public publicClient: PublicClient, /** Viem Wallet Client instance. */ - public walletClient: WalletClient, - ) {} - - get l1FeeJuiceAddress() { - return EthAddress.fromString(this.underlyingERC20.address); - } + public walletClient: WalletClient, + ) { + this.feeJuicePortalManager = new L1FeeJuicePortalManager( + this.feeJuicePortalAddress, + this.l1FeeJuiceAddress, + this.publicClient, + this.walletClient, + this.logger, + ); - generateClaimSecret(): [Fr, Fr] { - this.logger.debug("Generating a claim secret using pedersen's hash function"); - const secret = Fr.random(); - const secretHash = computeSecretHash(secret); - this.logger.info('Generated claim secret: ' + secretHash.toString()); - return [secret, secretHash]; + this.l1TokenManager = this.feeJuicePortalManager.getTokenManager(); } async mintTokensOnL1(amount: bigint, to: EthAddress = this.ethAccount) { - this.logger.info('Minting tokens on L1'); - const balanceBefore = await this.underlyingERC20.read.balanceOf([to.toString()]); - await this.publicClient.waitForTransactionReceipt({ - hash: await this.underlyingERC20.write.mint([to.toString(), amount]), - }); - expect(await this.underlyingERC20.read.balanceOf([to.toString()])).toBe(balanceBefore + amount); + const balanceBefore = await this.l1TokenManager.getL1TokenBalance(to.toString()); + await this.l1TokenManager.mint(amount, to.toString()); + expect(await this.l1TokenManager.getL1TokenBalance(to.toString())).toEqual(balanceBefore + amount); } async getL1FeeJuiceBalance(address: EthAddress) { - return await this.underlyingERC20.read.balanceOf([address.toString()]); + return await this.l1TokenManager.getL1TokenBalance(address.toString()); } - async sendTokensToPortalPublic(bridgeAmount: bigint, l2Address: AztecAddress, secretHash: Fr) { - await this.publicClient.waitForTransactionReceipt({ - hash: await this.underlyingERC20.write.approve([this.tokenPortalAddress.toString(), bridgeAmount]), - }); - - // Deposit tokens to the TokenPortal - this.logger.info('Sending messages to L1 portal to be consumed publicly'); - const args = [l2Address.toString(), bridgeAmount, secretHash.toString()] as const; - const { result: messageHash } = await this.tokenPortal.simulate.depositToAztecPublic(args, { - account: this.ethAccount.toString(), - } as any); - await this.publicClient.waitForTransactionReceipt({ - hash: await this.tokenPortal.write.depositToAztecPublic(args), - }); - - return Fr.fromString(messageHash); + sendTokensToPortalPublic(bridgeAmount: bigint, l2Address: AztecAddress, mint = false) { + return this.feeJuicePortalManager.bridgeTokensPublic(l2Address, bridgeAmount, mint); } - async consumeMessageOnAztecAndClaimPrivately(bridgeAmount: bigint, owner: AztecAddress, secret: Fr) { + async consumeMessageOnAztecAndClaimPrivately(owner: AztecAddress, claim: L2AmountClaim) { this.logger.info('Consuming messages on L2 Privately'); - // Call the claim function on the Aztec.nr Fee Juice contract - await this.l2Token.methods.claim(owner, bridgeAmount, secret).send().wait(); + const { claimAmount, claimSecret, messageLeafIndex } = claim; + await this.feeJuice.methods.claim(owner, claimAmount, claimSecret, messageLeafIndex).send().wait(); } async getL2PublicBalanceOf(owner: AztecAddress) { - return await this.l2Token.methods.balance_of_public(owner).simulate(); + return await this.feeJuice.methods.balance_of_public(owner).simulate(); } async expectPublicBalanceOnL2(owner: AztecAddress, expectedBalance: bigint) { @@ -192,29 +140,22 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness { expect(balance).toBe(expectedBalance); } - async prepareTokensOnL1(l1TokenBalance: bigint, bridgeAmount: bigint, owner: AztecAddress) { - const [secret, secretHash] = this.generateClaimSecret(); - - // Mint tokens on L1 - await this.mintTokensOnL1(l1TokenBalance); - - // Deposit tokens to the TokenPortal - const msgHash = await this.sendTokensToPortalPublic(bridgeAmount, owner, secretHash); - expect(await this.getL1FeeJuiceBalance(this.ethAccount)).toBe(l1TokenBalance - bridgeAmount); + async prepareTokensOnL1(bridgeAmount: bigint, owner: AztecAddress) { + const claim = await this.sendTokensToPortalPublic(bridgeAmount, owner, true); // Perform an unrelated transactions on L2 to progress the rollup by 2 blocks. - await this.l2Token.methods.check_balance(0).send().wait(); - await this.l2Token.methods.check_balance(0).send().wait(); + await this.feeJuice.methods.check_balance(0).send().wait(); + await this.feeJuice.methods.check_balance(0).send().wait(); - return { secret, msgHash, secretHash }; + return claim; } - async bridgeFromL1ToL2(l1TokenBalance: bigint, bridgeAmount: bigint, owner: AztecAddress) { + async bridgeFromL1ToL2(bridgeAmount: bigint, owner: AztecAddress) { // Prepare the tokens on the L1 side - const { secret } = await this.prepareTokensOnL1(l1TokenBalance, bridgeAmount, owner); + const claim = await this.prepareTokensOnL1(bridgeAmount, owner); - // Consume L1-> L2 message and claim tokens privately on L2 - await this.consumeMessageOnAztecAndClaimPrivately(bridgeAmount, owner, secret); + // Consume L1 -> L2 message and claim tokens privately on L2 + await this.consumeMessageOnAztecAndClaimPrivately(owner, claim); await this.expectPublicBalanceOnL2(owner, bridgeAmount); } } diff --git a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts index 08772b3e617..203ceb9a501 100644 --- a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts +++ b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts @@ -7,8 +7,9 @@ import { Fr, type PXE, computeAuthWitMessageHash, + generateClaimSecret, } from '@aztec/aztec.js'; -import { type DeployL1Contracts, deployL1Contract } from '@aztec/ethereum'; +import { type DeployL1Contracts, deployL1Contract, extractEvent } from '@aztec/ethereum'; import { sha256ToField } from '@aztec/foundation/crypto'; import { InboxAbi, RollupAbi, UniswapPortalAbi, UniswapPortalBytecode } from '@aztec/l1-artifacts'; import { UniswapContract } from '@aztec/noir-contracts.js/Uniswap'; @@ -21,7 +22,6 @@ import { type HttpTransport, type PublicClient, type WalletClient, - decodeEventLog, getContract, parseEther, toFunctionSelector, @@ -93,7 +93,7 @@ export const uniswapL1L2TestSuite = ( let deployL1ContractsValues: DeployL1Contracts; let rollup: GetContractReturnType>; - let uniswapPortal: GetContractReturnType>; + let uniswapPortal: GetContractReturnType>; let uniswapPortalAddress: EthAddress; let uniswapL2Contract: UniswapContract; @@ -185,32 +185,22 @@ export const uniswapL1L2TestSuite = ( const wethL1BeforeBalance = await wethCrossChainHarness.getL1BalanceOf(ownerEthAddress); // 1. Approve and deposit weth to the portal and move to L2 - const [secretForMintingWeth, secretHashForMintingWeth] = wethCrossChainHarness.generateClaimSecret(); - const [secretForRedeemingWeth, secretHashForRedeemingWeth] = wethCrossChainHarness.generateClaimSecret(); + const wethDepositClaim = await wethCrossChainHarness.sendTokensToPortalPrivate(wethAmountToBridge); - const tokenDepositMsgHash = await wethCrossChainHarness.sendTokensToPortalPrivate( - secretHashForRedeemingWeth, - wethAmountToBridge, - secretHashForMintingWeth, - ); // funds transferred from owner to token portal - expect(await wethCrossChainHarness.getL1BalanceOf(ownerEthAddress)).toBe( + expect(await wethCrossChainHarness.getL1BalanceOf(ownerEthAddress)).toEqual( wethL1BeforeBalance - wethAmountToBridge, ); - expect(await wethCrossChainHarness.getL1BalanceOf(wethCrossChainHarness.tokenPortalAddress)).toBe( + expect(await wethCrossChainHarness.getL1BalanceOf(wethCrossChainHarness.tokenPortalAddress)).toEqual( wethAmountToBridge, ); - await wethCrossChainHarness.makeMessageConsumable(tokenDepositMsgHash); + await wethCrossChainHarness.makeMessageConsumable(Fr.fromString(wethDepositClaim.messageHash)); // 2. Claim WETH on L2 logger.info('Minting weth on L2'); - await wethCrossChainHarness.consumeMessageOnAztecAndMintPrivately( - secretHashForRedeemingWeth, - wethAmountToBridge, - secretForMintingWeth, - ); - await wethCrossChainHarness.redeemShieldPrivatelyOnL2(wethAmountToBridge, secretForRedeemingWeth); + await wethCrossChainHarness.consumeMessageOnAztecAndMintPrivately(wethDepositClaim); + await wethCrossChainHarness.redeemShieldPrivatelyOnL2(wethAmountToBridge, wethDepositClaim.redeemSecret); await wethCrossChainHarness.expectPrivateBalanceOnL2(ownerAddress, wethAmountToBridge); // Store balances @@ -232,9 +222,8 @@ export const uniswapL1L2TestSuite = ( // 4. Swap on L1 - sends L2 to L1 message to withdraw WETH to L1 and another message to swap assets. logger.info('Withdrawing weth to L1 and sending message to swap to dai'); - const [secretForDepositingSwappedDai, secretHashForDepositingSwappedDai] = - daiCrossChainHarness.generateClaimSecret(); - const [secretForRedeemingDai, secretHashForRedeemingDai] = daiCrossChainHarness.generateClaimSecret(); + const [secretForDepositingSwappedDai, secretHashForDepositingSwappedDai] = generateClaimSecret(); + const [secretForRedeemingDai, secretHashForRedeemingDai] = generateClaimSecret(); const l2UniswapInteractionReceipt = await uniswapL2Contract.methods .swap_private( @@ -252,13 +241,9 @@ export const uniswapL1L2TestSuite = ( .send() .wait(); + const swapPrivateFunction = 'swap_private(address,uint256,uint24,address,uint256,bytes32,bytes32,address)'; const swapPrivateContent = sha256ToField([ - Buffer.from( - toFunctionSelector('swap_private(address,uint256,uint24,address,uint256,bytes32,bytes32,address)').substring( - 2, - ), - 'hex', - ), + Buffer.from(toFunctionSelector(swapPrivateFunction).substring(2), 'hex'), wethCrossChainHarness.tokenPortalAddress.toBuffer32(), new Fr(wethAmountToBridge), new Fr(uniswapFeeTier), @@ -344,23 +329,15 @@ export const uniswapL1L2TestSuite = ( ] as const; // this should also insert a message into the inbox. - const txHash = await uniswapPortal.write.swapPrivate(swapArgs, {} as any); + const txReceipt = await daiCrossChainHarness.publicClient.waitForTransactionReceipt({ + hash: await uniswapPortal.write.swapPrivate(swapArgs), + }); // We get the msg leaf from event so that we can later wait for it to be available for consumption - let tokenOutMsgHash: Fr; - { - const txReceipt = await daiCrossChainHarness.publicClient.waitForTransactionReceipt({ - hash: txHash, - }); - - const txLog = txReceipt.logs[9]; - const topics = decodeEventLog({ - abi: InboxAbi, - data: txLog.data, - topics: txLog.topics, - }); - tokenOutMsgHash = Fr.fromString(topics.args.hash); - } + const inboxAddress = daiCrossChainHarness.l1ContractAddresses.inboxAddress.toString(); + const txLog = extractEvent(txReceipt.logs, inboxAddress, InboxAbi, 'MessageSent'); + const tokenOutMsgHash = Fr.fromString(txLog.args.hash); + const tokenOutMsgIndex = txLog.args.index; // weth was swapped to dai and send to portal const daiL1BalanceOfPortalAfter = await daiCrossChainHarness.getL1BalanceOf( @@ -374,11 +351,12 @@ export const uniswapL1L2TestSuite = ( // 6. claim dai on L2 logger.info('Consuming messages to mint dai on L2'); - await daiCrossChainHarness.consumeMessageOnAztecAndMintPrivately( - secretHashForRedeemingDai, - daiAmountToBridge, - secretForDepositingSwappedDai, - ); + await daiCrossChainHarness.consumeMessageOnAztecAndMintPrivately({ + redeemSecretHash: secretHashForRedeemingDai, + claimAmount: new Fr(daiAmountToBridge), + claimSecret: secretForDepositingSwappedDai, + messageLeafIndex: tokenOutMsgIndex, + }); await daiCrossChainHarness.redeemShieldPrivatelyOnL2(daiAmountToBridge, secretForRedeemingDai); await daiCrossChainHarness.expectPrivateBalanceOnL2(ownerAddress, daiL2BalanceBeforeSwap + daiAmountToBridge); @@ -666,7 +644,7 @@ export const uniswapL1L2TestSuite = ( it("can't swap if user passes a token different to what the bridge tracks", async () => { // 1. give user private funds on L2: - const [secretForRedeemingWeth, secretHashForRedeemingWeth] = wethCrossChainHarness.generateClaimSecret(); + const [secretForRedeemingWeth, secretHashForRedeemingWeth] = generateClaimSecret(); await wethCrossChainHarness.mintTokensPrivateOnL2(wethAmountToBridge, secretHashForRedeemingWeth); await wethCrossChainHarness.redeemShieldPrivatelyOnL2(wethAmountToBridge, secretForRedeemingWeth); await wethCrossChainHarness.expectPrivateBalanceOnL2(ownerAddress, wethAmountToBridge); @@ -731,7 +709,7 @@ export const uniswapL1L2TestSuite = ( .wait(); // No approval to call `swap` but should work even without it: - const [_, secretHashForDepositingSwappedDai] = daiCrossChainHarness.generateClaimSecret(); + const [_, secretHashForDepositingSwappedDai] = generateClaimSecret(); await uniswapL2Contract.methods .swap_public( @@ -824,7 +802,7 @@ export const uniswapL1L2TestSuite = ( // tests when trying to mix private and public flows: it("can't call swap_public on L1 if called swap_private on L2", async () => { // get tokens on L2: - const [secretForRedeemingWeth, secretHashForRedeemingWeth] = wethCrossChainHarness.generateClaimSecret(); + const [secretForRedeemingWeth, secretHashForRedeemingWeth] = generateClaimSecret(); logger.info('minting weth on L2'); await wethCrossChainHarness.mintTokensPrivateOnL2(wethAmountToBridge, secretHashForRedeemingWeth); await wethCrossChainHarness.redeemShieldPrivatelyOnL2(wethAmountToBridge, secretForRedeemingWeth); @@ -845,9 +823,9 @@ export const uniswapL1L2TestSuite = ( // Swap logger.info('Withdrawing weth to L1 and sending message to swap to dai'); - const [, secretHashForRedeemingDai] = daiCrossChainHarness.generateClaimSecret(); + const [, secretHashForRedeemingDai] = generateClaimSecret(); - const [, secretHashForDepositingSwappedDai] = daiCrossChainHarness.generateClaimSecret(); + const [, secretHashForDepositingSwappedDai] = generateClaimSecret(); const withdrawReceipt = await uniswapL2Contract.methods .swap_private( wethCrossChainHarness.l2Token.address, diff --git a/yarn-project/ethereum/src/index.ts b/yarn-project/ethereum/src/index.ts index aac126e1108..2e39ebaa8f5 100644 --- a/yarn-project/ethereum/src/index.ts +++ b/yarn-project/ethereum/src/index.ts @@ -3,3 +3,4 @@ export * from './deploy_l1_contracts.js'; export * from './l1_contract_addresses.js'; export * from './l1_reader.js'; export * from './ethereum_chain.js'; +export * from './utils.js'; diff --git a/yarn-project/ethereum/src/utils.ts b/yarn-project/ethereum/src/utils.ts new file mode 100644 index 00000000000..9b8f7837e98 --- /dev/null +++ b/yarn-project/ethereum/src/utils.ts @@ -0,0 +1,66 @@ +import { type Fr } from '@aztec/foundation/fields'; +import { type DebugLogger } from '@aztec/foundation/log'; + +import { + type Abi, + type ContractEventName, + type DecodeEventLogReturnType, + type Hex, + type Log, + decodeEventLog, +} from 'viem'; + +export interface L2Claim { + claimSecret: Fr; + claimAmount: Fr; + messageHash: Hex; + messageLeafIndex: bigint; +} + +export function extractEvent< + const TAbi extends Abi | readonly unknown[], + TEventName extends ContractEventName, + TEventType = DecodeEventLogReturnType, +>( + logs: Log[], + address: Hex, + abi: TAbi, + eventName: TEventName, + filter?: (log: TEventType) => boolean, + logger?: DebugLogger, +): TEventType { + const event = tryExtractEvent(logs, address, abi, eventName, filter, logger); + if (!event) { + throw new Error(`Failed to find matching event ${eventName} for contract ${address}`); + } + return event; +} + +function tryExtractEvent< + const TAbi extends Abi | readonly unknown[], + TEventName extends ContractEventName, + TEventType = DecodeEventLogReturnType, +>( + logs: Log[], + address: Hex, + abi: TAbi, + eventName: TEventName, + filter?: (log: TEventType) => boolean, + logger?: DebugLogger, +): TEventType | undefined { + for (const log of logs) { + if (log.address === address) { + try { + const decodedEvent = decodeEventLog({ abi, ...log }); + if (decodedEvent.eventName === eventName) { + const matchingEvent = decodedEvent as TEventType; + if (!filter || filter(matchingEvent)) { + return matchingEvent; + } + } + } catch (err) { + logger?.warn(`Failed to decode event log for contract ${address}: ${err}`); + } + } + } +} diff --git a/yarn-project/protocol-contracts/src/protocol_contract_data.ts b/yarn-project/protocol-contracts/src/protocol_contract_data.ts index 8b1c4f4c09a..699341e49a1 100644 --- a/yarn-project/protocol-contracts/src/protocol_contract_data.ts +++ b/yarn-project/protocol-contracts/src/protocol_contract_data.ts @@ -54,10 +54,10 @@ export const ProtocolContractLeaf = { ContractInstanceDeployer: Fr.fromString('0x04a661c9d4d295fc485a7e0f3de40c09b35366343bce8ad229106a8ef4076fe5'), ContractClassRegisterer: Fr.fromString('0x147ba3294403576dbad10f86d3ffd4eb83fb230ffbcd5c8b153dd02942d0611f'), MultiCallEntrypoint: Fr.fromString('0x154b701b41d6cf6da7204fef36b2ee9578b449d21b3792a9287bf45eba48fd26'), - FeeJuice: Fr.fromString('0x0191fe64a9d9efca55572a5190479698b8a3b296295f0f2d917b91fcb5486251'), + FeeJuice: Fr.fromString('0x146cb9b7cda808b4d5e066773e2fe0131d4ac4f7238bc0f093dce70f7c0f3421'), Router: Fr.fromString('0x19e9ec99aedfe3ea69ba91b862b815df7d1796fa802985a154159cd739fe4817'), }; export const protocolContractTreeRoot = Fr.fromString( - '0x158c0b725f29c56278203f9d49c503c14bcf22888684ee73a4826e2edf2a56a8', + '0x149eb7ca5c1f23580f2449edfbb65ec70160e5d4b6f8313f061b8a8d73d014ab', ); diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 3b19bed0414..bd0ec3e05a2 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -596,6 +596,7 @@ describe('Private Execution test suite', () => { const artifact = getFunctionArtifact(TestContractArtifact, 'consume_mint_private_message'); let bridgedAmount = 100n; + const l1ToL2MessageIndex = 0; const secretHashForRedeemingNotes = new Fr(2n); let secretForL1ToL2MessageConsumption = new Fr(1n); @@ -620,6 +621,7 @@ describe('Private Execution test suite', () => { [secretHashForRedeemingNotes, new Fr(bridgedAmount)], crossChainMsgRecipient ?? contractAddress, secretForL1ToL2MessageConsumption, + l1ToL2MessageIndex, ); const computeArgs = () => @@ -628,6 +630,7 @@ describe('Private Execution test suite', () => { bridgedAmount, secretForL1ToL2MessageConsumption, crossChainMsgSender ?? preimage.sender.sender, + l1ToL2MessageIndex, ]); const mockOracles = async (updateHeader = true) => { diff --git a/yarn-project/simulator/src/public/public_db_sources.ts b/yarn-project/simulator/src/public/public_db_sources.ts index 0b3864d2ae7..553a483b4c9 100644 --- a/yarn-project/simulator/src/public/public_db_sources.ts +++ b/yarn-project/simulator/src/public/public_db_sources.ts @@ -203,24 +203,19 @@ export class WorldStateDB extends ContractsDataSourcePublicDB implements PublicS messageHash: Fr, secret: Fr, ): Promise> { - let nullifierIndex: bigint | undefined; - let messageIndex: bigint | undefined; - let startIndex = 0n; - - // We iterate over messages until we find one whose nullifier is not in the nullifier tree --> we need to check - // for nullifiers because messages can have duplicates. const timer = new Timer(); - do { - messageIndex = (await this.db.findLeafIndexAfter(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, messageHash, startIndex))!; - if (messageIndex === undefined) { - throw new Error(`No non-nullified L1 to L2 message found for message hash ${messageHash.toString()}`); - } - const messageNullifier = computeL1ToL2MessageNullifier(contractAddress, messageHash, secret, messageIndex); - nullifierIndex = await this.getNullifierIndex(messageNullifier); + const messageIndex = await this.db.findLeafIndex(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, messageHash); + if (messageIndex === undefined) { + throw new Error(`No L1 to L2 message found for message hash ${messageHash.toString()}`); + } + + const messageNullifier = computeL1ToL2MessageNullifier(contractAddress, messageHash, secret); + const nullifierIndex = await this.getNullifierIndex(messageNullifier); - startIndex = messageIndex + 1n; - } while (nullifierIndex !== undefined); + if (nullifierIndex !== undefined) { + throw new Error(`No non-nullified L1 to L2 message found for message hash ${messageHash.toString()}`); + } const siblingPath = await this.db.getSiblingPath( MerkleTreeId.L1_TO_L2_MESSAGE_TREE, diff --git a/yarn-project/simulator/src/test/utils.ts b/yarn-project/simulator/src/test/utils.ts index 69769b28c7b..363034d6686 100644 --- a/yarn-project/simulator/src/test/utils.ts +++ b/yarn-project/simulator/src/test/utils.ts @@ -1,5 +1,5 @@ import { L1Actor, L1ToL2Message, L2Actor } from '@aztec/circuit-types'; -import { type AztecAddress, EthAddress, type Fr } from '@aztec/circuits.js'; +import { type AztecAddress, EthAddress, Fr } from '@aztec/circuits.js'; import { computeSecretHash } from '@aztec/circuits.js/hash'; import { sha256ToField } from '@aztec/foundation/crypto'; @@ -16,6 +16,7 @@ export const buildL1ToL2Message = ( contentPreimage: Fr[], targetContract: AztecAddress, secret: Fr, + msgIndex: Fr | number, ) => { // Write the selector into a buffer. const selectorBuf = Buffer.from(selector, 'hex'); @@ -23,5 +24,11 @@ export const buildL1ToL2Message = ( const content = sha256ToField([selectorBuf, ...contentPreimage]); const secretHash = computeSecretHash(secret); - return new L1ToL2Message(new L1Actor(EthAddress.random(), 1), new L2Actor(targetContract, 1), content, secretHash); + return new L1ToL2Message( + new L1Actor(EthAddress.random(), 1), + new L2Actor(targetContract, 1), + content, + secretHash, + new Fr(msgIndex), + ); };