diff --git a/README.md b/README.md index b43176d3..46632777 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,56 @@ It is recommended to setup the ide to work with solidity development. In case of } ``` +## Error Signatures + +| Error | Signature | +| --------------------------- | ---------- | +| OnlyOwner() | 0x5fc483c5 | +| OnlyNominee() | 0x7c91ccdd | +| PacketNotProposed() | 0x1c936052 | +| InvalidPacketId() | 0xb21147c1 | +| InvalidProof() | 0x09bde339 | +| MessageAlreadyExecuted() | 0x7448c64c | +| NotExecutor() | 0xc32d1d76 | +| VerificationFailed() | 0x439cc0cd | +| ErrInSourceValidation() | 0xb39fb5d5 | +| LowGasLimit() | 0xd38edae0 | +| InvalidTransmitter() | 0x58a70a0a | +| SwitchboardExists() | 0x2dff8555 | +| InvalidConnection() | 0x63228f29 | +| NoPermit(bytes32) | 0x962f6333 | +| InvalidSigLength() | 0xd2453293 | +| ZeroAddress() | 0xd92e233d | +| InvalidTokenAddress() | 0x1eb00b06 | +| OnlySocket() | 0x503284dc | +| InvalidNonce() | 0x756688fe | +| MsgValueTooLow() | 0x508aaf00 | +| MsgValueTooHigh() | 0x5dffc92f | +| PayloadTooLarge() | 0x492f620d | +| InsufficientMsgValue() | 0x78f38f76 | +| InsufficientFees() | 0x8d53e553 | +| InvalidMsgValue() | 0x1841b4e1 | +| FeesTooHigh() | 0xc9034e18 | +| InvalidBatchSize() | 0x7862e959 | +| InsufficientMessageLength() | 0xbcfdc01d | +| InvalidPacketLength() | 0x5a375a8e | +| PacketLengthNotAllowed() | 0x83d360ad | +| WatcherFound() | 0xa080b558 | +| WatcherNotFound() | 0xa278e4ad | +| AlreadyAttested() | 0x35d90805 | +| InvalidRole() | 0xd954416a | +| InvalidRoot() | 0x504570e3 | +| UnequalArrayLengths() | 0x11e86f73 | +| InvalidCapacitorType() | 0x39354089 | +| OnlyExecutionManager() | 0x03c377c5 | +| InvalidCapacitorAddress() | 0x73d817fb | +| PlugDisconnected() | 0xe741bafb | +| NoPendingPacket() | 0xa9023923 | +| FeesNotEnough() | 0x217b529e | +| AlreadyInitialized() | 0x0dc149f0 | +| InvalidSender() | 0xddb5de5e | +| NoRootFound() | 0x849713b0 | + ## Contributing We really appreciate and value any form of contribution to Socket Data Layer Contracts. As a contributor, you are expected to fork this repository, work on your own fork and then submit pull requests. The PRs are expected to have a proper description to make reviewing easy. The code is expected to be properly linted and tested. diff --git a/contracts/socket/SocketBase.sol b/contracts/socket/SocketBase.sol index f7c6c2f4..2fd5aba0 100644 --- a/contracts/socket/SocketBase.sol +++ b/contracts/socket/SocketBase.sol @@ -18,8 +18,8 @@ abstract contract SocketBase is SocketConfig, AccessControlExtended { bytes32 public immutable version; // ChainSlug for this deployed socket instance uint32 public immutable chainSlug; - // Counter for messages giong outbound from current chain - uint64 public globalMessageCount = 0; + // Counter for messages going outbound from current chain + uint64 public globalMessageCount; /** * @dev constructs a new Socket contract instance. diff --git a/contracts/socket/SocketBatcher.sol b/contracts/socket/SocketBatcher.sol index 72cdc7d3..d226b12c 100644 --- a/contracts/socket/SocketBatcher.sol +++ b/contracts/socket/SocketBatcher.sol @@ -284,7 +284,7 @@ contract SocketBatcher is AccessControl { function proposeBatch( address socketAddress_, ProposeRequest[] calldata proposeRequests_ - ) external { + ) public { uint256 proposeRequestLength = proposeRequests_.length; for (uint256 index = 0; index < proposeRequestLength; ) { ISocket(socketAddress_).proposeForSwitchboard( @@ -307,7 +307,7 @@ contract SocketBatcher is AccessControl { function attestBatch( address switchboardAddress_, AttestRequest[] calldata attestRequests_ - ) external { + ) public { uint256 attestRequestLength = attestRequests_.length; for (uint256 index = 0; index < attestRequestLength; ) { FastSwitchboard(switchboardAddress_).attest( @@ -322,6 +322,26 @@ contract SocketBatcher is AccessControl { } } + /** + * @notice send a batch of propose, attest and execute transactions + * @param socketAddress_ address of socket + * @param switchboardAddress_ address of switchboard + * @param proposeRequests_ the list of requests with packets to be proposed + * @param attestRequests_ the list of requests with packets to be attested by switchboard + * @param executeRequests_ the list of requests with messages to be executed + */ + function sendBatch( + address socketAddress_, + address switchboardAddress_, + ProposeRequest[] calldata proposeRequests_, + AttestRequest[] calldata attestRequests_, + ExecuteRequest[] calldata executeRequests_ + ) external { + proposeBatch(socketAddress_, proposeRequests_); + attestBatch(switchboardAddress_, attestRequests_); + executeBatch(socketAddress_, executeRequests_); + } + /** * @notice trip a batch of Proposals * @param proposalTripRequests_ the list of requests for tripping proposals @@ -361,7 +381,7 @@ contract SocketBatcher is AccessControl { function executeBatch( address socketAddress_, ExecuteRequest[] calldata executeRequests_ - ) external payable { + ) public payable { uint256 executeRequestLength = executeRequests_.length; uint256 totalMsgValue = msg.value; for (uint256 index = 0; index < executeRequestLength; ) { diff --git a/contracts/socket/SocketDst.sol b/contracts/socket/SocketDst.sol index a84852b3..58517e76 100644 --- a/contracts/socket/SocketDst.sol +++ b/contracts/socket/SocketDst.sol @@ -119,7 +119,7 @@ abstract contract SocketDst is SocketBase { (address transmitter, bool isTransmitter) = transmitManager__ .checkTransmitter( - uint32(_decodeChainSlug(packetId_)), + _decodeChainSlug(packetId_), keccak256(abi.encode(version, chainSlug, packetId_, root_)), signature_ ); @@ -206,6 +206,12 @@ abstract contract SocketDst is SocketBase { .isExecutor(packedMessage, executionDetails_.signature); if (!isValidExecutor) revert NotExecutor(); + // finally make sure executor params were respected by the executor + executionManager__.verifyParams( + messageDetails_.executionParams, + msg.value + ); + // verify message was part of the packet and // authenticated by respective switchboard _verify( @@ -215,8 +221,7 @@ abstract contract SocketDst is SocketBase { packedMessage, packetRoot, plugConfig, - executionDetails_.decapacitorProof, - messageDetails_.executionParams + executionDetails_.decapacitorProof ); // execute message @@ -240,8 +245,7 @@ abstract contract SocketDst is SocketBase { bytes32 packedMessage_, bytes32 packetRoot_, PlugConfig memory plugConfig_, - bytes memory decapacitorProof_, - bytes32 executionParams_ + bytes memory decapacitorProof_ ) internal { // NOTE: is the the first un-trusted call in the system, another one is Plug.inbound if ( @@ -249,7 +253,7 @@ abstract contract SocketDst is SocketBase { packetRoot_, packetId_, proposalCount_, - uint32(remoteChainSlug_), + remoteChainSlug_, rootProposedAt[packetId_][proposalCount_][ address(plugConfig_.inboundSwitchboard__) ] @@ -263,9 +267,6 @@ abstract contract SocketDst is SocketBase { decapacitorProof_ ) ) revert InvalidProof(); - - // finally make sure executor params were respected by the executor - executionManager__.verifyParams(executionParams_, msg.value); } /** diff --git a/contracts/switchboard/default-switchboards/FastSwitchboard.sol b/contracts/switchboard/default-switchboards/FastSwitchboard.sol index 95b9aba4..cb6e6236 100644 --- a/contracts/switchboard/default-switchboards/FastSwitchboard.sol +++ b/contracts/switchboard/default-switchboards/FastSwitchboard.sol @@ -107,7 +107,13 @@ contract FastSwitchboard is SwitchboardBase { address watcher = signatureVerifier__.recoverSigner( keccak256( - abi.encode(address(this), chainSlug, packetId_, proposalCount_) + abi.encode( + address(this), + chainSlug, + packetId_, + proposalCount_, + root_ + ) ), signature_ ); diff --git a/deployments/dev_addresses.json b/deployments/dev_addresses.json index 2f7364ea..728a4d40 100644 --- a/deployments/dev_addresses.json +++ b/deployments/dev_addresses.json @@ -97,8 +97,8 @@ }, "FastSwitchboard": "0xd59d596B7C7cB4593F61bbE4A82C1E943C64558D", "OptimisticSwitchboard": "0x8aDA06F570A2FeA15De8e00547f4D090056D95c8", - "SocketBatcher": "0x4026aec0321cE907e0592396282eF56e6a6FB6f1", - "Counter": "0x97d796C3B7A4D7f31408B798452da6ed835358F5" + "Counter": "0x97d796C3B7A4D7f31408B798452da6ed835358F5", + "SocketBatcher": "0xe8E2409a355328CE99eAE1De3f0583136c909f42" }, "420": { "SignatureVerifier": "0x1B458A51D91f179DBbBb6C43d7962Ba17CC84628", @@ -188,8 +188,8 @@ }, "FastSwitchboard": "0xE350007007b84483CC5bd764e2220187Ad477F2D", "OptimisticSwitchboard": "0xc98a5dB97F7887302877C900f2F8d86f110A7F5d", - "SocketBatcher": "0x998C12F1F4C7c2C8D65aF394651aEB2f1397f145", - "Counter": "0xE89A18D6eB897236BEd71e5975940DA603d80084" + "Counter": "0xE89A18D6eB897236BEd71e5975940DA603d80084", + "SocketBatcher": "0xEe391E4a214E6312B42BF5FAcA9Bd214e8ebd206" }, "901": { "SignatureVerifier": "0x47140353947Bc127c9cf36fabd61112C8Fb8db2A", @@ -365,8 +365,8 @@ }, "FastSwitchboard": "0x27513Ed43490B6e0801e724ff1b1637be657447E", "OptimisticSwitchboard": "0xb1139c246ef3cc8F968ea2C61de813fEe1dB8758", - "SocketBatcher": "0x78559acfc3A2aa11142ECe5e16691D06FbB9E8e0", - "Counter": "0x154425b14538ca5dCE77357133CEA76DDc6650EA" + "Counter": "0x154425b14538ca5dCE77357133CEA76DDc6650EA", + "SocketBatcher": "0x50E32b5595d31dFBA53E189513718412EA42c804" }, "421613": { "SignatureVerifier": "0x353287093b3C9f5842396a6fc9704766aA5409c7", @@ -456,8 +456,8 @@ }, "FastSwitchboard": "0xA4C366be8e0BFE59011d26f66E683B1D290DcF45", "OptimisticSwitchboard": "0x0FE19867199d6c96f2e1FE022c38ebb4292543F8", - "SocketBatcher": "0xF14c3c5fFEBA923b2239959cbA4d7EdE43D0ADb4", - "Counter": "0xc8dD08F4f7392b4577f4D6499233792d163DA5Ee" + "Counter": "0xc8dD08F4f7392b4577f4D6499233792d163DA5Ee", + "SocketBatcher": "0x93D246a24B032C101c0858354dC5FbB7A113d500" }, "11155111": { "SignatureVerifier": "0x6f6f4C24bE59c42F524b133d623170376b8Eed64", @@ -542,8 +542,8 @@ }, "FastSwitchboard": "0xaDDaC77B80C990A87906C4273902be5EC7f25B42", "OptimisticSwitchboard": "0xaE51c633fb58df92FCcBb0694EA22E060d29F5F7", - "SocketBatcher": "0xA53185E856916228505372F1E3333BbbB4AC2063", - "Counter": "0x918ECe20236B9d8C2AA1742540246D6F9a9eB081" + "Counter": "0x918ECe20236B9d8C2AA1742540246D6F9a9eB081", + "SocketBatcher": "0xec7A753bc2D4Fab9Fcc459B7205d2FCa96b6c622" }, "11155112": { "SignatureVerifier": "0x1d7C105919E4D75e30581D4a5B3724F8D6159251", @@ -628,7 +628,7 @@ }, "FastSwitchboard": "0x85778A90ec60249f10d7AEe06484ffC290d5Efd8", "OptimisticSwitchboard": "0xB9EDe9aaEaA40e35033ABBC872D141950d08cc4d", - "SocketBatcher": "0xEDF6dB2f3BC8deE014762e0141EE4CA19d685dBd", - "Counter": "0xAE9bE0eA6DD7B53C88665Cb2EFf7d40dFD51cf91" + "Counter": "0xAE9bE0eA6DD7B53C88665Cb2EFf7d40dFD51cf91", + "SocketBatcher": "0x06845FE0597cD0027df2d62B152B17E5eBaE22DB" } } diff --git a/deployments/dev_verification.json b/deployments/dev_verification.json index 4e9c7105..48ab33a1 100644 --- a/deployments/dev_verification.json +++ b/deployments/dev_verification.json @@ -1,5 +1,11 @@ { "5": [ + [ + "0xe8E2409a355328CE99eAE1De3f0583136c909f42", + "SocketBatcher", + "contracts/socket/SocketBatcher.sol", + ["0x752B38FA38F53dF7fa60e6113CFd9094b7e040Aa"] + ], [ "0x97d796C3B7A4D7f31408B798452da6ed835358F5", "Counter", @@ -130,6 +136,12 @@ ] ], "420": [ + [ + "0xEe391E4a214E6312B42BF5FAcA9Bd214e8ebd206", + "SocketBatcher", + "contracts/socket/SocketBatcher.sol", + ["0x752B38FA38F53dF7fa60e6113CFd9094b7e040Aa"] + ], [ "0xE89A18D6eB897236BEd71e5975940DA603d80084", "Counter", @@ -337,6 +349,12 @@ ] ], "80001": [ + [ + "0x50E32b5595d31dFBA53E189513718412EA42c804", + "SocketBatcher", + "contracts/socket/SocketBatcher.sol", + ["0x752B38FA38F53dF7fa60e6113CFd9094b7e040Aa"] + ], [ "0x154425b14538ca5dCE77357133CEA76DDc6650EA", "Counter", @@ -439,6 +457,12 @@ ] ], "421613": [ + [ + "0x93D246a24B032C101c0858354dC5FbB7A113d500", + "SocketBatcher", + "contracts/socket/SocketBatcher.sol", + ["0x752B38FA38F53dF7fa60e6113CFd9094b7e040Aa"] + ], [ "0xc8dD08F4f7392b4577f4D6499233792d163DA5Ee", "Counter", @@ -540,6 +564,12 @@ ] ], "11155111": [ + [ + "0xec7A753bc2D4Fab9Fcc459B7205d2FCa96b6c622", + "SocketBatcher", + "contracts/socket/SocketBatcher.sol", + ["0x752B38FA38F53dF7fa60e6113CFd9094b7e040Aa"] + ], [ "0x918ECe20236B9d8C2AA1742540246D6F9a9eB081", "Counter", @@ -630,6 +660,12 @@ ] ], "11155112": [ + [ + "0x06845FE0597cD0027df2d62B152B17E5eBaE22DB", + "SocketBatcher", + "contracts/socket/SocketBatcher.sol", + ["0x752B38FA38F53dF7fa60e6113CFd9094b7e040Aa"] + ], [ "0xAE9bE0eA6DD7B53C88665Cb2EFf7d40dFD51cf91", "Counter", diff --git a/package.json b/package.json index 2143aa18..0f7dc5aa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@socket.tech/dl-core", "license": "UNLICENSED", - "version": "2.4.1", + "version": "2.4.0-test.8", "description": "Smart contracts for socket data layer.", "main": "./dist/src/index.js", "types": "./dist/src/index.d.ts", diff --git a/test/Setup.t.sol b/test/Setup.t.sol index 55712024..90241d4f 100644 --- a/test/Setup.t.sol +++ b/test/Setup.t.sol @@ -760,7 +760,13 @@ contract Setup is Test { uint256 watcherPrivateKey_ ) internal { bytes32 digest = keccak256( - abi.encode(switchboardAddress, dstSlug, packetId_, proposalCount_) + abi.encode( + switchboardAddress, + dstSlug, + packetId_, + proposalCount_, + root_ + ) ); // generate attest-signature diff --git a/test/socket/SocketBatcher.t.sol b/test/socket/SocketBatcher.t.sol new file mode 100644 index 00000000..e92baaa3 --- /dev/null +++ b/test/socket/SocketBatcher.t.sol @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity 0.8.19; + +import "../Setup.t.sol"; +import "../../contracts/socket/SocketBatcher.sol"; +import "../../contracts/examples/Counter.sol"; + +contract SocketBatcherTest is Setup { + Counter srcCounter__; + Counter dstCounter__; + SocketBatcher batcher__; + + uint256 addAmount = 100; + uint256 subAmount = 40; + bool isFast = true; + uint256 index = isFast ? 0 : 1; + + bytes32[] roots; + + event ExecutionSuccess(bytes32 msgId); + event ExecutionFailed(bytes32 msgId, string result); + event ExecutionFailedBytes(bytes32 msgId, bytes result); + event PacketVerifiedAndSealed( + address indexed transmitter, + bytes32 indexed packetId, + bytes32 root, + bytes signature + ); + + function setUp() external { + uint256[] memory transmitterPrivateKeys = new uint256[](1); + transmitterPrivateKeys[0] = _transmitterPrivateKey; + + _dualChainSetup(transmitterPrivateKeys); + _deployPlugContracts(); + + _configPlugContracts(index); + batcher__ = new SocketBatcher(address(this)); + } + + function testSendBatch() external { + uint256 amount = 100; + bytes memory payload = abi.encode( + keccak256("OP_ADD"), + amount, + _plugOwner + ); + bytes memory proof = abi.encode(0); + + address capacitor = address(_a.configs__[index].capacitor__); + uint256 executionFee; + { + (uint256 switchboardFees, uint256 verificationFee) = _a + .configs__[index] + .switchboard__ + .getMinFees(_b.chainSlug); + + uint256 socketFees; + (executionFee, socketFees) = _a + .executionManager__ + .getExecutionTransmissionMinFees( + _minMsgGasLimit, + 100, + bytes32(0), + _transmissionParams, + _b.chainSlug, + address(_a.transmitManager__) + ); + + uint256 value = switchboardFees + + socketFees + + verificationFee + + executionFee; + + // executionFees to be recomputed which is totalValue - (socketFees + switchboardFees) + // verificationOverheadFees also should go to Executor, hence we do the additional computation below + executionFee = verificationFee + executionFee; + + hoax(_plugOwner); + srcCounter__.remoteAddOperation{value: value}( + _b.chainSlug, + amount, + _minMsgGasLimit, + bytes32(0), + bytes32(0) + ); + } + + bytes32 packetId; + bytes32 root; + { + bytes memory sig_; + (root, packetId, sig_) = _getLatestSignature( + capacitor, + _a.chainSlug, + _b.chainSlug + ); + + _sealOnSrc(_a, capacitor, DEFAULT_BATCH_LENGTH, sig_); + + SocketBatcher.ProposeRequest memory proposeRequest = SocketBatcher + .ProposeRequest({ + packetId: packetId, + root: root, + switchboard: address(_b.configs__[0].switchboard__), + signature: sig_ + }); + SocketBatcher.ProposeRequest[] + memory proposeRequests = new SocketBatcher.ProposeRequest[](1); + proposeRequests[0] = proposeRequest; + + bytes32 digest = keccak256( + abi.encode( + address(_b.configs__[0].switchboard__), + _b.chainSlug, + packetId, + 0, + root + ) + ); + + // generate attest-signature + bytes memory attestSignature = _createSignature( + digest, + _watcherPrivateKey + ); + + SocketBatcher.AttestRequest memory attestRequest = SocketBatcher + .AttestRequest({ + packetId: packetId, + proposalCount: 0, + root: root, + signature: attestSignature + }); + + SocketBatcher.AttestRequest[] + memory attestRequests = new SocketBatcher.AttestRequest[](1); + attestRequests[0] = attestRequest; + SocketBatcher.ExecuteRequest[] memory executeRequests; + + batcher__.sendBatch( + address(_b.socket__), + address(_b.configs__[0].switchboard__), + proposeRequests, + attestRequests, + executeRequests + ); + } + + vm.expectEmit(true, false, false, false); + emit ExecutionSuccess( + _packMessageId(_a.chainSlug, address(dstCounter__), 0) + ); + _executePayloadOnDst( + _b, + ExecutePayloadOnDstParams( + packetId, + 0, + _packMessageId(_a.chainSlug, address(dstCounter__), 0), + _minMsgGasLimit, + bytes32(0), + executionFee, + root, + payload, + proof + ) + ); + + assertEq(dstCounter__.counter(), amount); + assertEq(srcCounter__.counter(), 0); + assertTrue( + _b.socket__.messageExecuted( + _packMessageId(_a.chainSlug, address(dstCounter__), 0) + ) + ); + } + + function _deployPlugContracts() internal { + vm.startPrank(_plugOwner); + + // deploy counters + srcCounter__ = new Counter(address(_a.socket__)); + dstCounter__ = new Counter(address(_b.socket__)); + + vm.stopPrank(); + } + + function _configPlugContracts(uint256 socketConfigIndex) internal { + hoax(_plugOwner); + srcCounter__.setSocketConfig( + _b.chainSlug, + address(dstCounter__), + address(_a.configs__[socketConfigIndex].switchboard__) + ); + + hoax(_plugOwner); + dstCounter__.setSocketConfig( + _a.chainSlug, + address(srcCounter__), + address(_b.configs__[socketConfigIndex].switchboard__) + ); + } +}