diff --git a/changelog.md b/changelog.md index b9991f0bdf..74d014ee4b 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,43 @@ --- # Ten Testnet Change Log +# July 2024-07-03 (v0.25.0) +* This is an L2 deployment release meaning state will be lost in order to upgrade the network. Any dApps on the + network will need to be redeployed. The release predominantly addresses performance improvements for large numbers + of transactions in the network, and for event log queries. +* A full list of the PRs merged in this release is as below; + * `ef0bf45e` Fix for xchain messages (#1973) + * `0dc7d5c5` Fix for integration test (#1971) + * `186bc2c3` Host db performance fixes (#1969) + * `c0ea9b43` Bundle submission rework (#1966) + * `0b01bfd1` Assets fix and clear console warnings (#1959) + * `ddff74a5` Update db schema (#1967) + * `e1c91243` Rework event logs and transaction database schema (#1961) + * `291a698d` Add db query tool (#1963) + * `b1af061b` Initiate bridge deployment (#1965) + * `95f35150` Initiate bridge deployment (#1960) + * `447ff489` Placeholder commit for new gh action workflow (#1964) + * `9d6e7747` Fix for startup (#1962) + * `b537d2e6` Add indexed events (#1954) + * `c6e5a48f` Adds log constraints to docker containers not instantiated by the go client (#1956) + * `85e6cd0d` Ignore empty l1 head status from enclave (#1955) + * `a9485fef` Fixes for ethereum bridge and json representation of batch header (#1953) + * `e6453082` Deploy separate ten gateway for dexynth (#1949) + * `b8935c31` Network tests: util func for l1 transfers (#1948) + * `0f3d42bf` Network tests: start gateway synchronously to fix race (#1947) + * `9cee2fd4` Increase max message size grpc config (#1946) + * `c0bf4086` Small mem pool fixes (#1943) + * `b335bd1e` Avoid spamming stuck l1 transactions (#1941) + * `99a8aeee` Fix panic on uninitialised mem pool (#1940) + * `1daffac0` Replace health-check db query (#1938) + * `a880b169` Hardcode response to client version request (#1937) + * `5780140e` Check conversions (#1936) + * `b97d0b93` Fix flakyness errors (#1927) + * `6e432a1a` Add net_version support and test (#1934) + * `4f39832e` Fixing log spam (#1931) + * `f8b86b52` Fix log spam (#1929) + * `762dfef9` Fix for interval (#1926) + # May 2024-05-20 (v0.24.0) * This is an L2 deployment release meaning state will be lost in order to upgrade the network. Any dApps on the network will need to be redeployed. High level changes made in the release include; diff --git a/contracts/generated/ManagementContract/ManagementContract.go b/contracts/generated/ManagementContract/ManagementContract.go index 56c3665803..c1b9fdc661 100644 --- a/contracts/generated/ManagementContract/ManagementContract.go +++ b/contracts/generated/ManagementContract/ManagementContract.go @@ -61,8 +61,8 @@ type StructsValueTransferMessage struct { // ManagementContractMetaData contains all meta data concerning the ManagementContract contract. var ManagementContractMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ECDSAInvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"ECDSAInvalidSignatureLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"ECDSAInvalidSignatureS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitialization\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInitializing\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"OwnableInvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"OwnableUnauthorizedAccount\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAddress\",\"type\":\"address\"}],\"name\":\"ImportantContractAddressUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"version\",\"type\":\"uint64\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"messageBusAddress\",\"type\":\"address\"}],\"name\":\"LogManagementContractCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"rollupHash\",\"type\":\"bytes32\"}],\"name\":\"RollupAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"enclaveID\",\"type\":\"address\"}],\"name\":\"SequencerEnclaveGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"enclaveID\",\"type\":\"address\"}],\"name\":\"SequencerEnclaveRevoked\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"Hash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"Signature\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"LastSequenceNumber\",\"type\":\"uint256\"}],\"internalType\":\"structStructs.MetaRollup\",\"name\":\"r\",\"type\":\"tuple\"},{\"internalType\":\"string\",\"name\":\"_rollupData\",\"type\":\"string\"},{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequence\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"topic\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"consistencyLevel\",\"type\":\"uint8\"}],\"internalType\":\"structStructs.CrossChainMessage[]\",\"name\":\"messages\",\"type\":\"tuple[]\"}],\"internalType\":\"structStructs.HeaderCrossChainData\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"AddRollup\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"Attested\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"sequence\",\"type\":\"uint64\"}],\"internalType\":\"structStructs.ValueTransferMessage\",\"name\":\"_msg\",\"type\":\"tuple\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"ExtractNativeValue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GetImportantContractKeys\",\"outputs\":[{\"internalType\":\"string[]\",\"name\":\"\",\"type\":\"string[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"rollupHash\",\"type\":\"bytes32\"}],\"name\":\"GetRollupByHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"Hash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"Signature\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"LastSequenceNumber\",\"type\":\"uint256\"}],\"internalType\":\"structStructs.MetaRollup\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"GrantSequencerEnclave\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_enclaveID\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_initSecret\",\"type\":\"bytes\"},{\"internalType\":\"string\",\"name\":\"_genesisAttestation\",\"type\":\"string\"}],\"name\":\"InitializeNetworkSecret\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"IsSequencerEnclave\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"IsWithdrawalAvailable\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"requestReport\",\"type\":\"string\"}],\"name\":\"RequestNetworkSecret\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"attesterID\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"requesterID\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"attesterSig\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"responseSecret\",\"type\":\"bytes\"},{\"internalType\":\"bool\",\"name\":\"verifyAttester\",\"type\":\"bool\"}],\"name\":\"RespondNetworkSecret\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"RetrieveAllBridgeFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"RevokeSequencerEnclave\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"newAddress\",\"type\":\"address\"}],\"name\":\"SetImportantContractAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_lastBatchHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNum\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"crossChainHashes\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"addCrossChainMessagesRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"name\":\"importantContractAddresses\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"importantContractKeys\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isWithdrawalSpent\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastBatchHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastBatchSeqNo\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"merkleMessageBus\",\"outputs\":[{\"internalType\":\"contractIMerkleTreeMessageBus\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"messageBus\",\"outputs\":[{\"internalType\":\"contractIMessageBus\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b5061001a3361001f565b610090565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b61413e80620000a06000396000f3fe60806040523480156200001157600080fd5b5060043610620001d05760003560e01c8063715018a61162000101578063a1a227fa11620000a3578063db5d91b1116200007a578063db5d91b11462000446578063e34fbfc81462000475578063e874eb20146200048a578063f2fde38b146200049e57600080fd5b8063a1a227fa146200040a578063a52f433c146200041e578063d4fab887146200042f57600080fd5b80638236a7ba11620000d85780638236a7ba146200038c5780638da5cb5b14620003b357806398077e8614620003e457600080fd5b8063715018a6146200036e5780637281099614620003785780638129fc1c146200038257600080fd5b806343348b2f11620001775780635371a216116200014e5780635371a216146200031057806368e1038314620003275780636a30d26c146200033e5780636b9707d6146200035757600080fd5b806343348b2f14620002c0578063440c953b14620002ef5780634766573814620002f957600080fd5b80632f0cb9e311620001ac5780632f0cb9e314620002225780633e60a22f14620002595780633f3f6a1814620002a957600080fd5b80620ddd2714620001d557806303e72e4814620001f25780632c77c81f146200020b575b600080fd5b620001df600a5481565b6040519081526020015b60405180910390f35b620002096200020336600462001c1b565b620004b5565b005b620002096200021c36600462001cbb565b620005c8565b620002486200023336600462001d5e565b60096020526000908152604090205460ff1681565b6040519015158152602001620001e9565b620002906200026a36600462001d78565b80516020818301810180516003825292820191909301209152546001600160a01b031681565b6040516001600160a01b039091168152602001620001e9565b62000209620002ba36600462001db9565b6200073c565b62000248620002d136600462001ed3565b6001600160a01b031660009081526020819052604090205460ff1690565b620001df60055481565b620002096200030a36600462001ed3565b620009c2565b620002096200032136600462001ef8565b62000a92565b620002096200033836600462001f92565b62000c9b565b6200034862000d88565b604051620001e9919062002070565b620002096200036836600462001ed3565b62000e6b565b6200020962000f31565b6200020962000f49565b6200020962000fcd565b620003a36200039d36600462001d5e565b620011ac565b604051620001e9929190620020d6565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031662000290565b620003fb620003f536600462001d5e565b6200129c565b604051620001e991906200211c565b60075462000290906001600160a01b031681565b600454610100900460ff1662000248565b620002096200044036600462002131565b62001351565b620002486200045736600462001ed3565b6001600160a01b031660009081526001602052604090205460ff1690565b6200020962000486366004620021dc565b5050565b60085462000290906001600160a01b031681565b62000209620004af36600462001ed3565b620014e4565b620004bf62001541565b60006001600160a01b0316600383604051620004dc919062002222565b908152604051908190036020019020546001600160a01b0316036200053b57600280546001810182556000919091527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01620005398382620022cf565b505b806003836040516200054e919062002222565b90815260405190819003602001812080546001600160a01b039390931673ffffffffffffffffffffffffffffffffffffffff19909316929092179091557f17b2f9f5748931099ffee882b5b64f4a560b5c55da9b4f4e396dae3bb9f98cb590620005bc90849084906200239c565b60405180910390a15050565b60006200061a8535620005df6020880188620023c9565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620015b892505050565b6001600160a01b03811660009081526020819052604090205490915060ff166200068b5760405162461bcd60e51b815260206004820152601660248201527f656e636c6176654944206e6f742061747465737465640000000000000000000060448201526064015b60405180910390fd5b6001600160a01b03811660009081526001602052604090205460ff16620006f55760405162461bcd60e51b815260206004820152601960248201527f656e636c6176654944206e6f7420612073657175656e63657200000000000000604482015260640162000682565b6200070085620015e8565b604051853581527fd6555bff8670bd3008dc064c30bb56d6ac7cb14ae801e36146fe4e7c6a504a58906020015b60405180910390a15050505050565b620007498460ff62002429565b4311156200079a5760405162461bcd60e51b815260206004820152601560248201527f426c6f636b2062696e64696e6720746f6f206f6c640000000000000000000000604482015260640162000682565b848440146200080157620007ae4362001620565b620007b98662001620565b620007c5864062001620565b604051602001620007d9939291906200243f565b60408051601f198184030181529082905262461bcd60e51b825262000682916004016200211c565b60006200087387878787604051602001620008209493929190620024e4565b6040516020818303038152906040528051906020012084848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620015b892505050565b6001600160a01b03811660009081526020819052604090205490915060ff16620008e05760405162461bcd60e51b815260206004820152601660248201527f656e636c6176654944206e6f7420617474657374656400000000000000000000604482015260640162000682565b600a87905560005b8451811015620009b85760085485516001600160a01b039091169063b6aed0cb908790849081106200091e576200091e62002560565b6020026020010151620009319062002576565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526004810191909152426024820152604401600060405180830381600087803b1580156200098957600080fd5b505af11580156200099e573d6000803e3d6000fd5b505050508080620009af906200259b565b915050620008e8565b5050505050505050565b620009cc62001541565b6001600160a01b03811660009081526020819052604090205460ff1662000a365760405162461bcd60e51b815260206004820152601660248201527f656e636c6176654944206e6f7420617474657374656400000000000000000000604482015260640162000682565b6001600160a01b038116600081815260016020818152604092839020805460ff191690921790915590519182527ffe64c7181f0fc60e300dc02cca368cdfa94d7ca45902de3b9a9d80070e76093691015b60405180910390a150565b6008546040517fb201246f0000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063b201246f9062000ae390879087908790879060040162002617565b60006040518083038186803b15801562000afc57600080fd5b505afa15801562000b11573d6000803e3d6000fd5b5050505060008460405160200162000b2a91906200267f565b60408051601f1981840301815291815281516020928301206000818152600990935291205490915060ff161562000ba45760405162461bcd60e51b815260206004820152601860248201527f7769746864726177616c20616c7265616479207370656e740000000000000000604482015260640162000682565b6001600960008760405160200162000bbd91906200267f565b60408051808303601f190181529181528151602092830120835282820193909352908201600020805460ff1916931515939093179092556007546001600160a01b0316916399a3ad219162000c189190890190890162001ed3565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526001600160a01b0390921660048301528801356024820152604401600060405180830381600087803b15801562000c7b57600080fd5b505af115801562000c90573d6000803e3d6000fd5b505050505050505050565b60045460ff161562000d165760405162461bcd60e51b815260206004820152602260248201527f6e6574776f726b2073656372657420616c726561647920696e697469616c697a60448201527f6564000000000000000000000000000000000000000000000000000000000000606482015260840162000682565b60048054600160ff1991821681179092556001600160a01b038716600081815260208181526040808320805486168717905585825291829020805490941690941790925590519081527ffe64c7181f0fc60e300dc02cca368cdfa94d7ca45902de3b9a9d80070e76093691016200072d565b60606002805480602002602001604051908101604052809291908181526020016000905b8282101562000e6257838290600052602060002001805462000dce9062002240565b80601f016020809104026020016040519081016040528092919081815260200182805462000dfc9062002240565b801562000e4d5780601f1062000e215761010080835404028352916020019162000e4d565b820191906000526020600020905b81548152906001019060200180831162000e2f57829003601f168201915b50505050508152602001906001019062000dac565b50505050905090565b62000e7562001541565b6001600160a01b03811660009081526001602052604090205460ff1662000edf5760405162461bcd60e51b815260206004820152601960248201527f656e636c6176654944206e6f7420612073657175656e63657200000000000000604482015260640162000682565b6001600160a01b038116600081815260016020908152604091829020805460ff1916905590519182527f0f279980343c7ca542fde9fa5396555068efb5cd560d9cf9c191aa2911079b47910162000a87565b62000f3b62001541565b62000f476000620016c7565b565b62000f5362001541565b6007546040517f36d2da900000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b03909116906336d2da9090602401600060405180830381600087803b15801562000fb257600080fd5b505af115801562000fc7573d6000803e3d6000fd5b50505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff16600081158015620010195750825b905060008267ffffffffffffffff166001148015620010375750303b155b90508115801562001046575080155b156200107e576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845467ffffffffffffffff191660011785558315620010b357845468ff00000000000000001916680100000000000000001785555b620010be3362001745565b6000600555604051620010d19062001b2f565b604051809103906000f080158015620010ee573d6000803e3d6000fd5b506008805473ffffffffffffffffffffffffffffffffffffffff199081166001600160a01b039390931692831790915560078054909116821790556040519081527fbd726cf82ac9c3260b1495107182e336e0654b25c10915648c0cc15b2bb72cbf9060200160405180910390a18315620011a557845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2906020016200072d565b5050505050565b604080516060808201835260008083526020808401839052838501829052858252600681528482208551938401909552845483526001850180549295869493909284019190620011fc9062002240565b80601f01602080910402602001604051908101604052809291908181526020018280546200122a9062002240565b80156200127b5780601f106200124f576101008083540402835291602001916200127b565b820191906000526020600020905b8154815290600101906020018083116200125d57829003601f168201915b50505091835250506002919091015460209091015280519094149492505050565b60028181548110620012ad57600080fd5b906000526020600020016000915090508054620012ca9062002240565b80601f0160208091040260200160405190810160405280929190818152602001828054620012f89062002240565b8015620013495780601f106200131d5761010080835404028352916020019162001349565b820191906000526020600020905b8154815290600101906020018083116200132b57829003601f168201915b505050505081565b6001600160a01b03851660009081526020819052604090205460ff1680620013e25760405162461bcd60e51b815260206004820152602360248201527f726573706f6e64696e67206174746573746572206973206e6f7420617474657360448201527f7465640000000000000000000000000000000000000000000000000000000000606482015260840162000682565b8115620014b95760006200141b87878660405160200162001406939291906200268f565b6040516020818303038152906040526200175a565b905060006200142b8287620015b8565b9050876001600160a01b0316816001600160a01b031614620014b65760405162461bcd60e51b815260206004820152602c60248201527f63616c63756c61746564206164647265737320616e642061747465737465724960448201527f4420646f6e74206d617463680000000000000000000000000000000000000000606482015260840162000682565b50505b5050506001600160a01b039091166000908152602081905260409020805460ff191660011790555050565b620014ee62001541565b6001600160a01b03811662001533576040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526000600482015260240162000682565b6200153e81620016c7565b50565b33620015747f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03161462000f47576040517f118cdaa700000000000000000000000000000000000000000000000000000000815233600482015260240162000682565b600080600080620015ca868662001799565b925092509250620015dc8282620017ea565b50909150505b92915050565b803560009081526006602052604090208190620016068282620026d7565b5050600554604082013511156200153e5760400135600555565b606060006200162f8362001900565b600101905060008167ffffffffffffffff81111562001652576200165262001b3d565b6040519080825280601f01601f1916602001820160405280156200167d576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a85049450846200168757509392505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300805473ffffffffffffffffffffffffffffffffffffffff1981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6200174f620019e9565b6200153e8162001a51565b600062001768825162001620565b826040516020016200177c929190620027e7565b604051602081830303815290604052805190602001209050919050565b60008060008351604103620017d75760208401516040850151606086015160001a620017c88882858562001a5b565b955095509550505050620017e3565b50508151600091506002905b9250925092565b600082600381111562001801576200180162002846565b036200180b575050565b600182600381111562001822576200182262002846565b036200185a576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600282600381111562001871576200187162002846565b03620018ad576040517ffce698f70000000000000000000000000000000000000000000000000000000081526004810182905260240162000682565b6003826003811115620018c457620018c462002846565b0362000486576040517fd78bce0c0000000000000000000000000000000000000000000000000000000081526004810182905260240162000682565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083106200194a577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef8100000000831062001977576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106200199657662386f26fc10000830492506010015b6305f5e1008310620019af576305f5e100830492506008015b6127108310620019c457612710830492506004015b60648310620019d7576064830492506002015b600a8310620015e25760010192915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff1662000f47576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b620014ee620019e9565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084111562001a98575060009150600390508262001b25565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa15801562001aed573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811662001b1b5750600092506001915082905062001b25565b9250600091508190505b9450945094915050565b6118ac806200285d83390190565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171562001b7f5762001b7f62001b3d565b604052919050565b600082601f83011262001b9957600080fd5b813567ffffffffffffffff81111562001bb65762001bb662001b3d565b62001bcb601f8201601f191660200162001b53565b81815284602083860101111562001be157600080fd5b816020850160208301376000918101602001919091529392505050565b80356001600160a01b038116811462001c1657600080fd5b919050565b6000806040838503121562001c2f57600080fd5b823567ffffffffffffffff81111562001c4757600080fd5b62001c558582860162001b87565b92505062001c666020840162001bfe565b90509250929050565b60008083601f84011262001c8257600080fd5b50813567ffffffffffffffff81111562001c9b57600080fd5b60208301915083602082850101111562001cb457600080fd5b9250929050565b6000806000806060858703121562001cd257600080fd5b843567ffffffffffffffff8082111562001ceb57600080fd5b908601906060828903121562001d0057600080fd5b9094506020860135908082111562001d1757600080fd5b62001d258883890162001c6f565b9095509350604087013591508082111562001d3f57600080fd5b5085016020818803121562001d5357600080fd5b939692955090935050565b60006020828403121562001d7157600080fd5b5035919050565b60006020828403121562001d8b57600080fd5b813567ffffffffffffffff81111562001da357600080fd5b62001db18482850162001b87565b949350505050565b60008060008060008060a0878903121562001dd357600080fd5b86359550602080880135955060408801359450606088013567ffffffffffffffff8082111562001e0257600080fd5b818a0191508a601f83011262001e1757600080fd5b81358181111562001e2c5762001e2c62001b3d565b8060051b62001e3d85820162001b53565b918252838101850191858101908e84111562001e5857600080fd5b86860192505b8383101562001e9757848335111562001e7657600080fd5b62001e878f88853589010162001b87565b8252918601919086019062001e5e565b985050505060808a013592508083111562001eb157600080fd5b505062001ec189828a0162001c6f565b979a9699509497509295939492505050565b60006020828403121562001ee657600080fd5b62001ef18262001bfe565b9392505050565b60008060008084860360c081121562001f1057600080fd5b608081121562001f1f57600080fd5b50849350608085013567ffffffffffffffff8082111562001f3f57600080fd5b818701915087601f83011262001f5457600080fd5b81358181111562001f6457600080fd5b8860208260051b850101111562001f7a57600080fd5b95986020929092019750949560a00135945092505050565b60008060008060006060868803121562001fab57600080fd5b62001fb68662001bfe565b9450602086013567ffffffffffffffff8082111562001fd457600080fd5b62001fe289838a0162001c6f565b9096509450604088013591508082111562001ffc57600080fd5b506200200b8882890162001c6f565b969995985093965092949392505050565b60005b83811015620020395781810151838201526020016200201f565b50506000910152565b600081518084526200205c8160208601602086016200201c565b601f01601f19169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015620020c957603f19888603018452620020b685835162002042565b9450928501929085019060010162002097565b5092979650505050505050565b82151581526040602082015281516040820152600060208301516060808401526200210560a084018262002042565b905060408401516080840152809150509392505050565b60208152600062001ef1602083018462002042565b600080600080600060a086880312156200214a57600080fd5b620021558662001bfe565b9450620021656020870162001bfe565b9350604086013567ffffffffffffffff808211156200218357600080fd5b6200219189838a0162001b87565b94506060880135915080821115620021a857600080fd5b50620021b78882890162001b87565b92505060808601358015158114620021ce57600080fd5b809150509295509295909350565b60008060208385031215620021f057600080fd5b823567ffffffffffffffff8111156200220857600080fd5b620022168582860162001c6f565b90969095509350505050565b60008251620022368184602087016200201c565b9190910192915050565b600181811c908216806200225557607f821691505b6020821081036200227657634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620022ca57600081815260208120601f850160051c81016020861015620022a55750805b601f850160051c820191505b81811015620022c657828155600101620022b1565b5050505b505050565b815167ffffffffffffffff811115620022ec57620022ec62001b3d565b6200230481620022fd845462002240565b846200227c565b602080601f8311600181146200233c5760008415620023235750858301515b600019600386901b1c1916600185901b178555620022c6565b600085815260208120601f198616915b828110156200236d578886015182559484019460019091019084016200234c565b50858210156200238c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b604081526000620023b1604083018562002042565b90506001600160a01b03831660208301529392505050565b6000808335601e19843603018112620023e157600080fd5b83018035915067ffffffffffffffff821115620023fd57600080fd5b60200191503681900382131562001cb457600080fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115620015e257620015e262002413565b7f496e76616c696420626c6f636b2062696e64696e673a00000000000000000000815260008451620024798160168501602089016200201c565b80830190507f3a000000000000000000000000000000000000000000000000000000000000008060168301528551620024ba816017850160208a016200201c565b60179201918201528351620024d78160188401602088016200201c565b0160180195945050505050565b600060808201868352602086818501528560408501526080606085015281855180845260a08601915060a08160051b870101935082870160005b828110156200255057609f198887030184526200253d86835162002042565b955092840192908401906001016200251e565b50939a9950505050505050505050565b634e487b7160e01b600052603260045260246000fd5b80516020808301519190811015620022765760001960209190910360031b1b16919050565b600060018201620025b057620025b062002413565b5060010190565b6001600160a01b0380620025cb8362001bfe565b16835280620025dd6020840162001bfe565b1660208401525060408101356040830152606081013567ffffffffffffffff81168082146200260b57600080fd5b80606085015250505050565b620026238186620025b7565b60c060808201528260c082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8411156200266057600080fd5b8360051b808660e085013760a0830193909352500160e0019392505050565b60808101620015e28284620025b7565b60006bffffffffffffffffffffffff19808660601b168352808560601b166014840152508251620026c88160288501602087016200201c565b91909101602801949350505050565b813581556001808201602080850135601e19863603018112620026f957600080fd5b8501803567ffffffffffffffff8111156200271357600080fd5b80360383830113156200272557600080fd5b6200273d8162002736865462002240565b866200227c565b6000601f8211600181146200277657600083156200275d57508382018501355b600019600385901b1c1916600184901b178655620027d2565b600086815260209020601f19841690835b82811015620027a85786850188013582559387019390890190870162002787565b5084821015620027c85760001960f88660031b161c198785880101351681555b50508683881b0186555b50505050505050604082013560028201555050565b7f19457468657265756d205369676e6564204d6573736167653a0a0000000000008152600083516200282181601a8501602088016200201c565b8351908301906200283a81601a8401602088016200201c565b01601a01949350505050565b634e487b7160e01b600052602160045260246000fdfe608060405234801561001057600080fd5b50338061003757604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b61004081610046565b50610096565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b611807806100a56000396000f3fe6080604052600436106100e15760003560e01c80639730886d1161007f578063b201246f11610059578063b201246f1461030e578063b6aed0cb1461032e578063e138a8d21461034e578063f2fde38b1461036e57610155565b80639730886d1461029557806399a3ad21146102b5578063b1454caa146102d557610155565b8063346633fb116100bb578063346633fb1461022557806336d2da9014610238578063715018a6146102585780638da5cb5b1461026d57610155565b80630fcfbd11146101a25780630fe9188e146101d557806333a88c72146101f557610155565b36610155576040517f346633fb0000000000000000000000000000000000000000000000000000000081523360048201523460248201819052309163346633fb91906044016000604051808303818588803b15801561013f57600080fd5b505af1158015610153573d6000803e3d6000fd5b005b60405162461bcd60e51b815260206004820152600b60248201527f756e737570706f7274656400000000000000000000000000000000000000000060448201526064015b60405180910390fd5b3480156101ae57600080fd5b506101c26101bd366004610f80565b61038e565b6040519081526020015b60405180910390f35b3480156101e157600080fd5b506101536101f0366004610fb5565b610428565b34801561020157600080fd5b50610215610210366004610f80565b61049e565b60405190151581526020016101cc565b610153610233366004610fe3565b6104f1565b34801561024457600080fd5b5061015361025336600461100f565b6105d4565b34801561026457600080fd5b50610153610683565b34801561027957600080fd5b506000546040516001600160a01b0390911681526020016101cc565b3480156102a157600080fd5b506101536102b036600461102c565b610697565b3480156102c157600080fd5b506101536102d0366004610fe3565b6107e9565b3480156102e157600080fd5b506102f56102f03660046110a2565b610899565b60405167ffffffffffffffff90911681526020016101cc565b34801561031a57600080fd5b5061015361032936600461119b565b6108fa565b34801561033a57600080fd5b506101536103493660046111fd565b610afc565b34801561035a57600080fd5b5061015361036936600461121f565b610b98565b34801561037a57600080fd5b5061015361038936600461100f565b610d78565b600080826040516020016103a291906112ce565b60408051601f19818403018152918152815160209283012060008181526001909352912054909150806104215760405162461bcd60e51b815260206004820152602160248201527f54686973206d65737361676520776173206e65766572207375626d69747465646044820152601760f91b6064820152608401610199565b9392505050565b610430610dcf565b600081815260046020526040812054900361048d5760405162461bcd60e51b815260206004820152601a60248201527f537461746520726f6f7420646f6573206e6f742065786973742e0000000000006044820152606401610199565b600090815260046020526040812055565b600080826040516020016104b291906112ce565b60408051601f1981840301815291815281516020928301206000818152600190935291205490915080158015906104e95750428111155b949350505050565b60003411801561050057508034145b6105725760405162461bcd60e51b815260206004820152603060248201527f417474656d7074696e6720746f2073656e642076616c756520776974686f757460448201527f2070726f766964696e67204574686572000000000000000000000000000000006064820152608401610199565b600061057d33610e15565b6040805134815267ffffffffffffffff831660208201529192506001600160a01b0385169133917f50c536ac33a920f00755865b831d17bf4cff0b2e0345f65b16d52bfc004068b6910160405180910390a3505050565b6105dc610dcf565b6000816001600160a01b03164760405160006040518083038185875af1925050503d8060008114610629576040519150601f19603f3d011682016040523d82523d6000602084013e61062e565b606091505b505090508061067f5760405162461bcd60e51b815260206004820152601460248201527f6661696c65642073656e64696e672076616c75650000000000000000000000006044820152606401610199565b5050565b61068b610dcf565b6106956000610e73565b565b61069f610dcf565b60006106ab82426113cf565b90506000836040516020016106c091906112ce565b60408051601f198184030181529181528151602092830120600081815260019093529120549091501561075b5760405162461bcd60e51b815260206004820152602160248201527f4d657373616765207375626d6974746564206d6f7265207468616e206f6e636560448201527f21000000000000000000000000000000000000000000000000000000000000006064820152608401610199565b600081815260016020908152604082208490556002919061077e9087018761100f565b6001600160a01b0316815260208101919091526040016000908120906107aa60808701606088016113e2565b63ffffffff168152602080820192909252604001600090812080546001810182559082529190208591600402016107e182826115b0565b505050505050565b6107f1610dcf565b6000826001600160a01b03168260405160006040518083038185875af1925050503d806000811461083e576040519150601f19603f3d011682016040523d82523d6000602084013e610843565b606091505b50509050806108945760405162461bcd60e51b815260206004820152601460248201527f6661696c65642073656e64696e672076616c75650000000000000000000000006044820152606401610199565b505050565b60006108a433610e15565b9050336001600160a01b03167fb93c37389233beb85a3a726c3f15c2d15533ee74cb602f20f490dfffef7759378288888888886040516108e9969594939291906116ca565b60405180910390a295945050505050565b600081815260046020526040812054900361096a5760405162461bcd60e51b815260206004820152602a60248201527f526f6f74206973206e6f74207075626c6973686564206f6e2074686973206d6560448201526939b9b0b3b290313ab99760b11b6064820152608401610199565b6000818152600460205260409020544210156109d25760405162461bcd60e51b815260206004820152602160248201527f526f6f74206973206e6f7420636f6e736964657265642066696e616c207965746044820152601760f91b6064820152608401610199565b6000846040516020016109e5919061171a565b60408051601f198184030181528282528051602091820120908301829052600160608401527f760000000000000000000000000000000000000000000000000000000000000060808401529082015260a001604051602081830303815290604052805190602001209050610a8384848484604051602001610a6891815260200190565b60405160208183030381529060405280519060200120610ed0565b610af55760405162461bcd60e51b815260206004820152603360248201527f496e76616c696420696e636c7573696f6e2070726f6f6620666f722076616c7560448201527f65207472616e73666572206d6573736167652e000000000000000000000000006064820152608401610199565b5050505050565b610b04610dcf565b60008281526004602052604090205415610b865760405162461bcd60e51b815260206004820152602560248201527f526f6f7420616c726561647920616464656420746f20746865206d657373616760448201527f65206275730000000000000000000000000000000000000000000000000000006064820152608401610199565b60009182526004602052604090912055565b6000818152600460205260408120549003610c085760405162461bcd60e51b815260206004820152602a60248201527f526f6f74206973206e6f74207075626c6973686564206f6e2074686973206d6560448201526939b9b0b3b290313ab99760b11b6064820152608401610199565b600081815260046020526040902054421015610c705760405162461bcd60e51b815260206004820152602160248201527f526f6f74206973206e6f7420636f6e736964657265642066696e616c207965746044820152601760f91b6064820152608401610199565b600084604051602001610c8391906112ce565b60408051601f198184030181528282528051602091820120908301829052600160608401527f6d0000000000000000000000000000000000000000000000000000000000000060808401529082015260a001604051602081830303815290604052805190602001209050610d0684848484604051602001610a6891815260200190565b610af55760405162461bcd60e51b815260206004820152603060248201527f496e76616c696420696e636c7573696f6e2070726f6f6620666f722063726f7360448201527f7320636861696e206d6573736167652e000000000000000000000000000000006064820152608401610199565b610d80610dcf565b6001600160a01b038116610dc3576040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260006004820152602401610199565b610dcc81610e73565b50565b6000546001600160a01b03163314610695576040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152602401610199565b6001600160a01b0381166000908152600360205260408120805467ffffffffffffffff169160019190610e48838561177a565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550919050565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600082610ede868685610ee8565b1495945050505050565b600081815b84811015610f2b57610f1782878784818110610f0b57610f0b6117a2565b90506020020135610f34565b915080610f23816117b8565b915050610eed565b50949350505050565b6000818310610f50576000828152602084905260409020610f5f565b60008381526020839052604090205b90505b92915050565b600060c08284031215610f7a57600080fd5b50919050565b600060208284031215610f9257600080fd5b813567ffffffffffffffff811115610fa957600080fd5b6104e984828501610f68565b600060208284031215610fc757600080fd5b5035919050565b6001600160a01b0381168114610dcc57600080fd5b60008060408385031215610ff657600080fd5b823561100181610fce565b946020939093013593505050565b60006020828403121561102157600080fd5b813561042181610fce565b6000806040838503121561103f57600080fd5b823567ffffffffffffffff81111561105657600080fd5b61106285828601610f68565b95602094909401359450505050565b63ffffffff81168114610dcc57600080fd5b60ff81168114610dcc57600080fd5b803561109d81611083565b919050565b6000806000806000608086880312156110ba57600080fd5b85356110c581611071565b945060208601356110d581611071565b9350604086013567ffffffffffffffff808211156110f257600080fd5b818801915088601f83011261110657600080fd5b81358181111561111557600080fd5b89602082850101111561112757600080fd5b602083019550809450505050606086013561114181611083565b809150509295509295909350565b60008083601f84011261116157600080fd5b50813567ffffffffffffffff81111561117957600080fd5b6020830191508360208260051b850101111561119457600080fd5b9250929050565b60008060008084860360c08112156111b257600080fd5b60808112156111c057600080fd5b50849350608085013567ffffffffffffffff8111156111de57600080fd5b6111ea8782880161114f565b9598909750949560a00135949350505050565b6000806040838503121561121057600080fd5b50508035926020909101359150565b6000806000806060858703121561123557600080fd5b843567ffffffffffffffff8082111561124d57600080fd5b61125988838901610f68565b9550602087013591508082111561126f57600080fd5b5061127c8782880161114f565b9598909750949560400135949350505050565b67ffffffffffffffff81168114610dcc57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60208152600082356112df81610fce565b6001600160a01b03811660208401525060208301356112fd8161128f565b67ffffffffffffffff80821660408501526040850135915061131e82611071565b63ffffffff80831660608601526060860135925061133b83611071565b80831660808601525060808501359150601e1985360301821261135d57600080fd5b602091850191820191358181111561137457600080fd5b80360383131561138357600080fd5b60c060a086015261139860e0860182856112a5565b925050506113a860a08501611092565b60ff811660c0850152509392505050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610f6257610f626113b9565b6000602082840312156113f457600080fd5b813561042181611071565b60008135610f6281611071565b6000808335601e1984360301811261142357600080fd5b83018035915067ffffffffffffffff82111561143e57600080fd5b60200191503681900382131561119457600080fd5b634e487b7160e01b600052604160045260246000fd5b600181811c9082168061147d57607f821691505b602082108103610f7a57634e487b7160e01b600052602260045260246000fd5b601f82111561089457600081815260208120601f850160051c810160208610156114c45750805b601f850160051c820191505b818110156107e1578281556001016114d0565b67ffffffffffffffff8311156114fb576114fb611453565b61150f836115098354611469565b8361149d565b6000601f841160018114611543576000851561152b5750838201355b600019600387901b1c1916600186901b178355610af5565b600083815260209020601f19861690835b828110156115745786850135825560209485019460019092019101611554565b50868210156115915760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b60008135610f6281611083565b81356115bb81610fce565b6001600160a01b038116905081548173ffffffffffffffffffffffffffffffffffffffff19821617835560208401356115f38161128f565b7bffffffffffffffff00000000000000000000000000000000000000008160a01b1690507fffffffff000000000000000000000000000000000000000000000000000000008184828516171785556040860135925061165183611071565b921760e09190911b90911617815561168961166e606084016113ff565b6001830163ffffffff821663ffffffff198254161781555050565b611696608083018361140c565b6116a48183600286016114e3565b505061067f6116b560a084016115a3565b6003830160ff821660ff198254161781555050565b67ffffffffffffffff87168152600063ffffffff808816602084015280871660408401525060a0606083015261170460a0830185876112a5565b905060ff83166080830152979650505050505050565b60808101823561172981610fce565b6001600160a01b03908116835260208401359061174582610fce565b1660208301526040838101359083015260608301356117638161128f565b67ffffffffffffffff811660608401525092915050565b67ffffffffffffffff81811683821601908082111561179b5761179b6113b9565b5092915050565b634e487b7160e01b600052603260045260246000fd5b6000600182016117ca576117ca6113b9565b506001019056fea2646970667358221220a449a197632e1117b118ced8f7cf97a1ae44b160775ee146e59b50cbc1b8dcaa64736f6c63430008140033a264697066735822122004ad9e0c30b943964e74350866e1fd532c6f29760dc01100bb23ad394323ba7064736f6c63430008140033", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ECDSAInvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"ECDSAInvalidSignatureLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"ECDSAInvalidSignatureS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitialization\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInitializing\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"OwnableInvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"OwnableUnauthorizedAccount\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAddress\",\"type\":\"address\"}],\"name\":\"ImportantContractAddressUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"version\",\"type\":\"uint64\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"messageBusAddress\",\"type\":\"address\"}],\"name\":\"LogManagementContractCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"rollupHash\",\"type\":\"bytes32\"}],\"name\":\"RollupAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"enclaveID\",\"type\":\"address\"}],\"name\":\"SequencerEnclaveGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"enclaveID\",\"type\":\"address\"}],\"name\":\"SequencerEnclaveRevoked\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"Hash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"Signature\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"LastSequenceNumber\",\"type\":\"uint256\"}],\"internalType\":\"structStructs.MetaRollup\",\"name\":\"r\",\"type\":\"tuple\"},{\"internalType\":\"string\",\"name\":\"_rollupData\",\"type\":\"string\"},{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sequence\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"topic\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"},{\"internalType\":\"uint8\",\"name\":\"consistencyLevel\",\"type\":\"uint8\"}],\"internalType\":\"structStructs.CrossChainMessage[]\",\"name\":\"messages\",\"type\":\"tuple[]\"}],\"internalType\":\"structStructs.HeaderCrossChainData\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"AddRollup\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"Attested\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"sequence\",\"type\":\"uint64\"}],\"internalType\":\"structStructs.ValueTransferMessage\",\"name\":\"_msg\",\"type\":\"tuple\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"ExtractNativeValue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GetImportantContractKeys\",\"outputs\":[{\"internalType\":\"string[]\",\"name\":\"\",\"type\":\"string[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"rollupHash\",\"type\":\"bytes32\"}],\"name\":\"GetRollupByHash\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"Hash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"Signature\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"LastSequenceNumber\",\"type\":\"uint256\"}],\"internalType\":\"structStructs.MetaRollup\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"GetRollupByNumber\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"Hash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"Signature\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"LastSequenceNumber\",\"type\":\"uint256\"}],\"internalType\":\"structStructs.MetaRollup\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"}],\"name\":\"GetUniqueForkID\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"Hash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"Signature\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"LastSequenceNumber\",\"type\":\"uint256\"}],\"internalType\":\"structStructs.MetaRollup\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"GrantSequencerEnclave\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_enclaveID\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_initSecret\",\"type\":\"bytes\"},{\"internalType\":\"string\",\"name\":\"_genesisAttestation\",\"type\":\"string\"}],\"name\":\"InitializeNetworkSecret\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"IsSequencerEnclave\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"IsWithdrawalAvailable\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"requestReport\",\"type\":\"string\"}],\"name\":\"RequestNetworkSecret\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"attesterID\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"requesterID\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"attesterSig\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"responseSecret\",\"type\":\"bytes\"},{\"internalType\":\"bool\",\"name\":\"verifyAttester\",\"type\":\"bool\"}],\"name\":\"RespondNetworkSecret\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"RetrieveAllBridgeFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"RevokeSequencerEnclave\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"newAddress\",\"type\":\"address\"}],\"name\":\"SetImportantContractAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_lastBatchHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNum\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"crossChainHashes\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"rollupNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"forkID\",\"type\":\"bytes32\"}],\"name\":\"addCrossChainMessagesRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"name\":\"importantContractAddresses\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"importantContractKeys\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"crossChainHashes\",\"type\":\"bytes[]\"}],\"name\":\"isBundleAvailable\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isBundleSaved\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"isWithdrawalSpent\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastBatchHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastBatchSeqNo\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"merkleMessageBus\",\"outputs\":[{\"internalType\":\"contractIMerkleTreeMessageBus\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"messageBus\",\"outputs\":[{\"internalType\":\"contractIMessageBus\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5061001a3361001f565b610090565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6143c180620000a06000396000f3fe60806040523480156200001157600080fd5b5060043610620002005760003560e01c8063728109961162000119578063a1a227fa11620000af578063db5d91b1116200007a578063db5d91b114620004f1578063e34fbfc81462000520578063e874eb201462000535578063f2fde38b146200054957600080fd5b8063a1a227fa146200049e578063a4ab2faa14620004b2578063a52f433c14620004c9578063d4fab88714620004da57600080fd5b80638415482611620000f057806384154826146200040a57806387059edb14620004305780638da5cb5b146200044757806398077e86146200047857600080fd5b80637281099614620003cf5780638129fc1c14620003d95780638236a7ba14620003e357600080fd5b8063440c953b116200019b57806368e10383116200016657806368e10383146200037e5780636a30d26c14620003955780636b9707d614620003ae578063715018a614620003c557600080fd5b8063440c953b146200031f5780634766573814620003295780635371a2161462000340578063568699c8146200035757600080fd5b80632c77c81f11620001dc5780632c77c81f14620002525780632f0cb9e314620002695780633e60a22f14620002a057806343348b2f14620002f057600080fd5b80620ddd27146200020557806303e72e481462000222578063073b6ef3146200023b575b600080fd5b6200020f600e5481565b6040519081526020015b60405180910390f35b620002396200023336600462001eb5565b62000560565b005b620002396200024c36600462002000565b62000673565b62000239620002633660046200209d565b62000906565b6200028f6200027a36600462002140565b600c6020526000908152604090205460ff1681565b604051901515815260200162000219565b620002d7620002b13660046200215a565b80516020818301810180516003825292820191909301209152546001600160a01b031681565b6040516001600160a01b03909116815260200162000219565b6200028f620003013660046200219b565b6001600160a01b031660009081526020819052604090205460ff1690565b6200020f60055481565b620002396200033a3660046200219b565b62000a76565b6200023962000351366004620021c0565b62000b46565b6200036e6200036836600462002140565b62000d4f565b60405162000219929190620022e3565b620002396200038f366004620022fe565b62000da8565b6200039f62000e95565b60405162000219919062002388565b62000239620003bf3660046200219b565b62000f78565b620002396200103e565b6200023962001056565b62000239620010da565b620003fa620003f436600462002140565b620012be565b60405162000219929190620023ee565b6200028f6200041b36600462002140565b600d6020526000908152604090205460ff1681565b620003fa6200044136600462002140565b620013ae565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b0316620002d7565b6200048f6200048936600462002140565b62001428565b6040516200021991906200240b565b600a54620002d7906001600160a01b031681565b6200028f620004c336600462002420565b620014dd565b600454610100900460ff166200028f565b62000239620004eb36600462002459565b6200156e565b6200028f620005023660046200219b565b6001600160a01b031660009081526001602052604090205460ff1690565b620002396200053136600462002504565b5050565b600b54620002d7906001600160a01b031681565b620002396200055a3660046200219b565b62001701565b6200056a6200175e565b60006001600160a01b03166003836040516200058791906200254a565b908152604051908190036020019020546001600160a01b031603620005e657600280546001810182556000919091527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01620005e48382620025f7565b505b80600383604051620005f991906200254a565b90815260405190819003602001812080546001600160a01b039390931673ffffffffffffffffffffffffffffffffffffffff19909316929092179091557f17b2f9f5748931099ffee882b5b64f4a560b5c55da9b4f4e396dae3bb9f98cb590620006679084908490620026c4565b60405180910390a15050565b6000828152600860205260409020548114620006d65760405162461bcd60e51b815260206004820152600e60248201527f496e76616c696420666f726b494400000000000000000000000000000000000060448201526064015b60405180910390fd5b60006200074889898989604051602001620006f59493929190620026f1565b6040516020818303038152906040528051906020012086868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620017d592505050565b6001600160a01b03811660009081526020819052604090205490915060ff16620007b55760405162461bcd60e51b815260206004820152601660248201527f656e636c6176654944206e6f74206174746573746564000000000000000000006044820152606401620006cd565b600e8990556000805b8751811015620008e157600b5488516001600160a01b039091169063b6aed0cb908a9084908110620007f457620007f46200276d565b6020026020010151620008079062002783565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526004810191909152426024820152604401600060405180830381600087803b1580156200085f57600080fd5b505af115801562000874573d6000803e3d6000fd5b50505050818882815181106200088e576200088e6200276d565b6020026020010151620008a19062002783565b6040805160208101939093528201526060016040516020818303038152906040528051906020012091508080620008d890620027be565b915050620007be565b506000908152600d60205260409020805460ff19166001179055505050505050505050565b60006200095885356200091d6020880188620027da565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620017d592505050565b6001600160a01b03811660009081526020819052604090205490915060ff16620009c55760405162461bcd60e51b815260206004820152601660248201527f656e636c6176654944206e6f74206174746573746564000000000000000000006044820152606401620006cd565b6001600160a01b03811660009081526001602052604090205460ff1662000a2f5760405162461bcd60e51b815260206004820152601960248201527f656e636c6176654944206e6f7420612073657175656e636572000000000000006044820152606401620006cd565b62000a3a8562001805565b604051853581527fd6555bff8670bd3008dc064c30bb56d6ac7cb14ae801e36146fe4e7c6a504a58906020015b60405180910390a15050505050565b62000a806200175e565b6001600160a01b03811660009081526020819052604090205460ff1662000aea5760405162461bcd60e51b815260206004820152601660248201527f656e636c6176654944206e6f74206174746573746564000000000000000000006044820152606401620006cd565b6001600160a01b038116600081815260016020818152604092839020805460ff191690921790915590519182527ffe64c7181f0fc60e300dc02cca368cdfa94d7ca45902de3b9a9d80070e76093691015b60405180910390a150565b600b546040517fb201246f0000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063b201246f9062000b9790879087908790879060040162002884565b60006040518083038186803b15801562000bb057600080fd5b505afa15801562000bc5573d6000803e3d6000fd5b5050505060008460405160200162000bde9190620028ec565b60408051601f1981840301815291815281516020928301206000818152600c90935291205490915060ff161562000c585760405162461bcd60e51b815260206004820152601860248201527f7769746864726177616c20616c7265616479207370656e7400000000000000006044820152606401620006cd565b6001600c60008760405160200162000c719190620028ec565b60408051808303601f190181529181528151602092830120835282820193909352908201600020805460ff191693151593909317909255600a546001600160a01b0316916399a3ad219162000ccc919089019089016200219b565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526001600160a01b0390921660048301528801356024820152604401600060405180830381600087803b15801562000d2f57600080fd5b505af115801562000d44573d6000803e3d6000fd5b505050505050505050565b60408051606080820183526000808352602083019190915291810182905260008062000d7b85620013ae565b915091508162000d915760009590945092505050565b600094855260086020526040909420549492505050565b60045460ff161562000e235760405162461bcd60e51b815260206004820152602260248201527f6e6574776f726b2073656372657420616c726561647920696e697469616c697a60448201527f65640000000000000000000000000000000000000000000000000000000000006064820152608401620006cd565b60048054600160ff1991821681179092556001600160a01b038716600081815260208181526040808320805486168717905585825291829020805490941690941790925590519081527ffe64c7181f0fc60e300dc02cca368cdfa94d7ca45902de3b9a9d80070e760936910162000a67565b60606002805480602002602001604051908101604052809291908181526020016000905b8282101562000f6f57838290600052602060002001805462000edb9062002568565b80601f016020809104026020016040519081016040528092919081815260200182805462000f099062002568565b801562000f5a5780601f1062000f2e5761010080835404028352916020019162000f5a565b820191906000526020600020905b81548152906001019060200180831162000f3c57829003601f168201915b50505050508152602001906001019062000eb9565b50505050905090565b62000f826200175e565b6001600160a01b03811660009081526001602052604090205460ff1662000fec5760405162461bcd60e51b815260206004820152601960248201527f656e636c6176654944206e6f7420612073657175656e636572000000000000006044820152606401620006cd565b6001600160a01b038116600081815260016020908152604091829020805460ff1916905590519182527f0f279980343c7ca542fde9fa5396555068efb5cd560d9cf9c191aa2911079b47910162000b3b565b620010486200175e565b620010546000620018ba565b565b620010606200175e565b600a546040517f36d2da900000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b03909116906336d2da9090602401600060405180830381600087803b158015620010bf57600080fd5b505af1158015620010d4573d6000803e3d6000fd5b50505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff16600081158015620011265750825b905060008267ffffffffffffffff166001148015620011445750303b155b90508115801562001153575080155b156200118b576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845467ffffffffffffffff191660011785558315620011c057845468ff00000000000000001916680100000000000000001785555b620011cb3362001938565b60006005556001600955604051620011e39062001dc9565b604051809103906000f08015801562001200573d6000803e3d6000fd5b50600b805473ffffffffffffffffffffffffffffffffffffffff199081166001600160a01b0393909316928317909155600a8054909116821790556040519081527fbd726cf82ac9c3260b1495107182e336e0654b25c10915648c0cc15b2bb72cbf9060200160405180910390a18315620012b757845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200162000a67565b5050505050565b6040805160608082018352600080835260208084018390528385018290528582526006815284822085519384019095528454835260018501805492958694939092840191906200130e9062002568565b80601f01602080910402602001604051908101604052809291908181526020018280546200133c9062002568565b80156200138d5780601f1062001361576101008083540402835291602001916200138d565b820191906000526020600020905b8154815290600101906020018083116200136f57829003601f168201915b50505091835250506002919091015460209091015280519094149492505050565b604080516060808201835260008083526020830191909152918101829052600083815260076020526040812054908190036200141357505060408051606081018252600080825282516020818101855282825283015291810182905290939092509050565b6200141e81620012be565b9250925050915091565b600281815481106200143957600080fd5b906000526020600020016000915090508054620014569062002568565b80601f0160208091040260200160405190810160405280929190818152602001828054620014849062002568565b8015620014d55780601f10620014a957610100808354040283529160200191620014d5565b820191906000526020600020905b815481529060010190602001808311620014b757829003601f168201915b505050505081565b600080805b83518110156200155557818482815181106200150257620015026200276d565b6020026020010151620015159062002783565b60408051602081019390935282015260600160405160208183030381529060405280519060200120915080806200154c90620027be565b915050620014e2565b506000908152600d602052604090205460ff1692915050565b6001600160a01b03851660009081526020819052604090205460ff1680620015ff5760405162461bcd60e51b815260206004820152602360248201527f726573706f6e64696e67206174746573746572206973206e6f7420617474657360448201527f74656400000000000000000000000000000000000000000000000000000000006064820152608401620006cd565b8115620016d6576000620016388787866040516020016200162393929190620028fc565b6040516020818303038152906040526200194d565b90506000620016488287620017d5565b9050876001600160a01b0316816001600160a01b031614620016d35760405162461bcd60e51b815260206004820152602c60248201527f63616c63756c61746564206164647265737320616e642061747465737465724960448201527f4420646f6e74206d6174636800000000000000000000000000000000000000006064820152608401620006cd565b50505b5050506001600160a01b039091166000908152602081905260409020805460ff191660011790555050565b6200170b6200175e565b6001600160a01b03811662001750576040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260006004820152602401620006cd565b6200175b81620018ba565b50565b33620017917f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03161462001054576040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152602401620006cd565b600080600080620017e786866200198c565b925092509250620017f98282620019dd565b50909150505b92915050565b80356000908152600660205260409020819062001823828262002944565b505060095460009081526007602052604090208135908190556200184960014362002a54565b4060405160200162001865929190918252602082015260400190565b60408051601f1981840301815291815281516020928301206009805460009081526008909452918320558054916200189d83620027be565b9190505550600554816040013511156200175b5760400135600555565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300805473ffffffffffffffffffffffffffffffffffffffff1981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6200194262001af3565b6200175b8162001b5b565b60006200195b825162001b65565b826040516020016200196f92919062002a6a565b604051602081830303815290604052805190602001209050919050565b60008060008351604103620019ca5760208401516040850151606086015160001a620019bb8882858562001c0c565b955095509550505050620019d6565b50508151600091506002905b9250925092565b6000826003811115620019f457620019f462002ac9565b03620019fe575050565b600182600381111562001a155762001a1562002ac9565b0362001a4d576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600282600381111562001a645762001a6462002ac9565b0362001aa0576040517ffce698f700000000000000000000000000000000000000000000000000000000815260048101829052602401620006cd565b600382600381111562001ab75762001ab762002ac9565b0362000531576040517fd78bce0c00000000000000000000000000000000000000000000000000000000815260048101829052602401620006cd565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff1662001054576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6200170b62001af3565b6060600062001b748362001ce0565b600101905060008167ffffffffffffffff81111562001b975762001b9762001dd7565b6040519080825280601f01601f19166020018201604052801562001bc2576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508462001bcc57509392505050565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084111562001c49575060009150600390508262001cd6565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa15801562001c9e573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811662001ccc5750600092506001915082905062001cd6565b9250600091508190505b9450945094915050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831062001d2a577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef8100000000831062001d57576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831062001d7657662386f26fc10000830492506010015b6305f5e100831062001d8f576305f5e100830492506008015b612710831062001da457612710830492506004015b6064831062001db7576064830492506002015b600a8310620017ff5760010192915050565b6118ac8062002ae083390190565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171562001e195762001e1962001dd7565b604052919050565b600082601f83011262001e3357600080fd5b813567ffffffffffffffff81111562001e505762001e5062001dd7565b62001e65601f8201601f191660200162001ded565b81815284602083860101111562001e7b57600080fd5b816020850160208301376000918101602001919091529392505050565b80356001600160a01b038116811462001eb057600080fd5b919050565b6000806040838503121562001ec957600080fd5b823567ffffffffffffffff81111562001ee157600080fd5b62001eef8582860162001e21565b92505062001f006020840162001e98565b90509250929050565b600082601f83011262001f1b57600080fd5b8135602067ffffffffffffffff8083111562001f3b5762001f3b62001dd7565b8260051b62001f4c83820162001ded565b938452858101830193838101908886111562001f6757600080fd5b84880192505b8583101562001fa85782358481111562001f875760008081fd5b62001f978a87838c010162001e21565b835250918401919084019062001f6d565b98975050505050505050565b60008083601f84011262001fc757600080fd5b50813567ffffffffffffffff81111562001fe057600080fd5b60208301915083602082850101111562001ff957600080fd5b9250929050565b60008060008060008060008060e0898b0312156200201d57600080fd5b883597506020890135965060408901359550606089013567ffffffffffffffff808211156200204b57600080fd5b620020598c838d0162001f09565b965060808b01359150808211156200207057600080fd5b506200207f8b828c0162001fb4565b999c989b5096999598969760a08701359660c0013595509350505050565b60008060008060608587031215620020b457600080fd5b843567ffffffffffffffff80821115620020cd57600080fd5b9086019060608289031215620020e257600080fd5b90945060208601359080821115620020f957600080fd5b620021078883890162001fb4565b909550935060408701359150808211156200212157600080fd5b508501602081880312156200213557600080fd5b939692955090935050565b6000602082840312156200215357600080fd5b5035919050565b6000602082840312156200216d57600080fd5b813567ffffffffffffffff8111156200218557600080fd5b620021938482850162001e21565b949350505050565b600060208284031215620021ae57600080fd5b620021b98262001e98565b9392505050565b60008060008084860360c0811215620021d857600080fd5b6080811215620021e757600080fd5b50849350608085013567ffffffffffffffff808211156200220757600080fd5b818701915087601f8301126200221c57600080fd5b8135818111156200222c57600080fd5b8860208260051b85010111156200224257600080fd5b95986020929092019750949560a00135945092505050565b60005b83811015620022775781810151838201526020016200225d565b50506000910152565b600081518084526200229a8160208601602086016200225a565b601f01601f19169290920160200192915050565b805182526000602082015160606020850152620022cf606085018262002280565b604093840151949093019390935250919050565b828152604060208201526000620021936040830184620022ae565b6000806000806000606086880312156200231757600080fd5b620023228662001e98565b9450602086013567ffffffffffffffff808211156200234057600080fd5b6200234e89838a0162001fb4565b909650945060408801359150808211156200236857600080fd5b50620023778882890162001fb4565b969995985093965092949392505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015620023e157603f19888603018452620023ce85835162002280565b94509285019290850190600101620023af565b5092979650505050505050565b8215158152604060208201526000620021936040830184620022ae565b602081526000620021b9602083018462002280565b6000602082840312156200243357600080fd5b813567ffffffffffffffff8111156200244b57600080fd5b620021938482850162001f09565b600080600080600060a086880312156200247257600080fd5b6200247d8662001e98565b94506200248d6020870162001e98565b9350604086013567ffffffffffffffff80821115620024ab57600080fd5b620024b989838a0162001e21565b94506060880135915080821115620024d057600080fd5b50620024df8882890162001e21565b92505060808601358015158114620024f657600080fd5b809150509295509295909350565b600080602083850312156200251857600080fd5b823567ffffffffffffffff8111156200253057600080fd5b6200253e8582860162001fb4565b90969095509350505050565b600082516200255e8184602087016200225a565b9190910192915050565b600181811c908216806200257d57607f821691505b6020821081036200259e57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620025f257600081815260208120601f850160051c81016020861015620025cd5750805b601f850160051c820191505b81811015620025ee57828155600101620025d9565b5050505b505050565b815167ffffffffffffffff81111562002614576200261462001dd7565b6200262c8162002625845462002568565b84620025a4565b602080601f8311600181146200266457600084156200264b5750858301515b600019600386901b1c1916600185901b178555620025ee565b600085815260208120601f198616915b82811015620026955788860151825594840194600190910190840162002674565b5085821015620026b45787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b604081526000620026d9604083018562002280565b90506001600160a01b03831660208301529392505050565b600060808201868352602086818501528560408501526080606085015281855180845260a08601915060a08160051b870101935082870160005b828110156200275d57609f198887030184526200274a86835162002280565b955092840192908401906001016200272b565b50939a9950505050505050505050565b634e487b7160e01b600052603260045260246000fd5b805160208083015191908110156200259e5760001960209190910360031b1b16919050565b634e487b7160e01b600052601160045260246000fd5b600060018201620027d357620027d3620027a8565b5060010190565b6000808335601e19843603018112620027f257600080fd5b83018035915067ffffffffffffffff8211156200280e57600080fd5b60200191503681900382131562001ff957600080fd5b6001600160a01b0380620028388362001e98565b168352806200284a6020840162001e98565b1660208401525060408101356040830152606081013567ffffffffffffffff81168082146200287857600080fd5b80606085015250505050565b62002890818662002824565b60c060808201528260c082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841115620028cd57600080fd5b8360051b808660e085013760a0830193909352500160e0019392505050565b60808101620017ff828462002824565b60006bffffffffffffffffffffffff19808660601b168352808560601b166014840152508251620029358160288501602087016200225a565b91909101602801949350505050565b813581556001808201602080850135601e198636030181126200296657600080fd5b8501803567ffffffffffffffff8111156200298057600080fd5b80360383830113156200299257600080fd5b620029aa81620029a3865462002568565b86620025a4565b6000601f821160018114620029e35760008315620029ca57508382018501355b600019600385901b1c1916600184901b17865562002a3f565b600086815260209020601f19841690835b8281101562002a1557868501880135825593870193908901908701620029f4565b508482101562002a355760001960f88660031b161c198785880101351681555b50508683881b0186555b50505050505050604082013560028201555050565b81810381811115620017ff57620017ff620027a8565b7f19457468657265756d205369676e6564204d6573736167653a0a00000000000081526000835162002aa481601a8501602088016200225a565b83519083019062002abd81601a8401602088016200225a565b01601a01949350505050565b634e487b7160e01b600052602160045260246000fdfe608060405234801561001057600080fd5b50338061003757604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b61004081610046565b50610096565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b611807806100a56000396000f3fe6080604052600436106100e15760003560e01c80639730886d1161007f578063b201246f11610059578063b201246f1461030e578063b6aed0cb1461032e578063e138a8d21461034e578063f2fde38b1461036e57610155565b80639730886d1461029557806399a3ad21146102b5578063b1454caa146102d557610155565b8063346633fb116100bb578063346633fb1461022557806336d2da9014610238578063715018a6146102585780638da5cb5b1461026d57610155565b80630fcfbd11146101a25780630fe9188e146101d557806333a88c72146101f557610155565b36610155576040517f346633fb0000000000000000000000000000000000000000000000000000000081523360048201523460248201819052309163346633fb91906044016000604051808303818588803b15801561013f57600080fd5b505af1158015610153573d6000803e3d6000fd5b005b60405162461bcd60e51b815260206004820152600b60248201527f756e737570706f7274656400000000000000000000000000000000000000000060448201526064015b60405180910390fd5b3480156101ae57600080fd5b506101c26101bd366004610f80565b61038e565b6040519081526020015b60405180910390f35b3480156101e157600080fd5b506101536101f0366004610fb5565b610428565b34801561020157600080fd5b50610215610210366004610f80565b61049e565b60405190151581526020016101cc565b610153610233366004610fe3565b6104f1565b34801561024457600080fd5b5061015361025336600461100f565b6105d4565b34801561026457600080fd5b50610153610683565b34801561027957600080fd5b506000546040516001600160a01b0390911681526020016101cc565b3480156102a157600080fd5b506101536102b036600461102c565b610697565b3480156102c157600080fd5b506101536102d0366004610fe3565b6107e9565b3480156102e157600080fd5b506102f56102f03660046110a2565b610899565b60405167ffffffffffffffff90911681526020016101cc565b34801561031a57600080fd5b5061015361032936600461119b565b6108fa565b34801561033a57600080fd5b506101536103493660046111fd565b610afc565b34801561035a57600080fd5b5061015361036936600461121f565b610b98565b34801561037a57600080fd5b5061015361038936600461100f565b610d78565b600080826040516020016103a291906112ce565b60408051601f19818403018152918152815160209283012060008181526001909352912054909150806104215760405162461bcd60e51b815260206004820152602160248201527f54686973206d65737361676520776173206e65766572207375626d69747465646044820152601760f91b6064820152608401610199565b9392505050565b610430610dcf565b600081815260046020526040812054900361048d5760405162461bcd60e51b815260206004820152601a60248201527f537461746520726f6f7420646f6573206e6f742065786973742e0000000000006044820152606401610199565b600090815260046020526040812055565b600080826040516020016104b291906112ce565b60408051601f1981840301815291815281516020928301206000818152600190935291205490915080158015906104e95750428111155b949350505050565b60003411801561050057508034145b6105725760405162461bcd60e51b815260206004820152603060248201527f417474656d7074696e6720746f2073656e642076616c756520776974686f757460448201527f2070726f766964696e67204574686572000000000000000000000000000000006064820152608401610199565b600061057d33610e15565b6040805134815267ffffffffffffffff831660208201529192506001600160a01b0385169133917f50c536ac33a920f00755865b831d17bf4cff0b2e0345f65b16d52bfc004068b6910160405180910390a3505050565b6105dc610dcf565b6000816001600160a01b03164760405160006040518083038185875af1925050503d8060008114610629576040519150601f19603f3d011682016040523d82523d6000602084013e61062e565b606091505b505090508061067f5760405162461bcd60e51b815260206004820152601460248201527f6661696c65642073656e64696e672076616c75650000000000000000000000006044820152606401610199565b5050565b61068b610dcf565b6106956000610e73565b565b61069f610dcf565b60006106ab82426113cf565b90506000836040516020016106c091906112ce565b60408051601f198184030181529181528151602092830120600081815260019093529120549091501561075b5760405162461bcd60e51b815260206004820152602160248201527f4d657373616765207375626d6974746564206d6f7265207468616e206f6e636560448201527f21000000000000000000000000000000000000000000000000000000000000006064820152608401610199565b600081815260016020908152604082208490556002919061077e9087018761100f565b6001600160a01b0316815260208101919091526040016000908120906107aa60808701606088016113e2565b63ffffffff168152602080820192909252604001600090812080546001810182559082529190208591600402016107e182826115b0565b505050505050565b6107f1610dcf565b6000826001600160a01b03168260405160006040518083038185875af1925050503d806000811461083e576040519150601f19603f3d011682016040523d82523d6000602084013e610843565b606091505b50509050806108945760405162461bcd60e51b815260206004820152601460248201527f6661696c65642073656e64696e672076616c75650000000000000000000000006044820152606401610199565b505050565b60006108a433610e15565b9050336001600160a01b03167fb93c37389233beb85a3a726c3f15c2d15533ee74cb602f20f490dfffef7759378288888888886040516108e9969594939291906116ca565b60405180910390a295945050505050565b600081815260046020526040812054900361096a5760405162461bcd60e51b815260206004820152602a60248201527f526f6f74206973206e6f74207075626c6973686564206f6e2074686973206d6560448201526939b9b0b3b290313ab99760b11b6064820152608401610199565b6000818152600460205260409020544210156109d25760405162461bcd60e51b815260206004820152602160248201527f526f6f74206973206e6f7420636f6e736964657265642066696e616c207965746044820152601760f91b6064820152608401610199565b6000846040516020016109e5919061171a565b60408051601f198184030181528282528051602091820120908301829052600160608401527f760000000000000000000000000000000000000000000000000000000000000060808401529082015260a001604051602081830303815290604052805190602001209050610a8384848484604051602001610a6891815260200190565b60405160208183030381529060405280519060200120610ed0565b610af55760405162461bcd60e51b815260206004820152603360248201527f496e76616c696420696e636c7573696f6e2070726f6f6620666f722076616c7560448201527f65207472616e73666572206d6573736167652e000000000000000000000000006064820152608401610199565b5050505050565b610b04610dcf565b60008281526004602052604090205415610b865760405162461bcd60e51b815260206004820152602560248201527f526f6f7420616c726561647920616464656420746f20746865206d657373616760448201527f65206275730000000000000000000000000000000000000000000000000000006064820152608401610199565b60009182526004602052604090912055565b6000818152600460205260408120549003610c085760405162461bcd60e51b815260206004820152602a60248201527f526f6f74206973206e6f74207075626c6973686564206f6e2074686973206d6560448201526939b9b0b3b290313ab99760b11b6064820152608401610199565b600081815260046020526040902054421015610c705760405162461bcd60e51b815260206004820152602160248201527f526f6f74206973206e6f7420636f6e736964657265642066696e616c207965746044820152601760f91b6064820152608401610199565b600084604051602001610c8391906112ce565b60408051601f198184030181528282528051602091820120908301829052600160608401527f6d0000000000000000000000000000000000000000000000000000000000000060808401529082015260a001604051602081830303815290604052805190602001209050610d0684848484604051602001610a6891815260200190565b610af55760405162461bcd60e51b815260206004820152603060248201527f496e76616c696420696e636c7573696f6e2070726f6f6620666f722063726f7360448201527f7320636861696e206d6573736167652e000000000000000000000000000000006064820152608401610199565b610d80610dcf565b6001600160a01b038116610dc3576040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260006004820152602401610199565b610dcc81610e73565b50565b6000546001600160a01b03163314610695576040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152602401610199565b6001600160a01b0381166000908152600360205260408120805467ffffffffffffffff169160019190610e48838561177a565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550919050565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600082610ede868685610ee8565b1495945050505050565b600081815b84811015610f2b57610f1782878784818110610f0b57610f0b6117a2565b90506020020135610f34565b915080610f23816117b8565b915050610eed565b50949350505050565b6000818310610f50576000828152602084905260409020610f5f565b60008381526020839052604090205b90505b92915050565b600060c08284031215610f7a57600080fd5b50919050565b600060208284031215610f9257600080fd5b813567ffffffffffffffff811115610fa957600080fd5b6104e984828501610f68565b600060208284031215610fc757600080fd5b5035919050565b6001600160a01b0381168114610dcc57600080fd5b60008060408385031215610ff657600080fd5b823561100181610fce565b946020939093013593505050565b60006020828403121561102157600080fd5b813561042181610fce565b6000806040838503121561103f57600080fd5b823567ffffffffffffffff81111561105657600080fd5b61106285828601610f68565b95602094909401359450505050565b63ffffffff81168114610dcc57600080fd5b60ff81168114610dcc57600080fd5b803561109d81611083565b919050565b6000806000806000608086880312156110ba57600080fd5b85356110c581611071565b945060208601356110d581611071565b9350604086013567ffffffffffffffff808211156110f257600080fd5b818801915088601f83011261110657600080fd5b81358181111561111557600080fd5b89602082850101111561112757600080fd5b602083019550809450505050606086013561114181611083565b809150509295509295909350565b60008083601f84011261116157600080fd5b50813567ffffffffffffffff81111561117957600080fd5b6020830191508360208260051b850101111561119457600080fd5b9250929050565b60008060008084860360c08112156111b257600080fd5b60808112156111c057600080fd5b50849350608085013567ffffffffffffffff8111156111de57600080fd5b6111ea8782880161114f565b9598909750949560a00135949350505050565b6000806040838503121561121057600080fd5b50508035926020909101359150565b6000806000806060858703121561123557600080fd5b843567ffffffffffffffff8082111561124d57600080fd5b61125988838901610f68565b9550602087013591508082111561126f57600080fd5b5061127c8782880161114f565b9598909750949560400135949350505050565b67ffffffffffffffff81168114610dcc57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60208152600082356112df81610fce565b6001600160a01b03811660208401525060208301356112fd8161128f565b67ffffffffffffffff80821660408501526040850135915061131e82611071565b63ffffffff80831660608601526060860135925061133b83611071565b80831660808601525060808501359150601e1985360301821261135d57600080fd5b602091850191820191358181111561137457600080fd5b80360383131561138357600080fd5b60c060a086015261139860e0860182856112a5565b925050506113a860a08501611092565b60ff811660c0850152509392505050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610f6257610f626113b9565b6000602082840312156113f457600080fd5b813561042181611071565b60008135610f6281611071565b6000808335601e1984360301811261142357600080fd5b83018035915067ffffffffffffffff82111561143e57600080fd5b60200191503681900382131561119457600080fd5b634e487b7160e01b600052604160045260246000fd5b600181811c9082168061147d57607f821691505b602082108103610f7a57634e487b7160e01b600052602260045260246000fd5b601f82111561089457600081815260208120601f850160051c810160208610156114c45750805b601f850160051c820191505b818110156107e1578281556001016114d0565b67ffffffffffffffff8311156114fb576114fb611453565b61150f836115098354611469565b8361149d565b6000601f841160018114611543576000851561152b5750838201355b600019600387901b1c1916600186901b178355610af5565b600083815260209020601f19861690835b828110156115745786850135825560209485019460019092019101611554565b50868210156115915760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b60008135610f6281611083565b81356115bb81610fce565b6001600160a01b038116905081548173ffffffffffffffffffffffffffffffffffffffff19821617835560208401356115f38161128f565b7bffffffffffffffff00000000000000000000000000000000000000008160a01b1690507fffffffff000000000000000000000000000000000000000000000000000000008184828516171785556040860135925061165183611071565b921760e09190911b90911617815561168961166e606084016113ff565b6001830163ffffffff821663ffffffff198254161781555050565b611696608083018361140c565b6116a48183600286016114e3565b505061067f6116b560a084016115a3565b6003830160ff821660ff198254161781555050565b67ffffffffffffffff87168152600063ffffffff808816602084015280871660408401525060a0606083015261170460a0830185876112a5565b905060ff83166080830152979650505050505050565b60808101823561172981610fce565b6001600160a01b03908116835260208401359061174582610fce565b1660208301526040838101359083015260608301356117638161128f565b67ffffffffffffffff811660608401525092915050565b67ffffffffffffffff81811683821601908082111561179b5761179b6113b9565b5092915050565b634e487b7160e01b600052603260045260246000fd5b6000600182016117ca576117ca6113b9565b506001019056fea2646970667358221220a449a197632e1117b118ced8f7cf97a1ae44b160775ee146e59b50cbc1b8dcaa64736f6c63430008140033a26469706673582212202db2c4edc3c70178755866034f9c9976755cd4234b737f234daf445437ad909f64736f6c63430008140033", } // ManagementContractABI is the input ABI used to generate the binding from. @@ -326,6 +326,70 @@ func (_ManagementContract *ManagementContractCallerSession) GetRollupByHash(roll return _ManagementContract.Contract.GetRollupByHash(&_ManagementContract.CallOpts, rollupHash) } +// GetRollupByNumber is a free data retrieval call binding the contract method 0x87059edb. +// +// Solidity: function GetRollupByNumber(uint256 number) view returns(bool, (bytes32,bytes,uint256)) +func (_ManagementContract *ManagementContractCaller) GetRollupByNumber(opts *bind.CallOpts, number *big.Int) (bool, StructsMetaRollup, error) { + var out []interface{} + err := _ManagementContract.contract.Call(opts, &out, "GetRollupByNumber", number) + + if err != nil { + return *new(bool), *new(StructsMetaRollup), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + out1 := *abi.ConvertType(out[1], new(StructsMetaRollup)).(*StructsMetaRollup) + + return out0, out1, err + +} + +// GetRollupByNumber is a free data retrieval call binding the contract method 0x87059edb. +// +// Solidity: function GetRollupByNumber(uint256 number) view returns(bool, (bytes32,bytes,uint256)) +func (_ManagementContract *ManagementContractSession) GetRollupByNumber(number *big.Int) (bool, StructsMetaRollup, error) { + return _ManagementContract.Contract.GetRollupByNumber(&_ManagementContract.CallOpts, number) +} + +// GetRollupByNumber is a free data retrieval call binding the contract method 0x87059edb. +// +// Solidity: function GetRollupByNumber(uint256 number) view returns(bool, (bytes32,bytes,uint256)) +func (_ManagementContract *ManagementContractCallerSession) GetRollupByNumber(number *big.Int) (bool, StructsMetaRollup, error) { + return _ManagementContract.Contract.GetRollupByNumber(&_ManagementContract.CallOpts, number) +} + +// GetUniqueForkID is a free data retrieval call binding the contract method 0x568699c8. +// +// Solidity: function GetUniqueForkID(uint256 number) view returns(bytes32, (bytes32,bytes,uint256)) +func (_ManagementContract *ManagementContractCaller) GetUniqueForkID(opts *bind.CallOpts, number *big.Int) ([32]byte, StructsMetaRollup, error) { + var out []interface{} + err := _ManagementContract.contract.Call(opts, &out, "GetUniqueForkID", number) + + if err != nil { + return *new([32]byte), *new(StructsMetaRollup), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + out1 := *abi.ConvertType(out[1], new(StructsMetaRollup)).(*StructsMetaRollup) + + return out0, out1, err + +} + +// GetUniqueForkID is a free data retrieval call binding the contract method 0x568699c8. +// +// Solidity: function GetUniqueForkID(uint256 number) view returns(bytes32, (bytes32,bytes,uint256)) +func (_ManagementContract *ManagementContractSession) GetUniqueForkID(number *big.Int) ([32]byte, StructsMetaRollup, error) { + return _ManagementContract.Contract.GetUniqueForkID(&_ManagementContract.CallOpts, number) +} + +// GetUniqueForkID is a free data retrieval call binding the contract method 0x568699c8. +// +// Solidity: function GetUniqueForkID(uint256 number) view returns(bytes32, (bytes32,bytes,uint256)) +func (_ManagementContract *ManagementContractCallerSession) GetUniqueForkID(number *big.Int) ([32]byte, StructsMetaRollup, error) { + return _ManagementContract.Contract.GetUniqueForkID(&_ManagementContract.CallOpts, number) +} + // IsSequencerEnclave is a free data retrieval call binding the contract method 0xdb5d91b1. // // Solidity: function IsSequencerEnclave(address _addr) view returns(bool) @@ -450,6 +514,68 @@ func (_ManagementContract *ManagementContractCallerSession) ImportantContractKey return _ManagementContract.Contract.ImportantContractKeys(&_ManagementContract.CallOpts, arg0) } +// IsBundleAvailable is a free data retrieval call binding the contract method 0xa4ab2faa. +// +// Solidity: function isBundleAvailable(bytes[] crossChainHashes) view returns(bool) +func (_ManagementContract *ManagementContractCaller) IsBundleAvailable(opts *bind.CallOpts, crossChainHashes [][]byte) (bool, error) { + var out []interface{} + err := _ManagementContract.contract.Call(opts, &out, "isBundleAvailable", crossChainHashes) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsBundleAvailable is a free data retrieval call binding the contract method 0xa4ab2faa. +// +// Solidity: function isBundleAvailable(bytes[] crossChainHashes) view returns(bool) +func (_ManagementContract *ManagementContractSession) IsBundleAvailable(crossChainHashes [][]byte) (bool, error) { + return _ManagementContract.Contract.IsBundleAvailable(&_ManagementContract.CallOpts, crossChainHashes) +} + +// IsBundleAvailable is a free data retrieval call binding the contract method 0xa4ab2faa. +// +// Solidity: function isBundleAvailable(bytes[] crossChainHashes) view returns(bool) +func (_ManagementContract *ManagementContractCallerSession) IsBundleAvailable(crossChainHashes [][]byte) (bool, error) { + return _ManagementContract.Contract.IsBundleAvailable(&_ManagementContract.CallOpts, crossChainHashes) +} + +// IsBundleSaved is a free data retrieval call binding the contract method 0x84154826. +// +// Solidity: function isBundleSaved(bytes32 ) view returns(bool) +func (_ManagementContract *ManagementContractCaller) IsBundleSaved(opts *bind.CallOpts, arg0 [32]byte) (bool, error) { + var out []interface{} + err := _ManagementContract.contract.Call(opts, &out, "isBundleSaved", arg0) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsBundleSaved is a free data retrieval call binding the contract method 0x84154826. +// +// Solidity: function isBundleSaved(bytes32 ) view returns(bool) +func (_ManagementContract *ManagementContractSession) IsBundleSaved(arg0 [32]byte) (bool, error) { + return _ManagementContract.Contract.IsBundleSaved(&_ManagementContract.CallOpts, arg0) +} + +// IsBundleSaved is a free data retrieval call binding the contract method 0x84154826. +// +// Solidity: function isBundleSaved(bytes32 ) view returns(bool) +func (_ManagementContract *ManagementContractCallerSession) IsBundleSaved(arg0 [32]byte) (bool, error) { + return _ManagementContract.Contract.IsBundleSaved(&_ManagementContract.CallOpts, arg0) +} + // IsWithdrawalSpent is a free data retrieval call binding the contract method 0x2f0cb9e3. // // Solidity: function isWithdrawalSpent(bytes32 ) view returns(bool) @@ -825,25 +951,25 @@ func (_ManagementContract *ManagementContractTransactorSession) SetImportantCont return _ManagementContract.Contract.SetImportantContractAddress(&_ManagementContract.TransactOpts, key, newAddress) } -// AddCrossChainMessagesRoot is a paid mutator transaction binding the contract method 0x3f3f6a18. +// AddCrossChainMessagesRoot is a paid mutator transaction binding the contract method 0x073b6ef3. // -// Solidity: function addCrossChainMessagesRoot(bytes32 _lastBatchHash, bytes32 blockHash, uint256 blockNum, bytes[] crossChainHashes, bytes signature) returns() -func (_ManagementContract *ManagementContractTransactor) AddCrossChainMessagesRoot(opts *bind.TransactOpts, _lastBatchHash [32]byte, blockHash [32]byte, blockNum *big.Int, crossChainHashes [][]byte, signature []byte) (*types.Transaction, error) { - return _ManagementContract.contract.Transact(opts, "addCrossChainMessagesRoot", _lastBatchHash, blockHash, blockNum, crossChainHashes, signature) +// Solidity: function addCrossChainMessagesRoot(bytes32 _lastBatchHash, bytes32 blockHash, uint256 blockNum, bytes[] crossChainHashes, bytes signature, uint256 rollupNumber, bytes32 forkID) returns() +func (_ManagementContract *ManagementContractTransactor) AddCrossChainMessagesRoot(opts *bind.TransactOpts, _lastBatchHash [32]byte, blockHash [32]byte, blockNum *big.Int, crossChainHashes [][]byte, signature []byte, rollupNumber *big.Int, forkID [32]byte) (*types.Transaction, error) { + return _ManagementContract.contract.Transact(opts, "addCrossChainMessagesRoot", _lastBatchHash, blockHash, blockNum, crossChainHashes, signature, rollupNumber, forkID) } -// AddCrossChainMessagesRoot is a paid mutator transaction binding the contract method 0x3f3f6a18. +// AddCrossChainMessagesRoot is a paid mutator transaction binding the contract method 0x073b6ef3. // -// Solidity: function addCrossChainMessagesRoot(bytes32 _lastBatchHash, bytes32 blockHash, uint256 blockNum, bytes[] crossChainHashes, bytes signature) returns() -func (_ManagementContract *ManagementContractSession) AddCrossChainMessagesRoot(_lastBatchHash [32]byte, blockHash [32]byte, blockNum *big.Int, crossChainHashes [][]byte, signature []byte) (*types.Transaction, error) { - return _ManagementContract.Contract.AddCrossChainMessagesRoot(&_ManagementContract.TransactOpts, _lastBatchHash, blockHash, blockNum, crossChainHashes, signature) +// Solidity: function addCrossChainMessagesRoot(bytes32 _lastBatchHash, bytes32 blockHash, uint256 blockNum, bytes[] crossChainHashes, bytes signature, uint256 rollupNumber, bytes32 forkID) returns() +func (_ManagementContract *ManagementContractSession) AddCrossChainMessagesRoot(_lastBatchHash [32]byte, blockHash [32]byte, blockNum *big.Int, crossChainHashes [][]byte, signature []byte, rollupNumber *big.Int, forkID [32]byte) (*types.Transaction, error) { + return _ManagementContract.Contract.AddCrossChainMessagesRoot(&_ManagementContract.TransactOpts, _lastBatchHash, blockHash, blockNum, crossChainHashes, signature, rollupNumber, forkID) } -// AddCrossChainMessagesRoot is a paid mutator transaction binding the contract method 0x3f3f6a18. +// AddCrossChainMessagesRoot is a paid mutator transaction binding the contract method 0x073b6ef3. // -// Solidity: function addCrossChainMessagesRoot(bytes32 _lastBatchHash, bytes32 blockHash, uint256 blockNum, bytes[] crossChainHashes, bytes signature) returns() -func (_ManagementContract *ManagementContractTransactorSession) AddCrossChainMessagesRoot(_lastBatchHash [32]byte, blockHash [32]byte, blockNum *big.Int, crossChainHashes [][]byte, signature []byte) (*types.Transaction, error) { - return _ManagementContract.Contract.AddCrossChainMessagesRoot(&_ManagementContract.TransactOpts, _lastBatchHash, blockHash, blockNum, crossChainHashes, signature) +// Solidity: function addCrossChainMessagesRoot(bytes32 _lastBatchHash, bytes32 blockHash, uint256 blockNum, bytes[] crossChainHashes, bytes signature, uint256 rollupNumber, bytes32 forkID) returns() +func (_ManagementContract *ManagementContractTransactorSession) AddCrossChainMessagesRoot(_lastBatchHash [32]byte, blockHash [32]byte, blockNum *big.Int, crossChainHashes [][]byte, signature []byte, rollupNumber *big.Int, forkID [32]byte) (*types.Transaction, error) { + return _ManagementContract.Contract.AddCrossChainMessagesRoot(&_ManagementContract.TransactOpts, _lastBatchHash, blockHash, blockNum, crossChainHashes, signature, rollupNumber, forkID) } // Initialize is a paid mutator transaction binding the contract method 0x8129fc1c. diff --git a/contracts/generated/ObscuroBridge/ObscuroBridge.go b/contracts/generated/ObscuroBridge/ObscuroBridge.go index d683460488..e666104716 100644 --- a/contracts/generated/ObscuroBridge/ObscuroBridge.go +++ b/contracts/generated/ObscuroBridge/ObscuroBridge.go @@ -32,7 +32,7 @@ var ( // ObscuroBridgeMetaData contains all meta data concerning the ObscuroBridge contract. var ObscuroBridgeMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"AddressEmptyCode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"AddressInsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedInnerCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitialization\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInitializing\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SafeERC20FailedOperation\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"version\",\"type\":\"uint64\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ERC20_TOKEN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"NATIVE_TOKEN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"messengerAddress\",\"type\":\"address\"}],\"name\":\"configure\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"messenger\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"promoteToAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"receiveAssets\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"removeToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"callerConfirmation\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"sendERC20\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"sendNative\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"bridge\",\"type\":\"address\"}],\"name\":\"setRemoteBridge\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"}],\"name\":\"whitelistToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60806040526001805463ffffffff60a01b1916905534801561002057600080fd5b50611944806100306000396000f3fe6080604052600436106101445760003560e01c806375b238fc116100c0578063a217fddf11610074578063c4d66de811610059578063c4d66de8146103b4578063d547741f146103d4578063e4c3ebc7146103f457600080fd5b8063a217fddf1461037f578063a381c8e21461039457600080fd5b806383bece4d116100a557806383bece4d146102f957806391d148541461031957806393b374421461035f57600080fd5b806375b238fc146102a557806375cb2672146102d957600080fd5b80632f2ff15d11610117578063498d82ab116100fc578063498d82ab146102315780635d872970146102515780635fa7b5841461028557600080fd5b80632f2ff15d146101f157806336568abe1461021157600080fd5b806301ffc9a71461014957806316ce81491461017e5780631888d712146101a0578063248a9ca3146101b3575b600080fd5b34801561015557600080fd5b5061016961016436600461154f565b610428565b60405190151581526020015b60405180910390f35b34801561018a57600080fd5b5061019e61019936600461158e565b610491565b005b61019e6101ae36600461158e565b6104eb565b3480156101bf57600080fd5b506101e36101ce3660046115ab565b60009081526002602052604090206001015490565b604051908152602001610175565b3480156101fd57600080fd5b5061019e61020c3660046115c4565b610624565b34801561021d57600080fd5b5061019e61022c3660046115c4565b61064f565b34801561023d57600080fd5b5061019e61024c36600461163d565b6106a0565b34801561025d57600080fd5b506101e37f9f225881f6e7ac8a885b63aa2269cbce78dd6a669864ccd2cd2517a8e709d73a81565b34801561029157600080fd5b5061019e6102a036600461158e565b610765565b3480156102b157600080fd5b506101e37fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177581565b3480156102e557600080fd5b5061019e6102f436600461158e565b6107b9565b34801561030557600080fd5b5061019e6103143660046116c0565b610894565b34801561032557600080fd5b506101696103343660046115c4565b60009182526002602090815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561036b57600080fd5b5061019e61037a36600461158e565b610ab0565b34801561038b57600080fd5b506101e3600081565b3480156103a057600080fd5b5061019e6103af3660046116c0565b610b04565b3480156103c057600080fd5b5061019e6103cf36600461158e565b610cb1565b3480156103e057600080fd5b5061019e6103ef3660046115c4565b610e45565b34801561040057600080fd5b506101e37fd2fb17ceaa388942529b17e0006ffc4d559f040dd4f2157b8070f17ad211057881565b60006001600160e01b031982167f7965db0b00000000000000000000000000000000000000000000000000000000148061048b57507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b03198316145b92915050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217756104bb81610e6a565b506003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b600034116105405760405162461bcd60e51b815260206004820152600f60248201527f456d707479207472616e736665722e000000000000000000000000000000000060448201526064015b60405180910390fd5b604080518082018252348082526001600160a01b03848116602093840190815284519384019290925290518116828401528251808303840181526060909201909252600354909161059891168260025b600080610e77565b6001546001600160a01b03166040517f346633fb0000000000000000000000000000000000000000000000000000000081526001600160a01b038481166004830152346024830181905292169163346633fb916044016000604051808303818588803b15801561060757600080fd5b505af115801561061b573d6000803e3d6000fd5b50505050505050565b60008281526002602052604090206001015461063f81610e6a565b6106498383610f83565b50505050565b6001600160a01b0381163314610691576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61069b8282611031565b505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217756106ca81610e6a565b6106f47f9f225881f6e7ac8a885b63aa2269cbce78dd6a669864ccd2cd2517a8e709d73a87610f83565b50600063458ffd6360e01b878787878760405160240161071895949392919061172b565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915260035490915061061b906001600160a01b0316826001610590565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561078f81610e6a565b61069b7f9f225881f6e7ac8a885b63aa2269cbce78dd6a669864ccd2cd2517a8e709d73a83611031565b6107c16110b8565b6000805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038316908117909155604080517fa1a227fa000000000000000000000000000000000000000000000000000000008152905163a1a227fa916004808201926020929091908290030181865afa158015610840573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610864919061176d565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b039290921691909117905550565b6003546000546001600160a01b039182169116331461091b5760405162461bcd60e51b815260206004820152603060248201527f436f6e74726163742063616c6c6572206973206e6f742074686520726567697360448201527f7465726564206d657373656e67657221000000000000000000000000000000006064820152608401610537565b806001600160a01b031661092d611121565b6001600160a01b0316146109a95760405162461bcd60e51b815260206004820152603160248201527f43726f737320636861696e206d65737361676520636f6d696e672066726f6d2060448201527f696e636f72726563742073656e646572210000000000000000000000000000006064820152608401610537565b6001600160a01b03841660009081527f32ef73018533fa188e9e42b313c0a4048c6052342b662fb7510c0d1abcea3413602052604090205460ff16156109f9576109f484848461119e565b610649565b6001600160a01b03841660009081527f13ad2d85210d477fe1a6e25654c8250308cf29b050a4bf0b039d70467486712c602052604090205460ff1615610a42576109f4826111a9565b60405162461bcd60e51b815260206004820152602560248201527f417474656d7074696e6720746f20776974686472617720756e6b6e6f776e206160448201527f737365742e0000000000000000000000000000000000000000000000000000006064820152608401610537565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610ada81610e6a565b61069b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177583610f83565b60008211610b545760405162461bcd60e51b815260206004820152601a60248201527f417474656d7074696e6720656d707479207472616e736665722e0000000000006044820152606401610537565b6001600160a01b03831660009081527f32ef73018533fa188e9e42b313c0a4048c6052342b662fb7510c0d1abcea3413602052604090205460ff16610c275760405162461bcd60e51b815260206004820152604e60248201527f54686973206164647265737320686173206e6f74206265656e20676976656e2060448201527f61207479706520616e64206973207468757320636f6e73696465726564206e6f60648201527f742077686974656c69737465642e000000000000000000000000000000000000608482015260a401610537565b610c338333308561124b565b604080516001600160a01b038581166024830152604482018590528381166064808401919091528351808403909101815260849092019092526020810180516001600160e01b03167f83bece4d0000000000000000000000000000000000000000000000000000000017905260035490916106499116826000610590565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff16600081158015610cfc5750825b905060008267ffffffffffffffff166001148015610d195750303b155b905081158015610d27575080155b15610d5e576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610d9257845468ff00000000000000001916680100000000000000001785555b610d9b866107b9565b610dc57fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177533610f83565b50610df17fd2fb17ceaa388942529b17e0006ffc4d559f040dd4f2157b8070f17ad21105786000610f83565b508315610e3d57845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b600082815260026020526040902060010154610e6081610e6a565b6106498383611031565b610e7481336112b2565b50565b60006040518060600160405280876001600160a01b0316815260200186815260200184815250604051602001610ead91906117da565b60408051808303601f19018152919052600180549192506001600160a01b0382169163b1454caa917401000000000000000000000000000000000000000090910463ffffffff16906014610f008361181f565b91906101000a81548163ffffffff021916908363ffffffff1602179055508684866040518563ffffffff1660e01b8152600401610f409493929190611869565b6020604051808303816000875af1158015610f5f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061061b91906118a6565b60008281526002602090815260408083206001600160a01b038516845290915281205460ff166110295760008381526002602090815260408083206001600160a01b03861684529091529020805460ff19166001179055610fe13390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a450600161048b565b50600061048b565b60008281526002602090815260408083206001600160a01b038516845290915281205460ff16156110295760008381526002602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a450600161048b565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff1661111f576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b60008060009054906101000a90046001600160a01b03166001600160a01b03166363012de56040518163ffffffff1660e01b8152600401602060405180830381865afa158015611175573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611199919061176d565b905090565b61069b838284611320565b6040516000906001600160a01b038316908281818181865af19150503d80600081146111f1576040519150601f19603f3d011682016040523d82523d6000602084013e6111f6565b606091505b50509050806112475760405162461bcd60e51b815260206004820152601460248201527f4661696c656420746f2073656e642045746865720000000000000000000000006044820152606401610537565b5050565b6040516001600160a01b0384811660248301528381166044830152606482018390526106499186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050611351565b60008281526002602090815260408083206001600160a01b038516845290915290205460ff16611247576040517fe2517d3f0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260248101839052604401610537565b6040516001600160a01b0383811660248301526044820183905261069b91859182169063a9059cbb90606401611280565b60006113666001600160a01b038416836113cd565b9050805160001415801561138b57508080602001905181019061138991906118d0565b155b1561069b576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610537565b60606113db838360006113e2565b9392505050565b606081471015611420576040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152602401610537565b600080856001600160a01b0316848660405161143c91906118f2565b60006040518083038185875af1925050503d8060008114611479576040519150601f19603f3d011682016040523d82523d6000602084013e61147e565b606091505b509150915061148e868383611498565b9695505050505050565b6060826114ad576114a88261150d565b6113db565b81511580156114c457506001600160a01b0384163b155b15611506576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610537565b50806113db565b80511561151d5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006020828403121561156157600080fd5b81356001600160e01b0319811681146113db57600080fd5b6001600160a01b0381168114610e7457600080fd5b6000602082840312156115a057600080fd5b81356113db81611579565b6000602082840312156115bd57600080fd5b5035919050565b600080604083850312156115d757600080fd5b8235915060208301356115e981611579565b809150509250929050565b60008083601f84011261160657600080fd5b50813567ffffffffffffffff81111561161e57600080fd5b60208301915083602082850101111561163657600080fd5b9250929050565b60008060008060006060868803121561165557600080fd5b853561166081611579565b9450602086013567ffffffffffffffff8082111561167d57600080fd5b61168989838a016115f4565b909650945060408801359150808211156116a257600080fd5b506116af888289016115f4565b969995985093965092949392505050565b6000806000606084860312156116d557600080fd5b83356116e081611579565b92506020840135915060408401356116f781611579565b809150509250925092565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b038616815260606020820152600061174e606083018688611702565b8281036040840152611761818587611702565b98975050505050505050565b60006020828403121561177f57600080fd5b81516113db81611579565b60005b838110156117a557818101518382015260200161178d565b50506000910152565b600081518084526117c681602086016020860161178a565b601f01601f19169290920160200192915050565b602081526001600160a01b038251166020820152600060208301516060604084015261180960808401826117ae565b9050604084015160608401528091505092915050565b600063ffffffff80831681810361185f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6001019392505050565b600063ffffffff80871683528086166020840152506080604083015261189260808301856117ae565b905060ff8316606083015295945050505050565b6000602082840312156118b857600080fd5b815167ffffffffffffffff811681146113db57600080fd5b6000602082840312156118e257600080fd5b815180151581146113db57600080fd5b6000825161190481846020870161178a565b919091019291505056fea264697066735822122061c41db942ea30cee8f9aa373bbf91f7fb86ce91e5cc4ce247e562fefa3d756864736f6c63430008140033", + Bin: "0x60806040526001805463ffffffff60a01b1916905534801561002057600080fd5b50611944806100306000396000f3fe6080604052600436106101445760003560e01c806375b238fc116100c0578063a217fddf11610074578063c4d66de811610059578063c4d66de8146103b4578063d547741f146103d4578063e4c3ebc7146103f457600080fd5b8063a217fddf1461037f578063a381c8e21461039457600080fd5b806383bece4d116100a557806383bece4d146102f957806391d148541461031957806393b374421461035f57600080fd5b806375b238fc146102a557806375cb2672146102d957600080fd5b80632f2ff15d11610117578063498d82ab116100fc578063498d82ab146102315780635d872970146102515780635fa7b5841461028557600080fd5b80632f2ff15d146101f157806336568abe1461021157600080fd5b806301ffc9a71461014957806316ce81491461017e5780631888d712146101a0578063248a9ca3146101b3575b600080fd5b34801561015557600080fd5b5061016961016436600461154f565b610428565b60405190151581526020015b60405180910390f35b34801561018a57600080fd5b5061019e61019936600461158e565b610491565b005b61019e6101ae36600461158e565b6104eb565b3480156101bf57600080fd5b506101e36101ce3660046115ab565b60009081526002602052604090206001015490565b604051908152602001610175565b3480156101fd57600080fd5b5061019e61020c3660046115c4565b610624565b34801561021d57600080fd5b5061019e61022c3660046115c4565b61064f565b34801561023d57600080fd5b5061019e61024c36600461163d565b6106a0565b34801561025d57600080fd5b506101e37f9f225881f6e7ac8a885b63aa2269cbce78dd6a669864ccd2cd2517a8e709d73a81565b34801561029157600080fd5b5061019e6102a036600461158e565b610765565b3480156102b157600080fd5b506101e37fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177581565b3480156102e557600080fd5b5061019e6102f436600461158e565b6107b9565b34801561030557600080fd5b5061019e6103143660046116c0565b610894565b34801561032557600080fd5b506101696103343660046115c4565b60009182526002602090815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561036b57600080fd5b5061019e61037a36600461158e565b610ab0565b34801561038b57600080fd5b506101e3600081565b3480156103a057600080fd5b5061019e6103af3660046116c0565b610b04565b3480156103c057600080fd5b5061019e6103cf36600461158e565b610cb1565b3480156103e057600080fd5b5061019e6103ef3660046115c4565b610e45565b34801561040057600080fd5b506101e37fd2fb17ceaa388942529b17e0006ffc4d559f040dd4f2157b8070f17ad211057881565b60006001600160e01b031982167f7965db0b00000000000000000000000000000000000000000000000000000000148061048b57507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b03198316145b92915050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217756104bb81610e6a565b506003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b600034116105405760405162461bcd60e51b815260206004820152600f60248201527f456d707479207472616e736665722e000000000000000000000000000000000060448201526064015b60405180910390fd5b604080518082018252348082526001600160a01b03848116602093840190815284519384019290925290518116828401528251808303840181526060909201909252600354909161059891168260025b600080610e77565b6001546001600160a01b03166040517f346633fb0000000000000000000000000000000000000000000000000000000081526001600160a01b038481166004830152346024830181905292169163346633fb916044016000604051808303818588803b15801561060757600080fd5b505af115801561061b573d6000803e3d6000fd5b50505050505050565b60008281526002602052604090206001015461063f81610e6a565b6106498383610f83565b50505050565b6001600160a01b0381163314610691576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61069b8282611031565b505050565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217756106ca81610e6a565b6106f47f9f225881f6e7ac8a885b63aa2269cbce78dd6a669864ccd2cd2517a8e709d73a87610f83565b50600063458ffd6360e01b878787878760405160240161071895949392919061172b565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915260035490915061061b906001600160a01b0316826001610590565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177561078f81610e6a565b61069b7f9f225881f6e7ac8a885b63aa2269cbce78dd6a669864ccd2cd2517a8e709d73a83611031565b6107c16110b8565b6000805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038316908117909155604080517fa1a227fa000000000000000000000000000000000000000000000000000000008152905163a1a227fa916004808201926020929091908290030181865afa158015610840573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610864919061176d565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b039290921691909117905550565b6003546000546001600160a01b039182169116331461091b5760405162461bcd60e51b815260206004820152603060248201527f436f6e74726163742063616c6c6572206973206e6f742074686520726567697360448201527f7465726564206d657373656e67657221000000000000000000000000000000006064820152608401610537565b806001600160a01b031661092d611121565b6001600160a01b0316146109a95760405162461bcd60e51b815260206004820152603160248201527f43726f737320636861696e206d65737361676520636f6d696e672066726f6d2060448201527f696e636f72726563742073656e646572210000000000000000000000000000006064820152608401610537565b6001600160a01b03841660009081527f32ef73018533fa188e9e42b313c0a4048c6052342b662fb7510c0d1abcea3413602052604090205460ff16156109f9576109f484848461119e565b610649565b6001600160a01b03841660009081527f13ad2d85210d477fe1a6e25654c8250308cf29b050a4bf0b039d70467486712c602052604090205460ff1615610a42576109f4826111a9565b60405162461bcd60e51b815260206004820152602560248201527f417474656d7074696e6720746f20776974686472617720756e6b6e6f776e206160448201527f737365742e0000000000000000000000000000000000000000000000000000006064820152608401610537565b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775610ada81610e6a565b61069b7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177583610f83565b60008211610b545760405162461bcd60e51b815260206004820152601a60248201527f417474656d7074696e6720656d707479207472616e736665722e0000000000006044820152606401610537565b6001600160a01b03831660009081527f32ef73018533fa188e9e42b313c0a4048c6052342b662fb7510c0d1abcea3413602052604090205460ff16610c275760405162461bcd60e51b815260206004820152604e60248201527f54686973206164647265737320686173206e6f74206265656e20676976656e2060448201527f61207479706520616e64206973207468757320636f6e73696465726564206e6f60648201527f742077686974656c69737465642e000000000000000000000000000000000000608482015260a401610537565b610c338333308561124b565b604080516001600160a01b038581166024830152604482018590528381166064808401919091528351808403909101815260849092019092526020810180516001600160e01b03167f83bece4d0000000000000000000000000000000000000000000000000000000017905260035490916106499116826000610590565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff16600081158015610cfc5750825b905060008267ffffffffffffffff166001148015610d195750303b155b905081158015610d27575080155b15610d5e576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610d9257845468ff00000000000000001916680100000000000000001785555b610d9b866107b9565b610dc57fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177533610f83565b50610df17fd2fb17ceaa388942529b17e0006ffc4d559f040dd4f2157b8070f17ad21105786000610f83565b508315610e3d57845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b600082815260026020526040902060010154610e6081610e6a565b6106498383611031565b610e7481336112b2565b50565b60006040518060600160405280876001600160a01b0316815260200186815260200184815250604051602001610ead91906117da565b60408051808303601f19018152919052600180549192506001600160a01b0382169163b1454caa917401000000000000000000000000000000000000000090910463ffffffff16906014610f008361181f565b91906101000a81548163ffffffff021916908363ffffffff1602179055508684866040518563ffffffff1660e01b8152600401610f409493929190611869565b6020604051808303816000875af1158015610f5f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061061b91906118a6565b60008281526002602090815260408083206001600160a01b038516845290915281205460ff166110295760008381526002602090815260408083206001600160a01b03861684529091529020805460ff19166001179055610fe13390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a450600161048b565b50600061048b565b60008281526002602090815260408083206001600160a01b038516845290915281205460ff16156110295760008381526002602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a450600161048b565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff1661111f576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b60008060009054906101000a90046001600160a01b03166001600160a01b03166363012de56040518163ffffffff1660e01b8152600401602060405180830381865afa158015611175573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611199919061176d565b905090565b61069b838284611320565b6040516000906001600160a01b038316908281818181865af19150503d80600081146111f1576040519150601f19603f3d011682016040523d82523d6000602084013e6111f6565b606091505b50509050806112475760405162461bcd60e51b815260206004820152601460248201527f4661696c656420746f2073656e642045746865720000000000000000000000006044820152606401610537565b5050565b6040516001600160a01b0384811660248301528381166044830152606482018390526106499186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050611351565b60008281526002602090815260408083206001600160a01b038516845290915290205460ff16611247576040517fe2517d3f0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260248101839052604401610537565b6040516001600160a01b0383811660248301526044820183905261069b91859182169063a9059cbb90606401611280565b60006113666001600160a01b038416836113cd565b9050805160001415801561138b57508080602001905181019061138991906118d0565b155b1561069b576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610537565b60606113db838360006113e2565b9392505050565b606081471015611420576040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152602401610537565b600080856001600160a01b0316848660405161143c91906118f2565b60006040518083038185875af1925050503d8060008114611479576040519150601f19603f3d011682016040523d82523d6000602084013e61147e565b606091505b509150915061148e868383611498565b9695505050505050565b6060826114ad576114a88261150d565b6113db565b81511580156114c457506001600160a01b0384163b155b15611506576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610537565b50806113db565b80511561151d5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006020828403121561156157600080fd5b81356001600160e01b0319811681146113db57600080fd5b6001600160a01b0381168114610e7457600080fd5b6000602082840312156115a057600080fd5b81356113db81611579565b6000602082840312156115bd57600080fd5b5035919050565b600080604083850312156115d757600080fd5b8235915060208301356115e981611579565b809150509250929050565b60008083601f84011261160657600080fd5b50813567ffffffffffffffff81111561161e57600080fd5b60208301915083602082850101111561163657600080fd5b9250929050565b60008060008060006060868803121561165557600080fd5b853561166081611579565b9450602086013567ffffffffffffffff8082111561167d57600080fd5b61168989838a016115f4565b909650945060408801359150808211156116a257600080fd5b506116af888289016115f4565b969995985093965092949392505050565b6000806000606084860312156116d557600080fd5b83356116e081611579565b92506020840135915060408401356116f781611579565b809150509250925092565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b038616815260606020820152600061174e606083018688611702565b8281036040840152611761818587611702565b98975050505050505050565b60006020828403121561177f57600080fd5b81516113db81611579565b60005b838110156117a557818101518382015260200161178d565b50506000910152565b600081518084526117c681602086016020860161178a565b601f01601f19169290920160200192915050565b602081526001600160a01b038251166020820152600060208301516060604084015261180960808401826117ae565b9050604084015160608401528091505092915050565b600063ffffffff80831681810361185f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6001019392505050565b600063ffffffff80871683528086166020840152506080604083015261189260808301856117ae565b905060ff8316606083015295945050505050565b6000602082840312156118b857600080fd5b815167ffffffffffffffff811681146113db57600080fd5b6000602082840312156118e257600080fd5b815180151581146113db57600080fd5b6000825161190481846020870161178a565b919091019291505056fea2646970667358221220250922b026915b246ec7ed013e9805ea7bb8beecaee481d1c9edd976f5cbaea964736f6c63430008140033", } // ObscuroBridgeABI is the input ABI used to generate the binding from. diff --git a/contracts/src/bridge/frontend/src/lib/constants.ts b/contracts/src/bridge/frontend/src/lib/constants.ts index 947d68e8f5..6006fb8955 100644 --- a/contracts/src/bridge/frontend/src/lib/constants.ts +++ b/contracts/src/bridge/frontend/src/lib/constants.ts @@ -1,6 +1,6 @@ export const socialLinks = { github: "https://github.com/ten-protocol", - discord: "https://discord.gg/QJZ39Den7d", + discord: "https://discord.gg/tenprotocol", twitter: "https://twitter.com/tenprotocol", twitterHandle: "@tenprotocol", }; diff --git a/contracts/src/management/ManagementContract.sol b/contracts/src/management/ManagementContract.sol index 9d8bef28bc..0c7a7df677 100644 --- a/contracts/src/management/ManagementContract.sol +++ b/contracts/src/management/ManagementContract.sol @@ -55,12 +55,14 @@ contract ManagementContract is Initializable, OwnableUpgradeable { MessageBus.IMessageBus public messageBus; MerkleTreeMessageBus.IMerkleTreeMessageBus public merkleMessageBus; mapping(bytes32 =>bool) public isWithdrawalSpent; + mapping(bytes32 =>bool) public isBundleSaved; bytes32 public lastBatchHash; function initialize() public initializer { __Ownable_init(msg.sender); lastBatchSeqNo = 0; + rollups.nextFreeSequenceNumber = 1; // rollup 0 == nil hash merkleMessageBus = new MerkleTreeMessageBus.MerkleTreeMessageBus(); messageBus = MessageBus.IMessageBus(address(merkleMessageBus)); @@ -72,20 +74,56 @@ contract ManagementContract is Initializable, OwnableUpgradeable { return (rol.Hash == rollupHash , rol); } + function GetRollupByNumber(uint256 number) view public returns(bool, Structs.MetaRollup memory) { + bytes32 hash = rollups.byOrder[number]; + if (hash == 0x0) { // ensure we don't try to get rollup for hash zero as that would not pull anything, but the hash would match and return true + return (false, Structs.MetaRollup(0x0, "", 0)); + } + + return GetRollupByHash(hash); + } + + function GetUniqueForkID(uint256 number) view public returns(bytes32, Structs.MetaRollup memory) { + (bool success, Structs.MetaRollup memory rollup) = GetRollupByNumber(number); + if (!success) { + return (0x0, rollup); + } + + return (rollups.toUniqueForkID[number], rollup); + } + function AppendRollup(Structs.MetaRollup calldata _r) internal { rollups.byHash[_r.Hash] = _r; + rollups.byOrder[rollups.nextFreeSequenceNumber] = _r.Hash; + rollups.toUniqueForkID[rollups.nextFreeSequenceNumber] = keccak256(abi.encode(_r.Hash, blockhash(block.number-1))); + rollups.nextFreeSequenceNumber++; + if (_r.LastSequenceNumber > lastBatchSeqNo) { lastBatchSeqNo = _r.LastSequenceNumber; } } - function addCrossChainMessagesRoot(bytes32 _lastBatchHash, bytes32 blockHash, uint256 blockNum, bytes[] memory crossChainHashes, bytes calldata signature) external { - if (block.number > blockNum + 255) { + function isBundleAvailable(bytes[] memory crossChainHashes) public view returns (bool) { + bytes32 bundleHash = bytes32(0); + + for(uint256 i = 0; i < crossChainHashes.length; i++) { + bundleHash = keccak256(abi.encode(bundleHash, bytes32(crossChainHashes[i]))); + } + + return isBundleSaved[bundleHash]; + } + + function addCrossChainMessagesRoot(bytes32 _lastBatchHash, bytes32 blockHash, uint256 blockNum, bytes[] memory crossChainHashes, bytes calldata signature, uint256 rollupNumber, bytes32 forkID) external { + /* if (block.number > blockNum + 255) { revert("Block binding too old"); } if ((blockhash(blockNum) != blockHash)) { revert(string(abi.encodePacked("Invalid block binding:", Strings.toString(block.number),":", Strings.toString(uint256(blockHash)), ":", Strings.toString(uint256(blockhash(blockNum)))))); + } */ + + if (rollups.toUniqueForkID[rollupNumber] != forkID) { + revert("Invalid forkID"); } address enclaveID = ECDSA.recover(keccak256(abi.encode(_lastBatchHash, blockHash, blockNum, crossChainHashes)), signature); @@ -93,9 +131,14 @@ contract ManagementContract is Initializable, OwnableUpgradeable { lastBatchHash = _lastBatchHash; + bytes32 bundleHash = bytes32(0); + for(uint256 i = 0; i < crossChainHashes.length; i++) { merkleMessageBus.addStateRoot(bytes32(crossChainHashes[i]), block.timestamp); //todo: change the activation time. + bundleHash = keccak256(abi.encode(bundleHash, bytes32(crossChainHashes[i]))); } + + isBundleSaved[bundleHash] = true; } // TODO: ensure challenge period is added on top of block timestamp. diff --git a/contracts/src/management/Structs.sol b/contracts/src/management/Structs.sol index 7261d55e28..336fed12a1 100644 --- a/contracts/src/management/Structs.sol +++ b/contracts/src/management/Structs.sol @@ -12,6 +12,9 @@ interface Structs { struct RollupStorage { mapping(bytes32=>MetaRollup) byHash; + mapping(uint256=>bytes32) byOrder; + mapping(uint256=>bytes32) toUniqueForkID; + uint256 nextFreeSequenceNumber; } struct HeaderCrossChainData { diff --git a/dockerfiles/enclave.Dockerfile b/dockerfiles/enclave.Dockerfile index 375d72a284..cc92f36c17 100644 --- a/dockerfiles/enclave.Dockerfile +++ b/dockerfiles/enclave.Dockerfile @@ -12,7 +12,7 @@ # Defaults to restricted flag mode ARG TESTMODE=false -FROM ghcr.io/edgelesssys/ego-dev:v1.5.0 AS build-base +FROM ghcr.io/edgelesssys/ego-dev:v1.5.3 AS build-base # setup container data structure RUN mkdir -p /home/obscuro/go-obscuro @@ -48,7 +48,7 @@ RUN ego sign enclave-test.json FROM build-enclave-testmode-${TESTMODE} as build-enclave # Trigger a new build stage and use the smaller ego version: -FROM ghcr.io/edgelesssys/ego-deploy:v1.5.0 +FROM ghcr.io/edgelesssys/ego-deploy:v1.5.3 # Copy just the binary for the enclave into this build stage COPY --from=build-enclave \ diff --git a/go.mod b/go.mod index bfddf28278..3a9695bc7e 100644 --- a/go.mod +++ b/go.mod @@ -1,27 +1,27 @@ module github.com/ten-protocol/go-ten -go 1.21 +go 1.21.11 replace github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220224222438-c78f6963a1c0+incompatible require ( github.com/FantasyJony/openzeppelin-merkle-tree-go v1.1.2 - github.com/Microsoft/go-winio v0.6.1 + github.com/Microsoft/go-winio v0.6.2 github.com/andybalholm/brotli v1.1.0 github.com/codeclysm/extract/v3 v3.1.1 github.com/deckarep/golang-set/v2 v2.6.0 github.com/dgraph-io/ristretto v0.1.1 github.com/docker/docker v25.0.4+incompatible github.com/docker/go-connections v0.5.0 - github.com/edgelesssys/ego v1.5.0 + github.com/edgelesssys/ego v1.5.3 github.com/eko/gocache/lib/v4 v4.1.5 github.com/eko/gocache/store/ristretto/v4 v4.2.1 - github.com/ethereum/go-ethereum v1.14.0 + github.com/ethereum/go-ethereum v1.14.6 github.com/fjl/memsize v0.0.2 github.com/gin-contrib/cors v1.7.0 github.com/gin-gonic/gin v1.9.1 github.com/go-kit/kit v0.13.0 - github.com/go-sql-driver/mysql v1.8.0 + github.com/go-sql-driver/mysql v1.8.1 github.com/gofrs/flock v0.8.1 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang-jwt/jwt/v4 v4.5.0 @@ -45,7 +45,7 @@ require ( github.com/urfave/cli/v2 v2.27.1 github.com/valyala/fasthttp v1.52.0 gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40 - golang.org/x/crypto v0.22.0 + golang.org/x/crypto v0.24.0 golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 golang.org/x/sync v0.7.0 google.golang.org/grpc v1.62.1 @@ -64,19 +64,20 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/bytedance/sonic v1.11.3 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect - github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/errors v1.11.3 // indirect + github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble v1.1.0 // indirect + github.com/cockroachdb/pebble v1.1.1 // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/containerd/containerd v1.7.14 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect - github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect @@ -85,12 +86,13 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ethereum/c-kzg-4844 v1.0.0 // indirect + github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect - github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-jose/go-jose/v4 v4.0.2 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -155,14 +157,11 @@ require ( github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/arch v0.7.0 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.20.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect - gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect diff --git a/go.sum b/go.sum index 1f3cdfd66b..c7c325f5ad 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/FantasyJony/openzeppelin-merkle-tree-go v1.1.2 h1:oZixv5U6eReqc1COEtng6/bVdAOAAWgtf38Ngn9Squc= github.com/FantasyJony/openzeppelin-merkle-tree-go v1.1.2/go.mod h1:OiwyYqbtMkQH+VzA4b8lI+qHnExJy0fIdz+59/8nFes= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= @@ -32,8 +32,9 @@ github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf5 github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= @@ -43,12 +44,14 @@ github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0 github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= -github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= -github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= -github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= +github.com/cockroachdb/pebble v1.1.1 h1:XnKU22oiCLy2Xn8vp1re67cXg4SAasg/WDt1NtcRFaw= +github.com/cockroachdb/pebble v1.1.1/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= @@ -63,8 +66,8 @@ github.com/containerd/containerd v1.7.14 h1:H/XLzbnGuenZEGK+v0RkwTdv2u1QFAruMe5N github.com/containerd/containerd v1.7.14/go.mod h1:YMC9Qt5yzNqXx/fO4j/5yYVIHXSRrlB3H7sxkUTvspg= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= -github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -95,16 +98,18 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/edgelesssys/ego v1.5.0 h1:euwXc69GRGlxpklIaVZtyh0v27YXzf9ow3iODE7CrPc= -github.com/edgelesssys/ego v1.5.0/go.mod h1:N58b0J+s3U4sxXeNUT5uiQV9Q9M/U2KsILC44Ku5dnw= +github.com/edgelesssys/ego v1.5.3 h1:Ec8lAjGQnKT9s+4U4o+AdSp2tYH5JN99cJMnNAfMEuU= +github.com/edgelesssys/ego v1.5.3/go.mod h1:xpgzdPWmxBGeF/d6X3Nk78hSjUfW6f05X28/jkXLRzE= github.com/eko/gocache/lib/v4 v4.1.5 h1:CeMQmdIzwBKKLRjk3FCDXzNFsQTyqJ01JLI7Ib0C9r8= github.com/eko/gocache/lib/v4 v4.1.5/go.mod h1:XaNfCwW8KYW1bRZ/KoHA1TugnnkMz0/gT51NDIu7LSY= github.com/eko/gocache/store/ristretto/v4 v4.2.1 h1:xB5E1LP1gh8yUV1G3KVRSL4T0OTnxp4OixuTljn2848= github.com/eko/gocache/store/ristretto/v4 v4.2.1/go.mod h1:KyshDyWQqfSVrg2rH06fFQZTj6vG2fxlY7oAW9oxNHY= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.14.0 h1:xRWC5NlB6g1x7vNy4HDBLuqVNbtLrc7v8S6+Uxim1LU= -github.com/ethereum/go-ethereum v1.14.0/go.mod h1:1STrq471D0BQbCX9He0hUj4bHxX2k6mt5nOQJhDNOJ8= +github.com/ethereum/go-ethereum v1.14.6 h1:ZTxnErSopkDyxdvB8zW/KcK+/AVrdil/TzoWXVKaaC8= +github.com/ethereum/go-ethereum v1.14.6/go.mod h1:hglUZo/5pVIYXNyYjWzsAUDpT/zI+WbWo/Nih7ot+G0= +github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 h1:KrE8I4reeVvf7C1tm8elRjj4BdscTYzz/WAbYyf/JI4= +github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0/go.mod h1:D9AJLVXSyZQXJQVk8oh1EwjISE+sJTn2duYIZC0dy3w= github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -117,8 +122,6 @@ github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uq github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= -github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/gin-contrib/cors v1.7.0 h1:wZX2wuZ0o7rV2/1i7gb4Jn+gW7HBqaP91fizJkBUJOA= @@ -129,6 +132,8 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= +github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= @@ -146,8 +151,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4= -github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= @@ -382,15 +387,13 @@ golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 h1:6R2FC06FonbXQ8pK11/PDFY6N6LWlf9KlzibaCapmqc= golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -399,8 +402,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -430,14 +433,14 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -445,8 +448,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -473,8 +474,6 @@ gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/go/common/cache_util.go b/go/common/cache_util.go deleted file mode 100644 index b35cc9068b..0000000000 --- a/go/common/cache_util.go +++ /dev/null @@ -1,39 +0,0 @@ -package common - -import ( - "context" - - "github.com/eko/gocache/lib/v4/cache" - gethlog "github.com/ethereum/go-ethereum/log" - "github.com/ten-protocol/go-ten/go/common/log" -) - -// GetCachedValue - returns the cached value for the provided key. If the key is not found, then invoke the 'onFailed' function -// which returns the value, and cache it -func GetCachedValue[V any](ctx context.Context, cache *cache.Cache[*V], logger gethlog.Logger, key any, onCacheMiss func(any) (*V, error)) (*V, error) { - value, err := cache.Get(ctx, key) - if err != nil || value == nil { - // todo metrics for cache misses - v, err := onCacheMiss(key) - if err != nil { - return v, err - } - if v == nil { - logger.Crit("Returned a nil value from the onCacheMiss function. Should not happen.") - } - CacheValue(ctx, cache, logger, key, v) - return v, nil - } - - return value, err -} - -func CacheValue[V any](ctx context.Context, cache *cache.Cache[*V], logger gethlog.Logger, key any, v *V) { - if v == nil { - return - } - err := cache.Set(ctx, key, v) - if err != nil { - logger.Error("Could not store value in cache", log.ErrKey, err) - } -} diff --git a/go/common/custom_query_types.go b/go/common/custom_query_types.go new file mode 100644 index 0000000000..e3e483e63b --- /dev/null +++ b/go/common/custom_query_types.go @@ -0,0 +1,29 @@ +package common + +import "github.com/ethereum/go-ethereum/common" + +// CustomQueries are Ten-specific queries that are not supported by the Ethereum RPC API but that we wish to support +// through the same interface. +// +// We currently use the eth_getStorageAt method to route these queries through the Ethereum RPC API. +// +// In order to match the eth_getStorageAt method signature, we require that all custom queries use an incrementing "address" +// to specify the method we are calling (e.g. 0x000...001 is getUserID, 0x000...002 is listPrivateTransactions). +// +// The signature is: eth_getStorageAt(method, params, nil) where: +// - method is the address of the custom query as an address (e.g. 0x000...001) +// - params is a JSON string with the parameters for the query (this complies with the eth_getStorageAt method signature since position gets encoded as a hex string) +// +// NOTE: Private custom queries must also include "address" as a top-level field in the params json object to indicate +// the account the query is being made for. + +// CustomQuery methods +const ( + UserIDRequestCQMethod = "0x0000000000000000000000000000000000000001" + ListPrivateTransactionsCQMethod = "0x0000000000000000000000000000000000000002" +) + +type ListPrivateTransactionsQueryParams struct { + Address common.Address `json:"address"` + Pagination QueryPagination `json:"pagination"` +} diff --git a/go/common/docker/docker.go b/go/common/docker/docker.go index eb115c7d9c..e299330b48 100644 --- a/go/common/docker/docker.go +++ b/go/common/docker/docker.go @@ -104,10 +104,11 @@ func StartNewContainer(containerName, image string, cmds []string, ports []int, Env: envVars, }, &container.HostConfig{ - PortBindings: portBindings, - Mounts: mountVolumes, - Resources: container.Resources{Devices: deviceMapping}, - LogConfig: container.LogConfig{Type: "json-file", Config: logOptions}, + PortBindings: portBindings, + Mounts: mountVolumes, + RestartPolicy: container.RestartPolicy{Name: "unless-stopped"}, + Resources: container.Resources{Devices: deviceMapping}, + LogConfig: container.LogConfig{Type: "json-file", Config: logOptions}, }, &network.NetworkingConfig{ EndpointsConfig: map[string]*network.EndpointSettings{ diff --git a/go/common/enclave.go b/go/common/enclave.go index d528b001c2..94bb74a03a 100644 --- a/go/common/enclave.go +++ b/go/common/enclave.go @@ -86,6 +86,8 @@ type Enclave interface { // GetCode returns the code stored at the given address in the state for the given rollup hash. GetCode(ctx context.Context, address gethcommon.Address, rollupHash *gethcommon.Hash) ([]byte, SystemError) + GetStorageSlot(ctx context.Context, encryptedParams EncryptedParamsGetStorageSlot) (*responses.EnclaveResponse, SystemError) + // Subscribe adds a log subscription to the enclave under the given ID, provided the request is authenticated // correctly. The events will be populated in the BlockSubmissionResponse. If there is an existing subscription // with the given ID, it is overwritten. @@ -141,9 +143,8 @@ type EnclaveScan interface { // GetTotalContractCount returns the total number of contracts that have been deployed GetTotalContractCount(context.Context) (*big.Int, SystemError) - // GetCustomQuery returns the data of a custom query - // todo - better name and description - GetCustomQuery(ctx context.Context, encryptedParams EncryptedParamsGetStorageAt) (*responses.PrivateQueryResponse, SystemError) + // GetPersonalTransactions returns the user's recent transactions according to specified pagination + GetPersonalTransactions(ctx context.Context, encryptedParams EncryptedParamsGetPersonalTransactions) (*responses.PersonalTransactionsResponse, SystemError) // EnclavePublicConfig returns network data that is known to the enclave but can be shared publicly EnclavePublicConfig(context.Context) (*EnclavePublicConfig, SystemError) diff --git a/go/common/errutil/errors_util.go b/go/common/errutil/errors_util.go index 2e72aeb154..bbac791733 100644 --- a/go/common/errutil/errors_util.go +++ b/go/common/errutil/errors_util.go @@ -25,6 +25,9 @@ var ( ErrAncestorBatchNotFound = errors.New("parent for batch not found") ErrCrossChainBundleRepublished = errors.New("root already added to the message bus") ErrCrossChainBundleNoBatches = errors.New("no batches for cross chain bundle") + ErrNoNextRollup = errors.New("no next rollup") + ErrRollupForkMismatch = errors.New("rollup fork mismatch") + ErrNoBundleToPublish = errors.New("no bundle to publish") ) // BlockRejectError is used as a standard format for error response from enclave for block submission errors diff --git a/go/common/gethencoding/geth_encoding.go b/go/common/gethencoding/geth_encoding.go index bac9685bfb..33f819591a 100644 --- a/go/common/gethencoding/geth_encoding.go +++ b/go/common/gethencoding/geth_encoding.go @@ -2,6 +2,7 @@ package gethencoding import ( "context" + "encoding/base64" "encoding/json" "fmt" "math/big" @@ -10,10 +11,6 @@ import ( "time" "unsafe" - "github.com/dgraph-io/ristretto" - "github.com/eko/gocache/lib/v4/cache" - ristretto_store "github.com/eko/gocache/store/ristretto/v4" - gethlog "github.com/ethereum/go-ethereum/log" "github.com/ten-protocol/go-ten/go/common/log" "github.com/ten-protocol/go-ten/go/enclave/storage" @@ -51,29 +48,16 @@ type EncodingService interface { } type gethEncodingServiceImpl struct { - // conversion is expensive. Cache the converted headers. The key is the hash of the batch. - gethHeaderCache *cache.Cache[*types.Header] - - storage storage.Storage - logger gethlog.Logger + storage storage.Storage + logger gethlog.Logger + cachingService *storage.CacheService } -func NewGethEncodingService(storage storage.Storage, logger gethlog.Logger) EncodingService { - // todo (tudor) figure out the best values - ristrettoCache, err := ristretto.NewCache(&ristretto.Config{ - NumCounters: 5000, // number of keys to track frequency of. - MaxCost: 500, // todo - this represents how many items. - BufferItems: 64, // number of keys per Get buffer. Todo - what is this - }) - if err != nil { - panic(err) - } - ristrettoStore := ristretto_store.NewRistretto(ristrettoCache) - +func NewGethEncodingService(storage storage.Storage, cachingService *storage.CacheService, logger gethlog.Logger) EncodingService { return &gethEncodingServiceImpl{ - gethHeaderCache: cache.New[*types.Header](ristrettoStore), - storage: storage, - logger: logger, + storage: storage, + logger: logger, + cachingService: cachingService, } } @@ -276,7 +260,7 @@ func ExtractEthCall(param interface{}) (*gethapi.TransactionArgs, error) { // Special care must be taken to maintain a valid chain of these converted headers. func (enc *gethEncodingServiceImpl) CreateEthHeaderForBatch(ctx context.Context, h *common.BatchHeader) (*types.Header, error) { // wrap in a caching layer - return common.GetCachedValue(ctx, enc.gethHeaderCache, enc.logger, h.Hash(), func(a any) (*types.Header, error) { + return enc.cachingService.ReadConvertedHeader(ctx, h.Hash(), func(a any) (*types.Header, error) { // deterministically calculate the private randomness that will be exposed to the EVM secret, err := enc.storage.FetchSecret(ctx) if err != nil { @@ -361,18 +345,31 @@ func (enc *gethEncodingServiceImpl) CreateEthBlockFromBatch(ctx context.Context, return (*types.Block)(unsafe.Pointer(&lb)), nil } -func ExtractPrivateCustomQuery(_ interface{}, query interface{}) (*common.PrivateCustomQueryListTransactions, error) { - // Convert the map to a JSON string - jsonData, err := json.Marshal(query) - if err != nil { - return nil, err +// ExtractPrivateTransactionsQuery is designed to support a wide range of custom Ten queries. +// The first parameter here is the method name, which is used to determine the query type. +// The second parameter is the query parameters. +func ExtractPrivateTransactionsQuery(queryParams any) (*common.ListPrivateTransactionsQueryParams, error) { + // we expect second param to be a json string + queryParamsStr, ok := queryParams.(string) + if !ok { + return nil, fmt.Errorf("expected queryParams as string but was type %T", queryParams) } - var result common.PrivateCustomQueryListTransactions - err = json.Unmarshal(jsonData, &result) + var privateQueryParams common.ListPrivateTransactionsQueryParams + err := json.Unmarshal([]byte(queryParamsStr), &privateQueryParams) if err != nil { - return nil, err + // if it fails, check if the string was base64 encoded + bytesStr, err64 := base64.StdEncoding.DecodeString(queryParamsStr) + if err64 != nil { + // was not base64 encoded, give up + return nil, fmt.Errorf("unable to unmarshal params string: %w", err) + } + // was base64 encoded, try to unmarshal + err = json.Unmarshal(bytesStr, &privateQueryParams) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal params string: %w", err) + } } - return &result, nil + return &privateQueryParams, nil } diff --git a/go/common/host/services.go b/go/common/host/services.go index 5a6be435db..970ebf22f2 100644 --- a/go/common/host/services.go +++ b/go/common/host/services.go @@ -22,6 +22,7 @@ const ( EnclaveServiceName = "enclaves" LogSubscriptionServiceName = "log-subs" FilterAPIServiceName = "filter-api" + CrossChainServiceName = "cross-chain" ) // The host has a number of services that encapsulate the various responsibilities of the host. @@ -109,7 +110,7 @@ type L1Publisher interface { PublishSecretResponse(secretResponse *common.ProducedSecretResponse) error // PublishCrossChainBundle will create and publish a cross-chain bundle tx to the management contract - PublishCrossChainBundle(bundle *common.ExtCrossChainBundle) error + PublishCrossChainBundle(*common.ExtCrossChainBundle, *big.Int, gethcommon.Hash) error FetchLatestSeqNo() (*big.Int, error) @@ -119,7 +120,7 @@ type L1Publisher interface { ResyncImportantContracts() error // GetBundleRangeFromManagementContract returns the range of batches for which to build a bundle - GetBundleRangeFromManagementContract() (*big.Int, *big.Int, error) + GetBundleRangeFromManagementContract(lastRollupNumber *big.Int, lastRollupUID gethcommon.Hash) (*gethcommon.Hash, *big.Int, *big.Int, error) } // L2BatchRepository provides an interface for the host to request L2 batch data (live-streaming and historical) diff --git a/go/common/log_events.go b/go/common/log_events.go index 5f5df48b44..0db83be12f 100644 --- a/go/common/log_events.go +++ b/go/common/log_events.go @@ -43,7 +43,7 @@ func CreateAuthenticatedLogSubscriptionPayload(args []interface{}, vk *viewingke if !ok { return nil, fmt.Errorf("invalid subscription") } - fc := FromCriteria(filterCriteria) + fc := SerializableFilterCriteria(filterCriteria) logSubscription.Filter = &fc return logSubscription, nil } @@ -59,7 +59,7 @@ type FilterCriteriaJSON struct { Topics [][]common.Hash `json:"topics"` } -func FromCriteria(crit FilterCriteria) FilterCriteriaJSON { +func SerializableFilterCriteria(crit FilterCriteria) FilterCriteriaJSON { var from *rpc.BlockNumber if crit.FromBlock != nil { f := (rpc.BlockNumber)(crit.FromBlock.Int64()) diff --git a/go/common/privacy/whitelist.go b/go/common/privacy/whitelist.go new file mode 100644 index 0000000000..e48fcee45e --- /dev/null +++ b/go/common/privacy/whitelist.go @@ -0,0 +1,40 @@ +package privacy + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/crypto" +) + +type Whitelist struct { + AllowedStorageSlots map[string]bool +} + +func NewWhitelist() *Whitelist { + whitelistMap := make(map[string]bool) + whitelistMap[toEip1967HashHex("eip1967.proxy.beacon")] = true + whitelistMap[toEip1967HashHex("eip1967.proxy.implementation")] = true + whitelistMap[toEip1967FallbackHashHex("org.zeppelinos.proxy.implementation")] = true + whitelistMap[toEip1967HashHex("eip1967.proxy.admin")] = true + whitelistMap[toEip1967FallbackHashHex("org.zeppelinos.proxy.admin")] = true + + return &Whitelist{ + AllowedStorageSlots: whitelistMap, + } +} + +func toEip1967HashHex(key string) string { + hash := crypto.Keccak256Hash([]byte(key)) + hashAsBig := hash.Big() + eipHashHex := "0x" + hashAsBig.Sub(hashAsBig, big.NewInt(1)).Text(16) + + return eipHashHex +} + +func toEip1967FallbackHashHex(key string) string { + hash := crypto.Keccak256Hash([]byte(key)) + hashAsBig := hash.Big() + eipHashHex := "0x" + hashAsBig.Text(16) + + return eipHashHex +} diff --git a/go/common/query_types.go b/go/common/query_types.go index eb6ed1f89c..5a56fe9248 100644 --- a/go/common/query_types.go +++ b/go/common/query_types.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" ) -type PrivateQueryResponse struct { +type PrivateTransactionsQueryResponse struct { Receipts types.Receipts Total uint64 } @@ -49,7 +49,6 @@ type PublicTransaction struct { type PublicBatch struct { SequencerOrderNo *big.Int `json:"sequence"` - Hash string `json:"hash"` FullHash common.Hash `json:"fullHash"` Height *big.Int `json:"height"` TxCount *big.Int `json:"txCount"` @@ -111,11 +110,6 @@ func (p *QueryPagination) UnmarshalJSON(data []byte) error { return nil } -type PrivateCustomQueryListTransactions struct { - Address common.Address `json:"address"` - Pagination QueryPagination `json:"pagination"` -} - type ObscuroNetworkInfo struct { ManagementContractAddress common.Address L1StartHash common.Hash diff --git a/go/common/rpc/generated/enclave.pb.go b/go/common/rpc/generated/enclave.pb.go index 6fa2bb7841..6fcfefa80b 100644 --- a/go/common/rpc/generated/enclave.pb.go +++ b/go/common/rpc/generated/enclave.pb.go @@ -2804,6 +2804,108 @@ func (x *GetBalanceResponse) GetSystemError() *SystemError { return nil } +type GetStorageSlotRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EncryptedParams []byte `protobuf:"bytes,1,opt,name=encryptedParams,proto3" json:"encryptedParams,omitempty"` +} + +func (x *GetStorageSlotRequest) Reset() { + *x = GetStorageSlotRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_enclave_proto_msgTypes[56] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetStorageSlotRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetStorageSlotRequest) ProtoMessage() {} + +func (x *GetStorageSlotRequest) ProtoReflect() protoreflect.Message { + mi := &file_enclave_proto_msgTypes[56] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetStorageSlotRequest.ProtoReflect.Descriptor instead. +func (*GetStorageSlotRequest) Descriptor() ([]byte, []int) { + return file_enclave_proto_rawDescGZIP(), []int{56} +} + +func (x *GetStorageSlotRequest) GetEncryptedParams() []byte { + if x != nil { + return x.EncryptedParams + } + return nil +} + +type GetStorageSlotResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EncodedEnclaveResponse []byte `protobuf:"bytes,1,opt,name=encodedEnclaveResponse,proto3" json:"encodedEnclaveResponse,omitempty"` + SystemError *SystemError `protobuf:"bytes,2,opt,name=systemError,proto3" json:"systemError,omitempty"` +} + +func (x *GetStorageSlotResponse) Reset() { + *x = GetStorageSlotResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_enclave_proto_msgTypes[57] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetStorageSlotResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetStorageSlotResponse) ProtoMessage() {} + +func (x *GetStorageSlotResponse) ProtoReflect() protoreflect.Message { + mi := &file_enclave_proto_msgTypes[57] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetStorageSlotResponse.ProtoReflect.Descriptor instead. +func (*GetStorageSlotResponse) Descriptor() ([]byte, []int) { + return file_enclave_proto_rawDescGZIP(), []int{57} +} + +func (x *GetStorageSlotResponse) GetEncodedEnclaveResponse() []byte { + if x != nil { + return x.EncodedEnclaveResponse + } + return nil +} + +func (x *GetStorageSlotResponse) GetSystemError() *SystemError { + if x != nil { + return x.SystemError + } + return nil +} + type GetCodeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2816,7 +2918,7 @@ type GetCodeRequest struct { func (x *GetCodeRequest) Reset() { *x = GetCodeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[56] + mi := &file_enclave_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2829,7 +2931,7 @@ func (x *GetCodeRequest) String() string { func (*GetCodeRequest) ProtoMessage() {} func (x *GetCodeRequest) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[56] + mi := &file_enclave_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2842,7 +2944,7 @@ func (x *GetCodeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCodeRequest.ProtoReflect.Descriptor instead. func (*GetCodeRequest) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{56} + return file_enclave_proto_rawDescGZIP(), []int{58} } func (x *GetCodeRequest) GetAddress() []byte { @@ -2871,7 +2973,7 @@ type GetCodeResponse struct { func (x *GetCodeResponse) Reset() { *x = GetCodeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[57] + mi := &file_enclave_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2884,7 +2986,7 @@ func (x *GetCodeResponse) String() string { func (*GetCodeResponse) ProtoMessage() {} func (x *GetCodeResponse) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[57] + mi := &file_enclave_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2897,7 +2999,7 @@ func (x *GetCodeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCodeResponse.ProtoReflect.Descriptor instead. func (*GetCodeResponse) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{57} + return file_enclave_proto_rawDescGZIP(), []int{59} } func (x *GetCodeResponse) GetCode() []byte { @@ -2926,7 +3028,7 @@ type SubscribeRequest struct { func (x *SubscribeRequest) Reset() { *x = SubscribeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[58] + mi := &file_enclave_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2939,7 +3041,7 @@ func (x *SubscribeRequest) String() string { func (*SubscribeRequest) ProtoMessage() {} func (x *SubscribeRequest) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[58] + mi := &file_enclave_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2952,7 +3054,7 @@ func (x *SubscribeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SubscribeRequest.ProtoReflect.Descriptor instead. func (*SubscribeRequest) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{58} + return file_enclave_proto_rawDescGZIP(), []int{60} } func (x *SubscribeRequest) GetId() []byte { @@ -2980,7 +3082,7 @@ type SubscribeResponse struct { func (x *SubscribeResponse) Reset() { *x = SubscribeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[59] + mi := &file_enclave_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2993,7 +3095,7 @@ func (x *SubscribeResponse) String() string { func (*SubscribeResponse) ProtoMessage() {} func (x *SubscribeResponse) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[59] + mi := &file_enclave_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3006,7 +3108,7 @@ func (x *SubscribeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SubscribeResponse.ProtoReflect.Descriptor instead. func (*SubscribeResponse) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{59} + return file_enclave_proto_rawDescGZIP(), []int{61} } func (x *SubscribeResponse) GetSystemError() *SystemError { @@ -3027,7 +3129,7 @@ type UnsubscribeRequest struct { func (x *UnsubscribeRequest) Reset() { *x = UnsubscribeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[60] + mi := &file_enclave_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3040,7 +3142,7 @@ func (x *UnsubscribeRequest) String() string { func (*UnsubscribeRequest) ProtoMessage() {} func (x *UnsubscribeRequest) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[60] + mi := &file_enclave_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3053,7 +3155,7 @@ func (x *UnsubscribeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UnsubscribeRequest.ProtoReflect.Descriptor instead. func (*UnsubscribeRequest) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{60} + return file_enclave_proto_rawDescGZIP(), []int{62} } func (x *UnsubscribeRequest) GetId() []byte { @@ -3074,7 +3176,7 @@ type UnsubscribeResponse struct { func (x *UnsubscribeResponse) Reset() { *x = UnsubscribeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[61] + mi := &file_enclave_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3087,7 +3189,7 @@ func (x *UnsubscribeResponse) String() string { func (*UnsubscribeResponse) ProtoMessage() {} func (x *UnsubscribeResponse) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[61] + mi := &file_enclave_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3100,7 +3202,7 @@ func (x *UnsubscribeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UnsubscribeResponse.ProtoReflect.Descriptor instead. func (*UnsubscribeResponse) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{61} + return file_enclave_proto_rawDescGZIP(), []int{63} } func (x *UnsubscribeResponse) GetSystemError() *SystemError { @@ -3121,7 +3223,7 @@ type EstimateGasRequest struct { func (x *EstimateGasRequest) Reset() { *x = EstimateGasRequest{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[62] + mi := &file_enclave_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3134,7 +3236,7 @@ func (x *EstimateGasRequest) String() string { func (*EstimateGasRequest) ProtoMessage() {} func (x *EstimateGasRequest) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[62] + mi := &file_enclave_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3147,7 +3249,7 @@ func (x *EstimateGasRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use EstimateGasRequest.ProtoReflect.Descriptor instead. func (*EstimateGasRequest) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{62} + return file_enclave_proto_rawDescGZIP(), []int{64} } func (x *EstimateGasRequest) GetEncryptedParams() []byte { @@ -3169,7 +3271,7 @@ type EstimateGasResponse struct { func (x *EstimateGasResponse) Reset() { *x = EstimateGasResponse{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[63] + mi := &file_enclave_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3182,7 +3284,7 @@ func (x *EstimateGasResponse) String() string { func (*EstimateGasResponse) ProtoMessage() {} func (x *EstimateGasResponse) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[63] + mi := &file_enclave_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3195,7 +3297,7 @@ func (x *EstimateGasResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use EstimateGasResponse.ProtoReflect.Descriptor instead. func (*EstimateGasResponse) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{63} + return file_enclave_proto_rawDescGZIP(), []int{65} } func (x *EstimateGasResponse) GetEncodedEnclaveResponse() []byte { @@ -3223,7 +3325,7 @@ type GetLogsRequest struct { func (x *GetLogsRequest) Reset() { *x = GetLogsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[64] + mi := &file_enclave_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3236,7 +3338,7 @@ func (x *GetLogsRequest) String() string { func (*GetLogsRequest) ProtoMessage() {} func (x *GetLogsRequest) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[64] + mi := &file_enclave_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3249,7 +3351,7 @@ func (x *GetLogsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLogsRequest.ProtoReflect.Descriptor instead. func (*GetLogsRequest) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{64} + return file_enclave_proto_rawDescGZIP(), []int{66} } func (x *GetLogsRequest) GetEncryptedParams() []byte { @@ -3271,7 +3373,7 @@ type GetLogsResponse struct { func (x *GetLogsResponse) Reset() { *x = GetLogsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[65] + mi := &file_enclave_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3284,7 +3386,7 @@ func (x *GetLogsResponse) String() string { func (*GetLogsResponse) ProtoMessage() {} func (x *GetLogsResponse) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[65] + mi := &file_enclave_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3297,7 +3399,7 @@ func (x *GetLogsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLogsResponse.ProtoReflect.Descriptor instead. func (*GetLogsResponse) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{65} + return file_enclave_proto_rawDescGZIP(), []int{67} } func (x *GetLogsResponse) GetEncodedEnclaveResponse() []byte { @@ -3326,7 +3428,7 @@ type HealthCheckResponse struct { func (x *HealthCheckResponse) Reset() { *x = HealthCheckResponse{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[66] + mi := &file_enclave_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3339,7 +3441,7 @@ func (x *HealthCheckResponse) String() string { func (*HealthCheckResponse) ProtoMessage() {} func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[66] + mi := &file_enclave_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3352,7 +3454,7 @@ func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use HealthCheckResponse.ProtoReflect.Descriptor instead. func (*HealthCheckResponse) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{66} + return file_enclave_proto_rawDescGZIP(), []int{68} } func (x *HealthCheckResponse) GetStatus() bool { @@ -3378,7 +3480,7 @@ type EmptyArgs struct { func (x *EmptyArgs) Reset() { *x = EmptyArgs{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[67] + mi := &file_enclave_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3391,7 +3493,7 @@ func (x *EmptyArgs) String() string { func (*EmptyArgs) ProtoMessage() {} func (x *EmptyArgs) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[67] + mi := &file_enclave_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3404,7 +3506,7 @@ func (x *EmptyArgs) ProtoReflect() protoreflect.Message { // Deprecated: Use EmptyArgs.ProtoReflect.Descriptor instead. func (*EmptyArgs) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{67} + return file_enclave_proto_rawDescGZIP(), []int{69} } type AttestationReportMsg struct { @@ -3422,7 +3524,7 @@ type AttestationReportMsg struct { func (x *AttestationReportMsg) Reset() { *x = AttestationReportMsg{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[68] + mi := &file_enclave_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3435,7 +3537,7 @@ func (x *AttestationReportMsg) String() string { func (*AttestationReportMsg) ProtoMessage() {} func (x *AttestationReportMsg) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[68] + mi := &file_enclave_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3448,7 +3550,7 @@ func (x *AttestationReportMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use AttestationReportMsg.ProtoReflect.Descriptor instead. func (*AttestationReportMsg) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{68} + return file_enclave_proto_rawDescGZIP(), []int{70} } func (x *AttestationReportMsg) GetReport() []byte { @@ -3498,7 +3600,7 @@ type BlockSubmissionResponseMsg struct { func (x *BlockSubmissionResponseMsg) Reset() { *x = BlockSubmissionResponseMsg{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[69] + mi := &file_enclave_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3511,7 +3613,7 @@ func (x *BlockSubmissionResponseMsg) String() string { func (*BlockSubmissionResponseMsg) ProtoMessage() {} func (x *BlockSubmissionResponseMsg) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[69] + mi := &file_enclave_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3524,7 +3626,7 @@ func (x *BlockSubmissionResponseMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use BlockSubmissionResponseMsg.ProtoReflect.Descriptor instead. func (*BlockSubmissionResponseMsg) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{69} + return file_enclave_proto_rawDescGZIP(), []int{71} } func (x *BlockSubmissionResponseMsg) GetProducedSecretResponses() []*SecretResponseMsg { @@ -3553,7 +3655,7 @@ type BlockSubmissionErrorMsg struct { func (x *BlockSubmissionErrorMsg) Reset() { *x = BlockSubmissionErrorMsg{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[70] + mi := &file_enclave_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3566,7 +3668,7 @@ func (x *BlockSubmissionErrorMsg) String() string { func (*BlockSubmissionErrorMsg) ProtoMessage() {} func (x *BlockSubmissionErrorMsg) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[70] + mi := &file_enclave_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3579,7 +3681,7 @@ func (x *BlockSubmissionErrorMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use BlockSubmissionErrorMsg.ProtoReflect.Descriptor instead. func (*BlockSubmissionErrorMsg) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{70} + return file_enclave_proto_rawDescGZIP(), []int{72} } func (x *BlockSubmissionErrorMsg) GetCause() string { @@ -3611,7 +3713,7 @@ type CrossChainMsg struct { func (x *CrossChainMsg) Reset() { *x = CrossChainMsg{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[71] + mi := &file_enclave_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3624,7 +3726,7 @@ func (x *CrossChainMsg) String() string { func (*CrossChainMsg) ProtoMessage() {} func (x *CrossChainMsg) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[71] + mi := &file_enclave_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3637,7 +3739,7 @@ func (x *CrossChainMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use CrossChainMsg.ProtoReflect.Descriptor instead. func (*CrossChainMsg) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{71} + return file_enclave_proto_rawDescGZIP(), []int{73} } func (x *CrossChainMsg) GetSender() []byte { @@ -3688,7 +3790,7 @@ type ExtBatchMsg struct { func (x *ExtBatchMsg) Reset() { *x = ExtBatchMsg{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[72] + mi := &file_enclave_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3701,7 +3803,7 @@ func (x *ExtBatchMsg) String() string { func (*ExtBatchMsg) ProtoMessage() {} func (x *ExtBatchMsg) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[72] + mi := &file_enclave_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3714,7 +3816,7 @@ func (x *ExtBatchMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use ExtBatchMsg.ProtoReflect.Descriptor instead. func (*ExtBatchMsg) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{72} + return file_enclave_proto_rawDescGZIP(), []int{74} } func (x *ExtBatchMsg) GetHeader() *BatchHeaderMsg { @@ -3767,7 +3869,7 @@ type BatchHeaderMsg struct { func (x *BatchHeaderMsg) Reset() { *x = BatchHeaderMsg{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[73] + mi := &file_enclave_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3780,7 +3882,7 @@ func (x *BatchHeaderMsg) String() string { func (*BatchHeaderMsg) ProtoMessage() {} func (x *BatchHeaderMsg) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[73] + mi := &file_enclave_proto_msgTypes[75] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3793,7 +3895,7 @@ func (x *BatchHeaderMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchHeaderMsg.ProtoReflect.Descriptor instead. func (*BatchHeaderMsg) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{73} + return file_enclave_proto_rawDescGZIP(), []int{75} } func (x *BatchHeaderMsg) GetParentHash() []byte { @@ -3942,7 +4044,7 @@ type ExtRollupMsg struct { func (x *ExtRollupMsg) Reset() { *x = ExtRollupMsg{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[74] + mi := &file_enclave_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3955,7 +4057,7 @@ func (x *ExtRollupMsg) String() string { func (*ExtRollupMsg) ProtoMessage() {} func (x *ExtRollupMsg) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[74] + mi := &file_enclave_proto_msgTypes[76] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3968,7 +4070,7 @@ func (x *ExtRollupMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use ExtRollupMsg.ProtoReflect.Descriptor instead. func (*ExtRollupMsg) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{74} + return file_enclave_proto_rawDescGZIP(), []int{76} } func (x *ExtRollupMsg) GetHeader() *RollupHeaderMsg { @@ -4011,7 +4113,7 @@ type RollupHeaderMsg struct { func (x *RollupHeaderMsg) Reset() { *x = RollupHeaderMsg{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[75] + mi := &file_enclave_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4024,7 +4126,7 @@ func (x *RollupHeaderMsg) String() string { func (*RollupHeaderMsg) ProtoMessage() {} func (x *RollupHeaderMsg) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[75] + mi := &file_enclave_proto_msgTypes[77] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4037,7 +4139,7 @@ func (x *RollupHeaderMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use RollupHeaderMsg.ProtoReflect.Descriptor instead. func (*RollupHeaderMsg) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{75} + return file_enclave_proto_rawDescGZIP(), []int{77} } func (x *RollupHeaderMsg) GetParentHash() []byte { @@ -4118,7 +4220,7 @@ type SecretResponseMsg struct { func (x *SecretResponseMsg) Reset() { *x = SecretResponseMsg{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[76] + mi := &file_enclave_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4131,7 +4233,7 @@ func (x *SecretResponseMsg) String() string { func (*SecretResponseMsg) ProtoMessage() {} func (x *SecretResponseMsg) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[76] + mi := &file_enclave_proto_msgTypes[78] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4144,7 +4246,7 @@ func (x *SecretResponseMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use SecretResponseMsg.ProtoReflect.Descriptor instead. func (*SecretResponseMsg) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{76} + return file_enclave_proto_rawDescGZIP(), []int{78} } func (x *SecretResponseMsg) GetSecret() []byte { @@ -4195,7 +4297,7 @@ type WithdrawalMsg struct { func (x *WithdrawalMsg) Reset() { *x = WithdrawalMsg{} if protoimpl.UnsafeEnabled { - mi := &file_enclave_proto_msgTypes[77] + mi := &file_enclave_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4208,7 +4310,7 @@ func (x *WithdrawalMsg) String() string { func (*WithdrawalMsg) ProtoMessage() {} func (x *WithdrawalMsg) ProtoReflect() protoreflect.Message { - mi := &file_enclave_proto_msgTypes[77] + mi := &file_enclave_proto_msgTypes[79] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4221,7 +4323,7 @@ func (x *WithdrawalMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use WithdrawalMsg.ProtoReflect.Descriptor instead. func (*WithdrawalMsg) Descriptor() ([]byte, []int) { - return file_enclave_proto_rawDescGZIP(), []int{77} + return file_enclave_proto_rawDescGZIP(), []int{79} } func (x *WithdrawalMsg) GetAmount() []byte { @@ -4547,6 +4649,19 @@ var file_enclave_proto_rawDesc = []byte{ 0x73, 0x74, 0x65, 0x6d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x22, 0x41, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, + 0x0f, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, + 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x8a, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x36, 0x0a, 0x16, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x45, 0x6e, 0x63, + 0x6c, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x16, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x45, 0x6e, 0x63, 0x6c, 0x61, + 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x53, 0x79, 0x73, 0x74, + 0x65, 0x6d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x4a, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, @@ -4745,7 +4860,7 @@ var file_enclave_proto_rawDesc = []byte{ 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x61, 0x63, 0x74, 0x32, 0xab, 0x15, 0x0a, 0x0c, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, + 0x74, 0x72, 0x61, 0x63, 0x74, 0x32, 0x84, 0x16, 0x0a, 0x0c, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x3f, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x65, 0x6e, @@ -4821,104 +4936,109 @@ var file_enclave_proto_rawDesc = []byte{ 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x48, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, - 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0b, - 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x65, - 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x65, 0x6e, - 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0b, - 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x47, 0x61, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x65, - 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, - 0x47, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x65, 0x6e, - 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x47, - 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x07, - 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x19, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, - 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, - 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x45, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, - 0x14, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x1e, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, - 0x64, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, 0x61, - 0x74, 0x63, 0x68, 0x12, 0x1a, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, - 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, - 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, - 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x42, 0x79, 0x53, 0x65, 0x71, 0x4e, - 0x6f, 0x12, 0x21, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, - 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x42, 0x79, 0x53, 0x65, 0x71, 0x4e, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, - 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, - 0x44, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, - 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, - 0x64, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x44, 0x61, 0x74, 0x61, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0b, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x65, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, - 0x74, 0x65, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0c, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x12, 0x1e, 0x2e, 0x67, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x6c, - 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x6c, - 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x69, 0x0a, 0x14, - 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x44, 0x61, 0x74, 0x61, 0x12, 0x26, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, - 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x67, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, - 0x72, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6c, 0x0a, 0x15, 0x44, 0x65, 0x62, 0x75, 0x67, - 0x54, 0x72, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x27, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x44, 0x65, 0x62, - 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x65, 0x6e, 0x65, + 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x20, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x6c, 0x6f, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x09, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0b, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x2e, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, + 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, + 0x65, 0x47, 0x61, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x47, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, + 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x47, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, + 0x12, 0x19, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, + 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0b, 0x48, 0x65, 0x61, + 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x14, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x1e, + 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, + 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x45, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1a, 0x2e, 0x67, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x42, 0x79, 0x53, 0x65, 0x71, 0x4e, 0x6f, 0x12, 0x21, 0x2e, 0x67, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x42, + 0x79, 0x53, 0x65, 0x71, 0x4e, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0d, + 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x2e, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, + 0x6c, 0x75, 0x70, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, + 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, + 0x6c, 0x6c, 0x75, 0x70, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x12, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1e, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x6c, + 0x75, 0x70, 0x12, 0x1e, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x69, 0x0a, 0x14, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, + 0x72, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x26, 0x2e, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, + 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x6c, 0x0a, 0x15, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4c, - 0x32, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x65, 0x64, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4c, 0x32, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x65, - 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, - 0x01, 0x12, 0x6f, 0x0a, 0x16, 0x44, 0x65, 0x62, 0x75, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, - 0x6f, 0x67, 0x52, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x2e, 0x67, 0x65, - 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x63, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, - 0x64, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x52, - 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x6c, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, - 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x27, 0x2e, 0x67, 0x65, - 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x74, 0x61, 0x6c, - 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x44, + 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, + 0x0a, 0x0f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4c, 0x32, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x73, 0x12, 0x21, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x4c, 0x32, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x6f, 0x0a, 0x16, 0x44, 0x65, + 0x62, 0x75, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x6c, 0x65, 0x76, + 0x61, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, + 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, + 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x63, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6c, 0x0a, 0x15, 0x47, + 0x65, 0x74, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x27, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, - 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x69, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, 0x42, - 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, - 0x42, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x27, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, + 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x74, + 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x69, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, 0x42, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x66, 0x0a, 0x13, 0x45, - 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x25, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, + 0x73, 0x12, 0x26, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, 0x42, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x67, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, + 0x73, 0x42, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x66, 0x0a, 0x13, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x25, 0x2e, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x50, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x42, 0x17, 0x5a, 0x15, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x2f, 0x72, - 0x70, 0x63, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x17, 0x5a, 0x15, + 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x67, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -4933,7 +5053,7 @@ func file_enclave_proto_rawDescGZIP() []byte { return file_enclave_proto_rawDescData } -var file_enclave_proto_msgTypes = make([]protoimpl.MessageInfo, 78) +var file_enclave_proto_msgTypes = make([]protoimpl.MessageInfo, 80) var file_enclave_proto_goTypes = []interface{}{ (*EnclavePublicConfigRequest)(nil), // 0: generated.EnclavePublicConfigRequest (*EnclavePublicConfigResponse)(nil), // 1: generated.EnclavePublicConfigResponse @@ -4991,28 +5111,30 @@ var file_enclave_proto_goTypes = []interface{}{ (*GetTransactionReceiptResponse)(nil), // 53: generated.GetTransactionReceiptResponse (*GetBalanceRequest)(nil), // 54: generated.GetBalanceRequest (*GetBalanceResponse)(nil), // 55: generated.GetBalanceResponse - (*GetCodeRequest)(nil), // 56: generated.GetCodeRequest - (*GetCodeResponse)(nil), // 57: generated.GetCodeResponse - (*SubscribeRequest)(nil), // 58: generated.SubscribeRequest - (*SubscribeResponse)(nil), // 59: generated.SubscribeResponse - (*UnsubscribeRequest)(nil), // 60: generated.UnsubscribeRequest - (*UnsubscribeResponse)(nil), // 61: generated.UnsubscribeResponse - (*EstimateGasRequest)(nil), // 62: generated.EstimateGasRequest - (*EstimateGasResponse)(nil), // 63: generated.EstimateGasResponse - (*GetLogsRequest)(nil), // 64: generated.GetLogsRequest - (*GetLogsResponse)(nil), // 65: generated.GetLogsResponse - (*HealthCheckResponse)(nil), // 66: generated.HealthCheckResponse - (*EmptyArgs)(nil), // 67: generated.EmptyArgs - (*AttestationReportMsg)(nil), // 68: generated.AttestationReportMsg - (*BlockSubmissionResponseMsg)(nil), // 69: generated.BlockSubmissionResponseMsg - (*BlockSubmissionErrorMsg)(nil), // 70: generated.BlockSubmissionErrorMsg - (*CrossChainMsg)(nil), // 71: generated.CrossChainMsg - (*ExtBatchMsg)(nil), // 72: generated.ExtBatchMsg - (*BatchHeaderMsg)(nil), // 73: generated.BatchHeaderMsg - (*ExtRollupMsg)(nil), // 74: generated.ExtRollupMsg - (*RollupHeaderMsg)(nil), // 75: generated.RollupHeaderMsg - (*SecretResponseMsg)(nil), // 76: generated.SecretResponseMsg - (*WithdrawalMsg)(nil), // 77: generated.WithdrawalMsg + (*GetStorageSlotRequest)(nil), // 56: generated.GetStorageSlotRequest + (*GetStorageSlotResponse)(nil), // 57: generated.GetStorageSlotResponse + (*GetCodeRequest)(nil), // 58: generated.GetCodeRequest + (*GetCodeResponse)(nil), // 59: generated.GetCodeResponse + (*SubscribeRequest)(nil), // 60: generated.SubscribeRequest + (*SubscribeResponse)(nil), // 61: generated.SubscribeResponse + (*UnsubscribeRequest)(nil), // 62: generated.UnsubscribeRequest + (*UnsubscribeResponse)(nil), // 63: generated.UnsubscribeResponse + (*EstimateGasRequest)(nil), // 64: generated.EstimateGasRequest + (*EstimateGasResponse)(nil), // 65: generated.EstimateGasResponse + (*GetLogsRequest)(nil), // 66: generated.GetLogsRequest + (*GetLogsResponse)(nil), // 67: generated.GetLogsResponse + (*HealthCheckResponse)(nil), // 68: generated.HealthCheckResponse + (*EmptyArgs)(nil), // 69: generated.EmptyArgs + (*AttestationReportMsg)(nil), // 70: generated.AttestationReportMsg + (*BlockSubmissionResponseMsg)(nil), // 71: generated.BlockSubmissionResponseMsg + (*BlockSubmissionErrorMsg)(nil), // 72: generated.BlockSubmissionErrorMsg + (*CrossChainMsg)(nil), // 73: generated.CrossChainMsg + (*ExtBatchMsg)(nil), // 74: generated.ExtBatchMsg + (*BatchHeaderMsg)(nil), // 75: generated.BatchHeaderMsg + (*ExtRollupMsg)(nil), // 76: generated.ExtRollupMsg + (*RollupHeaderMsg)(nil), // 77: generated.RollupHeaderMsg + (*SecretResponseMsg)(nil), // 78: generated.SecretResponseMsg + (*WithdrawalMsg)(nil), // 79: generated.WithdrawalMsg } var file_enclave_proto_depIdxs = []int32{ 13, // 0: generated.EnclavePublicConfigResponse.systemError:type_name -> generated.SystemError @@ -5023,19 +5145,19 @@ var file_enclave_proto_depIdxs = []int32{ 13, // 5: generated.GetTotalContractCountResponse.systemError:type_name -> generated.SystemError 13, // 6: generated.DebugEventLogRelevancyResponse.systemError:type_name -> generated.SystemError 13, // 7: generated.DebugTraceTransactionResponse.systemError:type_name -> generated.SystemError - 74, // 8: generated.CreateRollupResponse.msg:type_name -> generated.ExtRollupMsg + 76, // 8: generated.CreateRollupResponse.msg:type_name -> generated.ExtRollupMsg 13, // 9: generated.CreateRollupResponse.systemError:type_name -> generated.SystemError 13, // 10: generated.StatusResponse.systemError:type_name -> generated.SystemError - 68, // 11: generated.AttestationResponse.attestationReportMsg:type_name -> generated.AttestationReportMsg + 70, // 11: generated.AttestationResponse.attestationReportMsg:type_name -> generated.AttestationReportMsg 13, // 12: generated.AttestationResponse.systemError:type_name -> generated.SystemError 13, // 13: generated.GenerateSecretResponse.systemError:type_name -> generated.SystemError 13, // 14: generated.InitEnclaveResponse.systemError:type_name -> generated.SystemError 13, // 15: generated.EnclaveIDResponse.systemError:type_name -> generated.SystemError 13, // 16: generated.StartResponse.systemError:type_name -> generated.SystemError - 69, // 17: generated.SubmitBlockResponse.blockSubmissionResponse:type_name -> generated.BlockSubmissionResponseMsg + 71, // 17: generated.SubmitBlockResponse.blockSubmissionResponse:type_name -> generated.BlockSubmissionResponseMsg 13, // 18: generated.SubmitBlockResponse.systemError:type_name -> generated.SystemError 13, // 19: generated.SubmitTxResponse.systemError:type_name -> generated.SystemError - 72, // 20: generated.SubmitBatchRequest.batch:type_name -> generated.ExtBatchMsg + 74, // 20: generated.SubmitBatchRequest.batch:type_name -> generated.ExtBatchMsg 13, // 21: generated.SubmitBatchResponse.systemError:type_name -> generated.SystemError 13, // 22: generated.ObsCallResponse.systemError:type_name -> generated.SystemError 13, // 23: generated.GetTransactionCountResponse.systemError:type_name -> generated.SystemError @@ -5043,89 +5165,92 @@ var file_enclave_proto_depIdxs = []int32{ 13, // 25: generated.GetTransactionResponse.systemError:type_name -> generated.SystemError 13, // 26: generated.GetTransactionReceiptResponse.systemError:type_name -> generated.SystemError 13, // 27: generated.GetBalanceResponse.systemError:type_name -> generated.SystemError - 13, // 28: generated.GetCodeResponse.systemError:type_name -> generated.SystemError - 13, // 29: generated.SubscribeResponse.systemError:type_name -> generated.SystemError - 13, // 30: generated.UnsubscribeResponse.systemError:type_name -> generated.SystemError - 13, // 31: generated.EstimateGasResponse.systemError:type_name -> generated.SystemError - 13, // 32: generated.GetLogsResponse.systemError:type_name -> generated.SystemError - 13, // 33: generated.HealthCheckResponse.systemError:type_name -> generated.SystemError - 13, // 34: generated.AttestationReportMsg.systemError:type_name -> generated.SystemError - 76, // 35: generated.BlockSubmissionResponseMsg.producedSecretResponses:type_name -> generated.SecretResponseMsg - 70, // 36: generated.BlockSubmissionResponseMsg.error:type_name -> generated.BlockSubmissionErrorMsg - 73, // 37: generated.ExtBatchMsg.header:type_name -> generated.BatchHeaderMsg - 71, // 38: generated.BatchHeaderMsg.CrossChainMessages:type_name -> generated.CrossChainMsg - 75, // 39: generated.ExtRollupMsg.header:type_name -> generated.RollupHeaderMsg - 71, // 40: generated.RollupHeaderMsg.CrossChainMessages:type_name -> generated.CrossChainMsg - 13, // 41: generated.SecretResponseMsg.systemError:type_name -> generated.SystemError - 26, // 42: generated.EnclaveProto.Status:input_type -> generated.StatusRequest - 28, // 43: generated.EnclaveProto.Attestation:input_type -> generated.AttestationRequest - 30, // 44: generated.EnclaveProto.GenerateSecret:input_type -> generated.GenerateSecretRequest - 32, // 45: generated.EnclaveProto.InitEnclave:input_type -> generated.InitEnclaveRequest - 34, // 46: generated.EnclaveProto.EnclaveID:input_type -> generated.EnclaveIDRequest - 38, // 47: generated.EnclaveProto.SubmitL1Block:input_type -> generated.SubmitBlockRequest - 40, // 48: generated.EnclaveProto.SubmitTx:input_type -> generated.SubmitTxRequest - 42, // 49: generated.EnclaveProto.SubmitBatch:input_type -> generated.SubmitBatchRequest - 44, // 50: generated.EnclaveProto.ObsCall:input_type -> generated.ObsCallRequest - 46, // 51: generated.EnclaveProto.GetTransactionCount:input_type -> generated.GetTransactionCountRequest - 48, // 52: generated.EnclaveProto.Stop:input_type -> generated.StopRequest - 50, // 53: generated.EnclaveProto.GetTransaction:input_type -> generated.GetTransactionRequest - 52, // 54: generated.EnclaveProto.GetTransactionReceipt:input_type -> generated.GetTransactionReceiptRequest - 54, // 55: generated.EnclaveProto.GetBalance:input_type -> generated.GetBalanceRequest - 56, // 56: generated.EnclaveProto.GetCode:input_type -> generated.GetCodeRequest - 58, // 57: generated.EnclaveProto.Subscribe:input_type -> generated.SubscribeRequest - 60, // 58: generated.EnclaveProto.Unsubscribe:input_type -> generated.UnsubscribeRequest - 62, // 59: generated.EnclaveProto.EstimateGas:input_type -> generated.EstimateGasRequest - 64, // 60: generated.EnclaveProto.GetLogs:input_type -> generated.GetLogsRequest - 67, // 61: generated.EnclaveProto.HealthCheck:input_type -> generated.EmptyArgs - 4, // 62: generated.EnclaveProto.GetBatch:input_type -> generated.GetBatchRequest - 5, // 63: generated.EnclaveProto.GetBatchBySeqNo:input_type -> generated.GetBatchBySeqNoRequest - 7, // 64: generated.EnclaveProto.GetRollupData:input_type -> generated.GetRollupDataRequest - 20, // 65: generated.EnclaveProto.CreateBatch:input_type -> generated.CreateBatchRequest - 22, // 66: generated.EnclaveProto.CreateRollup:input_type -> generated.CreateRollupRequest - 24, // 67: generated.EnclaveProto.ExportCrossChainData:input_type -> generated.ExportCrossChainDataRequest - 18, // 68: generated.EnclaveProto.DebugTraceTransaction:input_type -> generated.DebugTraceTransactionRequest - 10, // 69: generated.EnclaveProto.StreamL2Updates:input_type -> generated.StreamL2UpdatesRequest - 16, // 70: generated.EnclaveProto.DebugEventLogRelevancy:input_type -> generated.DebugEventLogRelevancyRequest - 14, // 71: generated.EnclaveProto.GetTotalContractCount:input_type -> generated.GetTotalContractCountRequest - 2, // 72: generated.EnclaveProto.GetReceiptsByAddress:input_type -> generated.GetReceiptsByAddressRequest - 0, // 73: generated.EnclaveProto.EnclavePublicConfig:input_type -> generated.EnclavePublicConfigRequest - 27, // 74: generated.EnclaveProto.Status:output_type -> generated.StatusResponse - 29, // 75: generated.EnclaveProto.Attestation:output_type -> generated.AttestationResponse - 31, // 76: generated.EnclaveProto.GenerateSecret:output_type -> generated.GenerateSecretResponse - 33, // 77: generated.EnclaveProto.InitEnclave:output_type -> generated.InitEnclaveResponse - 35, // 78: generated.EnclaveProto.EnclaveID:output_type -> generated.EnclaveIDResponse - 39, // 79: generated.EnclaveProto.SubmitL1Block:output_type -> generated.SubmitBlockResponse - 41, // 80: generated.EnclaveProto.SubmitTx:output_type -> generated.SubmitTxResponse - 43, // 81: generated.EnclaveProto.SubmitBatch:output_type -> generated.SubmitBatchResponse - 45, // 82: generated.EnclaveProto.ObsCall:output_type -> generated.ObsCallResponse - 47, // 83: generated.EnclaveProto.GetTransactionCount:output_type -> generated.GetTransactionCountResponse - 49, // 84: generated.EnclaveProto.Stop:output_type -> generated.StopResponse - 51, // 85: generated.EnclaveProto.GetTransaction:output_type -> generated.GetTransactionResponse - 53, // 86: generated.EnclaveProto.GetTransactionReceipt:output_type -> generated.GetTransactionReceiptResponse - 55, // 87: generated.EnclaveProto.GetBalance:output_type -> generated.GetBalanceResponse - 57, // 88: generated.EnclaveProto.GetCode:output_type -> generated.GetCodeResponse - 59, // 89: generated.EnclaveProto.Subscribe:output_type -> generated.SubscribeResponse - 61, // 90: generated.EnclaveProto.Unsubscribe:output_type -> generated.UnsubscribeResponse - 63, // 91: generated.EnclaveProto.EstimateGas:output_type -> generated.EstimateGasResponse - 65, // 92: generated.EnclaveProto.GetLogs:output_type -> generated.GetLogsResponse - 66, // 93: generated.EnclaveProto.HealthCheck:output_type -> generated.HealthCheckResponse - 6, // 94: generated.EnclaveProto.GetBatch:output_type -> generated.GetBatchResponse - 6, // 95: generated.EnclaveProto.GetBatchBySeqNo:output_type -> generated.GetBatchResponse - 8, // 96: generated.EnclaveProto.GetRollupData:output_type -> generated.GetRollupDataResponse - 21, // 97: generated.EnclaveProto.CreateBatch:output_type -> generated.CreateBatchResponse - 23, // 98: generated.EnclaveProto.CreateRollup:output_type -> generated.CreateRollupResponse - 25, // 99: generated.EnclaveProto.ExportCrossChainData:output_type -> generated.ExportCrossChainDataResponse - 19, // 100: generated.EnclaveProto.DebugTraceTransaction:output_type -> generated.DebugTraceTransactionResponse - 11, // 101: generated.EnclaveProto.StreamL2Updates:output_type -> generated.EncodedUpdateResponse - 17, // 102: generated.EnclaveProto.DebugEventLogRelevancy:output_type -> generated.DebugEventLogRelevancyResponse - 15, // 103: generated.EnclaveProto.GetTotalContractCount:output_type -> generated.GetTotalContractCountResponse - 3, // 104: generated.EnclaveProto.GetReceiptsByAddress:output_type -> generated.GetReceiptsByAddressResponse - 1, // 105: generated.EnclaveProto.EnclavePublicConfig:output_type -> generated.EnclavePublicConfigResponse - 74, // [74:106] is the sub-list for method output_type - 42, // [42:74] is the sub-list for method input_type - 42, // [42:42] is the sub-list for extension type_name - 42, // [42:42] is the sub-list for extension extendee - 0, // [0:42] is the sub-list for field type_name + 13, // 28: generated.GetStorageSlotResponse.systemError:type_name -> generated.SystemError + 13, // 29: generated.GetCodeResponse.systemError:type_name -> generated.SystemError + 13, // 30: generated.SubscribeResponse.systemError:type_name -> generated.SystemError + 13, // 31: generated.UnsubscribeResponse.systemError:type_name -> generated.SystemError + 13, // 32: generated.EstimateGasResponse.systemError:type_name -> generated.SystemError + 13, // 33: generated.GetLogsResponse.systemError:type_name -> generated.SystemError + 13, // 34: generated.HealthCheckResponse.systemError:type_name -> generated.SystemError + 13, // 35: generated.AttestationReportMsg.systemError:type_name -> generated.SystemError + 78, // 36: generated.BlockSubmissionResponseMsg.producedSecretResponses:type_name -> generated.SecretResponseMsg + 72, // 37: generated.BlockSubmissionResponseMsg.error:type_name -> generated.BlockSubmissionErrorMsg + 75, // 38: generated.ExtBatchMsg.header:type_name -> generated.BatchHeaderMsg + 73, // 39: generated.BatchHeaderMsg.CrossChainMessages:type_name -> generated.CrossChainMsg + 77, // 40: generated.ExtRollupMsg.header:type_name -> generated.RollupHeaderMsg + 73, // 41: generated.RollupHeaderMsg.CrossChainMessages:type_name -> generated.CrossChainMsg + 13, // 42: generated.SecretResponseMsg.systemError:type_name -> generated.SystemError + 26, // 43: generated.EnclaveProto.Status:input_type -> generated.StatusRequest + 28, // 44: generated.EnclaveProto.Attestation:input_type -> generated.AttestationRequest + 30, // 45: generated.EnclaveProto.GenerateSecret:input_type -> generated.GenerateSecretRequest + 32, // 46: generated.EnclaveProto.InitEnclave:input_type -> generated.InitEnclaveRequest + 34, // 47: generated.EnclaveProto.EnclaveID:input_type -> generated.EnclaveIDRequest + 38, // 48: generated.EnclaveProto.SubmitL1Block:input_type -> generated.SubmitBlockRequest + 40, // 49: generated.EnclaveProto.SubmitTx:input_type -> generated.SubmitTxRequest + 42, // 50: generated.EnclaveProto.SubmitBatch:input_type -> generated.SubmitBatchRequest + 44, // 51: generated.EnclaveProto.ObsCall:input_type -> generated.ObsCallRequest + 46, // 52: generated.EnclaveProto.GetTransactionCount:input_type -> generated.GetTransactionCountRequest + 48, // 53: generated.EnclaveProto.Stop:input_type -> generated.StopRequest + 50, // 54: generated.EnclaveProto.GetTransaction:input_type -> generated.GetTransactionRequest + 52, // 55: generated.EnclaveProto.GetTransactionReceipt:input_type -> generated.GetTransactionReceiptRequest + 54, // 56: generated.EnclaveProto.GetBalance:input_type -> generated.GetBalanceRequest + 58, // 57: generated.EnclaveProto.GetCode:input_type -> generated.GetCodeRequest + 56, // 58: generated.EnclaveProto.GetStorageSlot:input_type -> generated.GetStorageSlotRequest + 60, // 59: generated.EnclaveProto.Subscribe:input_type -> generated.SubscribeRequest + 62, // 60: generated.EnclaveProto.Unsubscribe:input_type -> generated.UnsubscribeRequest + 64, // 61: generated.EnclaveProto.EstimateGas:input_type -> generated.EstimateGasRequest + 66, // 62: generated.EnclaveProto.GetLogs:input_type -> generated.GetLogsRequest + 69, // 63: generated.EnclaveProto.HealthCheck:input_type -> generated.EmptyArgs + 4, // 64: generated.EnclaveProto.GetBatch:input_type -> generated.GetBatchRequest + 5, // 65: generated.EnclaveProto.GetBatchBySeqNo:input_type -> generated.GetBatchBySeqNoRequest + 7, // 66: generated.EnclaveProto.GetRollupData:input_type -> generated.GetRollupDataRequest + 20, // 67: generated.EnclaveProto.CreateBatch:input_type -> generated.CreateBatchRequest + 22, // 68: generated.EnclaveProto.CreateRollup:input_type -> generated.CreateRollupRequest + 24, // 69: generated.EnclaveProto.ExportCrossChainData:input_type -> generated.ExportCrossChainDataRequest + 18, // 70: generated.EnclaveProto.DebugTraceTransaction:input_type -> generated.DebugTraceTransactionRequest + 10, // 71: generated.EnclaveProto.StreamL2Updates:input_type -> generated.StreamL2UpdatesRequest + 16, // 72: generated.EnclaveProto.DebugEventLogRelevancy:input_type -> generated.DebugEventLogRelevancyRequest + 14, // 73: generated.EnclaveProto.GetTotalContractCount:input_type -> generated.GetTotalContractCountRequest + 2, // 74: generated.EnclaveProto.GetReceiptsByAddress:input_type -> generated.GetReceiptsByAddressRequest + 0, // 75: generated.EnclaveProto.EnclavePublicConfig:input_type -> generated.EnclavePublicConfigRequest + 27, // 76: generated.EnclaveProto.Status:output_type -> generated.StatusResponse + 29, // 77: generated.EnclaveProto.Attestation:output_type -> generated.AttestationResponse + 31, // 78: generated.EnclaveProto.GenerateSecret:output_type -> generated.GenerateSecretResponse + 33, // 79: generated.EnclaveProto.InitEnclave:output_type -> generated.InitEnclaveResponse + 35, // 80: generated.EnclaveProto.EnclaveID:output_type -> generated.EnclaveIDResponse + 39, // 81: generated.EnclaveProto.SubmitL1Block:output_type -> generated.SubmitBlockResponse + 41, // 82: generated.EnclaveProto.SubmitTx:output_type -> generated.SubmitTxResponse + 43, // 83: generated.EnclaveProto.SubmitBatch:output_type -> generated.SubmitBatchResponse + 45, // 84: generated.EnclaveProto.ObsCall:output_type -> generated.ObsCallResponse + 47, // 85: generated.EnclaveProto.GetTransactionCount:output_type -> generated.GetTransactionCountResponse + 49, // 86: generated.EnclaveProto.Stop:output_type -> generated.StopResponse + 51, // 87: generated.EnclaveProto.GetTransaction:output_type -> generated.GetTransactionResponse + 53, // 88: generated.EnclaveProto.GetTransactionReceipt:output_type -> generated.GetTransactionReceiptResponse + 55, // 89: generated.EnclaveProto.GetBalance:output_type -> generated.GetBalanceResponse + 59, // 90: generated.EnclaveProto.GetCode:output_type -> generated.GetCodeResponse + 57, // 91: generated.EnclaveProto.GetStorageSlot:output_type -> generated.GetStorageSlotResponse + 61, // 92: generated.EnclaveProto.Subscribe:output_type -> generated.SubscribeResponse + 63, // 93: generated.EnclaveProto.Unsubscribe:output_type -> generated.UnsubscribeResponse + 65, // 94: generated.EnclaveProto.EstimateGas:output_type -> generated.EstimateGasResponse + 67, // 95: generated.EnclaveProto.GetLogs:output_type -> generated.GetLogsResponse + 68, // 96: generated.EnclaveProto.HealthCheck:output_type -> generated.HealthCheckResponse + 6, // 97: generated.EnclaveProto.GetBatch:output_type -> generated.GetBatchResponse + 6, // 98: generated.EnclaveProto.GetBatchBySeqNo:output_type -> generated.GetBatchResponse + 8, // 99: generated.EnclaveProto.GetRollupData:output_type -> generated.GetRollupDataResponse + 21, // 100: generated.EnclaveProto.CreateBatch:output_type -> generated.CreateBatchResponse + 23, // 101: generated.EnclaveProto.CreateRollup:output_type -> generated.CreateRollupResponse + 25, // 102: generated.EnclaveProto.ExportCrossChainData:output_type -> generated.ExportCrossChainDataResponse + 19, // 103: generated.EnclaveProto.DebugTraceTransaction:output_type -> generated.DebugTraceTransactionResponse + 11, // 104: generated.EnclaveProto.StreamL2Updates:output_type -> generated.EncodedUpdateResponse + 17, // 105: generated.EnclaveProto.DebugEventLogRelevancy:output_type -> generated.DebugEventLogRelevancyResponse + 15, // 106: generated.EnclaveProto.GetTotalContractCount:output_type -> generated.GetTotalContractCountResponse + 3, // 107: generated.EnclaveProto.GetReceiptsByAddress:output_type -> generated.GetReceiptsByAddressResponse + 1, // 108: generated.EnclaveProto.EnclavePublicConfig:output_type -> generated.EnclavePublicConfigResponse + 76, // [76:109] is the sub-list for method output_type + 43, // [43:76] is the sub-list for method input_type + 43, // [43:43] is the sub-list for extension type_name + 43, // [43:43] is the sub-list for extension extendee + 0, // [0:43] is the sub-list for field type_name } func init() { file_enclave_proto_init() } @@ -5807,7 +5932,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCodeRequest); i { + switch v := v.(*GetStorageSlotRequest); i { case 0: return &v.state case 1: @@ -5819,7 +5944,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCodeResponse); i { + switch v := v.(*GetStorageSlotResponse); i { case 0: return &v.state case 1: @@ -5831,7 +5956,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubscribeRequest); i { + switch v := v.(*GetCodeRequest); i { case 0: return &v.state case 1: @@ -5843,7 +5968,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubscribeResponse); i { + switch v := v.(*GetCodeResponse); i { case 0: return &v.state case 1: @@ -5855,7 +5980,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UnsubscribeRequest); i { + switch v := v.(*SubscribeRequest); i { case 0: return &v.state case 1: @@ -5867,7 +5992,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UnsubscribeResponse); i { + switch v := v.(*SubscribeResponse); i { case 0: return &v.state case 1: @@ -5879,7 +6004,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EstimateGasRequest); i { + switch v := v.(*UnsubscribeRequest); i { case 0: return &v.state case 1: @@ -5891,7 +6016,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EstimateGasResponse); i { + switch v := v.(*UnsubscribeResponse); i { case 0: return &v.state case 1: @@ -5903,7 +6028,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetLogsRequest); i { + switch v := v.(*EstimateGasRequest); i { case 0: return &v.state case 1: @@ -5915,7 +6040,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetLogsResponse); i { + switch v := v.(*EstimateGasResponse); i { case 0: return &v.state case 1: @@ -5927,7 +6052,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HealthCheckResponse); i { + switch v := v.(*GetLogsRequest); i { case 0: return &v.state case 1: @@ -5939,7 +6064,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EmptyArgs); i { + switch v := v.(*GetLogsResponse); i { case 0: return &v.state case 1: @@ -5951,7 +6076,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AttestationReportMsg); i { + switch v := v.(*HealthCheckResponse); i { case 0: return &v.state case 1: @@ -5963,7 +6088,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlockSubmissionResponseMsg); i { + switch v := v.(*EmptyArgs); i { case 0: return &v.state case 1: @@ -5975,7 +6100,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlockSubmissionErrorMsg); i { + switch v := v.(*AttestationReportMsg); i { case 0: return &v.state case 1: @@ -5987,7 +6112,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CrossChainMsg); i { + switch v := v.(*BlockSubmissionResponseMsg); i { case 0: return &v.state case 1: @@ -5999,7 +6124,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExtBatchMsg); i { + switch v := v.(*BlockSubmissionErrorMsg); i { case 0: return &v.state case 1: @@ -6011,7 +6136,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BatchHeaderMsg); i { + switch v := v.(*CrossChainMsg); i { case 0: return &v.state case 1: @@ -6023,7 +6148,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExtRollupMsg); i { + switch v := v.(*ExtBatchMsg); i { case 0: return &v.state case 1: @@ -6035,7 +6160,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RollupHeaderMsg); i { + switch v := v.(*BatchHeaderMsg); i { case 0: return &v.state case 1: @@ -6047,7 +6172,7 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SecretResponseMsg); i { + switch v := v.(*ExtRollupMsg); i { case 0: return &v.state case 1: @@ -6059,6 +6184,30 @@ func file_enclave_proto_init() { } } file_enclave_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RollupHeaderMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_enclave_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SecretResponseMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_enclave_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WithdrawalMsg); i { case 0: return &v.state @@ -6078,7 +6227,7 @@ func file_enclave_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_enclave_proto_rawDesc, NumEnums: 0, - NumMessages: 78, + NumMessages: 80, NumExtensions: 0, NumServices: 1, }, diff --git a/go/common/rpc/generated/enclave.proto b/go/common/rpc/generated/enclave.proto index 6ea5be4800..4ed20ae996 100644 --- a/go/common/rpc/generated/enclave.proto +++ b/go/common/rpc/generated/enclave.proto @@ -59,6 +59,9 @@ service EnclaveProto { // GetCode returns the code stored at the given address in the state for the given rollup height or rollup hash rpc GetCode(GetCodeRequest) returns (GetCodeResponse) {} + rpc GetStorageSlot(GetStorageSlotRequest) returns (GetStorageSlotResponse) {} + + rpc Subscribe(SubscribeRequest) returns (SubscribeResponse) {} rpc Unsubscribe(UnsubscribeRequest) returns (UnsubscribeResponse) {} @@ -320,6 +323,14 @@ message GetBalanceResponse { SystemError systemError = 2; } +message GetStorageSlotRequest { + bytes encryptedParams = 1; +} +message GetStorageSlotResponse { + bytes encodedEnclaveResponse = 1; + SystemError systemError = 2; +} + message GetCodeRequest { bytes address = 1; bytes rollupHash = 2; diff --git a/go/common/rpc/generated/enclave_grpc.pb.go b/go/common/rpc/generated/enclave_grpc.pb.go index d1fd02ebc0..748bced462 100644 --- a/go/common/rpc/generated/enclave_grpc.pb.go +++ b/go/common/rpc/generated/enclave_grpc.pb.go @@ -34,6 +34,7 @@ const ( EnclaveProto_GetTransactionReceipt_FullMethodName = "/generated.EnclaveProto/GetTransactionReceipt" EnclaveProto_GetBalance_FullMethodName = "/generated.EnclaveProto/GetBalance" EnclaveProto_GetCode_FullMethodName = "/generated.EnclaveProto/GetCode" + EnclaveProto_GetStorageSlot_FullMethodName = "/generated.EnclaveProto/GetStorageSlot" EnclaveProto_Subscribe_FullMethodName = "/generated.EnclaveProto/Subscribe" EnclaveProto_Unsubscribe_FullMethodName = "/generated.EnclaveProto/Unsubscribe" EnclaveProto_EstimateGas_FullMethodName = "/generated.EnclaveProto/EstimateGas" @@ -97,6 +98,7 @@ type EnclaveProtoClient interface { GetBalance(ctx context.Context, in *GetBalanceRequest, opts ...grpc.CallOption) (*GetBalanceResponse, error) // GetCode returns the code stored at the given address in the state for the given rollup height or rollup hash GetCode(ctx context.Context, in *GetCodeRequest, opts ...grpc.CallOption) (*GetCodeResponse, error) + GetStorageSlot(ctx context.Context, in *GetStorageSlotRequest, opts ...grpc.CallOption) (*GetStorageSlotResponse, error) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (*SubscribeResponse, error) Unsubscribe(ctx context.Context, in *UnsubscribeRequest, opts ...grpc.CallOption) (*UnsubscribeResponse, error) // EstimateGas returns the estimation of gas used for the given transactions @@ -265,6 +267,15 @@ func (c *enclaveProtoClient) GetCode(ctx context.Context, in *GetCodeRequest, op return out, nil } +func (c *enclaveProtoClient) GetStorageSlot(ctx context.Context, in *GetStorageSlotRequest, opts ...grpc.CallOption) (*GetStorageSlotResponse, error) { + out := new(GetStorageSlotResponse) + err := c.cc.Invoke(ctx, EnclaveProto_GetStorageSlot_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *enclaveProtoClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (*SubscribeResponse, error) { out := new(SubscribeResponse) err := c.cc.Invoke(ctx, EnclaveProto_Subscribe_FullMethodName, in, out, opts...) @@ -485,6 +496,7 @@ type EnclaveProtoServer interface { GetBalance(context.Context, *GetBalanceRequest) (*GetBalanceResponse, error) // GetCode returns the code stored at the given address in the state for the given rollup height or rollup hash GetCode(context.Context, *GetCodeRequest) (*GetCodeResponse, error) + GetStorageSlot(context.Context, *GetStorageSlotRequest) (*GetStorageSlotResponse, error) Subscribe(context.Context, *SubscribeRequest) (*SubscribeResponse, error) Unsubscribe(context.Context, *UnsubscribeRequest) (*UnsubscribeResponse, error) // EstimateGas returns the estimation of gas used for the given transactions @@ -560,6 +572,9 @@ func (UnimplementedEnclaveProtoServer) GetBalance(context.Context, *GetBalanceRe func (UnimplementedEnclaveProtoServer) GetCode(context.Context, *GetCodeRequest) (*GetCodeResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetCode not implemented") } +func (UnimplementedEnclaveProtoServer) GetStorageSlot(context.Context, *GetStorageSlotRequest) (*GetStorageSlotResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetStorageSlot not implemented") +} func (UnimplementedEnclaveProtoServer) Subscribe(context.Context, *SubscribeRequest) (*SubscribeResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Subscribe not implemented") } @@ -894,6 +909,24 @@ func _EnclaveProto_GetCode_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _EnclaveProto_GetStorageSlot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetStorageSlotRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EnclaveProtoServer).GetStorageSlot(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: EnclaveProto_GetStorageSlot_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EnclaveProtoServer).GetStorageSlot(ctx, req.(*GetStorageSlotRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _EnclaveProto_Subscribe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(SubscribeRequest) if err := dec(in); err != nil { @@ -1270,6 +1303,10 @@ var EnclaveProto_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetCode", Handler: _EnclaveProto_GetCode_Handler, }, + { + MethodName: "GetStorageSlot", + Handler: _EnclaveProto_GetStorageSlot_Handler, + }, { MethodName: "Subscribe", Handler: _EnclaveProto_Subscribe_Handler, diff --git a/go/common/types.go b/go/common/types.go index 12442745f6..33654c3ea7 100644 --- a/go/common/types.go +++ b/go/common/types.go @@ -63,16 +63,17 @@ type ( EncryptedTx []byte // A single transaction, encoded as a JSON list of transaction binary hexes and encrypted using the enclave's public key EncryptedTransactions []byte // A blob of encrypted transactions, as they're stored in the rollup, with the nonce prepended. - EncryptedParamsGetBalance []byte // The params for an RPC getBalance request, as a JSON object encrypted with the public key of the enclave. - EncryptedParamsCall []byte // As above, but for an RPC call request. - EncryptedParamsGetTxByHash []byte // As above, but for an RPC getTransactionByHash request. - EncryptedParamsGetTxReceipt []byte // As above, but for an RPC getTransactionReceipt request. - EncryptedParamsLogSubscription []byte // As above, but for an RPC logs subscription request. - EncryptedParamsSendRawTx []byte // As above, but for an RPC sendRawTransaction request. - EncryptedParamsGetTxCount []byte // As above, but for an RPC getTransactionCount request. - EncryptedParamsEstimateGas []byte // As above, but for an RPC estimateGas request. - EncryptedParamsGetLogs []byte // As above, but for an RPC getLogs request. - EncryptedParamsGetStorageAt []byte + EncryptedParamsGetBalance []byte // The params for an RPC getBalance request, as a JSON object encrypted with the public key of the enclave. + EncryptedParamsCall []byte // As above, but for an RPC call request. + EncryptedParamsGetTxByHash []byte // As above, but for an RPC getTransactionByHash request. + EncryptedParamsGetTxReceipt []byte // As above, but for an RPC getTransactionReceipt request. + EncryptedParamsLogSubscription []byte // As above, but for an RPC logs subscription request. + EncryptedParamsSendRawTx []byte // As above, but for an RPC sendRawTransaction request. + EncryptedParamsGetTxCount []byte // As above, but for an RPC getTransactionCount request. + EncryptedParamsEstimateGas []byte // As above, but for an RPC estimateGas request. + EncryptedParamsGetLogs []byte // As above, but for an RPC getLogs request. + EncryptedParamsGetPersonalTransactions []byte + EncryptedParamsGetStorageSlot []byte Nonce = uint64 EncodedRollup []byte diff --git a/go/enclave/components/consumer_test.go b/go/enclave/components/consumer_test.go index dc99fbc7f5..34b3032705 100644 --- a/go/enclave/components/consumer_test.go +++ b/go/enclave/components/consumer_test.go @@ -25,7 +25,7 @@ func TestInvalidBlocksAreRejected(t *testing.T) { for _, header := range invalidHeaders { loopHeader := header - _, err := blockConsumer.ingestBlock(context.Background(), types.NewBlock(&loopHeader, nil, nil, nil, &trie.StackTrie{})) + _, err := blockConsumer.ingestBlock(context.Background(), types.NewBlock(&loopHeader, nil, nil, &trie.StackTrie{})) if err == nil { t.Errorf("expected block with invalid header to be rejected but was accepted") } diff --git a/go/enclave/crosschain/common.go b/go/enclave/crosschain/common.go index 0acd0025bc..0d5c674e7b 100644 --- a/go/enclave/crosschain/common.go +++ b/go/enclave/crosschain/common.go @@ -221,7 +221,7 @@ func (ms MessageStructs) HashPacked(index int) gethcommon.Hash { addrType, _ := abi.NewType("address", "", nil) uint64Type, _ := abi.NewType("uint64", "", nil) uint32Type, _ := abi.NewType("uint32", "", nil) - uint8Type, _ := abi.NewType("uint32", "", nil) + uint8Type, _ := abi.NewType("uint8", "", nil) bytesType, _ := abi.NewType("bytes", "", nil) args := abi.Arguments{ { @@ -245,7 +245,10 @@ func (ms MessageStructs) HashPacked(index int) gethcommon.Hash { } // todo @siliev: err - packed, _ := args.Pack(messageStruct.Sender, messageStruct.Sequence, messageStruct.Nonce, messageStruct.Topic, messageStruct.Payload, messageStruct.ConsistencyLevel) + packed, err := args.Pack(messageStruct.Sender, messageStruct.Sequence, messageStruct.Nonce, messageStruct.Topic, messageStruct.Payload, messageStruct.ConsistencyLevel) + if err != nil { + panic(err) + } hash := crypto.Keccak256Hash(packed) return hash } @@ -298,7 +301,10 @@ func (vt ValueTransfers) HashPacked(index int) gethcommon.Hash { }, } - bytes, _ := args.Pack(valueTransfer.Sender, valueTransfer.Receiver, valueTransfer.Amount, valueTransfer.Sequence) + bytes, err := args.Pack(valueTransfer.Sender, valueTransfer.Receiver, valueTransfer.Amount, valueTransfer.Sequence) + if err != nil { + panic(err) + } hash := crypto.Keccak256Hash(bytes) return hash diff --git a/go/enclave/crosschain/message_bus_manager.go b/go/enclave/crosschain/message_bus_manager.go index 26b3b5249e..cb59ab764b 100644 --- a/go/enclave/crosschain/message_bus_manager.go +++ b/go/enclave/crosschain/message_bus_manager.go @@ -202,6 +202,7 @@ const BalanceIncreaseXChainValueTransfer tracing.BalanceChangeReason = 110 func (m *MessageBusManager) ExecuteValueTransfers(ctx context.Context, transfers common.ValueTransferEvents, rollupState *state.StateDB) { for _, transfer := range transfers { rollupState.AddBalance(transfer.Receiver, uint256.MustFromBig(transfer.Amount), BalanceIncreaseXChainValueTransfer) + m.logger.Debug(fmt.Sprintf("Executed cross chain value transfer from %s to %s with amount %s", transfer.Sender.Hex(), transfer.Receiver.Hex(), transfer.Amount.String())) } } diff --git a/go/enclave/enclave.go b/go/enclave/enclave.go index a12e7a7028..ea2b930956 100644 --- a/go/enclave/enclave.go +++ b/go/enclave/enclave.go @@ -114,8 +114,9 @@ func NewEnclave( } // Initialise the database + cachingService := storage.NewCacheService(logger) chainConfig := ethchainadapter.ChainParams(big.NewInt(config.ObscuroChainID)) - storage := storage.NewStorageFromConfig(config, chainConfig, logger) + storage := storage.NewStorageFromConfig(config, cachingService, chainConfig, logger) // Initialise the Ethereum "Blockchain" structure that will allow us to validate incoming blocks // todo (#1056) - valid block @@ -160,7 +161,7 @@ func NewEnclave( obscuroKey := crypto.GetObscuroKey(logger) - gethEncodingService := gethencoding.NewGethEncodingService(storage, logger) + gethEncodingService := gethencoding.NewGethEncodingService(storage, cachingService, logger) dataEncryptionService := crypto.NewDataEncryptionService(logger) dataCompressionService := compression.NewBrotliDataCompressionService() @@ -697,6 +698,14 @@ func (e *enclaveImpl) GetCode(ctx context.Context, address gethcommon.Address, b return stateDB.GetCode(address), nil } +func (e *enclaveImpl) GetStorageSlot(ctx context.Context, encryptedParams common.EncryptedParamsGetStorageSlot) (*responses.EnclaveResponse, common.SystemError) { + if e.stopControl.IsStopping() { + return nil, responses.ToInternalError(fmt.Errorf("requested GetCode with the enclave stopping")) + } + + return rpc.WithVKEncryption(ctx, e.rpcEncryptionManager, encryptedParams, rpc.TenStorageReadValidate, rpc.TenStorageReadExecute) +} + func (e *enclaveImpl) Subscribe(ctx context.Context, id gethrpc.ID, encryptedSubscription common.EncryptedParamsLogSubscription) common.SystemError { if e.stopControl.IsStopping() { return responses.ToInternalError(fmt.Errorf("requested SubscribeForExecutedBatches with the enclave stopping")) @@ -854,13 +863,14 @@ func (e *enclaveImpl) GetTotalContractCount(ctx context.Context) (*big.Int, comm return e.storage.GetContractCount(ctx) } -func (e *enclaveImpl) GetCustomQuery(ctx context.Context, encryptedParams common.EncryptedParamsGetStorageAt) (*responses.PrivateQueryResponse, common.SystemError) { +// GetPersonalTransactions returns the recent private transactions for the given account +func (e *enclaveImpl) GetPersonalTransactions(ctx context.Context, encryptedParams common.EncryptedParamsGetPersonalTransactions) (*responses.PersonalTransactionsResponse, common.SystemError) { // ensure the enclave is running if e.stopControl.IsStopping() { - return nil, responses.ToInternalError(fmt.Errorf("requested GetReceiptsByAddress with the enclave stopping")) + return nil, responses.ToInternalError(fmt.Errorf("requested GetPrivateTransactions with the enclave stopping")) } - return rpc.WithVKEncryption(ctx, e.rpcEncryptionManager, encryptedParams, rpc.GetCustomQueryValidate, rpc.GetCustomQueryExecute) + return rpc.WithVKEncryption(ctx, e.rpcEncryptionManager, encryptedParams, rpc.GetPersonalTransactionsValidate, rpc.GetPersonalTransactionsExecute) } func (e *enclaveImpl) EnclavePublicConfig(context.Context) (*common.EnclavePublicConfig, common.SystemError) { diff --git a/go/enclave/events/subscription_manager.go b/go/enclave/events/subscription_manager.go index 27e6515d41..5641d09b62 100644 --- a/go/enclave/events/subscription_manager.go +++ b/go/enclave/events/subscription_manager.go @@ -11,24 +11,15 @@ import ( "github.com/ten-protocol/go-ten/go/enclave/vkhandler" gethrpc "github.com/ten-protocol/go-ten/lib/gethfork/rpc" - "github.com/ten-protocol/go-ten/go/common/log" - "github.com/ten-protocol/go-ten/go/enclave/core" "github.com/ten-protocol/go-ten/go/enclave/storage" - gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" gethlog "github.com/ethereum/go-ethereum/log" "github.com/ten-protocol/go-ten/go/common" ) -const ( - // The leading zero bytes in a hash indicating that it is possibly an address, since it only has 20 bytes of data. - zeroBytesHex = "000000000000000000000000" -) - type logSubscription struct { Subscription *common.LogSubscription // Handles the viewing key encryption @@ -92,24 +83,6 @@ func (s *SubscriptionManager) RemoveSubscription(id gethrpc.ID) { delete(s.subscriptions, id) } -// FilterLogsForReceipt removes the logs that the sender of a transaction is not allowed to view -func FilterLogsForReceipt(ctx context.Context, receipt *types.Receipt, account *gethcommon.Address, registry components.BatchRegistry) ([]*types.Log, error) { - var filteredLogs []*types.Log - stateDB, err := registry.GetBatchState(ctx, &receipt.BlockHash) - if err != nil { - return nil, fmt.Errorf("could not create state DB to filter logs. Cause: %w", err) - } - - for _, logItem := range receipt.Logs { - userAddrs := getUserAddrsFromLogTopics(logItem, stateDB) - if isRelevant(account, userAddrs) { - filteredLogs = append(filteredLogs, logItem) - } - } - - return filteredLogs, nil -} - // GetSubscribedLogsForBatch - Retrieves and encrypts the logs for the batch in live mode. // The assumption is that this function is called synchronously after the batch is produced func (s *SubscriptionManager) GetSubscribedLogsForBatch(ctx context.Context, batch *core.Batch, receipts types.Receipts) (common.EncryptedSubscriptionLogs, error) { @@ -121,47 +94,17 @@ func (s *SubscriptionManager) GetSubscribedLogsForBatch(ctx context.Context, bat return nil, nil } + h := batch.Hash() relevantLogsPerSubscription := map[gethrpc.ID][]*types.Log{} - // extract the logs from all receipts - var allLogs []*types.Log - for _, receipt := range receipts { - allLogs = append(allLogs, receipt.Logs...) - } - - if len(allLogs) == 0 { + if len(receipts) == 0 { return nil, nil } - // the stateDb is needed to extract the user addresses from the topics - h := batch.Hash() - stateDB, err := s.registry.GetBatchState(ctx, &h) - if err != nil { - return nil, fmt.Errorf("could not create state DB to filter logs. Cause: %w", err) - } - - // cache for the user addresses extracted from the individual logs - // this is an expensive operation so we are doing it lazy, and caching the result - userAddrsForLog := map[*types.Log][]*gethcommon.Address{} - for id, sub := range s.subscriptions { - // first filter the logs - filteredLogs := filterLogs(allLogs, sub.Subscription.Filter.FromBlock, sub.Subscription.Filter.ToBlock, sub.Subscription.Filter.Addresses, sub.Subscription.Filter.Topics, s.logger) - - // the account requesting the logs is retrieved from the Viewing Key - requestingAccount := sub.ViewingKeyEncryptor.AccountAddress - relevantLogsForSub := []*types.Log{} - for _, logItem := range filteredLogs { - userAddrs, f := userAddrsForLog[logItem] - if !f { - userAddrs = getUserAddrsFromLogTopics(logItem, stateDB) - userAddrsForLog[logItem] = userAddrs - } - relevant := isRelevant(requestingAccount, userAddrs) - if relevant { - relevantLogsForSub = append(relevantLogsForSub, logItem) - } - s.logger.Debug("Subscription", log.SubIDKey, id, "acc", requestingAccount, "log", logItem, "extr_addr", userAddrs, "relev", relevant) + relevantLogsForSub, err := s.storage.FilterLogs(ctx, sub.ViewingKeyEncryptor.AccountAddress, nil, nil, &h, sub.Subscription.Filter.Addresses, sub.Subscription.Filter.Topics) + if err != nil { + return nil, err } if len(relevantLogsForSub) > 0 { relevantLogsPerSubscription[id] = relevantLogsForSub @@ -172,19 +115,6 @@ func (s *SubscriptionManager) GetSubscribedLogsForBatch(ctx context.Context, bat return s.encryptLogs(relevantLogsPerSubscription) } -func isRelevant(sub *gethcommon.Address, userAddrs []*gethcommon.Address) bool { - // If there are no user addresses, this is a lifecycle event, and is therefore relevant to everyone. - if len(userAddrs) == 0 { - return true - } - for _, addr := range userAddrs { - if *addr == *sub { - return true - } - } - return false -} - // Encrypts each log with the appropriate viewing key. func (s *SubscriptionManager) encryptLogs(logsByID map[gethrpc.ID][]*types.Log) (map[gethrpc.ID][]byte, error) { encryptedLogsByID := map[gethrpc.ID][]byte{} @@ -210,85 +140,3 @@ func (s *SubscriptionManager) encryptLogs(logsByID map[gethrpc.ID][]*types.Log) return encryptedLogsByID, nil } - -// Of the log's topics, returns those that are (potentially) user addresses. A topic is considered a user address if: -// - It has 12 leading zero bytes (since addresses are 20 bytes long, while hashes are 32) -// - It has a non-zero nonce (to prevent accidental or malicious creation of the address matching a given topic, -// forcing its events to become permanently private -// - It does not have associated code (meaning it's a smart-contract address) -func getUserAddrsFromLogTopics(log *types.Log, db *state.StateDB) []*gethcommon.Address { - var userAddrs []*gethcommon.Address - - // We skip over the first topic, which is always the hash of the event. - for _, topic := range log.Topics[1:len(log.Topics)] { - if topic.Hex()[2:len(zeroBytesHex)+2] != zeroBytesHex { - continue - } - - potentialAddr := gethcommon.BytesToAddress(topic.Bytes()) - - // A user address must have a non-zero nonce. This prevents accidental or malicious sending of funds to an - // address matching a topic, forcing its events to become permanently private. - if db.GetNonce(potentialAddr) != 0 { - // If the address has code, it's a smart contract address instead. - if db.GetCode(potentialAddr) == nil { - userAddrs = append(userAddrs, &potentialAddr) - } - } - } - - return userAddrs -} - -// Lifted from eth/filters/filter.go in the go-ethereum repository. -// filterLogs creates a slice of logs matching the given criteria. -func filterLogs(logs []*types.Log, fromBlock, toBlock *gethrpc.BlockNumber, addresses []gethcommon.Address, topics [][]gethcommon.Hash, logger gethlog.Logger) []*types.Log { //nolint:gocognit - var ret []*types.Log -Logs: - for _, logItem := range logs { - if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Int64() > int64(logItem.BlockNumber) { - logger.Debug("Skipping log ", "log", logItem, "reason", "In the past. The starting block num for filter is bigger than log") - continue - } - if toBlock != nil && toBlock.Int64() > 0 && toBlock.Int64() < int64(logItem.BlockNumber) { - logger.Debug("Skipping log ", "log", logItem, "reason", "In the future. The ending block num for filter is smaller than log") - continue - } - - if len(addresses) > 0 && !includes(addresses, logItem.Address) { - logger.Debug("Skipping log ", "log", logItem, "reason", "The contract address of the log is not an address of interest") - continue - } - // If the to filtered topics is greater than the amount of topics in logs, skip. - if len(topics) > len(logItem.Topics) { - logger.Debug("Skipping log ", "log", logItem, "reason", "Insufficient topics. The log has less topics than the required one to satisfy the query") - continue - } - for i, sub := range topics { - match := len(sub) == 0 // empty rule set == wildcard - for _, topic := range sub { - if logItem.Topics[i] == topic { - match = true - break - } - } - if !match { - logger.Debug("Skipping log ", "log", logItem, "reason", "Topics do not match.") - continue Logs - } - } - ret = append(ret, logItem) - } - return ret -} - -// Lifted from eth/filters/filter.go in the go-ethereum repository. -func includes(addresses []gethcommon.Address, a gethcommon.Address) bool { - for _, addr := range addresses { - if addr == a { - return true - } - } - - return false -} diff --git a/go/enclave/evm/ethchainadapter/eth_chainparams.go b/go/enclave/evm/ethchainadapter/eth_chainparams.go index 7e591c0b53..30d21dd38f 100644 --- a/go/enclave/evm/ethchainadapter/eth_chainparams.go +++ b/go/enclave/evm/ethchainadapter/eth_chainparams.go @@ -1,6 +1,7 @@ package ethchainadapter import ( + "math" "math/big" gethcommon "github.com/ethereum/go-ethereum/common" @@ -11,6 +12,7 @@ import ( // obscuro should typically be on the last fork version func ChainParams(obscuroChainID *big.Int) *params.ChainConfig { zeroTimestamp := uint64(0) + maxTimestamp := uint64(math.MaxUint64) // Initialise the database return ¶ms.ChainConfig{ @@ -31,6 +33,6 @@ func ChainParams(obscuroChainID *big.Int) *params.ChainConfig { CancunTime: &zeroTimestamp, ShanghaiTime: &zeroTimestamp, PragueTime: &zeroTimestamp, - VerkleTime: &zeroTimestamp, + VerkleTime: &maxTimestamp, } } diff --git a/go/enclave/genesis/genesis_test.go b/go/enclave/genesis/genesis_test.go index 66ef8f86b8..165caf653c 100644 --- a/go/enclave/genesis/genesis_test.go +++ b/go/enclave/genesis/genesis_test.go @@ -43,7 +43,7 @@ func TestDefaultGenesis(t *testing.T) { if err != nil { t.Fatalf("unable to create temp db: %s", err) } - storageDB := storage.NewStorage(backingDB, nil, gethlog.New()) + storageDB := storage.NewStorage(backingDB, storage.NewCacheService(gethlog.New()), nil, gethlog.New()) stateDB, err := gen.applyAllocations(storageDB) if err != nil { t.Fatalf("unable to apply genesis allocations") @@ -86,7 +86,7 @@ func TestCustomGenesis(t *testing.T) { if err != nil { t.Fatalf("unable to create temp db: %s", err) } - storageDB := storage.NewStorage(backingDB, nil, gethlog.New()) + storageDB := storage.NewStorage(backingDB, storage.NewCacheService(gethlog.New()), nil, gethlog.New()) stateDB, err := gen.applyAllocations(storageDB) if err != nil { t.Fatalf("unable to apply genesis allocations") diff --git a/go/enclave/nodetype/common.go b/go/enclave/nodetype/common.go index a0c083ac4d..c414f63c81 100644 --- a/go/enclave/nodetype/common.go +++ b/go/enclave/nodetype/common.go @@ -20,7 +20,13 @@ func ExportCrossChainData(ctx context.Context, storage storage.Storage, fromSeqN return nil, errutil.ErrCrossChainBundleNoBatches } - blockHash := canonicalBatches[len(canonicalBatches)-1].L1Proof + // todo - siliev - all those fetches need to be atomic + header, err := storage.FetchHeadBatchHeader(ctx) + if err != nil { + return nil, err + } + + blockHash := header.L1Proof batchHash := canonicalBatches[len(canonicalBatches)-1].Hash() block, err := storage.FetchBlock(ctx, blockHash) diff --git a/go/enclave/rpc/EstimateGas.go b/go/enclave/rpc/EstimateGas.go index f09a3d4b96..0873ec35ee 100644 --- a/go/enclave/rpc/EstimateGas.go +++ b/go/enclave/rpc/EstimateGas.go @@ -94,7 +94,7 @@ func EstimateGasExecute(builder *CallBuilder[CallParamsWithBlock, hexutil.Uint64 // TODO: Change to fixed time period quotes, rather than this. publishingGas = publishingGas.Mul(publishingGas, gethcommon.Big2) - executionGasEstimate, err := rpc.doEstimateGas(builder.ctx, txArgs, blockNumber, rpc.config.GasLocalExecutionCapFlag) + executionGasEstimate, gasPrice, err := rpc.doEstimateGas(builder.ctx, txArgs, blockNumber, rpc.config.GasLocalExecutionCapFlag) if err != nil { err = fmt.Errorf("unable to estimate transaction - %w", err) @@ -107,7 +107,16 @@ func EstimateGasExecute(builder *CallBuilder[CallParamsWithBlock, hexutil.Uint64 return nil } - totalGasEstimate := hexutil.Uint64(publishingGas.Uint64() + uint64(executionGasEstimate)) + totalGasEstimateUint64 := publishingGas.Uint64() + uint64(executionGasEstimate) + totalGasEstimate := hexutil.Uint64(totalGasEstimateUint64) + balance, err := rpc.chain.GetBalanceAtBlock(builder.ctx, *txArgs.From, blockNumber) + if err != nil { + return err + } + + if balance.ToInt().Cmp(big.NewInt(0).Mul(gasPrice, big.NewInt(0).SetUint64(totalGasEstimateUint64))) < 0 { + return fmt.Errorf("insufficient funds for gas estimate") + } builder.ReturnValue = &totalGasEstimate return nil } @@ -116,7 +125,7 @@ func EstimateGasExecute(builder *CallBuilder[CallParamsWithBlock, hexutil.Uint64 // This is a copy of https://github.com/ethereum/go-ethereum/blob/master/internal/ethapi/api.go#L1055 // there's a high complexity to the method due to geth business rules (which is mimic'd here) // once the work of obscuro gas mechanics is established this method should be simplified -func (rpc *EncryptionManager) doEstimateGas(ctx context.Context, args *gethapi.TransactionArgs, blkNumber *gethrpc.BlockNumber, gasCap uint64) (hexutil.Uint64, common.SystemError) { //nolint: gocognit +func (rpc *EncryptionManager) doEstimateGas(ctx context.Context, args *gethapi.TransactionArgs, blkNumber *gethrpc.BlockNumber, gasCap uint64) (hexutil.Uint64, *big.Int, common.SystemError) { //nolint: gocognit // Binary search the gas requirement, as it may be higher than the amount used var ( //nolint: revive lo = params.TxGas - 1 @@ -148,7 +157,7 @@ func (rpc *EncryptionManager) doEstimateGas(ctx context.Context, args *gethapi.T // Normalize the max fee per gas the call is willing to spend. var feeCap *big.Int if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { - return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + return 0, gethcommon.Big0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") } else if args.GasPrice != nil { feeCap = args.GasPrice.ToInt() } else if args.MaxFeePerGas != nil { @@ -160,13 +169,13 @@ func (rpc *EncryptionManager) doEstimateGas(ctx context.Context, args *gethapi.T if feeCap.BitLen() != 0 { //nolint:nestif balance, err := rpc.chain.GetBalanceAtBlock(ctx, *args.From, blkNumber) if err != nil { - return 0, fmt.Errorf("unable to fetch account balance - %w", err) + return 0, gethcommon.Big0, fmt.Errorf("unable to fetch account balance - %w", err) } available := new(big.Int).Set(balance.ToInt()) if args.Value != nil { if args.Value.ToInt().Cmp(available) >= 0 { - return 0, errors.New("insufficient funds for transfer") + return 0, gethcommon.Big0, errors.New("insufficient funds for transfer") } available.Sub(available, args.Value.ToInt()) } @@ -204,7 +213,7 @@ func (rpc *EncryptionManager) doEstimateGas(ctx context.Context, args *gethapi.T // call or transaction will never be accepted no matter how much gas it is // assigned. Return the error directly, don't struggle any more. if err != nil { - return 0, err + return 0, gethcommon.Big0, err } if failed { lo = mid @@ -216,20 +225,20 @@ func (rpc *EncryptionManager) doEstimateGas(ctx context.Context, args *gethapi.T if hi == cap { //nolint:nestif failed, result, err := rpc.isGasEnough(ctx, args, hi, blkNumber) if err != nil { - return 0, err + return 0, gethcommon.Big0, err } if failed { if result != nil && result.Err != vm.ErrOutOfGas { //nolint: errorlint if len(result.Revert()) > 0 { - return 0, newRevertError(result) + return 0, gethcommon.Big0, newRevertError(result) } - return 0, result.Err + return 0, gethcommon.Big0, result.Err } // Otherwise, the specified gas cap is too low - return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap) + return 0, gethcommon.Big0, fmt.Errorf("gas required exceeds allowance (%d)", cap) } } - return hexutil.Uint64(hi), nil + return hexutil.Uint64(hi), feeCap, nil } // Create a helper to check if a gas allowance results in an executable transaction diff --git a/go/enclave/rpc/GetCustomQuery.go b/go/enclave/rpc/GetCustomQuery.go deleted file mode 100644 index 148a50526e..0000000000 --- a/go/enclave/rpc/GetCustomQuery.go +++ /dev/null @@ -1,49 +0,0 @@ -package rpc - -import ( - "fmt" - - "github.com/ten-protocol/go-ten/go/common" - "github.com/ten-protocol/go-ten/go/common/gethencoding" -) - -func GetCustomQueryValidate(reqParams []any, builder *CallBuilder[common.PrivateCustomQueryListTransactions, common.PrivateQueryResponse], _ *EncryptionManager) error { - // Parameters are [PrivateCustomQueryHeader, PrivateCustomQueryArgs, null] - if len(reqParams) != 3 { - builder.Err = fmt.Errorf("unexpected number of parameters") - return nil - } - - privateCustomQuery, err := gethencoding.ExtractPrivateCustomQuery(reqParams[0], reqParams[1]) - if err != nil { - builder.Err = fmt.Errorf("unable to extract query - %w", err) - return nil - } - builder.From = &privateCustomQuery.Address - builder.Param = privateCustomQuery - return nil -} - -func GetCustomQueryExecute(builder *CallBuilder[common.PrivateCustomQueryListTransactions, common.PrivateQueryResponse], rpc *EncryptionManager) error { - err := authenticateFrom(builder.VK, builder.From) - if err != nil { - builder.Err = err - return nil //nolint:nilerr - } - - encryptReceipts, err := rpc.storage.GetTransactionsPerAddress(builder.ctx, &builder.Param.Address, &builder.Param.Pagination) - if err != nil { - return fmt.Errorf("GetTransactionsPerAddress - %w", err) - } - - receiptsCount, err := rpc.storage.CountTransactionsPerAddress(builder.ctx, &builder.Param.Address) - if err != nil { - return fmt.Errorf("CountTransactionsPerAddress - %w", err) - } - - builder.ReturnValue = &common.PrivateQueryResponse{ - Receipts: encryptReceipts, - Total: receiptsCount, - } - return nil -} diff --git a/go/enclave/rpc/GetPersonalTransactions.go b/go/enclave/rpc/GetPersonalTransactions.go new file mode 100644 index 0000000000..309c52eda3 --- /dev/null +++ b/go/enclave/rpc/GetPersonalTransactions.go @@ -0,0 +1,50 @@ +package rpc + +import ( + "fmt" + + "github.com/ten-protocol/go-ten/go/common" + "github.com/ten-protocol/go-ten/go/common/gethencoding" +) + +func GetPersonalTransactionsValidate(reqParams []any, builder *CallBuilder[common.ListPrivateTransactionsQueryParams, common.PrivateTransactionsQueryResponse], _ *EncryptionManager) error { + // Parameters are [PrivateTransactionListParams] + if len(reqParams) != 1 { + builder.Err = fmt.Errorf("unexpected number of parameters (expected %d, got %d)", 1, len(reqParams)) + return nil + } + + privateCustomQuery, err := gethencoding.ExtractPrivateTransactionsQuery(reqParams[0]) + if err != nil { + builder.Err = fmt.Errorf("unable to extract query - %w", err) + return nil + } + addr := privateCustomQuery.Address + builder.From = &addr + builder.Param = privateCustomQuery + return nil +} + +func GetPersonalTransactionsExecute(builder *CallBuilder[common.ListPrivateTransactionsQueryParams, common.PrivateTransactionsQueryResponse], rpc *EncryptionManager) error { + err := authenticateFrom(builder.VK, builder.From) + if err != nil { + builder.Err = err + return nil //nolint:nilerr + } + addr := builder.Param.Address + encryptReceipts, err := rpc.storage.GetTransactionsPerAddress(builder.ctx, &addr, &builder.Param.Pagination) + if err != nil { + return fmt.Errorf("GetTransactionsPerAddress - %w", err) + } + + receiptsCount, err := rpc.storage.CountTransactionsPerAddress(builder.ctx, &addr) + if err != nil { + return fmt.Errorf("CountTransactionsPerAddress - %w", err) + } + + builder.ReturnValue = &common.PrivateTransactionsQueryResponse{ + Receipts: encryptReceipts, + Total: receiptsCount, + } + return nil +} diff --git a/go/enclave/rpc/GetTransactionReceipt.go b/go/enclave/rpc/GetTransactionReceipt.go index 88c55f967d..6eb4d2ae79 100644 --- a/go/enclave/rpc/GetTransactionReceipt.go +++ b/go/enclave/rpc/GetTransactionReceipt.go @@ -14,7 +14,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ten-protocol/go-ten/go/common/errutil" "github.com/ten-protocol/go-ten/go/common/log" - "github.com/ten-protocol/go-ten/go/enclave/events" ) func GetTransactionReceiptValidate(reqParams []any, builder *CallBuilder[gethcommon.Hash, map[string]interface{}], _ *EncryptionManager) error { @@ -73,8 +72,8 @@ func GetTransactionReceiptExecute(builder *CallBuilder[gethcommon.Hash, map[stri return fmt.Errorf("could not retrieve transaction receipt in eth_getTransactionReceipt request. Cause: %w", err) } - // We filter out irrelevant logs. - txReceipt.Logs, err = events.FilterLogsForReceipt(builder.ctx, txReceipt, &txSigner, rpc.registry) + // We only keep the logs that the requester is allowed to see + txReceipt.Logs, err = rpc.storage.FilterLogsForReceipt(builder.ctx, &txSigner, txReceipt.TxHash) if err != nil { rpc.logger.Error("error filter logs ", log.TxKey, txHash, log.ErrKey, err) // this is a system error diff --git a/go/enclave/rpc/TenStorageRead.go b/go/enclave/rpc/TenStorageRead.go new file mode 100644 index 0000000000..0de76e6470 --- /dev/null +++ b/go/enclave/rpc/TenStorageRead.go @@ -0,0 +1,113 @@ +package rpc + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ten-protocol/go-ten/go/common/gethencoding" + "github.com/ten-protocol/go-ten/go/common/log" + "github.com/ten-protocol/go-ten/go/common/syserr" + gethrpc "github.com/ten-protocol/go-ten/lib/gethfork/rpc" +) + +type storageReadWithBlock struct { + address *common.Address + storageSlot string + block *gethrpc.BlockNumberOrHash +} + +func TenStorageReadValidate(reqParams []any, builder *CallBuilder[storageReadWithBlock, string], rpc *EncryptionManager) error { + if len(reqParams) < 2 || len(reqParams) > 3 { + builder.Err = fmt.Errorf("unexpected number of parameters") + return nil + } + + address, err := gethencoding.ExtractAddress(reqParams[0]) + if err != nil { + builder.Err = fmt.Errorf("error extracting address - %w", err) + return nil + } + + slot, ok := reqParams[1].(string) + if !ok { + builder.Err = fmt.Errorf("storage slot not provided in parameters") + return nil + } + + if !rpc.whitelist.AllowedStorageSlots[slot] { + builder.Err = fmt.Errorf("eth_getStorageAt is not supported on TEN") + return nil + } + + blkNumber, err := gethencoding.ExtractBlockNumber(reqParams[2]) + if err != nil { + builder.Err = fmt.Errorf("unable to extract requested block number - %w", err) + return nil + } + + builder.Param = &storageReadWithBlock{address, slot, blkNumber} + + return nil +} + +func TenStorageReadExecute(builder *CallBuilder[storageReadWithBlock, string], rpc *EncryptionManager) error { + var err error + var stateDb *state.StateDB + blkNumber := builder.Param.block + hash := blkNumber.BlockHash + if hash != nil { + stateDb, err = rpc.registry.GetBatchState(builder.ctx, hash) + } + + number := blkNumber.BlockNumber + if number != nil { + stateDb, err = rpc.registry.GetBatchStateAtHeight(builder.ctx, number) + } + if err != nil { + builder.Err = err + return nil + } + + storageSlot, err := common.ParseHexOrString(builder.Param.storageSlot) + if err != nil { + builder.Err = err + return nil + } + + account, err := stateDb.GetTrie().GetAccount(*builder.Param.address) + if err != nil { + builder.Err = err + return nil + } + + trie, err := stateDb.Database().OpenTrie(account.Root) + if err != nil { + builder.Err = err + return nil + } + + value, err := trie.GetStorage(*builder.Param.address, storageSlot) + if err != nil { + rpc.logger.Debug("Failed eth_getStorageAt.", log.ErrKey, err) + + // return system errors to the host + if errors.Is(err, syserr.InternalError{}) { + return err + } + + builder.Err = err + return nil + } + + if len(value) == 0 { + builder.ReturnValue = nil + return nil + } + + encodedResult := hexutil.Encode(value) + builder.ReturnValue = &encodedResult + return nil +} diff --git a/go/enclave/rpc/rpc_encryption_manager.go b/go/enclave/rpc/rpc_encryption_manager.go index a974a9389f..2c94d76a99 100644 --- a/go/enclave/rpc/rpc_encryption_manager.go +++ b/go/enclave/rpc/rpc_encryption_manager.go @@ -3,6 +3,7 @@ package rpc import ( "fmt" + "github.com/ten-protocol/go-ten/go/common/privacy" "github.com/ten-protocol/go-ten/go/config" "github.com/ten-protocol/go-ten/go/enclave/gas" @@ -29,6 +30,7 @@ type EncryptionManager struct { l1BlockProcessor components.L1BlockProcessor config *config.EnclaveConfig logger gethlog.Logger + whitelist *privacy.Whitelist } func NewEncryptionManager(enclavePrivateKeyECIES *ecies.PrivateKey, storage storage.Storage, registry components.BatchRegistry, processors *crosschain.Processors, service nodetype.NodeType, config *config.EnclaveConfig, oracle gas.Oracle, blockResolver storage.BlockResolver, l1BlockProcessor components.L1BlockProcessor, chain l2chain.ObscuroChain, logger gethlog.Logger) *EncryptionManager { @@ -44,6 +46,7 @@ func NewEncryptionManager(enclavePrivateKeyECIES *ecies.PrivateKey, storage stor gasOracle: oracle, logger: logger, enclavePrivateKeyECIES: enclavePrivateKeyECIES, + whitelist: privacy.NewWhitelist(), } } diff --git a/go/enclave/rpc_server.go b/go/enclave/rpc_server.go index 6b5cf31a28..f2f0ad6a50 100644 --- a/go/enclave/rpc_server.go +++ b/go/enclave/rpc_server.go @@ -214,6 +214,15 @@ func (s *RPCServer) GetTransactionReceipt(ctx context.Context, request *generate return &generated.GetTransactionReceiptResponse{EncodedEnclaveResponse: enclaveResponse.Encode()}, nil } +func (s *RPCServer) GetStorageSlot(ctx context.Context, request *generated.GetStorageSlotRequest) (*generated.GetStorageSlotResponse, error) { + enclaveResp, sysError := s.enclave.GetStorageSlot(ctx, request.EncryptedParams) + if sysError != nil { + s.logger.Error("Error getting storage slot", log.ErrKey, sysError) + return &generated.GetStorageSlotResponse{SystemError: toRPCError(sysError)}, nil + } + return &generated.GetStorageSlotResponse{EncodedEnclaveResponse: enclaveResp.Encode()}, nil +} + func (s *RPCServer) GetBalance(ctx context.Context, request *generated.GetBalanceRequest) (*generated.GetBalanceResponse, error) { enclaveResp, sysError := s.enclave.GetBalance(ctx, request.EncryptedParams) if sysError != nil { @@ -452,7 +461,7 @@ func (s *RPCServer) GetTotalContractCount(ctx context.Context, _ *generated.GetT } func (s *RPCServer) GetReceiptsByAddress(ctx context.Context, req *generated.GetReceiptsByAddressRequest) (*generated.GetReceiptsByAddressResponse, error) { - enclaveResp, sysError := s.enclave.GetCustomQuery(ctx, req.EncryptedParams) + enclaveResp, sysError := s.enclave.GetPersonalTransactions(ctx, req.EncryptedParams) if sysError != nil { s.logger.Error("Error getting receipt", log.ErrKey, sysError) return &generated.GetReceiptsByAddressResponse{SystemError: toRPCError(sysError)}, nil diff --git a/go/enclave/storage/cache_service.go b/go/enclave/storage/cache_service.go new file mode 100644 index 0000000000..8982434aa6 --- /dev/null +++ b/go/enclave/storage/cache_service.go @@ -0,0 +1,167 @@ +package storage + +import ( + "context" + "math/big" + + "github.com/eko/gocache/lib/v4/store" + + "github.com/ten-protocol/go-ten/go/enclave/core" + + "github.com/dgraph-io/ristretto" + "github.com/eko/gocache/lib/v4/cache" + ristretto_store "github.com/eko/gocache/store/ristretto/v4" + + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + gethlog "github.com/ethereum/go-ethereum/log" + "github.com/ten-protocol/go-ten/go/common" + "github.com/ten-protocol/go-ten/go/common/log" +) + +// approximate cost in bytes of the cached values +const ( + blockCost = 1024 + batchCost = 1024 + hashCost = 32 + idCost = 8 +) + +type CacheService struct { + // cache for the immutable blocks and batches. + // this avoids a trip to the database. + blockCache *cache.Cache[*types.Block] + + // stores batches using the sequence number as key + batchCacheBySeqNo *cache.Cache[*common.BatchHeader] + + // mapping between the hash and the sequence number + // note: to fetch a batch by hash will require 2 cache hits + seqCacheByHash *cache.Cache[*big.Int] + + // mapping between the height and the sequence number + // note: to fetch a batch by height will require 2 cache hits + seqCacheByHeight *cache.Cache[*big.Int] + + // batch hash - geth converted hash + convertedHashCache *cache.Cache[*gethcommon.Hash] + + // from address ( either eoa or contract) to the id of the db entry + eoaCache *cache.Cache[*uint64] + contractAddressCache *cache.Cache[*uint64] + + // from contract_address||event_sig to the event_type (id, isLifecycle) object + eventTypeCache *cache.Cache[*EventType] + + // store the converted ethereum header which is passed to the evm + convertedGethHeaderCache *cache.Cache[*types.Header] + + logger gethlog.Logger +} + +func NewCacheService(logger gethlog.Logger) *CacheService { + // todo (tudor) figure out the config + ristrettoCache, err := ristretto.NewCache(&ristretto.Config{ + NumCounters: 100_000_000, // 10 times the expected elements + MaxCost: 1024 * 1024 * 1024, // allocate 1GB + BufferItems: 64, // number of keys per Get buffer. + }) + if err != nil { + logger.Crit("Could not initialise ristretto cache", log.ErrKey, err) + } + ristrettoStore := ristretto_store.NewRistretto(ristrettoCache) + return &CacheService{ + blockCache: cache.New[*types.Block](ristrettoStore), + batchCacheBySeqNo: cache.New[*common.BatchHeader](ristrettoStore), + seqCacheByHash: cache.New[*big.Int](ristrettoStore), + seqCacheByHeight: cache.New[*big.Int](ristrettoStore), + convertedHashCache: cache.New[*gethcommon.Hash](ristrettoStore), + eoaCache: cache.New[*uint64](ristrettoStore), + contractAddressCache: cache.New[*uint64](ristrettoStore), + eventTypeCache: cache.New[*EventType](ristrettoStore), + convertedGethHeaderCache: cache.New[*types.Header](ristrettoStore), + logger: logger, + } +} + +func (cs *CacheService) CacheBlock(ctx context.Context, b *types.Block) { + cacheValue(ctx, cs.blockCache, cs.logger, b.Hash(), b, blockCost) +} + +func (cs *CacheService) CacheBatch(ctx context.Context, batch *core.Batch) { + cacheValue(ctx, cs.batchCacheBySeqNo, cs.logger, batch.SeqNo().Uint64(), batch.Header, batchCost) + cacheValue(ctx, cs.seqCacheByHash, cs.logger, batch.Hash(), batch.SeqNo(), idCost) + // note: the key is (height+1), because for some reason it doesn't like a key of 0 + // should always contain the canonical batch because the cache is overwritten by each new batch after a reorg + cacheValue(ctx, cs.seqCacheByHeight, cs.logger, batch.NumberU64()+1, batch.SeqNo(), idCost) +} + +func (cs *CacheService) ReadBlock(ctx context.Context, key gethcommon.Hash, onCacheMiss func(any) (*types.Block, error)) (*types.Block, error) { + return getCachedValue(ctx, cs.blockCache, cs.logger, key, blockCost, onCacheMiss) +} + +func (cs *CacheService) ReadBatchSeqByHash(ctx context.Context, hash common.L2BatchHash, onCacheMiss func(any) (*big.Int, error)) (*big.Int, error) { + return getCachedValue(ctx, cs.seqCacheByHash, cs.logger, hash, idCost, onCacheMiss) +} + +func (cs *CacheService) ReadBatchSeqByHeight(ctx context.Context, height uint64, onCacheMiss func(any) (*big.Int, error)) (*big.Int, error) { + // the key is (height+1), because for some reason it doesn't like a key of 0 + return getCachedValue(ctx, cs.seqCacheByHeight, cs.logger, height+1, idCost, onCacheMiss) +} + +func (cs *CacheService) ReadConvertedHash(ctx context.Context, hash common.L2BatchHash, onCacheMiss func(any) (*gethcommon.Hash, error)) (*gethcommon.Hash, error) { + return getCachedValue(ctx, cs.convertedHashCache, cs.logger, hash, hashCost, onCacheMiss) +} + +func (cs *CacheService) ReadBatch(ctx context.Context, seqNum uint64, onCacheMiss func(any) (*common.BatchHeader, error)) (*common.BatchHeader, error) { + return getCachedValue(ctx, cs.batchCacheBySeqNo, cs.logger, seqNum, batchCost, onCacheMiss) +} + +func (cs *CacheService) ReadEOA(ctx context.Context, addr gethcommon.Address, onCacheMiss func(any) (*uint64, error)) (*uint64, error) { + return getCachedValue(ctx, cs.eoaCache, cs.logger, addr, idCost, onCacheMiss) +} + +func (cs *CacheService) ReadContractAddr(ctx context.Context, addr gethcommon.Address, onCacheMiss func(any) (*uint64, error)) (*uint64, error) { + return getCachedValue(ctx, cs.contractAddressCache, cs.logger, addr, idCost, onCacheMiss) +} + +func (cs *CacheService) ReadEventType(ctx context.Context, contractAddress gethcommon.Address, eventSignature gethcommon.Hash, onCacheMiss func(any) (*EventType, error)) (*EventType, error) { + key := make([]byte, 0) + key = append(key, contractAddress.Bytes()...) + key = append(key, eventSignature.Bytes()...) + return getCachedValue(ctx, cs.eventTypeCache, cs.logger, key, idCost, onCacheMiss) +} + +func (cs *CacheService) ReadConvertedHeader(ctx context.Context, batchHash common.L2BatchHash, onCacheMiss func(any) (*types.Header, error)) (*types.Header, error) { + return getCachedValue(ctx, cs.convertedGethHeaderCache, cs.logger, batchHash, blockCost, onCacheMiss) +} + +// getCachedValue - returns the cached value for the provided key. If the key is not found, then invoke the 'onCacheMiss' function +// which returns the value, and cache it +func getCachedValue[V any](ctx context.Context, cache *cache.Cache[*V], logger gethlog.Logger, key any, cost int64, onCacheMiss func(any) (*V, error)) (*V, error) { + value, err := cache.Get(ctx, key) + if err != nil || value == nil { + // todo metrics for cache misses + v, err := onCacheMiss(key) + if err != nil { + return v, err + } + if v == nil { + logger.Crit("Returned a nil value from the onCacheMiss function. Should not happen.") + } + cacheValue(ctx, cache, logger, key, v, cost) + return v, nil + } + + return value, err +} + +func cacheValue[V any](ctx context.Context, cache *cache.Cache[*V], logger gethlog.Logger, key any, v *V, cost int64) { + if v == nil { + return + } + err := cache.Set(ctx, key, v, store.WithCost(cost)) + if err != nil { + logger.Error("Could not store value in cache", log.ErrKey, err) + } +} diff --git a/go/enclave/storage/enclavedb/batch.go b/go/enclave/storage/enclavedb/batch.go index 005cb69f73..a3ed53c042 100644 --- a/go/enclave/storage/enclavedb/batch.go +++ b/go/enclave/storage/enclavedb/batch.go @@ -428,11 +428,14 @@ func BatchWasExecuted(ctx context.Context, db *sql.DB, hash common.L2BatchHash) } func GetTransactionsPerAddress(ctx context.Context, db *sql.DB, config *params.ChainConfig, address *gethcommon.Address, pagination *common.QueryPagination) (types.Receipts, error) { - return selectReceipts(ctx, db, config, "where tx.sender_address = ? ORDER BY height DESC LIMIT ? OFFSET ? ", address.Bytes(), pagination.Size, pagination.Offset) + return selectReceipts(ctx, db, config, "join externally_owned_account eoa on tx.sender_address = eoa.id where eoa.address = ? ORDER BY height DESC LIMIT ? OFFSET ? ", address.Bytes(), pagination.Size, pagination.Offset) } func CountTransactionsPerAddress(ctx context.Context, db *sql.DB, address *gethcommon.Address) (uint64, error) { - row := db.QueryRowContext(ctx, "select count(1) from receipt join tx on tx.id=receipt.tx join batch on batch.sequence=receipt.batch "+" where tx.sender_address = ?", address.Bytes()) + row := db.QueryRowContext(ctx, "select count(1) from receipt "+ + "join tx on tx.id=receipt.tx "+ + "join externally_owned_account eoa on eoa.id = tx.sender_address "+ + "where eoa.address = ?", address.Bytes()) var count uint64 err := row.Scan(&count) diff --git a/go/enclave/storage/enclavedb/enclave_sql_db.go b/go/enclave/storage/enclavedb/enclave_sql_db.go index 8c69454de6..acd0607575 100644 --- a/go/enclave/storage/enclavedb/enclave_sql_db.go +++ b/go/enclave/storage/enclavedb/enclave_sql_db.go @@ -115,7 +115,7 @@ func (sqlDB *enclaveDB) NewIterator(prefix []byte, start []byte) ethdb.Iterator return NewIterator(context.Background(), sqlDB.sqldb, prefix, start) } -func (sqlDB *enclaveDB) Stat(_ string) (string, error) { +func (sqlDB *enclaveDB) Stat() (string, error) { // todo - implement me sqlDB.logger.Crit("implement me") return "", nil diff --git a/go/enclave/storage/enclavedb/events.go b/go/enclave/storage/enclavedb/events.go index dc53b3994d..3bb0a8519f 100644 --- a/go/enclave/storage/enclavedb/events.go +++ b/go/enclave/storage/enclavedb/events.go @@ -18,9 +18,9 @@ import ( const ( baseEventsJoin = "from event_log e " + - "join receipt extx on e.receipt=extx.id" + - " join tx on extx.tx=tx.id " + - " join batch b on extx.batch=b.sequence " + + "join receipt rec on e.receipt=rec.id" + + " join tx on rec.tx=tx.id " + + " join batch b on rec.batch=b.sequence " + "join event_type et on e.event_type=et.id " + " join contract c on et.contract=c.id " + "left join event_topic t1 on e.topic1=t1.id " + @@ -67,8 +67,8 @@ func WriteEventTopic(ctx context.Context, dbTX *sql.Tx, topic *gethcommon.Hash, return uint64(id), nil } -func UpdateEventTopicLifecycle(ctx context.Context, dbTx *sql.Tx, etId uint64, isLifecycle bool) error { - _, err := dbTx.ExecContext(ctx, "update event_topic set lifecycle_event=? where id=?", isLifecycle, etId) +func UpdateEventTypeLifecycle(ctx context.Context, dbTx *sql.Tx, etId uint64, isLifecycle bool) error { + _, err := dbTx.ExecContext(ctx, "update event_type set lifecycle_event=? where id=?", isLifecycle, etId) return err } @@ -89,17 +89,15 @@ func WriteEventLog(ctx context.Context, dbTX *sql.Tx, eventTypeId uint64, userTo return err } -func FilterLogs( - ctx context.Context, - db *sql.DB, - requestingAccount *gethcommon.Address, - fromBlock, toBlock *big.Int, - batchHash *common.L2BatchHash, - addresses []gethcommon.Address, - topics [][]gethcommon.Hash, -) ([]*types.Log, error) { +func FilterLogs(ctx context.Context, db *sql.DB, requestingAccount *gethcommon.Address, fromBlock, toBlock *big.Int, batchHash *common.L2BatchHash, addresses []gethcommon.Address, topics [][]gethcommon.Hash, txHash *gethcommon.Hash) ([]*types.Log, error) { queryParams := []any{} query := "" + + if txHash != nil { + query += " AND tx.hash = ? " + queryParams = append(queryParams, txHash.Bytes()) + } + if batchHash != nil { query += " AND b.hash = ? " queryParams = append(queryParams, batchHash.Bytes()) @@ -237,8 +235,6 @@ func loadLogs(ctx context.Context, db *sql.DB, requestingAccount *gethcommon.Add query += whereCondition queryParams = append(queryParams, whereParams...) - query += " order by b.height, tx.idx asc" - rows, err := db.QueryContext(ctx, query, queryParams...) if err != nil { return nil, err diff --git a/go/enclave/storage/events_storage.go b/go/enclave/storage/events_storage.go new file mode 100644 index 0000000000..84e7859901 --- /dev/null +++ b/go/enclave/storage/events_storage.go @@ -0,0 +1,230 @@ +package storage + +import ( + "context" + "database/sql" + "errors" + "fmt" + + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + gethlog "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ten-protocol/go-ten/go/common" + "github.com/ten-protocol/go-ten/go/common/errutil" + "github.com/ten-protocol/go-ten/go/common/measure" + "github.com/ten-protocol/go-ten/go/enclave/core" + "github.com/ten-protocol/go-ten/go/enclave/storage/enclavedb" +) + +// responsible for saving event logs +type eventsStorage struct { + cachingService *CacheService + logger gethlog.Logger +} + +func newEventsStorage(cachingService *CacheService, logger gethlog.Logger) *eventsStorage { + return &eventsStorage{cachingService: cachingService, logger: logger} +} + +func (es *eventsStorage) storeReceiptAndEventLogs(ctx context.Context, dbTX *sql.Tx, batch *common.BatchHeader, receipt *types.Receipt, createdContracts []*gethcommon.Address) error { + txId, senderId, err := enclavedb.ReadTransactionIdAndSender(ctx, dbTX, receipt.TxHash) + if err != nil && !errors.Is(err, errutil.ErrNotFound) { + return fmt.Errorf("could not get transaction id. Cause: %w", err) + } + + for _, createdContract := range createdContracts { + _, err = enclavedb.WriteContractAddress(ctx, dbTX, createdContract, *senderId) + if err != nil { + return fmt.Errorf("could not write contract address. cause %w", err) + } + } + + // Convert the receipt into its storage form and serialize + // this removes information that can be recreated + // todo - in a future iteration, this can be slimmed down further because we already store the logs separately + storageReceipt := (*types.ReceiptForStorage)(receipt) + receiptBytes, err := rlp.EncodeToBytes(storageReceipt) + if err != nil { + return fmt.Errorf("failed to encode block receipts. Cause: %w", err) + } + + execTxId, err := enclavedb.WriteReceipt(ctx, dbTX, batch.SequencerOrderNo.Uint64(), txId, receiptBytes) + if err != nil { + return fmt.Errorf("could not write receipt. Cause: %w", err) + } + + for _, l := range receipt.Logs { + err := es.storeEventLog(ctx, dbTX, execTxId, l) + if err != nil { + return fmt.Errorf("could not store log entry %v. Cause: %w", l, err) + } + } + return nil +} + +func (es *eventsStorage) storeEventLog(ctx context.Context, dbTX *sql.Tx, execTxId uint64, l *types.Log) error { + topicIds, isLifecycle, err := es.handleUserTopics(ctx, dbTX, l) + if err != nil { + return err + } + + eventTypeId, err := es.handleEventType(ctx, dbTX, l, isLifecycle) + if err != nil { + return err + } + + // normalize data + data := l.Data + if len(data) == 0 { + data = nil + } + err = enclavedb.WriteEventLog(ctx, dbTX, eventTypeId, topicIds, data, l.Index, execTxId) + if err != nil { + return fmt.Errorf("could not write event log. Cause: %w", err) + } + + return nil +} + +func (es *eventsStorage) handleEventType(ctx context.Context, dbTX *sql.Tx, l *types.Log, isLifecycle bool) (uint64, error) { + et, err := es.readEventType(ctx, dbTX, l.Address, l.Topics[0]) + if err != nil && !errors.Is(err, errutil.ErrNotFound) { + return 0, fmt.Errorf("could not read event type. Cause: %w", err) + } + if err == nil { + // in case we determined the current emitted event is not lifecycle, we must update the EventType + if !isLifecycle && et.isLifecycle { + err := enclavedb.UpdateEventTypeLifecycle(ctx, dbTX, et.id, isLifecycle) + if err != nil { + return 0, fmt.Errorf("could not update the event type. cause: %w", err) + } + } + return et.id, nil + } + + // the first time an event of this type is emitted we must store it + contractAddId, err := es.readContractAddress(ctx, dbTX, l.Address) + if err != nil { + // the contract was already stored when it was created + return 0, fmt.Errorf("could not read contract address. %s. Cause: %w", l.Address, err) + } + return enclavedb.WriteEventType(ctx, dbTX, contractAddId, l.Topics[0], isLifecycle) +} + +func (es *eventsStorage) handleUserTopics(ctx context.Context, dbTX *sql.Tx, l *types.Log) ([]*uint64, bool, error) { + topicIds := make([]*uint64, 3) + // iterate the topics containing user values + // reuse them if already inserted + // if not, discover if there is a relevant externally owned address + isLifecycle := true + for i := 1; i < len(l.Topics); i++ { + topic := l.Topics[i] + // first check if there is an entry already for this topic + eventTopicId, relAddressId, err := es.findEventTopic(ctx, dbTX, topic.Bytes()) + if err != nil && !errors.Is(err, errutil.ErrNotFound) { + return nil, false, fmt.Errorf("could not read the event topic. Cause: %w", err) + } + if errors.Is(err, errutil.ErrNotFound) { + // check whether the topic is an EOA + relAddressId, err = es.findRelevantAddress(ctx, dbTX, topic) + if err != nil && !errors.Is(err, errutil.ErrNotFound) { + return nil, false, fmt.Errorf("could not read relevant address. Cause %w", err) + } + eventTopicId, err = enclavedb.WriteEventTopic(ctx, dbTX, &topic, relAddressId) + if err != nil { + return nil, false, fmt.Errorf("could not write event topic. Cause: %w", err) + } + } + + if relAddressId != nil { + isLifecycle = false + } + topicIds[i-1] = &eventTopicId + } + return topicIds, isLifecycle, nil +} + +// Of the log's topics, returns those that are (potentially) user addresses. A topic is considered a user address if: +// - It has at least 12 leading zero bytes (since addresses are 20 bytes long, while hashes are 32) and at most 22 leading zero bytes +// - It is not a smart contract address +func (es *eventsStorage) findRelevantAddress(ctx context.Context, dbTX *sql.Tx, topic gethcommon.Hash) (*uint64, error) { + potentialAddr := common.ExtractPotentialAddress(topic) + if potentialAddr == nil { + return nil, errutil.ErrNotFound + } + + // first check whether there is already an entry in the EOA table + eoaID, err := es.readEOA(ctx, dbTX, *potentialAddr) + if err != nil && !errors.Is(err, errutil.ErrNotFound) { + return nil, err + } + if err == nil { + return eoaID, nil + } + + // if the address is a contract then it's clearly not an EOA + _, err = es.readContractAddress(ctx, dbTX, *potentialAddr) + if err != nil && !errors.Is(err, errutil.ErrNotFound) { + return nil, err + } + if err == nil { + return nil, errutil.ErrNotFound + } + + // when we reach this point, the value looks like an address, but we haven't yet seen it + // for the first iteration, we'll just assume it's an EOA + // we can make this smarter by passing in more information about the event + id, err := enclavedb.WriteEoa(ctx, dbTX, *potentialAddr) + if err != nil { + return nil, err + } + + return &id, nil +} + +func (es *eventsStorage) readEventType(ctx context.Context, dbTX *sql.Tx, contractAddress gethcommon.Address, eventSignature gethcommon.Hash) (*EventType, error) { + defer es.logDuration("ReadEventType", measure.NewStopwatch()) + + return es.cachingService.ReadEventType(ctx, contractAddress, eventSignature, func(v any) (*EventType, error) { + contractAddrId, err := enclavedb.ReadContractAddress(ctx, dbTX, contractAddress) + if err != nil { + return nil, err + } + id, isLifecycle, err := enclavedb.ReadEventType(ctx, dbTX, *contractAddrId, eventSignature) + if err != nil { + return nil, err + } + return &EventType{ + id: id, + isLifecycle: isLifecycle, + }, nil + }) +} + +func (es *eventsStorage) readContractAddress(ctx context.Context, dbTX *sql.Tx, addr gethcommon.Address) (*uint64, error) { + defer es.logDuration("readContractAddress", measure.NewStopwatch()) + return es.cachingService.ReadContractAddr(ctx, addr, func(v any) (*uint64, error) { + return enclavedb.ReadContractAddress(ctx, dbTX, addr) + }) +} + +func (es *eventsStorage) findEventTopic(ctx context.Context, dbTX *sql.Tx, topic []byte) (uint64, *uint64, error) { + defer es.logDuration("findEventTopic", measure.NewStopwatch()) + return enclavedb.ReadEventTopic(ctx, dbTX, topic) +} + +func (es *eventsStorage) readEOA(ctx context.Context, dbTX *sql.Tx, addr gethcommon.Address) (*uint64, error) { + defer es.logDuration("ReadEOA", measure.NewStopwatch()) + return es.cachingService.ReadEOA(ctx, addr, func(v any) (*uint64, error) { + id, err := enclavedb.ReadEoa(ctx, dbTX, addr) + if err != nil { + return nil, err + } + return &id, nil + }) +} + +func (es *eventsStorage) logDuration(method string, stopWatch *measure.Stopwatch) { + core.LogMethodDuration(es.logger, stopWatch, fmt.Sprintf("Storage::%s completed", method)) +} diff --git a/go/enclave/storage/init/edgelessdb/001_init.sql b/go/enclave/storage/init/edgelessdb/001_init.sql index 48046641b0..387417c697 100644 --- a/go/enclave/storage/init/edgelessdb/001_init.sql +++ b/go/enclave/storage/init/edgelessdb/001_init.sql @@ -104,12 +104,12 @@ GRANT ALL ON obsdb.tx TO obscuro; create table if not exists obsdb.receipt ( - id INTEGER AUTO_INCREMENT, - content mediumblob, - tx int, - batch int NOT NULL, + id INTEGER AUTO_INCREMENT, + content mediumblob, + tx int, + batch int NOT NULL, INDEX (batch), - INDEX (tx), + INDEX (tx, batch), primary key (id) ); GRANT ALL ON obsdb.receipt TO obscuro; @@ -166,6 +166,7 @@ create table if not exists obsdb.event_log log_idx INTEGER NOT NULL, receipt INTEGER NOT NULL, primary key (id), - INDEX (receipt, event_type, topic1, topic2, topic3) + INDEX (receipt, event_type, topic1, topic2, topic3), + INDEX (event_type, topic1, topic2, topic3) ); GRANT ALL ON obsdb.event_log TO obscuro; \ No newline at end of file diff --git a/go/enclave/storage/interfaces.go b/go/enclave/storage/interfaces.go index 01b7f81586..ac650b3107 100644 --- a/go/enclave/storage/interfaces.go +++ b/go/enclave/storage/interfaces.go @@ -141,6 +141,8 @@ type Storage interface { // the blockHash should always be nil. FilterLogs(ctx context.Context, requestingAccount *gethcommon.Address, fromBlock, toBlock *big.Int, blockHash *common.L2BatchHash, addresses []gethcommon.Address, topics [][]gethcommon.Hash) ([]*types.Log, error) + FilterLogsForReceipt(ctx context.Context, requestingAccount *gethcommon.Address, TxHash gethcommon.Hash) ([]*types.Log, error) + // DebugGetLogs returns logs for a given tx hash without any constraints - should only be used for debug purposes DebugGetLogs(ctx context.Context, txHash common.TxHash) ([]*tracers.DebugLogs, error) @@ -150,9 +152,6 @@ type Storage interface { // StateDB - return the underlying state database StateDB() state.Database - ReadEOA(ctx context.Context, addr gethcommon.Address) (*uint64, error) - - ReadContractAddress(ctx context.Context, addr gethcommon.Address) (*uint64, error) ReadContractOwner(ctx context.Context, address gethcommon.Address) (*gethcommon.Address, error) } diff --git a/go/enclave/storage/storage.go b/go/enclave/storage/storage.go index 662eeb08ea..947d982fb0 100644 --- a/go/enclave/storage/storage.go +++ b/go/enclave/storage/storage.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "math/big" + "sort" "time" "github.com/ten-protocol/go-ten/go/common/errutil" @@ -17,12 +18,8 @@ import ( "github.com/ethereum/go-ethereum/triedb" - "github.com/dgraph-io/ristretto" - "github.com/eko/gocache/lib/v4/cache" "github.com/ten-protocol/go-ten/go/common/measure" - ristretto_store "github.com/eko/gocache/store/ristretto/v4" - "github.com/ten-protocol/go-ten/go/config" "github.com/ten-protocol/go-ten/go/enclave/storage/enclavedb" @@ -49,40 +46,16 @@ const ( masterSeedCfg = "MASTER_SEED" ) -type eventType struct { +type EventType struct { id uint64 isLifecycle bool } // todo - this file needs splitting up based on concerns type storageImpl struct { - db enclavedb.EnclaveDB - - // cache for the immutable blocks and batches. - // this avoids a trip to the database. - blockCache *cache.Cache[*types.Block] - - // stores batches using the sequence number as key - batchCacheBySeqNo *cache.Cache[*common.BatchHeader] - - // mapping between the hash and the sequence number - // note: to fetch a batch by hash will require 2 cache hits - seqCacheByHash *cache.Cache[*big.Int] - - // mapping between the height and the sequence number - // note: to fetch a batch by height will require 2 cache hits - seqCacheByHeight *cache.Cache[*big.Int] - - // batch hash - geth converted hash - convertedHashCache *cache.Cache[*gethcommon.Hash] - - // from address ( either eoa or contract) to the id of the db entry - eoaCache *cache.Cache[*uint64] - contractAddressCache *cache.Cache[*uint64] - - // from contract_address||event_sig to the event_type (id, isLifecycle) object - eventTypeCache *cache.Cache[*eventType] - + db enclavedb.EnclaveDB + cachingService *CacheService + eventsStorage *eventsStorage cachedSharedSecret *crypto.SharedEnclaveSecret stateCache state.Database @@ -90,12 +63,12 @@ type storageImpl struct { logger gethlog.Logger } -func NewStorageFromConfig(config *config.EnclaveConfig, chainConfig *params.ChainConfig, logger gethlog.Logger) Storage { +func NewStorageFromConfig(config *config.EnclaveConfig, cachingService *CacheService, chainConfig *params.ChainConfig, logger gethlog.Logger) Storage { backingDB, err := CreateDBFromConfig(config, logger) if err != nil { logger.Crit("Failed to connect to backing database", log.ErrKey, err) } - return NewStorage(backingDB, chainConfig, logger) + return NewStorage(backingDB, cachingService, chainConfig, logger) } var defaultCacheConfig = &gethcore.CacheConfig{ @@ -115,35 +88,19 @@ var trieDBConfig = &triedb.Config{ }, } -func NewStorage(backingDB enclavedb.EnclaveDB, chainConfig *params.ChainConfig, logger gethlog.Logger) Storage { +func NewStorage(backingDB enclavedb.EnclaveDB, cachingService *CacheService, chainConfig *params.ChainConfig, logger gethlog.Logger) Storage { // Open trie database with provided config triedb := triedb.NewDatabase(backingDB, trieDBConfig) stateDB := state.NewDatabaseWithNodeDB(backingDB, triedb) - // todo (tudor) figure out the config - ristrettoCache, err := ristretto.NewCache(&ristretto.Config{ - NumCounters: 20_000, // 10*MaxCost - MaxCost: 2000, // - how many items to cache - BufferItems: 64, // number of keys per Get buffer. - }) - if err != nil { - logger.Crit("Could not initialise ristretto cache", log.ErrKey, err) - } - ristrettoStore := ristretto_store.NewRistretto(ristrettoCache) return &storageImpl{ - db: backingDB, - stateCache: stateDB, - chainConfig: chainConfig, - blockCache: cache.New[*types.Block](ristrettoStore), - batchCacheBySeqNo: cache.New[*common.BatchHeader](ristrettoStore), - seqCacheByHash: cache.New[*big.Int](ristrettoStore), - seqCacheByHeight: cache.New[*big.Int](ristrettoStore), - convertedHashCache: cache.New[*gethcommon.Hash](ristrettoStore), - eoaCache: cache.New[*uint64](ristrettoStore), - contractAddressCache: cache.New[*uint64](ristrettoStore), - eventTypeCache: cache.New[*eventType](ristrettoStore), - logger: logger, + db: backingDB, + stateCache: stateDB, + chainConfig: chainConfig, + cachingService: cachingService, + eventsStorage: newEventsStorage(cachingService, logger), + logger: logger, } } @@ -183,7 +140,7 @@ func (s *storageImpl) FetchBatch(ctx context.Context, hash common.L2BatchHash) ( } func (s *storageImpl) fetchSeqNoByHash(ctx context.Context, hash common.L2BatchHash) (*big.Int, error) { - seqNo, err := common.GetCachedValue(ctx, s.seqCacheByHash, s.logger, hash, func(v any) (*big.Int, error) { + seqNo, err := s.cachingService.ReadBatchSeqByHash(ctx, hash, func(v any) (*big.Int, error) { batch, err := enclavedb.ReadBatchHeaderByHash(ctx, s.db.GetSQLDB(), v.(common.L2BatchHash)) if err != nil { return nil, err @@ -200,7 +157,7 @@ func (s *storageImpl) FetchConvertedHash(ctx context.Context, hash common.L2Batc return gethcommon.Hash{}, err } - convertedHash, err := common.GetCachedValue(ctx, s.convertedHashCache, s.logger, hash, func(v any) (*gethcommon.Hash, error) { + convertedHash, err := s.cachingService.ReadConvertedHash(ctx, hash, func(v any) (*gethcommon.Hash, error) { ch, err := enclavedb.FetchConvertedBatchHash(ctx, s.db.GetSQLDB(), batch.SequencerOrderNo.Uint64()) if err != nil { return nil, err @@ -234,8 +191,7 @@ func (s *storageImpl) FetchBatchTransactionsBySeq(ctx context.Context, seqNo uin func (s *storageImpl) FetchBatchByHeight(ctx context.Context, height uint64) (*core.Batch, error) { defer s.logDuration("FetchBatchByHeight", measure.NewStopwatch()) - // the key is (height+1), because for some reason it doesn't like a key of 0 - seqNo, err := common.GetCachedValue(ctx, s.seqCacheByHeight, s.logger, height+1, func(h any) (*big.Int, error) { + seqNo, err := s.cachingService.ReadBatchSeqByHeight(ctx, height, func(h any) (*big.Int, error) { batch, err := enclavedb.ReadCanonicalBatchHeaderByHeight(ctx, s.db.GetSQLDB(), height) if err != nil { return nil, err @@ -321,14 +277,14 @@ func (s *storageImpl) StoreBlock(ctx context.Context, block *types.Block, chainF return fmt.Errorf("4. could not store block %s. Cause: %w", block.Hash(), err) } - common.CacheValue(ctx, s.blockCache, s.logger, block.Hash(), block) + s.cachingService.CacheBlock(ctx, block) return nil } func (s *storageImpl) FetchBlock(ctx context.Context, blockHash common.L1BlockHash) (*types.Block, error) { defer s.logDuration("FetchBlock", measure.NewStopwatch()) - return common.GetCachedValue(ctx, s.blockCache, s.logger, blockHash, func(hash any) (*types.Block, error) { + return s.cachingService.ReadBlock(ctx, blockHash, func(hash any) (*types.Block, error) { return enclavedb.FetchBlock(ctx, s.db.GetSQLDB(), hash.(common.L1BlockHash)) }) } @@ -510,9 +466,7 @@ func (s *storageImpl) StoreAttestedKey(ctx context.Context, aggregator gethcommo func (s *storageImpl) FetchBatchBySeqNo(ctx context.Context, seqNum uint64) (*core.Batch, error) { defer s.logDuration("FetchBatchBySeqNo", measure.NewStopwatch()) - h, err := common.GetCachedValue(ctx, s.batchCacheBySeqNo, s.logger, seqNum, func(seq any) (*common.BatchHeader, error) { - return enclavedb.ReadBatchHeaderBySeqNo(ctx, s.db.GetSQLDB(), seqNum) - }) + h, err := s.FetchBatchHeaderBySeqNo(ctx, seqNum) if err != nil { return nil, err } @@ -528,7 +482,7 @@ func (s *storageImpl) FetchBatchBySeqNo(ctx context.Context, seqNum uint64) (*co func (s *storageImpl) FetchBatchHeaderBySeqNo(ctx context.Context, seqNum uint64) (*common.BatchHeader, error) { defer s.logDuration("FetchBatchHeaderBySeqNo", measure.NewStopwatch()) - return common.GetCachedValue(ctx, s.batchCacheBySeqNo, s.logger, seqNum, func(seq any) (*common.BatchHeader, error) { + return s.cachingService.ReadBatch(ctx, seqNum, func(seq any) (*common.BatchHeader, error) { return enclavedb.ReadBatchHeaderBySeqNo(ctx, s.db.GetSQLDB(), seqNum) }) } @@ -607,11 +561,7 @@ func (s *storageImpl) StoreBatch(ctx context.Context, batch *core.Batch, convert return fmt.Errorf("could not commit batch %w", err) } - common.CacheValue(ctx, s.batchCacheBySeqNo, s.logger, batch.SeqNo().Uint64(), batch.Header) - common.CacheValue(ctx, s.seqCacheByHash, s.logger, batch.Hash(), batch.SeqNo()) - // note: the key is (height+1), because for some reason it doesn't like a key of 0 - // should always contain the canonical batch because the cache is overwritten by each new batch after a reorg - common.CacheValue(ctx, s.seqCacheByHeight, s.logger, batch.NumberU64()+1, batch.SeqNo()) + s.cachingService.CacheBatch(ctx, batch) return nil } @@ -656,7 +606,7 @@ func (s *storageImpl) StoreExecutedBatch(ctx context.Context, batch *common.Batc } for _, receipt := range receipts { - err = s.storeReceiptAndEventLogs(ctx, dbTx, batch, receipt, newContracts[receipt.TxHash]) + err = s.eventsStorage.storeReceiptAndEventLogs(ctx, dbTx, batch, receipt, newContracts[receipt.TxHash]) if err != nil { return fmt.Errorf("could not store receipt. Cause: %w", err) } @@ -668,185 +618,6 @@ func (s *storageImpl) StoreExecutedBatch(ctx context.Context, batch *common.Batc return nil } -// todo - move this to a separate service -func (s *storageImpl) storeReceiptAndEventLogs(ctx context.Context, dbTX *sql.Tx, batch *common.BatchHeader, receipt *types.Receipt, createdContracts []*gethcommon.Address) error { - txId, senderId, err := enclavedb.ReadTransactionIdAndSender(ctx, dbTX, receipt.TxHash) - if err != nil && !errors.Is(err, errutil.ErrNotFound) { - return fmt.Errorf("could not get transaction id. Cause: %w", err) - } - - for _, createdContract := range createdContracts { - _, err = enclavedb.WriteContractAddress(ctx, dbTX, createdContract, *senderId) - if err != nil { - return fmt.Errorf("could not write contract address. cause %w", err) - } - } - - // Convert the receipt into its storage form and serialize - // this removes information that can be recreated - // todo - in a future iteration, this can be slimmed down further because we already store the logs separately - storageReceipt := (*types.ReceiptForStorage)(receipt) - receiptBytes, err := rlp.EncodeToBytes(storageReceipt) - if err != nil { - return fmt.Errorf("failed to encode block receipts. Cause: %w", err) - } - - execTxId, err := enclavedb.WriteReceipt(ctx, dbTX, batch.SequencerOrderNo.Uint64(), txId, receiptBytes) - if err != nil { - return fmt.Errorf("could not write receipt. Cause: %w", err) - } - - for _, l := range receipt.Logs { - err := s.storeEventLog(ctx, dbTX, execTxId, l) - if err != nil { - return fmt.Errorf("could not store log entry %v. Cause: %w", l, err) - } - } - return nil -} - -func (s *storageImpl) storeEventLog(ctx context.Context, dbTX *sql.Tx, execTxId uint64, l *types.Log) error { - topicIds, isLifecycle, err := s.handleUserTopics(ctx, dbTX, l) - if err != nil { - return err - } - - eventTypeId, err := s.handleEventType(ctx, dbTX, l, isLifecycle) - if err != nil { - return err - } - - // normalize data - data := l.Data - if len(data) == 0 { - data = nil - } - err = enclavedb.WriteEventLog(ctx, dbTX, eventTypeId, topicIds, data, l.Index, execTxId) - if err != nil { - return fmt.Errorf("could not write event log. Cause: %w", err) - } - - return nil -} - -func (s *storageImpl) handleEventType(ctx context.Context, dbTX *sql.Tx, l *types.Log, isLifecycle bool) (uint64, error) { - et, err := s.readEventType(ctx, dbTX, l.Address, l.Topics[0]) - if err != nil && !errors.Is(err, errutil.ErrNotFound) { - return 0, fmt.Errorf("could not read event type. Cause: %w", err) - } - if err == nil { - // in case we determined the current emitted event is not lifecycle, we must update the eventType - if !isLifecycle && et.isLifecycle { - err := enclavedb.UpdateEventTopicLifecycle(ctx, dbTX, et.id, isLifecycle) - if err != nil { - return 0, fmt.Errorf("could not update the event type. cause: %w", err) - } - } - return et.id, nil - } - - // the first time an event of this type is emitted we must store it - contractAddId, err := s.readContractAddress(ctx, dbTX, l.Address) - if err != nil { - // the contract was already stored when it was created - return 0, fmt.Errorf("could not read contract address. %s. Cause: %w", l.Address, err) - } - return enclavedb.WriteEventType(ctx, dbTX, contractAddId, l.Topics[0], isLifecycle) -} - -func (s *storageImpl) handleUserTopics(ctx context.Context, dbTX *sql.Tx, l *types.Log) ([]*uint64, bool, error) { - topicIds := make([]*uint64, 3) - // iterate the topics containing user values - // reuse them if already inserted - // if not, discover if there is a relevant externally owned address - isLifecycle := true - for i := 1; i < len(l.Topics); i++ { - topic := l.Topics[i] - // first check if there is an entry already for this topic - eventTopicId, relAddressId, err := s.findEventTopic(ctx, dbTX, topic.Bytes()) - if err != nil && !errors.Is(err, errutil.ErrNotFound) { - return nil, false, fmt.Errorf("could not read the event topic. Cause: %w", err) - } - if errors.Is(err, errutil.ErrNotFound) { - // check whether the topic is an EOA - relAddressId, err = s.findRelevantAddress(ctx, dbTX, topic) - if err != nil && !errors.Is(err, errutil.ErrNotFound) { - return nil, false, fmt.Errorf("could not read relevant address. Cause %w", err) - } - eventTopicId, err = enclavedb.WriteEventTopic(ctx, dbTX, &topic, relAddressId) - if err != nil { - return nil, false, fmt.Errorf("could not write event topic. Cause: %w", err) - } - } - - if relAddressId != nil { - isLifecycle = false - } - topicIds[i-1] = &eventTopicId - } - return topicIds, isLifecycle, nil -} - -// Of the log's topics, returns those that are (potentially) user addresses. A topic is considered a user address if: -// - It has at least 12 leading zero bytes (since addresses are 20 bytes long, while hashes are 32) and at most 22 leading zero bytes -// - It is not a smart contract address -func (s *storageImpl) findRelevantAddress(ctx context.Context, dbTX *sql.Tx, topic gethcommon.Hash) (*uint64, error) { - potentialAddr := common.ExtractPotentialAddress(topic) - if potentialAddr == nil { - return nil, errutil.ErrNotFound - } - - // first check whether there is already an entry in the EOA table - eoaID, err := s.readEOA(ctx, dbTX, *potentialAddr) - if err != nil && !errors.Is(err, errutil.ErrNotFound) { - return nil, err - } - if err == nil { - return eoaID, nil - } - - // if the address is a contract then it's clearly not an EOA - _, err = s.readContractAddress(ctx, dbTX, *potentialAddr) - if err != nil && !errors.Is(err, errutil.ErrNotFound) { - return nil, err - } - if err == nil { - return nil, errutil.ErrNotFound - } - - // when we reach this point, the value looks like an address, but we haven't yet seen it - // for the first iteration, we'll just assume it's an EOA - // we can make this smarter by passing in more information about the event - id, err := enclavedb.WriteEoa(ctx, dbTX, *potentialAddr) - if err != nil { - return nil, err - } - - return &id, nil -} - -func (s *storageImpl) readEventType(ctx context.Context, dbTX *sql.Tx, contractAddress gethcommon.Address, eventSignature gethcommon.Hash) (*eventType, error) { - defer s.logDuration("readEventType", measure.NewStopwatch()) - - key := make([]byte, 0) - key = append(key, contractAddress.Bytes()...) - key = append(key, eventSignature.Bytes()...) - return common.GetCachedValue(ctx, s.eventTypeCache, s.logger, key, func(v any) (*eventType, error) { - contractAddrId, err := enclavedb.ReadContractAddress(ctx, dbTX, contractAddress) - if err != nil { - return nil, err - } - id, isLifecycle, err := enclavedb.ReadEventType(ctx, dbTX, *contractAddrId, eventSignature) - if err != nil { - return nil, err - } - return &eventType{ - id: id, - isLifecycle: isLifecycle, - }, nil - }) -} - func (s *storageImpl) StoreValueTransfers(ctx context.Context, blockHash common.L1BlockHash, transfers common.ValueTransferEvents) error { defer s.logDuration("StoreValueTransfers", measure.NewStopwatch()) dbtx, err := s.db.NewDBTransaction(ctx) @@ -970,6 +741,23 @@ func (s *storageImpl) DebugGetLogs(ctx context.Context, txHash common.TxHash) ([ return enclavedb.DebugGetLogs(ctx, s.db.GetSQLDB(), txHash) } +func (s *storageImpl) FilterLogsForReceipt(ctx context.Context, requestingAccount *gethcommon.Address, txHash gethcommon.Hash) ([]*types.Log, error) { + defer s.logDuration("FilterLogs", measure.NewStopwatch()) + logs, err := enclavedb.FilterLogs(ctx, s.db.GetSQLDB(), requestingAccount, nil, nil, nil, nil, nil, &txHash) + if err != nil { + return nil, err + } + // the database returns an unsorted list of event logs. + // we have to perform the sorting programatically + sort.Slice(logs, func(i, j int) bool { + if logs[i].BlockNumber == logs[j].BlockNumber { + return logs[i].Index < logs[j].Index + } + return logs[i].BlockNumber < logs[j].BlockNumber + }) + return logs, nil +} + func (s *storageImpl) FilterLogs( ctx context.Context, requestingAccount *gethcommon.Address, @@ -979,7 +767,19 @@ func (s *storageImpl) FilterLogs( topics [][]gethcommon.Hash, ) ([]*types.Log, error) { defer s.logDuration("FilterLogs", measure.NewStopwatch()) - return enclavedb.FilterLogs(ctx, s.db.GetSQLDB(), requestingAccount, fromBlock, toBlock, blockHash, addresses, topics) + logs, err := enclavedb.FilterLogs(ctx, s.db.GetSQLDB(), requestingAccount, fromBlock, toBlock, blockHash, addresses, topics, nil) + if err != nil { + return nil, err + } + // the database returns an unsorted list of event logs. + // we have to perform the sorting programatically + sort.Slice(logs, func(i, j int) bool { + if logs[i].BlockNumber == logs[j].BlockNumber { + return logs[i].Index < logs[j].Index + } + return logs[i].BlockNumber < logs[j].BlockNumber + }) + return logs, nil } func (s *storageImpl) GetContractCount(ctx context.Context) (*big.Int, error) { @@ -1007,29 +807,9 @@ func (s *storageImpl) CountTransactionsPerAddress(ctx context.Context, address * return enclavedb.CountTransactionsPerAddress(ctx, s.db.GetSQLDB(), address) } -func (s *storageImpl) ReadEOA(ctx context.Context, addr gethcommon.Address) (*uint64, error) { - dbtx, err := s.db.NewDBTransaction(ctx) - if err != nil { - return nil, err - } - defer dbtx.Rollback() - return s.readEOA(ctx, dbtx, addr) -} - -func (s *storageImpl) readEOA(ctx context.Context, dbTX *sql.Tx, addr gethcommon.Address) (*uint64, error) { - defer s.logDuration("readEOA", measure.NewStopwatch()) - return common.GetCachedValue(ctx, s.eoaCache, s.logger, addr, func(v any) (*uint64, error) { - id, err := enclavedb.ReadEoa(ctx, dbTX, addr) - if err != nil { - return nil, err - } - return &id, nil - }) -} - func (s *storageImpl) readOrWriteEOA(ctx context.Context, dbTX *sql.Tx, addr gethcommon.Address) (*uint64, error) { defer s.logDuration("readOrWriteEOA", measure.NewStopwatch()) - return common.GetCachedValue(ctx, s.eoaCache, s.logger, addr, func(v any) (*uint64, error) { + return s.cachingService.ReadEOA(ctx, addr, func(v any) (*uint64, error) { id, err := enclavedb.ReadEoa(ctx, dbTX, addr) if err != nil { if errors.Is(err, errutil.ErrNotFound) { @@ -1045,31 +825,10 @@ func (s *storageImpl) readOrWriteEOA(ctx context.Context, dbTX *sql.Tx, addr get }) } -func (s *storageImpl) ReadContractAddress(ctx context.Context, addr gethcommon.Address) (*uint64, error) { - dbtx, err := s.db.NewDBTransaction(ctx) - if err != nil { - return nil, err - } - defer dbtx.Commit() - return s.readContractAddress(ctx, dbtx, addr) -} - func (s *storageImpl) ReadContractOwner(ctx context.Context, address gethcommon.Address) (*gethcommon.Address, error) { return enclavedb.ReadContractOwner(ctx, s.db.GetSQLDB(), address) } -func (s *storageImpl) readContractAddress(ctx context.Context, dbTX *sql.Tx, addr gethcommon.Address) (*uint64, error) { - defer s.logDuration("readContractAddress", measure.NewStopwatch()) - return common.GetCachedValue(ctx, s.contractAddressCache, s.logger, addr, func(v any) (*uint64, error) { - return enclavedb.ReadContractAddress(ctx, dbTX, addr) - }) -} - -func (s *storageImpl) findEventTopic(ctx context.Context, dbTX *sql.Tx, topic []byte) (uint64, *uint64, error) { - defer s.logDuration("findEventTopic", measure.NewStopwatch()) - return enclavedb.ReadEventTopic(ctx, dbTX, topic) -} - func (s *storageImpl) logDuration(method string, stopWatch *measure.Stopwatch) { core.LogMethodDuration(s.logger, stopWatch, fmt.Sprintf("Storage::%s completed", method)) } diff --git a/go/host/enclave/guardian.go b/go/host/enclave/guardian.go index 9fcf7852f9..ce7e9e8b2c 100644 --- a/go/host/enclave/guardian.go +++ b/go/host/enclave/guardian.go @@ -46,6 +46,7 @@ type guardianServiceLocator interface { L1Repo() host.L1BlockRepository L2Repo() host.L2BatchRepository LogSubs() host.LogSubscriptionManager + CrossChainMachine() l1.CrossChainStateMachine } // Guardian is a host service which monitors an enclave, it's responsibilities include: @@ -462,6 +463,12 @@ func (g *Guardian) processL1BlockTransactions(block *common.L1Block) { // if there are any secret responses in the block we should refresh our P2P list to re-sync with the network _, rollupTxs, contractAddressTxs := g.sl.L1Publisher().ExtractObscuroRelevantTransactions(block) + // TODO (@will) this should be removed and pulled from the L1 + err := g.storage.AddBlock(block.Header()) + if err != nil { + g.logger.Error("Could not add block to host db.", log.ErrKey, err) + } + for _, rollup := range rollupTxs { r, err := common.DecodeRollup(rollup.Rollup) if err != nil { @@ -481,11 +488,6 @@ func (g *Guardian) processL1BlockTransactions(block *common.L1Block) { g.logger.Error("Could not store rollup.", log.ErrKey, err) } } - // TODO (@will) this should be removed and pulled from the L1 - err = g.storage.AddBlock(block.Header(), r.Header.Hash()) - if err != nil { - g.logger.Error("Could not add block to host db.", log.ErrKey, err) - } } if len(contractAddressTxs) > 0 { @@ -634,48 +636,24 @@ func (g *Guardian) periodicBundleSubmission() { bundleSubmissionTicker := time.NewTicker(interval) - fromSequenceNumber, _, err := g.sl.L1Publisher().GetBundleRangeFromManagementContract() - if err != nil { - g.logger.Error(`Unable to get bundle range from management contract and initialize cross chain publishing`, log.ErrKey, err) - return - } - for { select { case <-bundleSubmissionTicker.C: - from, to, err := g.sl.L1Publisher().GetBundleRangeFromManagementContract() + err := g.sl.CrossChainMachine().Synchronize() if err != nil { - g.logger.Error("Unable to get bundle range from management contract", log.ErrKey, err) + g.logger.Error("Failed to synchronize cross chain state machine", log.ErrKey, err) continue } - if from.Uint64() > fromSequenceNumber.Uint64() { - fromSequenceNumber.Set(from) - } - - bundle, err := g.enclaveClient.ExportCrossChainData(context.Background(), fromSequenceNumber.Uint64(), to.Uint64()) + err = g.sl.CrossChainMachine().PublishNextBundle() if err != nil { - if !errors.Is(err, errutil.ErrCrossChainBundleNoBatches) { - g.logger.Error("Unable to export cross chain bundle from enclave", log.ErrKey, err) - } - if errors.Is(err, context.DeadlineExceeded) { - g.logger.Error(`Cross chain bundle export timed out.`, log.ErrKey, err) - return // stop the process - if we are timing out we are not going to catch up + if errors.Is(err, errutil.ErrCrossChainBundleNoBatches) { + g.logger.Debug("No batches to publish") + } else { + g.logger.Error("Failed to publish next bundle", log.ErrKey, err) } continue } - - if len(bundle.CrossChainRootHashes) == 0 { - g.logger.Debug("No cross chain data to submit") - fromSequenceNumber.SetUint64(to.Uint64() + 1) - continue - } - - err = g.sl.L1Publisher().PublishCrossChainBundle(bundle) - if err != nil { - g.logger.Error("Unable to publish cross chain bundle", log.ErrKey, err) - continue - } case <-g.hostInterrupter.Done(): bundleSubmissionTicker.Stop() return diff --git a/go/host/host.go b/go/host/host.go index 985e791676..3d2dfa1f98 100644 --- a/go/host/host.go +++ b/go/host/host.go @@ -114,10 +114,13 @@ func NewHost(config *config.HostConfig, hostServices *ServicesRegistry, p2p host retryIntervalForL1Receipt, hostStorage, ) + hostServices.RegisterService(hostcommon.L1PublisherName, l1Publisher) hostServices.RegisterService(hostcommon.L2BatchRepositoryName, l2Repo) hostServices.RegisterService(hostcommon.EnclaveServiceName, enclService) hostServices.RegisterService(hostcommon.LogSubscriptionServiceName, subsService) + l1StateMachine := l1.NewCrossChainStateMachine(l1Publisher, mgmtContractLib, ethClient, hostServices.Enclaves().GetEnclaveClient(), logger, host.stopControl) + hostServices.RegisterService(hostcommon.CrossChainServiceName, l1StateMachine) var prof *profiler.Profiler if config.ProfilerEnabled { diff --git a/go/host/l1/publisher.go b/go/host/l1/publisher.go index 5971f92bda..cfc077456f 100644 --- a/go/host/l1/publisher.go +++ b/go/host/l1/publisher.go @@ -99,43 +99,46 @@ func (p *Publisher) Start() error { return nil } -func (p *Publisher) GetBundleRangeFromManagementContract() (*big.Int, *big.Int, error) { +func (p *Publisher) GetBundleRangeFromManagementContract(lastRollupNumber *big.Int, lastRollupUID gethcommon.Hash) (*gethcommon.Hash, *big.Int, *big.Int, error) { if p.mgmtContractLib.IsMock() { - return nil, nil, fmt.Errorf("bundle publishing unavailable for mocked environments") + return nil, nil, nil, fmt.Errorf("bundle publishing unavailable for mocked environments") } managementCtr, err := ManagementContract.NewManagementContract(*p.mgmtContractLib.GetContractAddr(), p.ethClient.EthClient()) if err != nil { p.logger.Error("Unable to instantiate management contract client") - return nil, nil, err + return nil, nil, nil, err } - lastBatchHash, err := managementCtr.LastBatchHash(&bind.CallOpts{}) + hashBytes, rollup, err := managementCtr.GetUniqueForkID(&bind.CallOpts{}, lastRollupNumber) if err != nil { - p.logger.Error("Unable to fetch last batch hash from management contract", log.ErrKey, err) - return nil, nil, err + p.logger.Error("Unable to get unique fork ID from management contract") + return nil, nil, nil, err } - var fromSeqNo *big.Int - if lastBatchHash == [32]byte{} { - fromSeqNo = big.NewInt(0) - } else { - batch, err := p.storage.FetchBatch(lastBatchHash) - if err != nil { - p.logger.Error("Unable to fetch last batch from host db", log.ErrKey, err) - return nil, nil, err - } - fromSeqNo = batch.SeqNo() - fromSeqNo = batch.SeqNo().Add(fromSeqNo, big.NewInt(1)) + rollupUid := gethcommon.BytesToHash(hashBytes[:]) + if rollupUid != lastRollupUID { + return nil, nil, nil, errutil.ErrRollupForkMismatch } - lastBatchRolledUpSeqNo, err := managementCtr.LastBatchSeqNo(&bind.CallOpts{}) + fromSeqNo := big.NewInt(0) + if lastRollupNumber.Cmp(big.NewInt(0)) != 0 { + fromSeqNo = big.NewInt(0).SetUint64(rollup.LastSequenceNumber.Uint64() + 1) + } + + nextRollupNumber := big.NewInt(0).SetUint64(lastRollupNumber.Uint64() + 1) + nextHashBytes, nextRollup, err := managementCtr.GetUniqueForkID(&bind.CallOpts{}, nextRollupNumber) if err != nil { - p.logger.Error("Unable to fetch last batch seq no from management contract", log.ErrKey, err) - return nil, nil, err + p.logger.Error("Unable to get unique fork ID from management contract") + return nil, nil, nil, err + } + + nextRollupUID := gethcommon.BytesToHash(nextHashBytes[:]) + if nextRollupUID.Big().Cmp(gethcommon.Big0) == 0 { + return nil, nil, nil, errutil.ErrNoNextRollup } - return fromSeqNo, lastBatchRolledUpSeqNo, nil + return &nextRollupUID, fromSeqNo, nextRollup.LastSequenceNumber, nil } func (p *Publisher) Stop() error { @@ -281,7 +284,7 @@ func (p *Publisher) PublishRollup(producedRollup *common.ExtRollup) { } } -func (p *Publisher) PublishCrossChainBundle(bundle *common.ExtCrossChainBundle) error { +func (p *Publisher) PublishCrossChainBundle(bundle *common.ExtCrossChainBundle, rollupNum *big.Int, forkID gethcommon.Hash) error { if p.mgmtContractLib.IsMock() { return nil } @@ -310,7 +313,7 @@ func (p *Publisher) PublishCrossChainBundle(bundle *common.ExtCrossChainBundle) transactor.Nonce = big.NewInt(0).SetUint64(nonce) - tx, err := managementCtr.AddCrossChainMessagesRoot(transactor, [32]byte(bundle.LastBatchHash.Bytes()), bundle.L1BlockHash, bundle.L1BlockNum, bundle.CrossChainRootHashes, bundle.Signature) + tx, err := managementCtr.AddCrossChainMessagesRoot(transactor, [32]byte(bundle.LastBatchHash.Bytes()), bundle.L1BlockHash, bundle.L1BlockNum, bundle.CrossChainRootHashes, bundle.Signature, rollupNum, forkID) if err != nil { if !errors.Is(err, errutil.ErrCrossChainBundleRepublished) { p.logger.Error("Error with submitting cross chain bundle transaction.", log.ErrKey, err, log.BundleHashKey, bundle.LastBatchHash) diff --git a/go/host/l1/statemachine.go b/go/host/l1/statemachine.go new file mode 100644 index 0000000000..f3ac218eda --- /dev/null +++ b/go/host/l1/statemachine.go @@ -0,0 +1,216 @@ +package l1 + +import ( + "context" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + gethcommon "github.com/ethereum/go-ethereum/common" + gethlog "github.com/ethereum/go-ethereum/log" + "github.com/ten-protocol/go-ten/contracts/generated/ManagementContract" + "github.com/ten-protocol/go-ten/go/common" + "github.com/ten-protocol/go-ten/go/common/errutil" + "github.com/ten-protocol/go-ten/go/common/host" + "github.com/ten-protocol/go-ten/go/common/stopcontrol" + "github.com/ten-protocol/go-ten/go/ethadapter" + "github.com/ten-protocol/go-ten/go/ethadapter/mgmtcontractlib" +) + +type ( + ForkUniqueID = gethcommon.Hash + RollupNumber = uint64 +) + +type CrossChainStateMachine interface { + GetRollupData(number RollupNumber) (RollupInfo, error) + Synchronize() error + PublishNextBundle() error + host.Service +} + +// crossChainStateMachine - responsible for maintaining a view of the submitted cross chain bundles for the rollups on the L1. +// Whenever a reorg happens, the state machine will revert to the latest known common ancestor rollup. +// Bundles are only submitted after a rollup is pushed on the L1. The state machine will keep track of the latest rollup and the bundles that have been submitted. +type crossChainStateMachine struct { + latestRollup RollupInfo + rollupHistory map[RollupNumber]RollupInfo + currentRollup RollupNumber + + enclaveClient common.Enclave + publisher host.L1Publisher + ethClient ethadapter.EthClient + mgmtContractLib mgmtcontractlib.MgmtContractLib // Library to handle Management Contract lib operations + logger gethlog.Logger + hostStopper *stopcontrol.StopControl +} + +type RollupInfo struct { + ForkUID ForkUniqueID + Number RollupNumber +} + +func NewCrossChainStateMachine( + publisher host.L1Publisher, + mgmtContractLib mgmtcontractlib.MgmtContractLib, + ethClient ethadapter.EthClient, + enclaveClient common.Enclave, + logger gethlog.Logger, + hostStopper *stopcontrol.StopControl, +) CrossChainStateMachine { + return &crossChainStateMachine{ + latestRollup: RollupInfo{ + ForkUID: gethcommon.Hash{}, + Number: 0, + }, + rollupHistory: make(map[RollupNumber]RollupInfo), + currentRollup: 0, + publisher: publisher, + ethClient: ethClient, + mgmtContractLib: mgmtContractLib, + enclaveClient: enclaveClient, + logger: logger, + hostStopper: hostStopper, + } +} + +func (c *crossChainStateMachine) Start() error { + return nil +} + +func (c *crossChainStateMachine) Stop() error { + return nil +} + +func (c *crossChainStateMachine) HealthStatus(context.Context) host.HealthStatus { + errMsg := "" + if c.hostStopper.IsStopping() { + errMsg = "not running" + } + return &host.BasicErrHealthStatus{ErrMsg: errMsg} +} + +func (c *crossChainStateMachine) GetRollupData(number RollupNumber) (RollupInfo, error) { + if number == c.latestRollup.Number { + return c.latestRollup, nil + } else if number > c.latestRollup.Number { + return RollupInfo{}, errutil.ErrNotFound + } else { + return c.rollupHistory[number], nil + } +} + +func (c *crossChainStateMachine) PublishNextBundle() error { + // If all bundles for the rollups have been published, there is nothing to do. + if c.currentRollup >= c.latestRollup.Number { + return nil + } + + // Get the bundle range from the management contract + nextForkUID, begin, end, err := c.publisher.GetBundleRangeFromManagementContract(big.NewInt(0).SetUint64(c.currentRollup), c.rollupHistory[c.currentRollup].ForkUID) + if err != nil { + return err + } + + data, err := c.GetRollupData(c.currentRollup + 1) + if err != nil { + return err + } + if data.ForkUID != *nextForkUID { + return errutil.ErrRollupForkMismatch + } + + bundle, err := c.enclaveClient.ExportCrossChainData(context.Background(), begin.Uint64(), end.Uint64()) + if err != nil { + return err + } + + alreadyPublished, err := c.IsBundleAlreadyPublished(bundle) + if err != nil { + return err + } + + if alreadyPublished { + c.currentRollup++ + return nil + } + + err = c.publisher.PublishCrossChainBundle(bundle, big.NewInt(0).SetUint64(data.Number), data.ForkUID) + if err != nil { + return err + } + + // Move the current rollup to the next rollup + c.currentRollup++ + + return nil +} + +func (c *crossChainStateMachine) IsBundleAlreadyPublished(bundle *common.ExtCrossChainBundle) (bool, error) { + managementContract, err := ManagementContract.NewManagementContract(*c.mgmtContractLib.GetContractAddr(), c.ethClient.EthClient()) + if err != nil { + return false, err + } + + return managementContract.IsBundleAvailable(&bind.CallOpts{}, bundle.CrossChainRootHashes) +} + +// Synchronize - checks if there are any new rollups or forks and moves the tracking needle to the latest common ancestor. +func (c *crossChainStateMachine) Synchronize() error { + forkUID, _, _, err := c.publisher.GetBundleRangeFromManagementContract(big.NewInt(0).SetUint64(c.latestRollup.Number), c.latestRollup.ForkUID) + if err != nil { + if errors.Is(err, errutil.ErrNoNextRollup) { + c.logger.Debug("No new rollup or fork found") + return nil + } + + if errors.Is(err, errutil.ErrRollupForkMismatch) { + return c.revertToLatestKnownCommonAncestorRollup() + } + + c.logger.Error("Failed to get bundle range from management contract", "error", err) + return err + } + + c.rollupHistory[c.latestRollup.Number] = c.latestRollup + c.latestRollup = RollupInfo{ + ForkUID: *forkUID, + Number: c.latestRollup.Number + 1, + } + + return nil +} + +func (c *crossChainStateMachine) revertToLatestKnownCommonAncestorRollup() error { + managementContract, err := ManagementContract.NewManagementContract(*c.mgmtContractLib.GetContractAddr(), c.ethClient.EthClient()) + if err != nil { + return err + } + + hashBytes, _, err := managementContract.GetUniqueForkID(&bind.CallOpts{}, big.NewInt(0).SetUint64(c.latestRollup.Number)) + if err != nil { + return err + } + + var forkHash gethcommon.Hash + forkHash = gethcommon.BytesToHash(hashBytes[:]) + + for forkHash != c.latestRollup.ForkUID { + // Revert to previous rollup; No need to wipe the map as the synchronization reinserts the latest rollup + c.latestRollup = c.rollupHistory[c.latestRollup.Number-1] // go to previous rollup + + hashBytes, _, err = managementContract.GetUniqueForkID(&bind.CallOpts{}, big.NewInt(0).SetUint64(c.latestRollup.Number)) + if err != nil { + return err + } + + forkHash = gethcommon.BytesToHash(hashBytes[:]) + } + + // Rollback current rollup if it was dumped due to a fork. + if c.currentRollup > c.latestRollup.Number { + c.currentRollup = c.latestRollup.Number + } + + return nil +} diff --git a/go/host/rpc/clientapi/client_api_eth.go b/go/host/rpc/clientapi/client_api_eth.go index 703da1c40b..ded05e75e7 100644 --- a/go/host/rpc/clientapi/client_api_eth.go +++ b/go/host/rpc/clientapi/client_api_eth.go @@ -178,9 +178,13 @@ func (api *EthereumAPI) GetTransactionByHash(ctx context.Context, encryptedParam return *enclaveResponse, nil } -// GetStorageAt is a reused method for listing the users transactions -func (api *EthereumAPI) GetStorageAt(ctx context.Context, encryptedParams common.EncryptedParamsGetStorageAt) (*responses.Receipts, error) { - return api.host.EnclaveClient().GetCustomQuery(ctx, encryptedParams) +// GetStorageAt is not currently supported (some narrow version of it may be supported in the future for proxy contracts). +func (api *EthereumAPI) GetStorageAt(ctx context.Context, encryptedParams common.EncryptedParamsGetStorageSlot) (responses.EnclaveResponse, error) { + enclaveResponse, sysError := api.host.EnclaveClient().GetStorageSlot(ctx, encryptedParams) + if sysError != nil { + return api.handleSysError("GetStorageAt", sysError) + } + return *enclaveResponse, nil } // FeeHistory is a placeholder for an RPC method required by MetaMask/Remix. diff --git a/go/host/rpc/clientapi/client_api_scan.go b/go/host/rpc/clientapi/client_api_scan.go index 7b6feb5395..57530a6b40 100644 --- a/go/host/rpc/clientapi/client_api_scan.go +++ b/go/host/rpc/clientapi/client_api_scan.go @@ -5,6 +5,7 @@ import ( "math/big" gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ten-protocol/go-ten/go/responses" "github.com/ethereum/go-ethereum/log" "github.com/ten-protocol/go-ten/go/common" @@ -118,3 +119,10 @@ func (s *ScanAPI) GetRollupBatches(rollupHash gethcommon.Hash) (*common.BatchLis func (s *ScanAPI) GetBatchTransactions(batchHash gethcommon.Hash) (*common.TransactionListingResponse, error) { return s.host.Storage().FetchBatchTransactions(batchHash) } + +// These methods are for private user data, they will need to be requested with VK (e.g. via the gateway) + +// GetPersonalTransactions gets the private transactions data for a given user +func (s *ScanAPI) GetPersonalTransactions(ctx context.Context, encryptedParams common.EncryptedParamsGetPersonalTransactions) (*responses.Receipts, error) { + return s.host.EnclaveClient().GetPersonalTransactions(ctx, encryptedParams) +} diff --git a/go/host/rpc/enclaverpc/enclave_client.go b/go/host/rpc/enclaverpc/enclave_client.go index 583434d4d3..cef24fb274 100644 --- a/go/host/rpc/enclaverpc/enclave_client.go +++ b/go/host/rpc/enclaverpc/enclave_client.go @@ -74,6 +74,21 @@ func NewClient(enclaveRPCAddress string, enclaveRPCTimeout time.Duration, logger } } +func (c *Client) GetStorageSlot(ctx context.Context, encryptedParams common.EncryptedParamsGetStorageSlot) (*responses.EnclaveResponse, common.SystemError) { + timeoutCtx, cancel := context.WithTimeout(ctx, c.enclaveRPCTimeout) + defer cancel() + + response, err := c.protoClient.GetStorageSlot(timeoutCtx, &generated.GetStorageSlotRequest{EncryptedParams: encryptedParams}) + if err != nil { + return nil, syserr.NewRPCError(err) + } + if response != nil && response.SystemError != nil { + return nil, syserr.NewInternalError(fmt.Errorf("%s", response.SystemError.ErrorString)) + } + + return responses.ToEnclaveResponse(response.EncodedEnclaveResponse), nil +} + func (c *Client) ExportCrossChainData(ctx context.Context, from uint64, to uint64) (*common.ExtCrossChainBundle, common.SystemError) { timeoutCtx, cancel := context.WithTimeout(ctx, c.enclaveRPCTimeout) defer cancel() @@ -587,7 +602,7 @@ func (c *Client) GetTotalContractCount(ctx context.Context) (*big.Int, common.Sy return big.NewInt(response.Count), nil } -func (c *Client) GetCustomQuery(ctx context.Context, encryptedParams common.EncryptedParamsGetStorageAt) (*responses.PrivateQueryResponse, common.SystemError) { +func (c *Client) GetPersonalTransactions(ctx context.Context, encryptedParams common.EncryptedParamsGetPersonalTransactions) (*responses.PersonalTransactionsResponse, common.SystemError) { timeoutCtx, cancel := context.WithTimeout(ctx, c.enclaveRPCTimeout) defer cancel() diff --git a/go/host/servicelocator.go b/go/host/servicelocator.go index d88cd86231..a5067617c8 100644 --- a/go/host/servicelocator.go +++ b/go/host/servicelocator.go @@ -3,6 +3,7 @@ package host import ( "github.com/ethereum/go-ethereum/log" hostcommon "github.com/ten-protocol/go-ten/go/common/host" + "github.com/ten-protocol/go-ten/go/host/l1" ) type ServicesRegistry struct { @@ -59,3 +60,7 @@ func (s *ServicesRegistry) Enclaves() hostcommon.EnclaveService { func (s *ServicesRegistry) LogSubs() hostcommon.LogSubscriptionManager { return s.getService(hostcommon.LogSubscriptionServiceName).(hostcommon.LogSubscriptionManager) } + +func (s *ServicesRegistry) CrossChainMachine() l1.CrossChainStateMachine { + return s.getService(hostcommon.CrossChainServiceName).(l1.CrossChainStateMachine) +} diff --git a/go/host/storage/hostdb/batch.go b/go/host/storage/hostdb/batch.go index 1d1cfd69cb..3d6e3e5f9e 100644 --- a/go/host/storage/hostdb/batch.go +++ b/go/host/storage/hostdb/batch.go @@ -14,13 +14,13 @@ import ( ) const ( - selectBatch = "SELECT sequence, full_hash, hash, height, ext_batch FROM batch_host" + selectBatch = "SELECT sequence, hash, height, ext_batch FROM batch_host" selectExtBatch = "SELECT ext_batch FROM batch_host" - selectLatestBatch = "SELECT sequence, full_hash, hash, height, ext_batch FROM batch_host ORDER BY sequence DESC LIMIT 1" + selectLatestBatch = "SELECT sequence, hash, height, ext_batch FROM batch_host ORDER BY sequence DESC LIMIT 1" selectTxsAndBatch = "SELECT t.hash FROM transaction_host t JOIN batch_host b ON t.b_sequence = b.sequence WHERE b.hash = " selectBatchSeqByTx = "SELECT b_sequence FROM transaction_host WHERE hash = " - selectTxBySeq = "SELECT full_hash FROM transaction_host WHERE b_sequence = " - selectBatchTxs = "SELECT t.full_hash, b.sequence, b.height, b.ext_batch FROM transaction_host t JOIN batch_host b ON t.b_sequence = b.sequence" + selectTxBySeq = "SELECT hash FROM transaction_host WHERE b_sequence = " + selectBatchTxs = "SELECT t.hash, b.sequence, b.height, b.ext_batch FROM transaction_host t JOIN batch_host b ON t.b_sequence = b.sequence" ) // AddBatch adds a batch and its header to the DB @@ -33,7 +33,6 @@ func AddBatch(dbtx *dbTransaction, statements *SQLStatements, batch *common.ExtB _, err = dbtx.tx.Exec(statements.InsertBatch, batch.SeqNo().Uint64(), // sequence batch.Hash(), // full hash - truncTo16(batch.Hash()), // shortened hash batch.Header.Number.Uint64(), // height extBatch, // ext_batch ) @@ -45,11 +44,16 @@ func AddBatch(dbtx *dbTransaction, statements *SQLStatements, batch *common.ExtB } if len(batch.TxHashes) > 0 { - for _, txHash := range batch.TxHashes { - _, err = dbtx.tx.Exec(statements.InsertTransactions, truncTo16(txHash), txHash.Bytes(), batch.SeqNo().Uint64()) - if err != nil { - return fmt.Errorf("failed to insert transaction with hash: %d", err) - } + insert := statements.InsertTransactions + args := make([]any, 0) + for i, txHash := range batch.TxHashes { + insert += fmt.Sprintf(" (%s, %s),", statements.GetPlaceHolder(i*2+1), statements.GetPlaceHolder(i*2+2)) + args = append(args, txHash.Bytes(), batch.SeqNo().Uint64()) + } + insert = strings.TrimRight(insert, ",") + _, err = dbtx.tx.Exec(insert, args...) + if err != nil { + return fmt.Errorf("failed to insert transactions. cause: %w", err) } } @@ -60,7 +64,7 @@ func AddBatch(dbtx *dbTransaction, statements *SQLStatements, batch *common.ExtB } newTotal := currentTotal + len(batch.TxHashes) - _, err = dbtx.tx.Exec(statements.InsertTxCount, 1, newTotal) + _, err = dbtx.tx.Exec(statements.UpdateTxCount, newTotal) if err != nil { return fmt.Errorf("failed to update transaction count: %w", err) } @@ -95,7 +99,7 @@ func GetBatchListing(db HostDB, pagination *common.QueryPagination) (*common.Bat return &common.BatchListingResponse{ BatchesData: batches, - Total: uint64(len(batches)), + Total: headBatch.SequencerOrderNo.Uint64(), }, nil } @@ -170,7 +174,7 @@ func GetCurrentHeadBatch(db HostDB) (*common.PublicBatch, error) { // GetBatchHeader returns the batch header given the hash. func GetBatchHeader(db HostDB, hash gethcommon.Hash) (*common.BatchHeader, error) { whereQuery := " WHERE hash=" + db.GetSQLStatement().Placeholder - return fetchBatchHeader(db.GetSQLDB(), whereQuery, truncTo16(hash)) + return fetchBatchHeader(db.GetSQLDB(), whereQuery, hash.Bytes()) } // GetBatchHashByNumber returns the hash of a batch given its number. @@ -195,7 +199,7 @@ func GetHeadBatchHeader(db HostDB) (*common.BatchHeader, error) { // GetBatchNumber returns the height of the batch containing the given transaction hash. func GetBatchNumber(db HostDB, txHash gethcommon.Hash) (*big.Int, error) { - batchHeight, err := fetchBatchNumber(db, truncTo16(txHash)) + batchHeight, err := fetchBatchNumber(db, txHash.Bytes()) if err != nil { return nil, fmt.Errorf("failed to fetch batch height - %w", err) } @@ -205,7 +209,7 @@ func GetBatchNumber(db HostDB, txHash gethcommon.Hash) (*big.Int, error) { // GetBatchTxHashes returns the transaction hashes of the batch with the given hash. func GetBatchTxHashes(db HostDB, batchHash gethcommon.Hash) ([]gethcommon.Hash, error) { query := selectTxsAndBatch + db.GetSQLStatement().Placeholder - rows, err := db.GetSQLDB().Query(query, truncTo16(batchHash)) + rows, err := db.GetSQLDB().Query(query, batchHash.Bytes()) if err != nil { return nil, fmt.Errorf("query execution failed: %w", err) } @@ -231,14 +235,14 @@ func GetBatchTxHashes(db HostDB, batchHash gethcommon.Hash) ([]gethcommon.Hash, // GetPublicBatch returns the batch with the given hash. func GetPublicBatch(db HostDB, hash common.L2BatchHash) (*common.PublicBatch, error) { whereQuery := " WHERE b.hash=" + db.GetSQLStatement().Placeholder - return fetchPublicBatch(db.GetSQLDB(), whereQuery, truncTo16(hash)) + return fetchPublicBatch(db.GetSQLDB(), whereQuery, hash.Bytes()) } // GetBatchByTx returns the batch with the given hash. func GetBatchByTx(db HostDB, txHash gethcommon.Hash) (*common.ExtBatch, error) { var seqNo uint64 query := selectBatchSeqByTx + db.GetSQLStatement().Placeholder - err := db.GetSQLDB().QueryRow(query, truncTo16(txHash)).Scan(&seqNo) + err := db.GetSQLDB().QueryRow(query, txHash.Bytes()).Scan(&seqNo) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, errutil.ErrNotFound @@ -251,7 +255,7 @@ func GetBatchByTx(db HostDB, txHash gethcommon.Hash) (*common.ExtBatch, error) { // GetBatchByHash returns the batch with the given hash. func GetBatchByHash(db HostDB, hash common.L2BatchHash) (*common.ExtBatch, error) { whereQuery := " WHERE hash=" + db.GetSQLStatement().Placeholder - return fetchFullBatch(db.GetSQLDB(), whereQuery, truncTo16(hash)) + return fetchFullBatch(db.GetSQLDB(), whereQuery, hash.Bytes()) } // GetLatestBatch returns the head batch header @@ -278,7 +282,7 @@ func GetBatchByHeight(db HostDB, height *big.Int) (*common.PublicBatch, error) { // GetBatchTransactions returns the TransactionListingResponse for a given batch hash func GetBatchTransactions(db HostDB, batchHash gethcommon.Hash) (*common.TransactionListingResponse, error) { whereQuery := " WHERE b.hash=" + db.GetSQLStatement().Placeholder - return fetchBatchTxs(db.GetSQLDB(), whereQuery, truncTo16(batchHash)) + return fetchBatchTxs(db.GetSQLDB(), whereQuery, batchHash.Bytes()) } func fetchBatchHeader(db *sql.DB, whereQuery string, args ...any) (*common.BatchHeader, error) { @@ -330,7 +334,6 @@ func fetchBatchNumber(db HostDB, args ...any) (*big.Int, error) { func fetchPublicBatch(db *sql.DB, whereQuery string, args ...any) (*common.PublicBatch, error) { var sequenceInt64 uint64 var fullHash common.TxHash - var hash []byte var heightInt64 int var extBatch []byte @@ -338,9 +341,9 @@ func fetchPublicBatch(db *sql.DB, whereQuery string, args ...any) (*common.Publi var err error if len(args) > 0 { - err = db.QueryRow(query, args...).Scan(&sequenceInt64, &fullHash, &hash, &heightInt64, &extBatch) + err = db.QueryRow(query, args...).Scan(&sequenceInt64, &fullHash, &heightInt64, &extBatch) } else { - err = db.QueryRow(query).Scan(&sequenceInt64, &fullHash, &hash, &heightInt64, &extBatch) + err = db.QueryRow(query).Scan(&sequenceInt64, &fullHash, &heightInt64, &extBatch) } if err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -356,7 +359,6 @@ func fetchPublicBatch(db *sql.DB, whereQuery string, args ...any) (*common.Publi batch := &common.PublicBatch{ SequencerOrderNo: new(big.Int).SetInt64(int64(sequenceInt64)), - Hash: bytesToHexString(hash), FullHash: fullHash, Height: new(big.Int).SetInt64(int64(heightInt64)), TxCount: new(big.Int).SetInt64(int64(len(b.TxHashes))), @@ -370,7 +372,6 @@ func fetchPublicBatch(db *sql.DB, whereQuery string, args ...any) (*common.Publi func fetchFullBatch(db *sql.DB, whereQuery string, args ...any) (*common.ExtBatch, error) { var sequenceInt64 uint64 var fullHash common.TxHash - var hash []byte var heightInt64 int var extBatch []byte @@ -378,9 +379,9 @@ func fetchFullBatch(db *sql.DB, whereQuery string, args ...any) (*common.ExtBatc var err error if len(args) > 0 { - err = db.QueryRow(query, args...).Scan(&sequenceInt64, &fullHash, &hash, &heightInt64, &extBatch) + err = db.QueryRow(query, args...).Scan(&sequenceInt64, &fullHash, &heightInt64, &extBatch) } else { - err = db.QueryRow(query).Scan(&sequenceInt64, &fullHash, &hash, &heightInt64, &extBatch) + err = db.QueryRow(query).Scan(&sequenceInt64, &fullHash, &heightInt64, &extBatch) } if err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -400,11 +401,10 @@ func fetchFullBatch(db *sql.DB, whereQuery string, args ...any) (*common.ExtBatc func fetchHeadBatch(db *sql.DB) (*common.PublicBatch, error) { var sequenceInt64 int var fullHash gethcommon.Hash // common.Hash - var hash []byte var heightInt64 int var extBatch []byte - err := db.QueryRow(selectLatestBatch).Scan(&sequenceInt64, &fullHash, &hash, &heightInt64, &extBatch) + err := db.QueryRow(selectLatestBatch).Scan(&sequenceInt64, &fullHash, &heightInt64, &extBatch) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, errutil.ErrNotFound @@ -420,7 +420,6 @@ func fetchHeadBatch(db *sql.DB) (*common.PublicBatch, error) { batch := &common.PublicBatch{ SequencerOrderNo: new(big.Int).SetInt64(int64(sequenceInt64)), - Hash: bytesToHexString(hash), FullHash: fullHash, Height: new(big.Int).SetInt64(int64(heightInt64)), TxCount: new(big.Int).SetInt64(int64(len(b.TxHashes))), diff --git a/go/host/storage/hostdb/batch_test.go b/go/host/storage/hostdb/batch_test.go index 4a32bc4282..9f455f2c73 100644 --- a/go/host/storage/hostdb/batch_test.go +++ b/go/host/storage/hostdb/batch_test.go @@ -295,8 +295,8 @@ func TestGetBatchListing(t *testing.T) { t.Errorf("could not get batch listing. Cause: %s", err) } - // should be two elements - if big.NewInt(int64(batchListing.Total)).Cmp(big.NewInt(2)) != 0 { + // should be the most recent sequence number + if big.NewInt(int64(batchListing.Total)).Cmp(big.NewInt(batchNumber+2)) != 0 { t.Errorf("batch listing was not paginated correctly") } @@ -316,30 +316,14 @@ func TestGetBatchListing(t *testing.T) { t.Errorf("batch listing was not paginated correctly") } - // should be 3 elements - if big.NewInt(int64(batchListing1.Total)).Cmp(big.NewInt(3)) != 0 { - t.Errorf("batch listing was not paginated correctly") - } - - // page 0, size 4 - batchListing2, err := GetBatchListing(db, &common.QueryPagination{Offset: 0, Size: 4}) - if err != nil { - t.Errorf("could not get batch listing. Cause: %s", err) - } - - // should be 3 elements - if big.NewInt(int64(batchListing2.Total)).Cmp(big.NewInt(3)) != 0 { - t.Errorf("rollup listing was not paginated correctly") - } - // page 5, size 1 - rollupListing3, err := GetBatchListing(db, &common.QueryPagination{Offset: 5, Size: 1}) + batchListing3, err := GetBatchListing(db, &common.QueryPagination{Offset: 5, Size: 1}) if err != nil { t.Errorf("could not get batch listing. Cause: %s", err) } - // should be 0 elements - if big.NewInt(int64(rollupListing3.Total)).Cmp(big.NewInt(0)) != 0 { + // should be still be the most recent batch sequence number + if big.NewInt(int64(batchListing3.Total)).Cmp(big.NewInt(batchNumber+2)) != 0 { t.Errorf("batch listing was not paginated correctly") } } @@ -411,17 +395,17 @@ func TestGetBatchListingDeprecated(t *testing.T) { // should be 3 elements if big.NewInt(int64(batchListing2.Total)).Cmp(big.NewInt(3)) != 0 { - t.Errorf("rollup listing was not paginated correctly") + t.Errorf("batch listing was not paginated correctly") } // page 5, size 1 - rollupListing3, err := GetBatchListing(db, &common.QueryPagination{Offset: 5, Size: 1}) + batchListing3, err := GetBatchListingDeprecated(db, &common.QueryPagination{Offset: 5, Size: 1}) if err != nil { t.Errorf("could not get batch listing. Cause: %s", err) } // should be 0 elements - if big.NewInt(int64(rollupListing3.Total)).Cmp(big.NewInt(0)) != 0 { + if big.NewInt(int64(batchListing3.Total)).Cmp(big.NewInt(0)) != 0 { t.Errorf("batch listing was not paginated correctly") } } diff --git a/go/host/storage/hostdb/block.go b/go/host/storage/hostdb/block.go index 922551d9c5..96022e8f31 100644 --- a/go/host/storage/hostdb/block.go +++ b/go/host/storage/hostdb/block.go @@ -9,25 +9,19 @@ import ( ) const ( - selectBlocks = "SELECT id, hash, header, rollup_hash FROM block_host ORDER BY id DESC " + selectBlocks = "SELECT b.id, b.hash, b.header, r.hash FROM block_host b join rollup_host r on r.compression_block=b.id ORDER BY b.id DESC " ) // AddBlock stores a block header with the given rollupHash it contains in the host DB -func AddBlock(dbtx *dbTransaction, statements *SQLStatements, b *types.Header, rollupHash common.L2RollupHash) error { +func AddBlock(dbtx *dbTransaction, statements *SQLStatements, b *types.Header) error { header, err := rlp.EncodeToBytes(b) if err != nil { return fmt.Errorf("could not encode block header. Cause: %w", err) } - r, err := rlp.EncodeToBytes(rollupHash) - if err != nil { - return fmt.Errorf("could not encode rollup hash transactions: %w", err) - } - _, err = dbtx.tx.Exec(statements.InsertBlock, - b.Hash(), // hash - header, // l1 block header - r, // rollup hash + b.Hash().Bytes(), // hash + header, // l1 block header ) if err != nil { return fmt.Errorf("could not insert block. Cause: %w", err) @@ -60,9 +54,7 @@ func GetBlockListing(db HostDB, pagination *common.QueryPagination) (*common.Blo return nil, fmt.Errorf("could not decode block header. Cause: %w", err) } r := new(common.L2RollupHash) - if err := rlp.DecodeBytes(rollupHash, r); err != nil { - return nil, fmt.Errorf("could not decode rollup hash. Cause: %w", err) - } + r.SetBytes(rollupHash) block := common.PublicBlock{ BlockHeader: *blockHeader, RollupHash: *r, diff --git a/go/host/storage/hostdb/rollup.go b/go/host/storage/hostdb/rollup.go index a07edea92d..9be5030735 100644 --- a/go/host/storage/hostdb/rollup.go +++ b/go/host/storage/hostdb/rollup.go @@ -14,10 +14,11 @@ import ( ) const ( - selectExtRollup = "SELECT ext_rollup from rollup_host r" - selectLatestRollup = "SELECT ext_rollup FROM rollup_host ORDER BY time_stamp DESC LIMIT 1" - selectRollupBatches = "SELECT b.sequence, b.hash, b.full_hash, b.height, b.ext_batch FROM rollup_host r JOIN batch_host b ON r.start_seq <= b.sequence AND r.end_seq >= b.sequence" - selectRollups = "SELECT id, hash, start_seq, end_seq, time_stamp, ext_rollup, compression_block FROM rollup_host" + selectExtRollup = "SELECT ext_rollup from rollup_host r join block_host b on r.compression_block=b.id " + selectLatestExtRollup = "SELECT ext_rollup FROM rollup_host ORDER BY time_stamp DESC LIMIT 1" + selectLatestRollupCount = "SELECT id FROM rollup_host ORDER BY id DESC LIMIT 1" + selectRollupBatches = "SELECT b.sequence, b.hash, b.height, b.ext_batch FROM rollup_host r JOIN batch_host b ON r.start_seq <= b.sequence AND r.end_seq >= b.sequence" + selectRollups = "SELECT rh.id, rh.hash, rh.start_seq, rh.end_seq, rh.time_stamp, rh.ext_rollup, bh.hash FROM rollup_host rh join block_host bh on rh.compression_block=bh.id " ) // AddRollup adds a rollup to the DB @@ -27,13 +28,19 @@ func AddRollup(dbtx *dbTransaction, statements *SQLStatements, rollup *common.Ex return fmt.Errorf("could not encode rollup: %w", err) } + var blockId int + err = dbtx.tx.QueryRow("select id from block_host where hash="+statements.Placeholder, block.Hash().Bytes()).Scan(&blockId) + if err != nil { + return fmt.Errorf("could not read block id: %w", err) + } + _, err = dbtx.tx.Exec(statements.InsertRollup, - truncTo16(rollup.Header.Hash()), // short hash + rollup.Header.Hash().Bytes(), // hash metadata.FirstBatchSequence.Uint64(), // first batch sequence rollup.Header.LastBatchSeqNo, // last batch sequence metadata.StartTime, // timestamp extRollup, // rollup blob - block.Hash(), // l1 block hash + blockId, // l1 block hash ) if err != nil { return fmt.Errorf("could not insert rollup. Cause: %w", err) @@ -44,7 +51,7 @@ func AddRollup(dbtx *dbTransaction, statements *SQLStatements, rollup *common.Ex // GetRollupListing returns latest rollups given a pagination. // For example, offset 1, size 10 will return the latest 11-20 rollups. func GetRollupListing(db HostDB, pagination *common.QueryPagination) (*common.RollupListingResponse, error) { - orderQuery := " ORDER BY id DESC " + orderQuery := " ORDER BY rh.id DESC " query := selectRollups + orderQuery + db.GetSQLStatement().Pagination rows, err := db.GetSQLDB().Query(query, pagination.Size, pagination.Offset) @@ -84,26 +91,32 @@ func GetRollupListing(db HostDB, pagination *common.QueryPagination) (*common.Ro return nil, err } + // TODO @will we will want to cache this value in the future + totalRollups, err := fetchTotalRollups(db.GetSQLDB()) + if err != nil { + return nil, fmt.Errorf("could not fetch total rollups. Cause: %w", err) + } + return &common.RollupListingResponse{ RollupsData: rollups, - Total: uint64(len(rollups)), + Total: totalRollups.Uint64(), }, nil } func GetExtRollup(db HostDB, hash gethcommon.Hash) (*common.ExtRollup, error) { whereQuery := " WHERE r.hash=" + db.GetSQLStatement().Placeholder - return fetchExtRollup(db.GetSQLDB(), whereQuery, truncTo16(hash)) + return fetchExtRollup(db.GetSQLDB(), whereQuery, hash.Bytes()) } // GetRollupHeader returns the rollup with the given hash. func GetRollupHeader(db HostDB, hash gethcommon.Hash) (*common.RollupHeader, error) { whereQuery := " WHERE r.hash=" + db.GetSQLStatement().Placeholder - return fetchRollupHeader(db.GetSQLDB(), whereQuery, truncTo16(hash)) + return fetchRollupHeader(db.GetSQLDB(), whereQuery, hash.Bytes()) } // GetRollupHeaderByBlock returns the rollup for the given block func GetRollupHeaderByBlock(db HostDB, blockHash gethcommon.Hash) (*common.RollupHeader, error) { - whereQuery := " WHERE r.compression_block=" + db.GetSQLStatement().Placeholder + whereQuery := " WHERE b.hash=" + db.GetSQLStatement().Placeholder return fetchRollupHeader(db.GetSQLDB(), whereQuery, blockHash) } @@ -117,8 +130,8 @@ func GetLatestRollup(db HostDB) (*common.RollupHeader, error) { } func GetRollupByHash(db HostDB, rollupHash gethcommon.Hash) (*common.PublicRollup, error) { - whereQuery := " WHERE hash=" + db.GetSQLStatement().Placeholder - return fetchPublicRollup(db.GetSQLDB(), whereQuery, truncTo16(rollupHash)) + whereQuery := " WHERE rh.hash=" + db.GetSQLStatement().Placeholder + return fetchPublicRollup(db.GetSQLDB(), whereQuery, rollupHash.Bytes()) } func GetRollupBySeqNo(db HostDB, seqNo uint64) (*common.PublicRollup, error) { @@ -130,7 +143,7 @@ func GetRollupBatches(db HostDB, rollupHash gethcommon.Hash) (*common.BatchListi whereQuery := " WHERE r.hash=" + db.GetSQLStatement().Placeholder orderQuery := " ORDER BY b.height DESC" query := selectRollupBatches + whereQuery + orderQuery - rows, err := db.GetSQLDB().Query(query, truncTo16(rollupHash)) + rows, err := db.GetSQLDB().Query(query, rollupHash.Bytes()) if err != nil { return nil, fmt.Errorf("query execution for select rollup batches failed: %w", err) } @@ -140,12 +153,11 @@ func GetRollupBatches(db HostDB, rollupHash gethcommon.Hash) (*common.BatchListi for rows.Next() { var ( sequenceInt64 int - hash []byte fullHash gethcommon.Hash heightInt64 int extBatch []byte ) - err := rows.Scan(&sequenceInt64, &hash, &fullHash, &heightInt64, &extBatch) + err := rows.Scan(&sequenceInt64, &fullHash, &heightInt64, &extBatch) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, errutil.ErrNotFound @@ -160,7 +172,6 @@ func GetRollupBatches(db HostDB, rollupHash gethcommon.Hash) (*common.BatchListi batch := common.PublicBatch{ SequencerOrderNo: new(big.Int).SetInt64(int64(sequenceInt64)), - Hash: bytesToHexString(hash), FullHash: fullHash, Height: new(big.Int).SetInt64(int64(heightInt64)), TxCount: new(big.Int).SetInt64(int64(len(b.TxHashes))), @@ -214,7 +225,7 @@ func fetchExtRollup(db *sql.DB, whereQuery string, args ...any) (*common.ExtRoll func fetchHeadRollup(db *sql.DB) (*common.ExtRollup, error) { var extRollup []byte - err := db.QueryRow(selectLatestRollup).Scan(&extRollup) + err := db.QueryRow(selectLatestExtRollup).Scan(&extRollup) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, errutil.ErrNotFound @@ -230,6 +241,20 @@ func fetchHeadRollup(db *sql.DB) (*common.ExtRollup, error) { return &rollup, nil } +func fetchTotalRollups(db *sql.DB) (*big.Int, error) { + var total int + err := db.QueryRow(selectLatestRollupCount).Scan(&total) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return big.NewInt(0), errutil.ErrNotFound + } + return big.NewInt(0), fmt.Errorf("failed to fetch rollup latest rollup ID: %w", err) + } + + bigTotal := big.NewInt(int64(total)) + return bigTotal, nil +} + func fetchPublicRollup(db *sql.DB, whereQuery string, args ...any) (*common.PublicRollup, error) { query := selectRollups + whereQuery var rollup common.PublicRollup diff --git a/go/host/storage/hostdb/rollup_test.go b/go/host/storage/hostdb/rollup_test.go index b72b3254b8..e1fb859dde 100644 --- a/go/host/storage/hostdb/rollup_test.go +++ b/go/host/storage/hostdb/rollup_test.go @@ -5,6 +5,8 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/core/types" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ten-protocol/go-ten/go/common" @@ -18,9 +20,15 @@ func TestCanStoreAndRetrieveRollup(t *testing.T) { metadata := createRollupMetadata(batchNumber - 10) rollup := createRollup(batchNumber) - block := common.L1Block{} + block := types.NewBlock(&types.Header{}, nil, nil, nil) dbtx, _ := db.NewDBTransaction() - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup, &metadata, &block) + err = AddBlock(dbtx, db.GetSQLStatement(), block.Header()) + if err != nil { + t.Errorf("could not store block. Cause: %s", err) + } + dbtx.Write() + dbtx, _ = db.NewDBTransaction() + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup, &metadata, block) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -52,9 +60,15 @@ func TestGetRollupByBlockHash(t *testing.T) { metadata := createRollupMetadata(batchNumber - 10) rollup := createRollup(batchNumber) - block := common.L1Block{} + block := types.NewBlock(&types.Header{}, nil, nil, nil) dbtx, _ := db.NewDBTransaction() - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup, &metadata, &block) + err = AddBlock(dbtx, db.GetSQLStatement(), block.Header()) + if err != nil { + t.Errorf("could not store block. Cause: %s", err) + } + dbtx.Write() + dbtx, _ = db.NewDBTransaction() + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup, &metadata, block) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -78,9 +92,15 @@ func TestGetLatestRollup(t *testing.T) { rollup1LastSeq := int64(batchNumber) metadata1 := createRollupMetadata(rollup1FirstSeq) rollup1 := createRollup(rollup1LastSeq) - block := common.L1Block{} + block := types.NewBlock(&types.Header{}, nil, nil, nil) dbtx, _ := db.NewDBTransaction() - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, &block) + err = AddBlock(dbtx, db.GetSQLStatement(), block.Header()) + if err != nil { + t.Errorf("could not store block. Cause: %s", err) + } + dbtx.Write() + dbtx, _ = db.NewDBTransaction() + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -91,7 +111,7 @@ func TestGetLatestRollup(t *testing.T) { rollup2LastSeq := int64(batchNumber + 10) metadata2 := createRollupMetadata(rollup2FirstSeq) rollup2 := createRollup(rollup2LastSeq) - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, &block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block) if err != nil { t.Errorf("could not store rollup 2. Cause: %s", err) } @@ -117,9 +137,15 @@ func TestGetRollupBySeqNo(t *testing.T) { rollup1LastSeq := int64(batchNumber) metadata1 := createRollupMetadata(rollup1FirstSeq) rollup1 := createRollup(rollup1LastSeq) - block := common.L1Block{} + block := types.NewBlock(&types.Header{}, nil, nil, nil) dbtx, _ := db.NewDBTransaction() - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, &block) + err = AddBlock(dbtx, db.GetSQLStatement(), block.Header()) + if err != nil { + t.Errorf("could not store block. Cause: %s", err) + } + dbtx.Write() + dbtx, _ = db.NewDBTransaction() + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -130,7 +156,7 @@ func TestGetRollupBySeqNo(t *testing.T) { rollup2LastSeq := int64(batchNumber + 10) // 787 metadata2 := createRollupMetadata(rollup2FirstSeq) rollup2 := createRollup(rollup2LastSeq) - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, &block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block) if err != nil { t.Errorf("could not store rollup 2. Cause: %s", err) } @@ -166,9 +192,15 @@ func TestGetRollupListing(t *testing.T) { rollup1LastSeq := int64(batchNumber) metadata1 := createRollupMetadata(rollup1FirstSeq) rollup1 := createRollup(rollup1LastSeq) - block := common.L1Block{} + block := types.NewBlock(&types.Header{}, nil, nil, nil) dbtx, _ := db.NewDBTransaction() - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, &block) + err = AddBlock(dbtx, db.GetSQLStatement(), block.Header()) + if err != nil { + t.Errorf("could not store block. Cause: %s", err) + } + dbtx.Write() + dbtx, _ = db.NewDBTransaction() + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -177,7 +209,7 @@ func TestGetRollupListing(t *testing.T) { rollup2LastSeq := int64(batchNumber + 10) metadata2 := createRollupMetadata(rollup2FirstSeq) rollup2 := createRollup(rollup2LastSeq) - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, &block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block) if err != nil { t.Errorf("could not store rollup 2. Cause: %s", err) } @@ -186,7 +218,7 @@ func TestGetRollupListing(t *testing.T) { rollup3LastSeq := int64(batchNumber + 20) metadata3 := createRollupMetadata(rollup3FirstSeq) rollup3 := createRollup(rollup3LastSeq) - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup3, &metadata3, &block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup3, &metadata3, block) dbtx.Write() if err != nil { t.Errorf("could not store rollup 3. Cause: %s", err) @@ -198,8 +230,8 @@ func TestGetRollupListing(t *testing.T) { t.Errorf("could not get rollup listing. Cause: %s", err) } - // should be two elements - if big.NewInt(int64(rollupListing.Total)).Cmp(big.NewInt(2)) != 0 { + // should be 3 elements + if big.NewInt(int64(rollupListing.Total)).Cmp(big.NewInt(3)) != 0 { t.Errorf("rollup listing was not paginated correctly") } @@ -247,8 +279,8 @@ func TestGetRollupListing(t *testing.T) { t.Errorf("could not get rollup listing. Cause: %s", err) } - // should be 0 elements - if big.NewInt(int64(rollupListing3.Total)).Cmp(big.NewInt(0)) != 0 { + // should be 3 elements + if big.NewInt(int64(rollupListing3.Total)).Cmp(big.NewInt(3)) != 0 { t.Errorf("rollup listing was not paginated correctly") } } @@ -263,9 +295,15 @@ func TestGetRollupByHash(t *testing.T) { rollup1LastSeq := int64(batchNumber) metadata1 := createRollupMetadata(rollup1FirstSeq) rollup1 := createRollup(rollup1LastSeq) - block := common.L1Block{} + block := types.NewBlock(&types.Header{}, nil, nil, nil) dbtx, _ := db.NewDBTransaction() - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, &block) + err = AddBlock(dbtx, db.GetSQLStatement(), block.Header()) + if err != nil { + t.Errorf("could not store block. Cause: %s", err) + } + dbtx.Write() + dbtx, _ = db.NewDBTransaction() + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -274,7 +312,7 @@ func TestGetRollupByHash(t *testing.T) { rollup2LastSeq := int64(batchNumber + 10) metadata2 := createRollupMetadata(rollup2FirstSeq) rollup2 := createRollup(rollup2LastSeq) - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, &block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block) if err != nil { t.Errorf("could not store rollup 2. Cause: %s", err) } @@ -298,8 +336,15 @@ func TestGetRollupBatches(t *testing.T) { db, _ := createSQLiteDB(t) txHashesOne := []common.L2TxHash{gethcommon.BytesToHash([]byte("magicStringOne")), gethcommon.BytesToHash([]byte("magicStringTwo"))} batchOne := createBatch(batchNumber, txHashesOne) + block := types.NewBlock(&types.Header{}, nil, nil, nil) dbtx, _ := db.NewDBTransaction() - err := AddBatch(dbtx, db.GetSQLStatement(), &batchOne) + err := AddBlock(dbtx, db.GetSQLStatement(), block.Header()) + if err != nil { + t.Errorf("could not store block. Cause: %s", err) + } + dbtx.Write() + dbtx, _ = db.NewDBTransaction() + err = AddBatch(dbtx, db.GetSQLStatement(), &batchOne) if err != nil { t.Errorf("could not store batch. Cause: %s", err) } @@ -332,8 +377,7 @@ func TestGetRollupBatches(t *testing.T) { rollup1LastSeq := int64(batchNumber + 1) metadata1 := createRollupMetadata(rollup1FirstSeq) rollup1 := createRollup(rollup1LastSeq) - block := common.L1Block{} - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, &block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup1, &metadata1, block) if err != nil { t.Errorf("could not store rollup. Cause: %s", err) } @@ -342,7 +386,7 @@ func TestGetRollupBatches(t *testing.T) { rollup2LastSeq := int64(batchNumber + 3) metadata2 := createRollupMetadata(rollup2FirstSeq) rollup2 := createRollup(rollup2LastSeq) - err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, &block) + err = AddRollup(dbtx, db.GetSQLStatement(), &rollup2, &metadata2, block) if err != nil { t.Errorf("could not store rollup 2. Cause: %s", err) } diff --git a/go/host/storage/hostdb/sql_statements.go b/go/host/storage/hostdb/sql_statements.go index 9a7021c576..ccec07ef4d 100644 --- a/go/host/storage/hostdb/sql_statements.go +++ b/go/host/storage/hostdb/sql_statements.go @@ -1,23 +1,32 @@ package hostdb +import "strconv" + // SQLStatements struct holds SQL statements for a specific database type type SQLStatements struct { InsertBatch string InsertTransactions string - InsertTxCount string + UpdateTxCount string InsertRollup string InsertBlock string Pagination string Placeholder string } +func (s SQLStatements) GetPlaceHolder(pos int) string { + if s.Placeholder == "?" { + return s.Placeholder + } + return "$" + strconv.Itoa(pos) +} + func SQLiteSQLStatements() *SQLStatements { return &SQLStatements{ - InsertBatch: "INSERT INTO batch_host (sequence, full_hash, hash, height, ext_batch) VALUES (?, ?, ?, ?, ?)", - InsertTransactions: "REPLACE INTO transaction_host (hash, full_hash, b_sequence) VALUES (?, ?, ?)", - InsertTxCount: "INSERT INTO transaction_count (id, total) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET total = EXCLUDED.total", + InsertBatch: "INSERT INTO batch_host (sequence, hash, height, ext_batch) VALUES (?, ?, ?, ?)", + InsertTransactions: "INSERT INTO transaction_host (hash, b_sequence) VALUES ", + UpdateTxCount: "UPDATE transaction_count SET total=? WHERE id=1", InsertRollup: "INSERT INTO rollup_host (hash, start_seq, end_seq, time_stamp, ext_rollup, compression_block) values (?,?,?,?,?,?)", - InsertBlock: "REPLACE INTO block_host (hash, header, rollup_hash) values (?,?,?)", + InsertBlock: "INSERT INTO block_host (hash, header) values (?,?)", Pagination: "LIMIT ? OFFSET ?", Placeholder: "?", } @@ -25,11 +34,11 @@ func SQLiteSQLStatements() *SQLStatements { func PostgresSQLStatements() *SQLStatements { return &SQLStatements{ - InsertBatch: "INSERT INTO batch_host (sequence, full_hash, hash, height, ext_batch) VALUES ($1, $2, $3, $4, $5)", - InsertTransactions: "INSERT INTO transaction_host (hash, full_hash, b_sequence) VALUES ($1, $2, $3) ON CONFLICT (hash) DO NOTHING", - InsertTxCount: "INSERT INTO transaction_count (id, total) VALUES ($1, $2) ON CONFLICT (id) DO UPDATE SET total = EXCLUDED.total", + InsertBatch: "INSERT INTO batch_host (sequence, hash, height, ext_batch) VALUES ($1, $2, $3, $4)", + InsertTransactions: "INSERT INTO transaction_host (hash, b_sequence) VALUES ", + UpdateTxCount: "UPDATE transaction_count SET total=$1 WHERE id=1", InsertRollup: "INSERT INTO rollup_host (hash, start_seq, end_seq, time_stamp, ext_rollup, compression_block) values ($1, $2, $3, $4, $5, $6)", - InsertBlock: "INSERT INTO block_host (hash, header, rollup_hash) VALUES ($1, $2, $3) ON CONFLICT (hash) DO NOTHING", + InsertBlock: "INSERT INTO block_host (hash, header) VALUES ($1, $2)", Pagination: "LIMIT $1 OFFSET $2", Placeholder: "$1", } diff --git a/go/host/storage/hostdb/transaction.go b/go/host/storage/hostdb/transaction.go index 49a4b0507e..d01b68fcbb 100644 --- a/go/host/storage/hostdb/transaction.go +++ b/go/host/storage/hostdb/transaction.go @@ -11,8 +11,8 @@ import ( const ( selectTxCount = "SELECT total FROM transaction_count WHERE id = 1" - selectTx = "SELECT full_hash, b_sequence FROM transaction_host WHERE hash = " - selectTxs = "SELECT t.full_hash, b.ext_batch FROM transaction_host t JOIN batch_host b ON t.b_sequence = b.sequence ORDER BY b.height DESC " + selectTx = "SELECT hash, b_sequence FROM transaction_host WHERE hash = " + selectTxs = "SELECT t.hash, b.ext_batch FROM transaction_host t JOIN batch_host b ON t.b_sequence = b.sequence ORDER BY b.height DESC " countTxs = "SELECT COUNT(b_sequence) AS row_count FROM transaction_host" ) @@ -51,9 +51,14 @@ func GetTransactionListing(db HostDB, pagination *common.QueryPagination) (*comm return nil, err } + totalTx, err := GetTotalTxCount(db) + if err != nil { + return nil, fmt.Errorf("could not fetch the transaction count. Cause: %w", err) + } + return &common.TransactionListingResponse{ TransactionsData: txs, - Total: uint64(len(txs)), + Total: totalTx.Uint64(), }, nil } @@ -63,7 +68,7 @@ func GetTransaction(db HostDB, hash gethcommon.Hash) (*common.PublicTransaction, var fullHash []byte var seq int - err := db.GetSQLDB().QueryRow(query, truncTo16(hash)).Scan(&fullHash, &seq) + err := db.GetSQLDB().QueryRow(query, hash.Bytes()).Scan(&fullHash, &seq) if err != nil { return nil, fmt.Errorf("failed to retrieve transaction sequence number: %w", err) } diff --git a/go/host/storage/hostdb/transaction_test.go b/go/host/storage/hostdb/transaction_test.go index f541342ac3..5f8a250b8a 100644 --- a/go/host/storage/hostdb/transaction_test.go +++ b/go/host/storage/hostdb/transaction_test.go @@ -42,9 +42,9 @@ func TestGetTransactionListing(t *testing.T) { t.Errorf("could not get tx listing. Cause: %s", err) } - // should be three elements - if big.NewInt(int64(txListing.Total)).Cmp(big.NewInt(3)) != 0 { - t.Errorf("tx listing was not paginated correctly") + // should be 6 elements total + if big.NewInt(int64(txListing.Total)).Cmp(big.NewInt(6)) != 0 { + t.Errorf("tx listing total was not retrieved correctly") } // second element should be in the third batch as they're descending @@ -63,11 +63,6 @@ func TestGetTransactionListing(t *testing.T) { t.Errorf("could not get batch listing. Cause: %s", err) } - // should be 3 elements - if big.NewInt(int64(txListing1.Total)).Cmp(big.NewInt(3)) != 0 { - t.Errorf("tx listing was not paginated correctly") - } - // first element should be in the second batch if txListing1.TransactionsData[0].BatchHeight.Cmp(batchTwo.Header.Number) != 0 { t.Errorf("tx listing was not paginated correctly") diff --git a/go/host/storage/hostdb/utils.go b/go/host/storage/hostdb/utils.go index c1e75150f0..b728031ad5 100644 --- a/go/host/storage/hostdb/utils.go +++ b/go/host/storage/hostdb/utils.go @@ -5,62 +5,12 @@ import ( "fmt" "testing" - gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ten-protocol/go-ten/go/host/storage/init/sqlite" ) -const truncHash = 16 - // An arbitrary number to put in the header const batchNumber = 777 -// truncTo16 checks if the leading half of the hash is filled with zeros and decides whether to truncate the first or last 16 bytes. -func truncTo16(hash gethcommon.Hash) []byte { - hashBytes := hash.Bytes() - // Check if the first half of the hash is all zeros - if isLeadingHalfZeros(hashBytes) { - return truncLastTo16(hashBytes) - } - return truncFirstTo16(hashBytes) -} - -// isLeadingHalfZeros checks if the leading half of the hash is all zeros. -func isLeadingHalfZeros(bytes []byte) bool { - halfLength := len(bytes) / 2 - for i := 0; i < halfLength; i++ { - if bytes[i] != 0 { - return false - } - } - return true -} - -// truncLastTo16 truncates the last 16 bytes of the hash. -func truncLastTo16(bytes []byte) []byte { - if len(bytes) == 0 { - return bytes - } - start := len(bytes) - truncHash - if start < 0 { - start = 0 - } - b := bytes[start:] - c := make([]byte, truncHash) - copy(c, b) - return c -} - -// truncFirstTo16 truncates the first 16 bytes of the hash. -func truncFirstTo16(bytes []byte) []byte { - if len(bytes) == 0 { - return bytes - } - b := bytes[0:truncHash] - c := make([]byte, truncHash) - copy(c, b) - return c -} - func createSQLiteDB(t *testing.T) (HostDB, error) { hostDB, err := sqlite.CreateTemporarySQLiteHostDB("", "mode=memory") if err != nil { diff --git a/go/host/storage/init/postgres/001_init.sql b/go/host/storage/init/postgres/001_init.sql index 87eece1b15..8f967f551a 100644 --- a/go/host/storage/init/postgres/001_init.sql +++ b/go/host/storage/init/postgres/001_init.sql @@ -1,9 +1,8 @@ CREATE TABLE IF NOT EXISTS block_host ( id SERIAL PRIMARY KEY, - hash BYTEA NOT NULL UNIQUE, - header BYTEA NOT NULL, - rollup_hash BYTEA NOT NULL + hash BYTEA NOT NULL, + header BYTEA NOT NULL ); CREATE INDEX IF NOT EXISTS IDX_BLOCK_HASH_HOST ON block_host USING HASH (hash); @@ -16,8 +15,9 @@ CREATE TABLE IF NOT EXISTS rollup_host end_seq INT NOT NULL, time_stamp INT NOT NULL, ext_rollup BYTEA NOT NULL, - compression_block BYTEA NOT NULL -); + compression_block INT NOT NULL, + FOREIGN KEY (compression_block) REFERENCES block_host(id) + ); CREATE INDEX IF NOT EXISTS IDX_ROLLUP_HASH_HOST ON rollup_host USING HASH (hash); CREATE INDEX IF NOT EXISTS IDX_ROLLUP_PROOF_HOST ON rollup_host (compression_block); @@ -26,8 +26,7 @@ CREATE INDEX IF NOT EXISTS IDX_ROLLUP_SEQ_HOST ON rollup_host (start_seq, end_se CREATE TABLE IF NOT EXISTS batch_host ( sequence INT PRIMARY KEY, - full_hash BYTEA NOT NULL, - hash BYTEA NOT NULL UNIQUE, + hash BYTEA NOT NULL , height INT NOT NULL, ext_batch BYTEA NOT NULL ); @@ -37,12 +36,13 @@ CREATE INDEX IF NOT EXISTS IDX_BATCH_HEIGHT_HOST ON batch_host (height); CREATE TABLE IF NOT EXISTS transaction_host ( - hash BYTEA PRIMARY KEY, - full_hash BYTEA NOT NULL UNIQUE, + id SERIAL PRIMARY KEY, + hash BYTEA, b_sequence INT, FOREIGN KEY (b_sequence) REFERENCES batch_host(sequence) ); +CREATE INDEX IF NOT EXISTS IDX_TX_HASH_HOST ON transaction_host USING HASH (hash); CREATE INDEX IF NOT EXISTS IDX_TX_SEQ_HOST ON transaction_host (b_sequence); CREATE TABLE IF NOT EXISTS transaction_count diff --git a/go/host/storage/init/sqlite/001_init.sql b/go/host/storage/init/sqlite/001_init.sql index c299176d58..371dad1d0a 100644 --- a/go/host/storage/init/sqlite/001_init.sql +++ b/go/host/storage/init/sqlite/001_init.sql @@ -2,8 +2,7 @@ create table if not exists block_host ( id INTEGER PRIMARY KEY AUTOINCREMENT, hash binary(32) NOT NULL UNIQUE, - header blob NOT NULL, - rollup_hash binary(32) NOT NULL + header blob NOT NULL ); create index IDX_BLOCK_HASH_HOST on block_host (hash); @@ -11,12 +10,12 @@ create index IDX_BLOCK_HASH_HOST on block_host (hash); create table if not exists rollup_host ( id INTEGER PRIMARY KEY AUTOINCREMENT, - hash binary(16) NOT NULL UNIQUE, + hash binary(32) NOT NULL UNIQUE, start_seq int NOT NULL, end_seq int NOT NULL, time_stamp int NOT NULL, ext_rollup blob NOT NULL, - compression_block binary(32) NOT NULL + compression_block int NOT NULL references block_host ); create index IDX_ROLLUP_HASH_HOST on rollup_host (hash); @@ -26,8 +25,7 @@ create index IDX_ROLLUP_SEQ_HOST on rollup_host (start_seq, end_seq); create table if not exists batch_host ( sequence int primary key, - full_hash binary(32) NOT NULL, - hash binary(16) NOT NULL UNIQUE, + hash binary(32) NOT NULL, height int NOT NULL, ext_batch mediumblob NOT NULL ); @@ -36,10 +34,11 @@ create index IDX_BATCH_HEIGHT_HOST on batch_host (height); create table if not exists transaction_host ( - hash binary(16) PRIMARY KEY, - full_hash binary(32) NOT NULL UNIQUE, + id int PRIMARY KEY, + hash binary(32) , b_sequence int REFERENCES batch_host ); +create index TX_HASH_HOST on transaction_host (hash); create table if not exists transaction_count ( diff --git a/go/host/storage/interfaces.go b/go/host/storage/interfaces.go index d7e229c36e..7abbf4fc9e 100644 --- a/go/host/storage/interfaces.go +++ b/go/host/storage/interfaces.go @@ -56,7 +56,7 @@ type BatchResolver interface { type BlockResolver interface { // AddBlock stores block data containing rollups in the host DB - AddBlock(b *types.Header, rollupHash common.L2RollupHash) error + AddBlock(b *types.Header) error // AddRollup stores a rollup in the host DB AddRollup(rollup *common.ExtRollup, metadata *common.PublicRollupMetadata, block *common.L1Block) error // FetchLatestRollupHeader returns the head `RollupHeader` diff --git a/go/host/storage/storage.go b/go/host/storage/storage.go index 6221993513..e1ac74b32b 100644 --- a/go/host/storage/storage.go +++ b/go/host/storage/storage.go @@ -75,13 +75,13 @@ func (s *storageImpl) AddRollup(rollup *common.ExtRollup, metadata *common.Publi return nil } -func (s *storageImpl) AddBlock(b *types.Header, rollupHash common.L2RollupHash) error { +func (s *storageImpl) AddBlock(b *types.Header) error { dbtx, err := s.db.NewDBTransaction() if err != nil { return err } - if err := hostdb.AddBlock(dbtx, s.db.GetSQLStatement(), b, rollupHash); err != nil { + if err := hostdb.AddBlock(dbtx, s.db.GetSQLStatement(), b); err != nil { if err := dbtx.Rollback(); err != nil { return err } diff --git a/go/obsclient/authclient.go b/go/obsclient/authclient.go index 6d66d63751..c15e627f66 100644 --- a/go/obsclient/authclient.go +++ b/go/obsclient/authclient.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "math/big" "github.com/ethereum/go-ethereum" @@ -249,14 +250,21 @@ func (ac *AuthObsClient) EstimateGasAndGasPrice(txData types.TxData) types.TxDat } } -// GetReceiptsByAddress retrieves the receipts for the account registered on this client (due to obscuro privacy restrictions, -// balance cannot be requested for other accounts) -func (ac *AuthObsClient) GetReceiptsByAddress(ctx context.Context, address *gethcommon.Address) (types.Receipts, error) { - var result types.Receipts - err := ac.rpcClient.CallContext(ctx, &result, rpc.GetStorageAt, address, nil, nil) +// GetPrivateTransactions retrieves the receipts for the specified account (must be registered on this client), returns requested range of receipts and the total number of receipts for that acc +func (ac *AuthObsClient) GetPrivateTransactions(ctx context.Context, address *gethcommon.Address, pagination common.QueryPagination) (types.Receipts, uint64, error) { + queryParam := &common.ListPrivateTransactionsQueryParams{ + Address: *address, + Pagination: pagination, + } + queryParamStr, err := json.Marshal(queryParam) if err != nil { - return nil, err + return nil, 0, fmt.Errorf("unable to marshal query params - %w", err) + } + var result common.PrivateTransactionsQueryResponse + err = ac.rpcClient.CallContext(ctx, &result, rpc.GetPersonalTransactions, queryParamStr) + if err != nil { + return nil, 0, err } - return result, nil + return result.Receipts, result.Total, nil } diff --git a/go/responses/types.go b/go/responses/types.go index d4d6ea2d06..7489f6bfd9 100644 --- a/go/responses/types.go +++ b/go/responses/types.go @@ -24,16 +24,17 @@ func (ur *UserResponse[T]) Error() error { // Responses type ( - Balance = EnclaveResponse // The response for an RPC getBalance request, as a JSON object encrypted with the viewing key of the user. - Call = EnclaveResponse // As above, but for an RPC call request. - TxReceipt = EnclaveResponse // As above, but for an RPC getTransactionReceipt request. - RawTx = EnclaveResponse // As above, but for an RPC sendRawTransaction request. - TxByHash = EnclaveResponse // As above, but for an RPC getTransactionByHash request. - TxCount = EnclaveResponse // As above, but for an RPC getTransactionCount request. - Gas = EnclaveResponse // As above, but for an RPC estimateGas response. - Logs = EnclaveResponse - Receipts = EnclaveResponse - PrivateQueryResponse = EnclaveResponse + Balance = EnclaveResponse // The response for an RPC getBalance request, as a JSON object encrypted with the viewing key of the user. + Call = EnclaveResponse // As above, but for an RPC call request. + TxReceipt = EnclaveResponse // As above, but for an RPC getTransactionReceipt request. + RawTx = EnclaveResponse // As above, but for an RPC sendRawTransaction request. + TxByHash = EnclaveResponse // As above, but for an RPC getTransactionByHash request. + TxCount = EnclaveResponse // As above, but for an RPC getTransactionCount request. + Gas = EnclaveResponse // As above, but for an RPC estimateGas response. + Logs = EnclaveResponse + Receipts = EnclaveResponse + PersonalTransactionsResponse = EnclaveResponse + StorageSlotResponse = EnclaveResponse ) // Data Types diff --git a/go/rpc/client.go b/go/rpc/client.go index 81227b0266..6a87987b4d 100644 --- a/go/rpc/client.go +++ b/go/rpc/client.go @@ -44,12 +44,13 @@ const ( GetBatchByHeight = "scan_getBatchByHeight" GetTransaction = "scan_getTransaction" - GetRollupListing = "scan_getRollupListing" - GetBatchListingNew = "scan_getBatchListingNew" - GetRollupByHash = "scan_getRollupByHash" - GetRollupBatches = "scan_getRollupBatches" - GetRollupBySeqNo = "scan_getRollupBySeqNo" - GetBatchTransactions = "scan_getBatchTransactions" + GetRollupListing = "scan_getRollupListing" + GetBatchListingNew = "scan_getBatchListingNew" + GetRollupByHash = "scan_getRollupByHash" + GetRollupBatches = "scan_getRollupBatches" + GetRollupBySeqNo = "scan_getRollupBySeqNo" + GetBatchTransactions = "scan_getBatchTransactions" + GetPersonalTransactions = "scan_getPersonalTransactions" ) // Client is used by client applications to interact with the Ten node diff --git a/go/rpc/encrypted_client.go b/go/rpc/encrypted_client.go index 764890a857..be553e6aff 100644 --- a/go/rpc/encrypted_client.go +++ b/go/rpc/encrypted_client.go @@ -41,6 +41,7 @@ var SensitiveMethods = []string{ EstimateGas, GetLogs, GetStorageAt, + GetPersonalTransactions, } // EncRPCClient is a Client wrapper that implements Client but also has extra functionality for managing viewing key registration and decryption @@ -166,6 +167,12 @@ func (c *EncRPCClient) executeSensitiveCall(ctx context.Context, result interfac // and never error. resultBytes, _ := decodedResult.MarshalJSON() + // if expected result type is bytes, we return the bytes + if _, ok := result.(*[]byte); ok { + *result.(*[]byte) = resultBytes + return nil + } + // We put the raw json in the passed result object. // This works for structs, strings, integers and interface types. err = json.Unmarshal(resultBytes, result) diff --git a/integration/ethereummock/mock_blocks.go b/integration/ethereummock/mock_blocks.go index 6c09b4ad9c..a954c35948 100644 --- a/integration/ethereummock/mock_blocks.go +++ b/integration/ethereummock/mock_blocks.go @@ -37,5 +37,5 @@ func NewBlock(parent *types.Block, nodeID common.Address, txs []*types.Transacti BaseFee: nil, } - return types.NewBlock(&header, txs, nil, nil, trie.NewStackTrie(nil)) + return types.NewBlock(&header, &types.Body{Transactions: txs}, nil, trie.NewStackTrie(nil)) } diff --git a/integration/networktest/env/network_setup.go b/integration/networktest/env/network_setup.go index bd107052da..a1a0a89f5b 100644 --- a/integration/networktest/env/network_setup.go +++ b/integration/networktest/env/network_setup.go @@ -57,8 +57,8 @@ func DevTestnet(opts ...TestnetEnvOption) networktest.Environment { // LongRunningLocalNetwork is a local network, the l1WSURL is optional (can be empty string), only required if testing L1 interactions func LongRunningLocalNetwork(l1WSURL string) networktest.Environment { connector := newTestnetConnectorWithFaucetAccount( - "ws://127.0.0.1:26900", - []string{"ws://127.0.0.1:26901"}, + "ws://127.0.0.1:17900", + []string{"ws://127.0.0.1:17901"}, genesis.TestnetPrefundedPK, l1WSURL, "", diff --git a/integration/networktest/tests/helpful/spin_up_local_network_test.go b/integration/networktest/tests/helpful/spin_up_local_network_test.go index 5f671fb8b4..9e96230a50 100644 --- a/integration/networktest/tests/helpful/spin_up_local_network_test.go +++ b/integration/networktest/tests/helpful/spin_up_local_network_test.go @@ -29,6 +29,8 @@ const ( _sepoliaValidator1PK = "" // account 0x ) +// Spins up a local network with a gateway, with all processes debuggable. The network will run until the test is stopped. +// Note: If you want to access the gateway frontend you need to `npm run build` its frontend with NEXT_PUBLIC_API_GATEWAY_URL=http://localhost:11180 func TestRunLocalNetwork(t *testing.T) { networktest.TestOnlyRunsInIDE(t) networktest.EnsureTestLogsSetUp("local-geth-network") diff --git a/integration/networktest/tests/tenscan/tenscan_rpc_test.go b/integration/networktest/tests/tenscan/tenscan_rpc_test.go index fb4b4835c4..3ccb517b45 100644 --- a/integration/networktest/tests/tenscan/tenscan_rpc_test.go +++ b/integration/networktest/tests/tenscan/tenscan_rpc_test.go @@ -1,14 +1,21 @@ package tenscan import ( + "context" + "fmt" + "math/big" "testing" + "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/integration/networktest" "github.com/ten-protocol/go-ten/integration/networktest/actions" "github.com/ten-protocol/go-ten/integration/networktest/actions/publicdata" "github.com/ten-protocol/go-ten/integration/networktest/env" + "github.com/ten-protocol/go-ten/integration/simulation/devnetwork" ) +var _transferAmount = big.NewInt(100_000_000) + // Verify and debug the RPC endpoints that Tenscan relies on for data in various environments func TestRPC(t *testing.T) { @@ -22,3 +29,62 @@ func TestRPC(t *testing.T) { ), ) } + +// Test the personal transactions endpoint in various environments (it uses getStorageAt so it can run through MM etc.) +// 1. create user +// 2. send some transactions +// 3. verify transactions are returned by the personal transactions endpoint that tenscan uses +func TestPersonalTransactions(t *testing.T) { + networktest.TestOnlyRunsInIDE(t) + networktest.Run( + "tenscan-personal-transactions", + t, + env.LocalDevNetwork(devnetwork.WithGateway()), + actions.Series( + // create 3 users + &actions.CreateTestUser{UserID: 0, UseGateway: true}, // <-- this user makes the PersonalTransactions request, choose gateway or not here + &actions.CreateTestUser{UserID: 1}, + &actions.CreateTestUser{UserID: 2}, + actions.SetContextValue(actions.KeyNumberOfTestUsers, 3), + + &actions.AllocateFaucetFunds{UserID: 0}, + actions.SnapshotUserBalances(actions.SnapAfterAllocation), // record user balances (we have no guarantee on how much the network faucet allocates) + + // user 0 sends funds to users 1 and 2 + &actions.SendNativeFunds{FromUser: 0, ToUser: 1, Amount: _transferAmount}, + &actions.SendNativeFunds{FromUser: 0, ToUser: 2, Amount: _transferAmount}, + + // after the test we will verify the other users received them + &actions.VerifyBalanceAfterTest{UserID: 1, ExpectedBalance: _transferAmount}, + &actions.VerifyBalanceAfterTest{UserID: 2, ExpectedBalance: _transferAmount}, + + // verify the personal transactions endpoint returns the two txs + actions.VerifyOnlyAction(func(ctx context.Context, network networktest.NetworkConnector) error { + user, err := actions.FetchTestUser(ctx, 0) + if err != nil { + return err + } + + pagination := common.QueryPagination{ + Offset: 0, + Size: 20, + } + personalTxs, total, err := user.GetPersonalTransactions(ctx, pagination) + if err != nil { + return fmt.Errorf("unable to get personal transactions - %w", err) + } + + // verify the transactions + if len(personalTxs) != 2 { + return fmt.Errorf("expected 2 transactions, got %d", len(personalTxs)) + } + + // verify total set + if total != 2 { + return fmt.Errorf("expected total receipts to be at least 2, got %d", total) + } + return nil + }), + ), + ) +} diff --git a/integration/networktest/userwallet/authclient.go b/integration/networktest/userwallet/authclient.go index fba2bd7e88..143e7dc0ab 100644 --- a/integration/networktest/userwallet/authclient.go +++ b/integration/networktest/userwallet/authclient.go @@ -8,6 +8,7 @@ import ( "time" "github.com/ethereum/go-ethereum" + "github.com/ten-protocol/go-ten/go/common" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -130,3 +131,8 @@ func (s *AuthClientUser) Init(ctx context.Context) (*AuthClientUser, error) { func (s *AuthClientUser) Wallet() wallet.Wallet { return s.wal } + +func (s *AuthClientUser) GetPersonalTransactions(ctx context.Context, pagination common.QueryPagination) (types.Receipts, uint64, error) { + address := s.wal.Address() + return s.client.GetPrivateTransactions(ctx, &address, pagination) +} diff --git a/integration/networktest/userwallet/gateway.go b/integration/networktest/userwallet/gateway.go index 9408c4c06c..7ca839b496 100644 --- a/integration/networktest/userwallet/gateway.go +++ b/integration/networktest/userwallet/gateway.go @@ -2,6 +2,7 @@ package userwallet import ( "context" + "encoding/json" "errors" "fmt" "math/big" @@ -9,9 +10,11 @@ import ( "github.com/ethereum/go-ethereum" gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" gethlog "github.com/ethereum/go-ethereum/log" + "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/common/retry" "github.com/ten-protocol/go-ten/go/wallet" "github.com/ten-protocol/go-ten/tools/walletextension/lib" @@ -110,6 +113,29 @@ func (g *GatewayUser) NativeBalance(ctx context.Context) (*big.Int, error) { return g.client.BalanceAt(ctx, g.wal.Address(), nil) } +func (g *GatewayUser) GetPersonalTransactions(ctx context.Context, pagination common.QueryPagination) (types.Receipts, uint64, error) { + rpcClient := g.client.Client() + queryParams := &common.ListPrivateTransactionsQueryParams{ + Address: g.wal.Address(), + Pagination: pagination, + } + queryParamsStr, err := json.Marshal(queryParams) + if err != nil { + return nil, 0, fmt.Errorf("unable to marshal query params - %w", err) + } + var resultBytes hexutil.Bytes + err = rpcClient.CallContext(ctx, &resultBytes, "eth_getStorageAt", common.ListPrivateTransactionsCQMethod, queryParamsStr, nil) + if err != nil { + return nil, 0, fmt.Errorf("rpc call failed - %w", err) + } + var result common.PrivateTransactionsQueryResponse + err = json.Unmarshal(resultBytes, &result) + if err != nil { + return nil, 0, fmt.Errorf("failed to unmarshal result - %w", err) + } + return result.Receipts, result.Total, nil +} + func (g *GatewayUser) Wallet() wallet.Wallet { return g.wal } diff --git a/integration/networktest/userwallet/user.go b/integration/networktest/userwallet/user.go index 105e2cd8e6..c4e1388720 100644 --- a/integration/networktest/userwallet/user.go +++ b/integration/networktest/userwallet/user.go @@ -6,6 +6,7 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/wallet" ) @@ -19,4 +20,5 @@ type User interface { SendFunds(ctx context.Context, addr gethcommon.Address, value *big.Int) (*gethcommon.Hash, error) AwaitReceipt(ctx context.Context, txHash *gethcommon.Hash) (*types.Receipt, error) NativeBalance(ctx context.Context) (*big.Int, error) + GetPersonalTransactions(ctx context.Context, pagination common.QueryPagination) (types.Receipts, uint64, error) } diff --git a/integration/obscurogateway/tengateway_test.go b/integration/obscurogateway/tengateway_test.go index 56f35b8a53..f411d35a4b 100644 --- a/integration/obscurogateway/tengateway_test.go +++ b/integration/obscurogateway/tengateway_test.go @@ -723,7 +723,7 @@ func testGetStorageAtForReturningUserID(t *testing.T, httpURL, wsURL string, w w var response JSONResponse // make a request to GetStorageAt with correct parameters to get userID that exists in the database - respBody := makeHTTPEthJSONReq(httpURL, tenrpc.GetStorageAt, user.tgClient.UserID(), []interface{}{wecommon.GetStorageAtUserIDRequestMethodName, "0", nil}) + respBody := makeHTTPEthJSONReq(httpURL, tenrpc.GetStorageAt, user.tgClient.UserID(), []interface{}{common.UserIDRequestCQMethod, "0", nil}) if err = json.Unmarshal(respBody, &response); err != nil { t.Error("Unable to unmarshal response") } @@ -732,15 +732,22 @@ func testGetStorageAtForReturningUserID(t *testing.T, httpURL, wsURL string, w w } // make a request to GetStorageAt with correct parameters to get userID, but with wrong userID - respBody2 := makeHTTPEthJSONReq(httpURL, tenrpc.GetStorageAt, "0x0000000000000000000000000000000000000001", []interface{}{wecommon.GetStorageAtUserIDRequestMethodName, "0", nil}) + respBody2 := makeHTTPEthJSONReq(httpURL, tenrpc.GetStorageAt, "0x0000000000000000000000000000000000000001", []interface{}{common.UserIDRequestCQMethod, "0", nil}) if !strings.Contains(string(respBody2), "not found") { t.Error("eth_getStorageAt did not respond with not found error") } + err = user.RegisterAccounts() + if err != nil { + t.Errorf("Failed to register accounts: %s", err) + return + } + // make a request to GetStorageAt with wrong parameters to get userID, but correct userID - respBody3 := makeHTTPEthJSONReq(httpURL, tenrpc.GetStorageAt, user.tgClient.UserID(), []interface{}{"0x0000000000000000000000000000000000000001", "0", nil}) - if !strings.Contains(string(respBody3), "illegal access") { - t.Error("eth_getStorageAt did not respond with error: illegal access") + respBody3 := makeHTTPEthJSONReq(httpURL, tenrpc.GetStorageAt, user.tgClient.UserID(), []interface{}{"0x0000000000000000000000000000000000000007", "0", nil}) + expectedErr := "not supported" + if !strings.Contains(string(respBody3), expectedErr) { + t.Errorf("eth_getStorageAt did not respond with error: %s, it was: %s", expectedErr, string(respBody3)) } } diff --git a/lib/gethfork/node/rpc_server.go b/lib/gethfork/node/rpc_server.go index a9b3d30b21..74f1dbe02f 100644 --- a/lib/gethfork/node/rpc_server.go +++ b/lib/gethfork/node/rpc_server.go @@ -53,14 +53,15 @@ func NewServer(config *RPCConfig, logger gethlog.Logger) Server { if config.EnableHTTP { rpcConfig.HTTPHost = config.Host rpcConfig.HTTPPort = config.HTTPPort - // todo (@pedro) - review if this poses a security issue + // todo - review if this poses a security issue + rpcConfig.HTTPCors = []string{allOrigins} rpcConfig.HTTPVirtualHosts = []string{allOrigins} rpcConfig.HTTPPathPrefix = config.HTTPPath } if config.EnableWs { rpcConfig.WSHost = config.Host rpcConfig.WSPort = config.WsPort - // todo (@pedro) - review if this poses a security issue + // todo - review if this poses a security issue rpcConfig.WSOrigins = []string{allOrigins} rpcConfig.WSPathPrefix = config.WsPath } diff --git a/tools/edbconnect/Dockerfile b/tools/edbconnect/Dockerfile index d8428035c0..b117a9154f 100644 --- a/tools/edbconnect/Dockerfile +++ b/tools/edbconnect/Dockerfile @@ -4,7 +4,7 @@ # deploy = copies over only the enclave executable without the source # in a lightweight base image specialized for deployment and prepares the /data/ folder. -FROM ghcr.io/edgelesssys/ego-dev:v1.5.0 AS build-base +FROM ghcr.io/edgelesssys/ego-dev:v1.5.3 AS build-base # setup container data structure RUN mkdir -p /home/ten/go-ten @@ -32,7 +32,7 @@ RUN ego sign edb-enclave.json # Trigger a new build stage and use the smaller ego version: -FROM ghcr.io/edgelesssys/ego-deploy:v1.5.0 +FROM ghcr.io/edgelesssys/ego-deploy:v1.5.3 # Copy the binary and the entrypoint script COPY --from=sign-built-enclave \ diff --git a/tools/tenscan/frontend/.nvmrc b/tools/tenscan/frontend/.nvmrc new file mode 100644 index 0000000000..7ea6a59d34 --- /dev/null +++ b/tools/tenscan/frontend/.nvmrc @@ -0,0 +1 @@ +v20.11.0 diff --git a/tools/tenscan/frontend/api/batches.ts b/tools/tenscan/frontend/api/batches.ts index d05314b54a..10ff925eaf 100644 --- a/tools/tenscan/frontend/api/batches.ts +++ b/tools/tenscan/frontend/api/batches.ts @@ -6,6 +6,7 @@ import { Batch, BatchDetails, BatchResponse, + LatestBatch, } from "@/src/types/interfaces/BatchInterfaces"; export const fetchBatches = async ( @@ -20,8 +21,8 @@ export const fetchBatches = async ( export const fetchLatestBatch = async ( payload?: Record -): Promise> => { - return await httpRequest>({ +): Promise> => { + return await httpRequest>({ method: "get", url: pathToUrl(apiRoutes.getLatestBatch), searchParams: payload, @@ -36,3 +37,23 @@ export const fetchBatchByHash = async ( url: pathToUrl(apiRoutes.getBatchByHash, { hash }), }); }; + +export const fetchBatchByHeight = async ( + height: string +): Promise> => { + return await httpRequest>({ + method: "get", + url: pathToUrl(apiRoutes.getBatchByHeight, { height }), + }); +}; + +export const fetchBatchTransactions = async ( + fullHash: string, + options?: Record +): Promise> => { + return await httpRequest>({ + method: "get", + url: pathToUrl(apiRoutes.getBatchTransactions, { fullHash }), + searchParams: options, + }); +}; diff --git a/tools/tenscan/frontend/api/rollups.ts b/tools/tenscan/frontend/api/rollups.ts index 375f0e0ecf..eddfc0ed12 100644 --- a/tools/tenscan/frontend/api/rollups.ts +++ b/tools/tenscan/frontend/api/rollups.ts @@ -2,8 +2,13 @@ import { httpRequest } from "."; import { apiRoutes } from "@/src/routes"; import { pathToUrl } from "@/src/routes/router"; import { ResponseDataInterface } from "@/src/types/interfaces"; +import { Batch, BatchResponse } from "@/src/types/interfaces/BatchInterfaces"; +import { + Rollup, + RollupsResponse, +} from "@/src/types/interfaces/RollupInterfaces"; -export const fetchRollups = async ( +export const fetchLatestRollups = async ( payload?: Record ): Promise> => { return await httpRequest>({ @@ -13,6 +18,16 @@ export const fetchRollups = async ( }); }; +export const fetchRollups = async ( + payload?: Record +): Promise> => { + return await httpRequest>({ + method: "get", + url: pathToUrl(apiRoutes.getRollups), + searchParams: payload, + }); +}; + export const decryptEncryptedRollup = async ({ StrData, }: { @@ -24,3 +39,32 @@ export const decryptEncryptedRollup = async ({ data: { StrData }, }); }; + +export const fetchRollupByHash = async ( + hash: string +): Promise> => { + return await httpRequest>({ + method: "get", + url: pathToUrl(apiRoutes.getRollupByHash, { hash }), + }); +}; + +export const fetchRollupByBatchSequence = async ( + seq: string +): Promise> => { + return await httpRequest>({ + method: "get", + url: pathToUrl(apiRoutes.getRollupByBatchSequence, { seq }), + }); +}; + +export const fetchBatchesInRollups = async ( + hash: string, + options: Record +): Promise> => { + return await httpRequest>({ + method: "get", + url: pathToUrl(apiRoutes.getBatchesInRollup, { hash }), + searchParams: options, + }); +}; diff --git a/tools/tenscan/frontend/api/transactions.ts b/tools/tenscan/frontend/api/transactions.ts index 19ad461fb4..051bf757ff 100644 --- a/tools/tenscan/frontend/api/transactions.ts +++ b/tools/tenscan/frontend/api/transactions.ts @@ -1,12 +1,15 @@ +import { jsonHexToObj } from "@/src/lib/utils"; import { httpRequest } from "."; -import { apiRoutes } from "@/src/routes"; +import { apiRoutes, ethMethods, tenCustomQueryMethods } from "@/src/routes"; import { pathToUrl } from "@/src/routes/router"; -import { ResponseDataInterface } from "@/src/types/interfaces"; +import { ResponseDataInterface, ToastType } from "@/src/types/interfaces"; import { TransactionCount, Price, TransactionResponse, + Transaction, } from "@/src/types/interfaces/TransactionInterfaces"; +import { showToast } from "@/src/components/ui/use-toast"; export const fetchTransactions = async ( payload?: Record @@ -31,3 +34,61 @@ export const fetchEtherPrice = async (): Promise => { url: apiRoutes.getEtherPrice, }); }; + +export const fetchTransactionByHash = async ( + hash: string +): Promise> => { + return await httpRequest>({ + method: "get", + url: pathToUrl(apiRoutes.getTransactionByHash, { hash }), + }); +}; + +export const personalTransactionsData = async ( + provider: any, + walletAddress: string | null, + options: Record +) => { + try { + if (provider && walletAddress) { + const requestPayload = { + address: walletAddress, + pagination: { + ...options, + }, + }; + const personalTxResp = await provider.send(ethMethods.getStorageAt, [ + tenCustomQueryMethods.listPersonalTransactions, + JSON.stringify(requestPayload), + null, + ]); + const personalTxData = jsonHexToObj(personalTxResp); + return personalTxData; + } + + return null; + } catch (error) { + console.error("Error fetching personal transactions:", error); + showToast(ToastType.DESTRUCTIVE, "Error fetching personal transactions"); + throw error; + } +}; + +export const fetchPersonalTxnByHash = async ( + provider: any, + hash: string +): Promise => { + try { + if (provider) { + const personalTxnResp = await provider.send( + ethMethods.getTransactionReceipt, + [hash] + ); + return personalTxnResp; + } + } catch (error) { + console.error("Error fetching personal transaction:", error); + showToast(ToastType.DESTRUCTIVE, "Error fetching personal transaction"); + throw error; + } +}; diff --git a/tools/tenscan/frontend/package.json b/tools/tenscan/frontend/package.json index dd73980224..87793b2534 100644 --- a/tools/tenscan/frontend/package.json +++ b/tools/tenscan/frontend/package.json @@ -37,9 +37,9 @@ "next": "14.0.1", "next-themes": "^0.2.1", "path-to-regexp": "^6.2.1", - "react": "^18", + "react": "^18.2.0", "react-day-picker": "^8.9.1", - "react-dom": "^18", + "react-dom": "^18.2.0", "react-json-pretty": "^2.2.0", "recharts": "^2.9.3", "tailwind-merge": "^2.0.0", diff --git a/tools/tenscan/frontend/pages/address/[address].tsx b/tools/tenscan/frontend/pages/address/[address].tsx index bb8a057374..46bc8e225b 100644 --- a/tools/tenscan/frontend/pages/address/[address].tsx +++ b/tools/tenscan/frontend/pages/address/[address].tsx @@ -21,3 +21,9 @@ const AddressDetails = () => { }; export default AddressDetails; + +export async function getServerSideProps(context: any) { + return { + props: {}, + }; +} diff --git a/tools/tenscan/frontend/pages/batches/[hash].tsx b/tools/tenscan/frontend/pages/batch/[hash].tsx similarity index 82% rename from tools/tenscan/frontend/pages/batches/[hash].tsx rename to tools/tenscan/frontend/pages/batch/[hash].tsx index 964444aa77..cd55e6f65c 100644 --- a/tools/tenscan/frontend/pages/batches/[hash].tsx +++ b/tools/tenscan/frontend/pages/batch/[hash].tsx @@ -1,6 +1,6 @@ import { fetchBatchByHash } from "@/api/batches"; import Layout from "@/src/components/layouts/default-layout"; -import { BatchDetailsComponent } from "@/src/components/modules/batches/batch-details"; +import { BatchHashDetailsComponent } from "@/src/components/modules/batches/batch-hash-details"; import { Card, CardHeader, @@ -36,7 +36,7 @@ export default function Batch() { - + ) : ( @@ -45,3 +45,9 @@ export default function Batch() { ); } + +export async function getServerSideProps(context: any) { + return { + props: {}, + }; +} diff --git a/tools/tenscan/frontend/pages/batch/height/[height].tsx b/tools/tenscan/frontend/pages/batch/height/[height].tsx new file mode 100644 index 0000000000..7982d825cf --- /dev/null +++ b/tools/tenscan/frontend/pages/batch/height/[height].tsx @@ -0,0 +1,53 @@ +import { fetchBatchByHeight } from "@/api/batches"; +import Layout from "@/src/components/layouts/default-layout"; +import { BatchHeightDetailsComponent } from "@/src/components/modules/batches/batch-height-details"; +import { + Card, + CardHeader, + CardTitle, + CardContent, + CardDescription, +} from "@/src/components/ui/card"; +import { Skeleton } from "@/src/components/ui/skeleton"; +import { useQuery } from "@tanstack/react-query"; +import { useRouter } from "next/router"; + +export default function Batch() { + const router = useRouter(); + const { height } = router.query; + + const { data, isLoading } = useQuery({ + queryKey: ["batchHeight", height], + queryFn: () => fetchBatchByHeight(height as string), + }); + + const batchDetails = data?.item; + + return ( + + {isLoading ? ( + + ) : batchDetails ? ( + + + Batch #{Number(batchDetails?.header?.number)} + + Overview of the batch #{Number(batchDetails?.header?.number)} + + + + + + + ) : ( +
Batch not found
+ )} +
+ ); +} + +export async function getServerSideProps(context: any) { + return { + props: {}, + }; +} diff --git a/tools/tenscan/frontend/pages/batch/txs/[fullHash].tsx b/tools/tenscan/frontend/pages/batch/txs/[fullHash].tsx new file mode 100644 index 0000000000..1d0748d532 --- /dev/null +++ b/tools/tenscan/frontend/pages/batch/txs/[fullHash].tsx @@ -0,0 +1,66 @@ +import { fetchBatchTransactions } from "@/api/batches"; +import Layout from "@/src/components/layouts/default-layout"; +import { DataTable } from "@/src/components/modules/common/data-table/data-table"; +import { columns } from "@/src/components/modules/batches/transaction-columns"; +import { + Card, + CardHeader, + CardTitle, + CardContent, + CardDescription, +} from "@/src/components/ui/card"; +import { useQuery } from "@tanstack/react-query"; +import { useRouter } from "next/router"; +import { getOptions } from "@/src/lib/constants"; +import TruncatedAddress from "@/src/components/modules/common/truncated-address"; + +export default function BatchTransactions() { + const router = useRouter(); + const { fullHash } = router.query; + const options = getOptions(router.query); + + const { data, isLoading, refetch } = useQuery({ + queryKey: ["batchTransactions", { fullHash, options }], + queryFn: () => fetchBatchTransactions(fullHash as string, options), + }); + + const { TransactionsData, Total } = data?.result || { + TransactionsData: [], + Total: 0, + }; + + return ( + + + + Transactions + +

Overview of all transactions in this batch:

+ +
+
+ + + +
+
+ ); +} + +export async function getServerSideProps(context: any) { + return { + props: {}, + }; +} diff --git a/tools/tenscan/frontend/pages/batches/index.tsx b/tools/tenscan/frontend/pages/batches/index.tsx index fd0b27f098..e8d88d3fbc 100644 --- a/tools/tenscan/frontend/pages/batches/index.tsx +++ b/tools/tenscan/frontend/pages/batches/index.tsx @@ -4,7 +4,8 @@ import { DataTable } from "@/src/components/modules/common/data-table/data-table import Layout from "@/src/components/layouts/default-layout"; import { Metadata } from "next"; import { useBatchesService } from "@/src/services/useBatchesService"; -import { formatNumber } from "@/src/lib/utils"; +import { getItem } from "@/src/lib/utils"; +import { ItemPosition } from "@/src/types/interfaces"; export const metadata: Metadata = { title: "Batches", @@ -12,7 +13,8 @@ export const metadata: Metadata = { }; export default function Batches() { - const { batches, refetchBatches, setNoPolling } = useBatchesService(); + const { batches, refetchBatches, isBatchesLoading, setNoPolling } = + useBatchesService(); const { BatchesData, Total } = batches?.result || { BatchesData: [], Total: 0, @@ -20,30 +22,43 @@ export default function Batches() { React.useEffect(() => { setNoPolling(true); + + return () => { + setNoPolling(false); + }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const firstBatchHeight = Number(getItem(BatchesData, "height")); + const lastBatchHeight = Number( + getItem(BatchesData, "height", ItemPosition.LAST) + ); + return (

Batches

-

- {formatNumber(Total)} Batches found. -

+ {BatchesData?.length > 0 && ( +

+ Showing batches #{firstBatchHeight}{" "} + {lastBatchHeight !== firstBatchHeight && + "to #" + lastBatchHeight} + {/* uncomment the following line when total count feature is implemented */} + {/* of {formatNumber(Total)} batches. */} +

+ )}
- {BatchesData ? ( - - ) : ( -

Loading...

- )} +
); diff --git a/tools/tenscan/frontend/pages/blocks/index.tsx b/tools/tenscan/frontend/pages/blocks/index.tsx index 566ddbc3e0..158a1214dd 100644 --- a/tools/tenscan/frontend/pages/blocks/index.tsx +++ b/tools/tenscan/frontend/pages/blocks/index.tsx @@ -4,7 +4,8 @@ import { DataTable } from "@/src/components/modules/common/data-table/data-table import Layout from "@/src/components/layouts/default-layout"; import { Metadata } from "next"; import { useBlocksService } from "@/src/services/useBlocksService"; -import { formatNumber } from "@/src/lib/utils"; +import { getItem } from "@/src/lib/utils"; +import { ItemPosition } from "@/src/types/interfaces"; export const metadata: Metadata = { title: "Blocks", @@ -12,7 +13,8 @@ export const metadata: Metadata = { }; export default function Blocks() { - const { blocks, setNoPolling, refetchBlocks } = useBlocksService(); + const { blocks, setNoPolling, refetchBlocks, isBlocksLoading } = + useBlocksService(); const { BlocksData, Total } = blocks?.result || { BlocksData: [], Total: 0, @@ -24,27 +26,36 @@ export default function Blocks() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const firstBlockNumber = Number(getItem(BlocksData, "blockHeader.number")); + const lastBlockNumber = Number( + getItem(BlocksData, "blockHeader.number", ItemPosition.LAST) + ); + return (

Blocks

-

- {formatNumber(Total)} Blocks found. -

+ {BlocksData?.length > 0 && ( +

+ Showing blocks #{firstBlockNumber}{" "} + {lastBlockNumber !== firstBlockNumber && + "to #" + lastBlockNumber} + {/* uncomment the following line when total count feature is implemented */} + {/* of {formatNumber(Total)} blocks. */} +

+ )}
- {BlocksData ? ( - - ) : ( -

Loading...

- )} +
); diff --git a/tools/tenscan/frontend/pages/docs/[id].tsx b/tools/tenscan/frontend/pages/docs/[id].tsx index 3400f95f5b..56ca02d6d0 100644 --- a/tools/tenscan/frontend/pages/docs/[id].tsx +++ b/tools/tenscan/frontend/pages/docs/[id].tsx @@ -100,3 +100,9 @@ const Document = () => { }; export default Document; + +export async function getServerSideProps(context: any) { + return { + props: {}, + }; +} diff --git a/tools/tenscan/frontend/pages/rollup/[hash]/batches.tsx b/tools/tenscan/frontend/pages/rollup/[hash]/batches.tsx new file mode 100644 index 0000000000..7df649aef2 --- /dev/null +++ b/tools/tenscan/frontend/pages/rollup/[hash]/batches.tsx @@ -0,0 +1,55 @@ +import React from "react"; +import { columns } from "@/src/components/modules/batches/columns"; +import { DataTable } from "@/src/components/modules/common/data-table/data-table"; +import Layout from "@/src/components/layouts/default-layout"; +import { Metadata } from "next"; +import { formatNumber } from "@/src/lib/utils"; +import { useRollupsService } from "@/src/services/useRollupsService"; + +export const metadata: Metadata = { + title: "Batches", + description: "A table of Batches.", +}; + +export default function RollupBatches() { + const { rollupBatches, isRollupBatchesLoading, refetchRollupBatches } = + useRollupsService(); + + const { BatchesData, Total } = rollupBatches?.result || { + BatchesData: [], + Total: 0, + }; + + return ( + +
+
+
+

Batches

+ {/* uncomment the following line when total count feature is implemented */} + {/*

+ {formatNumber(Total)} Batch(es) found in this rollup. +

*/} +
+
+ {BatchesData ? ( + + ) : ( +

Loading...

+ )} +
+
+ ); +} + +export async function getServerSideProps(context: any) { + return { + props: {}, + }; +} diff --git a/tools/tenscan/frontend/pages/rollup/[hash]/index.tsx b/tools/tenscan/frontend/pages/rollup/[hash]/index.tsx new file mode 100644 index 0000000000..889f780409 --- /dev/null +++ b/tools/tenscan/frontend/pages/rollup/[hash]/index.tsx @@ -0,0 +1,61 @@ +import { fetchRollupByHash } from "@/api/rollups"; +import Layout from "@/src/components/layouts/default-layout"; +import { RollupDetailsComponent } from "@/src/components/modules/rollups/rollup-details"; +import EmptyState from "@/src/components/modules/common/empty-state"; +import { Button } from "@/src/components/ui/button"; +import { + Card, + CardHeader, + CardTitle, + CardContent, + CardDescription, +} from "@/src/components/ui/card"; +import { Skeleton } from "@/src/components/ui/skeleton"; +import { useQuery } from "@tanstack/react-query"; +import { useRouter } from "next/router"; + +export default function RollupDetails() { + const router = useRouter(); + const { hash } = router.query; + + const { data, isLoading } = useQuery({ + queryKey: ["rollupDetails", hash], + queryFn: () => fetchRollupByHash(hash as string), + }); + + const rollupDetails = data?.item; + + return ( + + {isLoading ? ( + + ) : rollupDetails ? ( + + + Rollup #{Number(rollupDetails?.ID)} + + Overview of rollup #{Number(rollupDetails?.ID)} + + + + + + + ) : ( + router.push("/rollups")}>Go back + } + /> + )} + + ); +} + +export async function getServerSideProps(context: any) { + return { + props: {}, + }; +} diff --git a/tools/tenscan/frontend/pages/rollup/batch/sequence/[sequence].tsx b/tools/tenscan/frontend/pages/rollup/batch/sequence/[sequence].tsx new file mode 100644 index 0000000000..eb8b2716d8 --- /dev/null +++ b/tools/tenscan/frontend/pages/rollup/batch/sequence/[sequence].tsx @@ -0,0 +1,61 @@ +import { fetchRollupByBatchSequence } from "@/api/rollups"; +import Layout from "@/src/components/layouts/default-layout"; +import EmptyState from "@/src/components/modules/common/empty-state"; +import { RollupDetailsComponent } from "@/src/components/modules/rollups/rollup-details"; +import { Button } from "@/src/components/ui/button"; +import { + Card, + CardHeader, + CardTitle, + CardContent, + CardDescription, +} from "@/src/components/ui/card"; +import { Skeleton } from "@/src/components/ui/skeleton"; +import { useQuery } from "@tanstack/react-query"; +import { useRouter } from "next/router"; + +export default function RollupBatchSequenceDetails() { + const router = useRouter(); + const { sequence } = router.query; + + const { data, isLoading } = useQuery({ + queryKey: ["rollupSequenceDetails", sequence], + queryFn: () => fetchRollupByBatchSequence(sequence as string), + }); + + const rollupDetails = data?.item; + + return ( + + {isLoading ? ( + + ) : rollupDetails ? ( + + + Rollup #{Number(rollupDetails?.ID)} + + Overview of the Rollup with batch sequence #{Number(sequence)} + + + + + + + ) : ( + router.push("/rollups")}>Go back + } + /> + )} + + ); +} + +export async function getServerSideProps(context: any) { + return { + props: {}, + }; +} diff --git a/tools/tenscan/frontend/pages/rollups/index.tsx b/tools/tenscan/frontend/pages/rollups/index.tsx new file mode 100644 index 0000000000..8793dd2ad4 --- /dev/null +++ b/tools/tenscan/frontend/pages/rollups/index.tsx @@ -0,0 +1,62 @@ +import React from "react"; +import { DataTable } from "@/src/components/modules/common/data-table/data-table"; +import Layout from "@/src/components/layouts/default-layout"; +import { useRollupsService } from "@/src/services/useRollupsService"; +import { Metadata } from "next"; +import { columns } from "@/src/components/modules/rollups/columns"; +import { getItem } from "@/src/lib/utils"; +import { ItemPosition } from "@/src/types/interfaces"; + +export const metadata: Metadata = { + title: "Rollups", + description: "A table of rollups.", +}; + +export default function Rollups() { + const { rollups, setNoPolling, isRollupsLoading, refetchRollups } = + useRollupsService(); + const { RollupsData, Total } = rollups?.result || { + RollupsData: [], + Total: 0, + }; + + React.useEffect(() => { + setNoPolling(true); + + return () => { + setNoPolling(false); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const firstRollupID = Number(getItem(RollupsData, "ID")); + const lastRollupID = Number(getItem(RollupsData, "ID", ItemPosition.LAST)); + + return ( + +
+
+
+

Rollups

+ {RollupsData?.length > 0 && ( +

+ Showing rollups #{firstRollupID}{" "} + {lastRollupID !== firstRollupID && "to #" + lastRollupID} + {/* uncomment the following line when total count feature is implemented */} + {/* of {formatNumber(Total)} rollups. */} +

+ )} +
+
+ +
+
+ ); +} diff --git a/tools/tenscan/frontend/pages/transactions/index.tsx b/tools/tenscan/frontend/pages/transactions/index.tsx index 06ca008bb8..2a63269e16 100644 --- a/tools/tenscan/frontend/pages/transactions/index.tsx +++ b/tools/tenscan/frontend/pages/transactions/index.tsx @@ -4,7 +4,8 @@ import { DataTable } from "@/src/components/modules/common/data-table/data-table import Layout from "@/src/components/layouts/default-layout"; import { useTransactionsService } from "@/src/services/useTransactionsService"; import { Metadata } from "next"; -import { formatNumber } from "@/src/lib/utils"; +import { getItem } from "@/src/lib/utils"; +import { ItemPosition } from "@/src/types/interfaces"; export const metadata: Metadata = { title: "Transactions", @@ -12,8 +13,12 @@ export const metadata: Metadata = { }; export default function Transactions() { - const { transactions, refetchTransactions, setNoPolling } = - useTransactionsService(); + const { + transactions, + refetchTransactions, + setNoPolling, + isTransactionsLoading, + } = useTransactionsService(); const { TransactionsData, Total } = transactions?.result || { TransactionsData: [], Total: 0, @@ -21,30 +26,47 @@ export default function Transactions() { React.useEffect(() => { setNoPolling(true); + + return () => { + setNoPolling(false); + }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const firstBatchHeight = getItem(TransactionsData, "BatchHeight"); + const lastBatchHeight = getItem( + TransactionsData, + "BatchHeight", + ItemPosition.LAST + ); + return (

Transactions

-

- {formatNumber(Total)} Transactions found. -

+ {TransactionsData?.length > 0 && ( +

+ Showing transactions in batch + {firstBatchHeight !== lastBatchHeight && "es"} # + {firstBatchHeight}{" "} + {firstBatchHeight !== lastBatchHeight && + "to #" + lastBatchHeight} + {/* uncomment the following line when total count feature is implemented */} + {/* of {formatNumber(Total)} transactions. */} +

+ )}
- {TransactionsData ? ( - - ) : ( -

Loading...

- )} +
); diff --git a/tools/tenscan/frontend/pages/tx/[hash].tsx b/tools/tenscan/frontend/pages/tx/[hash].tsx index b1b9ea7402..c47d0fb2ff 100644 --- a/tools/tenscan/frontend/pages/tx/[hash].tsx +++ b/tools/tenscan/frontend/pages/tx/[hash].tsx @@ -1,23 +1,71 @@ +import { fetchTransactionByHash } from "@/api/transactions"; import Layout from "@/src/components/layouts/default-layout"; +import { TransactionDetailsComponent } from "@/src/components/modules/transactions/transaction-details"; import EmptyState from "@/src/components/modules/common/empty-state"; import { Button } from "@/src/components/ui/button"; +import { + Card, + CardHeader, + CardTitle, + CardContent, +} from "@/src/components/ui/card"; +import { Skeleton } from "@/src/components/ui/skeleton"; +import { useQuery } from "@tanstack/react-query"; import { useRouter } from "next/router"; -import React from "react"; -const TransactionDetails = () => { - const { push } = useRouter(); +export default function TransactionDetails() { + const router = useRouter(); + const { hash } = router.query; + + const { data, isLoading } = useQuery({ + queryKey: ["transactionDetails", hash], + queryFn: () => fetchTransactionByHash(hash as string), + }); + + const transactionDetails = data?.item; return ( - push("/")}>Go Home} - /> + + {isLoading ? ( + <> + + + + + + + + ) : transactionDetails ? ( + <> + + Transaction Details + + + + + + ) : ( + router.push("/transactions")}> + Go back + + } + className="p-8" + /> + )} + ); -}; +} -export default TransactionDetails; +export async function getServerSideProps(context: any) { + return { + props: {}, + }; +} diff --git a/tools/tenscan/frontend/pages/tx/personal/[hash].tsx b/tools/tenscan/frontend/pages/tx/personal/[hash].tsx new file mode 100644 index 0000000000..65e0c9889c --- /dev/null +++ b/tools/tenscan/frontend/pages/tx/personal/[hash].tsx @@ -0,0 +1,83 @@ +import Layout from "@/src/components/layouts/default-layout"; +import EmptyState from "@/src/components/modules/common/empty-state"; +import { Button } from "@/src/components/ui/button"; +import { + Card, + CardHeader, + CardTitle, + CardContent, +} from "@/src/components/ui/card"; +import { Skeleton } from "@/src/components/ui/skeleton"; +import { useQuery } from "@tanstack/react-query"; +import { useRouter } from "next/router"; +import { fetchPersonalTxnByHash } from "@/api/transactions"; +import { useWalletConnection } from "@/src/components/providers/wallet-provider"; +import { PersonalTxnDetailsComponent } from "@/src/components/modules/personal/personal-txn-details"; +import ConnectWalletButton from "@/src/components/modules/common/connect-wallet"; +import { ethereum } from "@/src/lib/utils"; + +export default function TransactionDetails() { + const router = useRouter(); + const { provider, walletConnected } = useWalletConnection(); + const { hash } = router.query; + + const { data: transactionDetails, isLoading } = useQuery({ + queryKey: ["personalTxnData", hash], + queryFn: () => fetchPersonalTxnByHash(provider, hash as string), + enabled: !!provider && !!hash, + }); + + return ( + + {walletConnected ? ( + isLoading ? ( + + ) : transactionDetails ? ( + + + Transaction Details + + + + + + ) : ( + router.push("/personal")}>Go back + } + /> + ) + ) : ( + + + + + } + /> + )} + + ); +} + +export async function getServerSideProps(context: any) { + return { + props: {}, + }; +} diff --git a/tools/tenscan/frontend/public/docs/terms.json b/tools/tenscan/frontend/public/docs/terms.json index ff0769351e..6becc3b6ed 100644 --- a/tools/tenscan/frontend/public/docs/terms.json +++ b/tools/tenscan/frontend/public/docs/terms.json @@ -55,8 +55,8 @@ { "heading": "8. LIMITATIONS OF LIABILITY", "content": [ - "TO THE FULLEST EXTENT ALLOWED BY APPLICABLE LAW, UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, TORT, CONTRACT, STRICT LIABILITY, OR OTHERWISE) SHALL THE INDEMNIFIED PARTIES OR ANY OF THEM BE LIABLE TO YOU OR TO ANY OTHER PERSON FOR:
  • ANY INDIRECT, SPECIAL, INCIDENTAL, PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING DAMAGES FOR LOST PROFITS, BUSINESS, OR REVENUE, BUSINESS INTERRUPTION, LOSS OF DATA, LOSS OF BUSINESS OPPORTUNITY, GOODWILL OR REPUTATION, WORK STOPPAGE, ACCURACY OF RESULTS, OR COMPUTER FAILURE OR MALFUNCTION;
  • ANY SUBSTITUTE GOODS, SERVICES OR TECHNOLOGY;
  • ANY AMOUNT, IN THE AGGREGATE, IN EXCESS OF ONE-HUNDRED POUNDS (£100); OR
  • ANY MATTER BEYOND THE REASONABLE CONTROL OF THE INDEMNIFIED PARTIES OR ANY OF THEM.
", - "SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL OR CERTAIN OTHER DAMAGES, SO THE FOREGOING LIMITATIONS AND EXCLUSIONS MAY NOT APPLY TO YOU.", + "To the fullest extent allowed by applicable law, under no circumstances and under no legal theory (including, without limitation, tort, contract, strict liability, or otherwise) shall the indemnified parties or any of them be liable to you or to any other person for:
  • any indirect, special, incidental, punitive or consequential damages of any kind, including damages for lost profits, business, or revenue, business interruption, loss of data, loss of business opportunity, goodwill or reputation, work stoppage, accuracy of results, or computer failure or malfunction;
  • any substitute goods, services or technology;
  • any amount, in the aggregate, in excess of one-hundred pounds (£100); or
  • any matter beyond the reasonable control of the indemnified parties or any of them.
", + "Some jurisdictions do not allow the exclusion or limitation of incidental or consequential or certain other damages, so the foregoing limitations and exclusions may not apply to you.", "Nothing in these Terms is intended to exclude or limit our liability for death or personal injury caused by our negligence, or for fraud or fraudulent misrepresentation, or to affect your statutory rights." ] }, diff --git a/tools/tenscan/frontend/src/components/health-indicator.tsx b/tools/tenscan/frontend/src/components/health-indicator.tsx index 4dd21804cd..c5c0f296dd 100644 --- a/tools/tenscan/frontend/src/components/health-indicator.tsx +++ b/tools/tenscan/frontend/src/components/health-indicator.tsx @@ -1,6 +1,13 @@ import React from "react"; import { Badge, badgeVariants } from "./ui/badge"; import { useGeneralService } from "../services/useGeneralService"; +import { BarChart } from "lucide-react"; +import { + TooltipProvider, + TooltipTrigger, + TooltipContent, + Tooltip, +} from "./ui/tooltip"; const HealthIndicator = () => { const [status, setStatus] = React.useState(false); @@ -20,16 +27,30 @@ const HealthIndicator = () => { }, [testnetStatus]); return ( -
-

Testnet Status:

- - {status ? "Live" : "Down"} - -
+ + + +
+ Testnet Status + + + {status ? "Live" : "Down"} + +
+
+ + {status ? "Testnet status: Live" : "Testnet status: Down"} + +
+
); }; diff --git a/tools/tenscan/frontend/src/components/layouts/header.tsx b/tools/tenscan/frontend/src/components/layouts/header.tsx index a04de0fea8..9ba8f94c00 100644 --- a/tools/tenscan/frontend/src/components/layouts/header.tsx +++ b/tools/tenscan/frontend/src/components/layouts/header.tsx @@ -3,6 +3,7 @@ import { ModeToggle } from "../mode-toggle"; import ConnectWalletButton from "../modules/common/connect-wallet"; import Link from "next/link"; import { HamburgerMenuIcon } from "@radix-ui/react-icons"; +import { X } from "lucide-react"; import { useState } from "react"; import { Button } from "../ui/button"; import HealthIndicator from "../health-indicator"; @@ -12,7 +13,7 @@ export default function Header() { return (
- + Logo -
- +
+
@@ -48,14 +49,14 @@ const MobileMenu = () => { const [menuOpen, setMenuOpen] = useState(false); return ( -
- +
+ {menuOpen && (
@@ -63,6 +64,7 @@ const MobileMenu = () => {
+
diff --git a/tools/tenscan/frontend/src/components/main-nav.tsx b/tools/tenscan/frontend/src/components/main-nav.tsx index 1e422c897c..704af970cb 100644 --- a/tools/tenscan/frontend/src/components/main-nav.tsx +++ b/tools/tenscan/frontend/src/components/main-nav.tsx @@ -42,7 +42,7 @@ const NavItem = ({ navLink }: { navLink: NavLink }) => { {navLink.label} - + {navLink.subNavLinks && navLink.subNavLinks.map((subNavLink: NavLink) => ( diff --git a/tools/tenscan/frontend/src/components/modules/batches/batch-details.tsx b/tools/tenscan/frontend/src/components/modules/batches/batch-details.tsx deleted file mode 100644 index 430281453b..0000000000 --- a/tools/tenscan/frontend/src/components/modules/batches/batch-details.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { Separator } from "@/src/components/ui/separator"; -import TruncatedAddress from "../common/truncated-address"; -import KeyValueItem, { KeyValueList } from "@/src/components/ui/key-value"; -import { formatNumber, formatTimeAgo } from "@/src/lib/utils"; -import { Badge } from "@/src/components/ui/badge"; -import { BatchDetails } from "@/src/types/interfaces/BatchInterfaces"; - -export function BatchDetailsComponent({ - batchDetails, -}: { - batchDetails: BatchDetails; -}) { - return ( -
- - - } - /> - - } - /> - } - /> - - } - /> - - } - /> - - {formatTimeAgo(batchDetails?.Header?.timestamp)} - - } - /> - } - /> - - - - - } - /> - - - } - /> - } - /> - - - - - - - - - } - isLastItem - /> - -
- ); -} diff --git a/tools/tenscan/frontend/src/components/modules/batches/batch-hash-details.tsx b/tools/tenscan/frontend/src/components/modules/batches/batch-hash-details.tsx new file mode 100644 index 0000000000..f48a609f0b --- /dev/null +++ b/tools/tenscan/frontend/src/components/modules/batches/batch-hash-details.tsx @@ -0,0 +1,201 @@ +import { Separator } from "@/src/components/ui/separator"; +import TruncatedAddress from "../common/truncated-address"; +import KeyValueItem, { KeyValueList } from "@/src/components/ui/key-value"; +import { formatNumber, formatTimeAgo } from "@/src/lib/utils"; +import { Badge } from "@/src/components/ui/badge"; +import { Batch, BatchDetails } from "@/src/types/interfaces/BatchInterfaces"; +import Link from "next/link"; +import { EyeClosedIcon, EyeOpenIcon } from "@radix-ui/react-icons"; +import { Button } from "../../ui/button"; +import { useRollupsService } from "@/src/services/useRollupsService"; +import JSONPretty from "react-json-pretty"; +import { useState } from "react"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "../../ui/tooltip"; +import Copy from "../common/copy"; + +export function BatchHashDetailsComponent({ + batchDetails, +}: { + batchDetails: BatchDetails; +}) { + const { decryptedRollup, decryptEncryptedData } = useRollupsService(); + const [showDecryptedData, setShowDecryptedData] = useState(false); + + return ( +
+ + + {"#" + Number(batchDetails?.Header?.number)} + + } + /> + } + /> + + } + /> + } + /> + + } + /> + + } + /> + + {formatTimeAgo(batchDetails?.Header?.timestamp)} + + } + /> + } + /> + + + + + } + /> + + } + /> + + + + + + {batchDetails?.TxHashes?.map((txHash, index) => ( +
  • + +
  • + ))} + + ) : ( + "-" + ) + } + isLastItem + /> +
    + + + +
    + {" "} + + {showDecryptedData && decryptedRollup ? ( + + + + + + + Copy Decrypted Data to Clipboard + + + + ) : null} +
    + {decryptedRollup && showDecryptedData ? ( + <> + + + + ) : null} + + } + isLastItem + /> +
    +
    + ); +} diff --git a/tools/tenscan/frontend/src/components/modules/batches/batch-height-details.tsx b/tools/tenscan/frontend/src/components/modules/batches/batch-height-details.tsx new file mode 100644 index 0000000000..c19a2d07c5 --- /dev/null +++ b/tools/tenscan/frontend/src/components/modules/batches/batch-height-details.tsx @@ -0,0 +1,196 @@ +import { Separator } from "@/src/components/ui/separator"; +import TruncatedAddress from "../common/truncated-address"; +import KeyValueItem, { KeyValueList } from "@/src/components/ui/key-value"; +import { formatNumber, formatTimeAgo } from "@/src/lib/utils"; +import { Badge } from "@/src/components/ui/badge"; +import { Batch, BatchDetails } from "@/src/types/interfaces/BatchInterfaces"; +import Link from "next/link"; +import { EyeClosedIcon, EyeOpenIcon } from "@radix-ui/react-icons"; +import { Button } from "../../ui/button"; +import { useRollupsService } from "@/src/services/useRollupsService"; +import JSONPretty from "react-json-pretty"; +import { useState } from "react"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "../../ui/tooltip"; + +export function BatchHeightDetailsComponent({ + batchDetails, +}: { + batchDetails: Batch; +}) { + const { decryptedRollup, decryptEncryptedData } = useRollupsService(); + const [showDecryptedData, setShowDecryptedData] = useState(false); + + return ( +
    + + + + + } + /> + + } + /> + } + /> + + } + /> + + } + /> + + {formatTimeAgo(batchDetails?.header?.timestamp)} + + } + /> + } + /> + + {formatNumber(batchDetails?.header?.gasLimit)} + + } + /> + + {formatNumber(batchDetails?.header?.gasUsed)} + + } + /> + + + } + /> + + } + /> + + + + + + {batchDetails?.txCount || "-"} + + } + isLastItem + /> + + + + +
    + {" "} + +
    + {decryptedRollup && showDecryptedData ? ( + <> + + + + ) : null} + + } + isLastItem + /> +
    +
    + ); +} diff --git a/tools/tenscan/frontend/src/components/modules/batches/columns.tsx b/tools/tenscan/frontend/src/components/modules/batches/columns.tsx index f67fdc6955..c53138b099 100644 --- a/tools/tenscan/frontend/src/components/modules/batches/columns.tsx +++ b/tools/tenscan/frontend/src/components/modules/batches/columns.tsx @@ -6,7 +6,6 @@ import { DataTableColumnHeader } from "../common/data-table/data-table-column-he import TruncatedAddress from "../common/truncated-address"; import { formatNumber, formatTimeAgo } from "@/src/lib/utils"; import { Batch } from "@/src/types/interfaces/BatchInterfaces"; -import { EyeOpenIcon } from "@radix-ui/react-icons"; import Link from "next/link"; import { Badge } from "../../ui/badge"; @@ -19,9 +18,14 @@ export const columns: ColumnDef[] = [ cell: ({ row }) => { return (
    - - #{Number(row.getValue("number"))} - + + + #{Number(row.original.height)} + +
    ); }, @@ -37,8 +41,8 @@ export const columns: ColumnDef[] = [ return (
    - {row.getValue("timestamp") - ? formatTimeAgo(row.getValue("timestamp")) + {row.original.header.timestamp + ? formatTimeAgo(row.original.header.timestamp) : "N/A"}
    @@ -57,7 +61,7 @@ export const columns: ColumnDef[] = [
    - {formatNumber(row.getValue("gasUsed"))} + {formatNumber(row.original?.header?.gasUsed) || "N/A"}
    @@ -75,7 +79,9 @@ export const columns: ColumnDef[] = [ return (
    - {formatNumber(row.getValue("gasLimit"))} + + {formatNumber(row.original?.header?.gasUsed) || "N/A"} +
    ); @@ -89,7 +95,12 @@ export const columns: ColumnDef[] = [ ), cell: ({ row }) => { - return ; + return ( + + ); }, enableSorting: false, enableHiding: false, @@ -100,41 +111,45 @@ export const columns: ColumnDef[] = [ ), cell: ({ row }) => { - return ; + return ; }, enableSorting: false, enableHiding: false, }, { - accessorKey: "l1Proof", + accessorKey: "sequence", header: ({ column }) => ( - + ), cell: ({ row }) => { - return ; + return ( + + {row.original.sequence} + + ); }, enableSorting: false, enableHiding: false, }, { - accessorKey: "miner", + accessorKey: "txCount", header: ({ column }) => ( - + ), - cell: ({ row }) => { - return ; - }, - enableSorting: false, - enableHiding: false, - }, - { - id: "actions", cell: ({ row }) => { return ( - - + + {row.original.txCount} ); }, + enableSorting: false, + enableHiding: false, }, ]; diff --git a/tools/tenscan/frontend/src/components/modules/batches/transaction-columns.tsx b/tools/tenscan/frontend/src/components/modules/batches/transaction-columns.tsx new file mode 100644 index 0000000000..4e1e97ff75 --- /dev/null +++ b/tools/tenscan/frontend/src/components/modules/batches/transaction-columns.tsx @@ -0,0 +1,76 @@ +"use client"; + +import { ColumnDef } from "@tanstack/react-table"; +import { Badge } from "@/src/components/ui/badge"; + +import { statuses } from "../transactions/constants"; +import { DataTableColumnHeader } from "../common/data-table/data-table-column-header"; +import { Transaction } from "@/src/types/interfaces/TransactionInterfaces"; +import TruncatedAddress from "../common/truncated-address"; +import { formatNumber, formatTimeAgo } from "@/src/lib/utils"; +import Link from "next/link"; +import { EyeOpenIcon } from "@radix-ui/react-icons"; + +export const columns: ColumnDef[] = [ + { + accessorKey: "BatchTimestamp", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return ( +
    + + {formatTimeAgo(row.getValue("BatchTimestamp"))} + +
    + ); + }, + enableSorting: false, + enableHiding: false, + }, + + { + accessorKey: "TransactionHash", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return ( + + ); + }, + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "Finality", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const finality = statuses.find( + (finality) => finality.value === row.getValue("Finality") + ); + + if (!finality) { + return null; + } + + return ( +
    + {finality.icon && ( + + )} + {finality.label} +
    + ); + }, + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)); + }, + }, +]; diff --git a/tools/tenscan/frontend/src/components/modules/blocks/columns.tsx b/tools/tenscan/frontend/src/components/modules/blocks/columns.tsx index 3400768554..ee189d114b 100644 --- a/tools/tenscan/frontend/src/components/modules/blocks/columns.tsx +++ b/tools/tenscan/frontend/src/components/modules/blocks/columns.tsx @@ -22,7 +22,7 @@ export const columns: ColumnDef[] = [ return (
    - {Number(blockHeader?.number)} + #{Number(blockHeader?.number)}
    ); @@ -60,7 +60,10 @@ export const columns: ColumnDef[] = [ return Number(row.original.rollupHash) === 0 ? ( No rollup ) : ( - + ); }, enableSorting: false, @@ -69,7 +72,7 @@ export const columns: ColumnDef[] = [ { accessorKey: "blockHeader.gasUsed", header: ({ column }) => ( - + ), cell: ({ row }) => { const blockHeader = row.original.blockHeader as BlockHeader; @@ -96,7 +99,9 @@ export const columns: ColumnDef[] = [ return (
    - {formatNumber(blockHeader?.gasLimit)} + + {formatNumber(blockHeader?.gasLimit)} +
    ); diff --git a/tools/tenscan/frontend/src/components/modules/common/connect-wallet.tsx b/tools/tenscan/frontend/src/components/modules/common/connect-wallet.tsx index e33e0d7c6c..0bdba6765e 100644 --- a/tools/tenscan/frontend/src/components/modules/common/connect-wallet.tsx +++ b/tools/tenscan/frontend/src/components/modules/common/connect-wallet.tsx @@ -10,7 +10,7 @@ const ConnectWalletButton = ({ text }: { text?: string }) => { return ( diff --git a/tools/tenscan/frontend/src/components/modules/common/data-table/data-table-pagination.tsx b/tools/tenscan/frontend/src/components/modules/common/data-table/data-table-pagination.tsx index 45ce947a9f..0a61d0f786 100644 --- a/tools/tenscan/frontend/src/components/modules/common/data-table/data-table-pagination.tsx +++ b/tools/tenscan/frontend/src/components/modules/common/data-table/data-table-pagination.tsx @@ -2,10 +2,8 @@ import { ChevronLeftIcon, ChevronRightIcon, DoubleArrowLeftIcon, - DoubleArrowRightIcon, } from "@radix-ui/react-icons"; -import { Table } from "@tanstack/react-table"; - +import { PaginationState, Table } from "@tanstack/react-table"; import { Button } from "@/src/components/ui/button"; import { Select, @@ -14,28 +12,50 @@ import { SelectTrigger, SelectValue, } from "@/src/components/ui/select"; -import { formatNumber } from "@/src/lib/utils"; +import { Input } from "@/src/components/ui/input"; +import { useState } from "react"; interface DataTablePaginationProps { table: Table; + refetch?: () => void; + setPagination: (pagination: PaginationState) => void; } export function DataTablePagination({ table, + refetch, + setPagination, }: DataTablePaginationProps) { + const [page, setPage] = useState(table.getState().pagination.pageIndex); + + const handlePageChange = (e: React.ChangeEvent) => { + setPage(Number(e.target.value)); + }; + + const handleKey = (e: React.KeyboardEvent) => { + if ( + e.key === "Enter" && + page > 0 && + page !== table.getState().pagination.pageIndex + ) { + table.setPageIndex(page); + refetch?.(); + } + }; + return ( -
    -
    - {table.getFilteredSelectedRowModel().rows.length} of{" "} - {table.getFilteredRowModel().rows.length} row(s) selected. +
    +
    + Showing {table?.getFilteredRowModel()?.rows?.length} row(s)
    -
    +

    Rows per page

    - Page {formatNumber(table.getState().pagination.pageIndex + 1)} of{" "} - {formatNumber(table.getPageCount())} + Page + e.target.select()} + onBlur={() => setPage(table.getState().pagination.pageIndex)} + /> + {/* uncomment the following line when total count feature is implemented */} + {/* of {formatNumber(table.getPageCount())} */}
    - + */}
    diff --git a/tools/tenscan/frontend/src/components/modules/common/data-table/data-table.tsx b/tools/tenscan/frontend/src/components/modules/common/data-table/data-table.tsx index 477ebbd53e..982835d6d4 100644 --- a/tools/tenscan/frontend/src/components/modules/common/data-table/data-table.tsx +++ b/tools/tenscan/frontend/src/components/modules/common/data-table/data-table.tsx @@ -4,6 +4,8 @@ import * as React from "react"; import { ColumnDef, ColumnFiltersState, + OnChangeFn, + PaginationState, SortingState, VisibilityState, flexRender, @@ -27,6 +29,8 @@ import { import { DataTablePagination } from "./data-table-pagination"; import { DataTableToolbar } from "./data-table-toolbar"; import { useRouter } from "next/router"; +import { Skeleton } from "@/src/components/ui/skeleton"; +import { Button } from "@/src/components/ui/button"; interface DataTableProps { columns: ColumnDef[]; @@ -39,6 +43,10 @@ interface DataTableProps { updateQueryParams?: (query: any) => void; refetch?: () => void; total: number; + isLoading?: boolean; + noPagination?: boolean; + noResultsText?: string; + noResultsMessage?: string; } export function DataTable({ @@ -47,7 +55,12 @@ export function DataTable({ toolbar, refetch, total, + isLoading, + noPagination, + noResultsText, + noResultsMessage, }: DataTableProps) { + const { query, push, pathname } = useRouter(); const [rowSelection, setRowSelection] = React.useState({}); const [columnVisibility, setColumnVisibility] = React.useState({}); @@ -55,10 +68,25 @@ export function DataTable({ [] ); const [sorting, setSorting] = React.useState([]); - const [pagination, setPagination] = React.useState({ - pageIndex: 0, - pageSize: 20, - }); + + const pagination = React.useMemo(() => { + return { + pageIndex: Number(query.page) || 1, + pageSize: Number(query.size) || 20, + }; + }, [query.page, query.size]); + + const setPagination: OnChangeFn = (func) => { + const { pageIndex, pageSize } = + typeof func === "function" ? func(pagination) : func; + const newPageIndex = pagination.pageSize !== pageSize ? 1 : pageIndex; + const params = { + ...query, + page: newPageIndex > 0 ? newPageIndex : 1, + size: pageSize <= 100 ? pageSize : 100, + }; + push({ pathname, query: params }); + }; const table = useReactTable({ data, @@ -72,7 +100,7 @@ export function DataTable({ }, onPaginationChange: setPagination, manualPagination: true, - pageCount: Math.ceil(total / pagination.pageSize), + // pageCount: Math.ceil(total / pagination.pageSize), enableRowSelection: true, onRowSelectionChange: setRowSelection, onSortingChange: setSorting, @@ -86,18 +114,11 @@ export function DataTable({ getFacetedUniqueValues: getFacetedUniqueValues(), }); - const { query, push, pathname } = useRouter(); - const { pageIndex, pageSize } = table.getState().pagination; - - React.useEffect(() => { - const params = { ...query, page: pageIndex + 1, size: pageSize }; - push({ pathname, query: params }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [pageIndex, pageSize]); - return (
    - + {data && ( + + )}
    @@ -119,7 +140,18 @@ export function DataTable({ ))} - {table?.getRowModel()?.rows?.length && data ? ( + {isLoading ? ( + <> + + + + + + + ) : data && table?.getRowModel()?.rows?.length ? ( table.getRowModel().rows.map((row) => ( ({ colSpan={columns.length} className="h-24 text-center" > - No results. + {pagination.pageIndex > 1 ? ( +

    + No {noResultsText || "results"} found for the selected + filters. + +

    + ) : ( +

    + {noResultsMessage || + `No ${noResultsText || "results"} found.`} +

    + )}
    )}
    - + {data && !isLoading && !noPagination && ( + + )}
    ); } diff --git a/tools/tenscan/frontend/src/components/modules/common/empty-state.tsx b/tools/tenscan/frontend/src/components/modules/common/empty-state.tsx index 1a3a94cf9e..0372c45bd1 100644 --- a/tools/tenscan/frontend/src/components/modules/common/empty-state.tsx +++ b/tools/tenscan/frontend/src/components/modules/common/empty-state.tsx @@ -1,3 +1,4 @@ +import { cn } from "@/src/lib/utils"; import Image from "next/image"; import React from "react"; @@ -8,6 +9,7 @@ const EmptyState = ({ imageSrc, imageAlt, action, + className, }: { title?: string; description?: string; @@ -15,9 +17,15 @@ const EmptyState = ({ imageSrc?: string; imageAlt?: string; action?: React.ReactNode; + className?: string; }) => { return ( -
    +
    {icon &&
    {icon}
    } {imageSrc && ( @@ -25,6 +33,8 @@ const EmptyState = ({ src={imageSrc} alt={imageAlt || "Empty state"} className="w-24 h-24 rounded-full" + width={96} + height={96} /> )} {title && ( diff --git a/tools/tenscan/frontend/src/components/modules/common/truncated-address.tsx b/tools/tenscan/frontend/src/components/modules/common/truncated-address.tsx index fffa21fad3..789a21e8fd 100644 --- a/tools/tenscan/frontend/src/components/modules/common/truncated-address.tsx +++ b/tools/tenscan/frontend/src/components/modules/common/truncated-address.tsx @@ -8,17 +8,25 @@ import { } from "@/src/components/ui/tooltip"; import Copy from "./copy"; +import Link from "next/link"; const TruncatedAddress = ({ address, prefixLength, suffixLength, showCopy = true, + link, }: { address: string; prefixLength?: number; suffixLength?: number; showCopy?: boolean; + link?: + | string + | { + pathname: string; + query: { [key: string]: string | number }; + }; }) => { const truncatedAddress = `${address?.substring( 0, @@ -29,14 +37,29 @@ const TruncatedAddress = ({ <> {address ? (
    - - - {truncatedAddress} - -

    {address}

    -
    -
    -
    + {link ? ( + + + + + {truncatedAddress} + + + +

    {address}

    +
    +
    +
    + ) : ( + + + {truncatedAddress} + +

    {address}

    +
    +
    +
    + )} {showCopy && }
    ) : ( diff --git a/tools/tenscan/frontend/src/components/modules/dashboard/analytics-card.tsx b/tools/tenscan/frontend/src/components/modules/dashboard/analytics-card.tsx index 996daf0e27..41c6b6448e 100644 --- a/tools/tenscan/frontend/src/components/modules/dashboard/analytics-card.tsx +++ b/tools/tenscan/frontend/src/components/modules/dashboard/analytics-card.tsx @@ -5,12 +5,13 @@ import { CardContent, } from "@/src/components/ui/card"; import { Skeleton } from "@/src/components/ui/skeleton"; +import { DashboardAnalyticsData } from "@/src/types/interfaces"; import React from "react"; export default function AnalyticsCard({ item, }: { - item: { title: string; value: string; change: string; icon: any }; + item: DashboardAnalyticsData; }) { return ( @@ -20,13 +21,13 @@ export default function AnalyticsCard({
    - {item.value ? ( - item.value - ) : ( + {item.loading ? ( + ) : ( + item.value )}
    - {item.change && ( + {item?.change && (

    {item.change}

    )}
    diff --git a/tools/tenscan/frontend/src/components/modules/dashboard/index.tsx b/tools/tenscan/frontend/src/components/modules/dashboard/index.tsx index f195ff81eb..18124148f6 100644 --- a/tools/tenscan/frontend/src/components/modules/dashboard/index.tsx +++ b/tools/tenscan/frontend/src/components/modules/dashboard/index.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { CalendarDateRangePicker } from "@/src/components/date-range-picker"; import { CardHeader, CardTitle, @@ -22,19 +21,34 @@ import { useBatchesService } from "@/src/services/useBatchesService"; import TruncatedAddress from "../common/truncated-address"; import { useContractsService } from "@/src/services/useContractsService"; import { Skeleton } from "@/src/components/ui/skeleton"; -import { RecentBlocks } from "./recent-blocks"; -import { useBlocksService } from "@/src/services/useBlocksService"; import AnalyticsCard from "./analytics-card"; import Link from "next/link"; import { cn, formatNumber } from "@/src/lib/utils"; import { Badge } from "../../ui/badge"; import { BlocksIcon } from "lucide-react"; +import { useRollupsService } from "@/src/services/useRollupsService"; +import { RecentRollups } from "./recent-rollups"; +import { DashboardAnalyticsData } from "@/src/types/interfaces"; + +interface RecentData { + title: string; + data: any; + component: JSX.Element; + goTo: string; + className: string; +} export default function Dashboard() { - const { price, transactions, transactionCount } = useTransactionsService(); - const { contractCount } = useContractsService(); - const { batches, latestBatch } = useBatchesService(); - const { blocks } = useBlocksService(); + const { + price, + isPriceLoading, + transactions, + transactionCount, + isTransactionCountLoading, + } = useTransactionsService(); + const { contractCount, isContractCountLoading } = useContractsService(); + const { batches, latestBatch, isLatestBatchLoading } = useBatchesService(); + const { rollups } = useRollupsService(); const DASHBOARD_DATA = [ { @@ -45,6 +59,7 @@ export default function Dashboard() { // TODO: add change // change: "+20.1%", icon: RocketIcon, + loading: isPriceLoading, }, { title: "Latest L2 Batch", @@ -54,6 +69,7 @@ export default function Dashboard() { // TODO: add change // change: "+20.1%", icon: LayersIcon, + loading: isLatestBatchLoading, }, { title: "Latest L1 Rollup", @@ -69,6 +85,7 @@ export default function Dashboard() { // TODO: add change // change: "+20.1%", icon: CubeIcon, + loading: isLatestBatchLoading, }, { title: "Transactions", @@ -78,6 +95,7 @@ export default function Dashboard() { // TODO: add change // change: "+20.1%", icon: ReaderIcon, + loading: isTransactionCountLoading, }, { title: "Contracts", @@ -85,6 +103,7 @@ export default function Dashboard() { // TODO: add change // change: "+20.1%", icon: FileTextIcon, + loading: isContractCountLoading, }, { title: "Nodes", @@ -95,10 +114,10 @@ export default function Dashboard() { const RECENT_DATA = [ { - title: "Recent Blocks", - data: blocks, - component: , - goTo: "/blocks", + title: "Recent Rollups", + data: rollups, + component: , + goTo: "/rollups", className: "col-span-1 md:col-span-2 lg:col-span-3", }, { @@ -123,17 +142,17 @@ export default function Dashboard() {

    Tenscan

    - {DASHBOARD_DATA.map((item: any, index) => ( + {DASHBOARD_DATA.map((item: DashboardAnalyticsData, index: number) => ( ))}
    - {RECENT_DATA.map((item: any, index) => ( + {RECENT_DATA.map((item: RecentData, index) => ( - + {item.title}

    - #{Number(batch?.number)} + + #{Number(batch?.height)} +

    - {formatTimeAgo(batch?.timestamp)} + {formatTimeAgo(batch?.header?.timestamp)}

    - +
    ))} diff --git a/tools/tenscan/frontend/src/components/modules/dashboard/recent-blocks.tsx b/tools/tenscan/frontend/src/components/modules/dashboard/recent-rollups.tsx similarity index 56% rename from tools/tenscan/frontend/src/components/modules/dashboard/recent-blocks.tsx rename to tools/tenscan/frontend/src/components/modules/dashboard/recent-rollups.tsx index 9ead81d7bc..30353562a7 100644 --- a/tools/tenscan/frontend/src/components/modules/dashboard/recent-blocks.tsx +++ b/tools/tenscan/frontend/src/components/modules/dashboard/recent-rollups.tsx @@ -1,26 +1,33 @@ import TruncatedAddress from "../common/truncated-address"; import { formatTimeAgo } from "@/src/lib/utils"; import { Avatar, AvatarFallback } from "@/src/components/ui/avatar"; -import { Block } from "@/src/types/interfaces/BlockInterfaces"; +import { + Rollup, + RollupsResponse, +} from "@/src/types/interfaces/RollupInterfaces"; +import Link from "next/link"; -export function RecentBlocks({ blocks }: { blocks: any }) { +export function RecentRollups({ rollups }: { rollups: any }) { return (
    - {blocks?.result?.BlocksData.map((block: Block, i: number) => ( + {rollups?.result?.RollupsData?.map((rollup: Rollup, i: number) => (
    - BK + RP

    - #{Number(block?.blockHeader?.number)} + #{Number(rollup?.ID)}

    - {formatTimeAgo(block?.blockHeader?.timestamp)} + {formatTimeAgo(rollup?.Timestamp)}

    - +
    ))} diff --git a/tools/tenscan/frontend/src/components/modules/dashboard/recent-transactions.tsx b/tools/tenscan/frontend/src/components/modules/dashboard/recent-transactions.tsx index 9133fcbc89..79698a5788 100644 --- a/tools/tenscan/frontend/src/components/modules/dashboard/recent-transactions.tsx +++ b/tools/tenscan/frontend/src/components/modules/dashboard/recent-transactions.tsx @@ -3,6 +3,7 @@ import { Avatar, AvatarFallback } from "@/src/components/ui/avatar"; import { Transaction } from "@/src/types/interfaces/TransactionInterfaces"; import { Badge } from "../../ui/badge"; import { formatTimeAgo } from "@/src/lib/utils"; +import Link from "next/link"; export function RecentTransactions({ transactions }: { transactions: any }) { return ( @@ -15,14 +16,23 @@ export function RecentTransactions({ transactions }: { transactions: any }) {

    - #{Number(transaction?.BatchHeight)} + Batch + + #{Number(transaction?.BatchHeight)} +

    {formatTimeAgo(transaction?.BatchTimestamp)}

    - +
    {transaction?.Finality} diff --git a/tools/tenscan/frontend/src/components/modules/personal/columns.tsx b/tools/tenscan/frontend/src/components/modules/personal/columns.tsx index b769e3d42c..03fc288e3d 100644 --- a/tools/tenscan/frontend/src/components/modules/personal/columns.tsx +++ b/tools/tenscan/frontend/src/components/modules/personal/columns.tsx @@ -8,6 +8,8 @@ import { DataTableColumnHeader } from "../common/data-table/data-table-column-he import { PersonalTransactions } from "@/src/types/interfaces/TransactionInterfaces"; import TruncatedAddress from "../common/truncated-address"; import { formatNumber } from "@/src/lib/utils"; +import Link from "next/link"; +import { EyeOpenIcon } from "@radix-ui/react-icons"; export const columns: ColumnDef[] = [ { @@ -123,14 +125,14 @@ export const columns: ColumnDef[] = [ return value.includes(row.getValue(id)); }, }, - // { - // id: "actions", - // cell: ({ row }) => { - // return ( - // - // - // - // ); - // }, - // }, + { + id: "actions", + cell: ({ row }) => { + return ( + + + + ); + }, + }, ]; diff --git a/tools/tenscan/frontend/src/components/modules/personal/index.tsx b/tools/tenscan/frontend/src/components/modules/personal/index.tsx index 9870a674f2..03630d4c07 100644 --- a/tools/tenscan/frontend/src/components/modules/personal/index.tsx +++ b/tools/tenscan/frontend/src/components/modules/personal/index.tsx @@ -2,8 +2,6 @@ import React from "react"; import { columns } from "@/src/components/modules/personal/columns"; import { DataTable } from "@/src/components/modules/common/data-table/data-table"; import { useTransactionsService } from "@/src/services/useTransactionsService"; -import { Skeleton } from "@/src/components/ui/skeleton"; -import { formatNumber } from "@/src/lib/utils"; export default function PersonalTransactions() { const { personalTxns, setNoPolling, personalTxnsLoading } = @@ -15,7 +13,10 @@ export default function PersonalTransactions() { React.useEffect(() => { setNoPolling(true); - return () => setNoPolling(false); + + return () => { + setNoPolling(false); + }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -26,18 +27,19 @@ export default function PersonalTransactions() {

    Personal Transactions

    -

    + {/* uncomment the following line when total count feature is implemented */} + {/*

    {formatNumber(Total)} personal transaction(s). -

    +

    */}
    - {personalTxnsLoading ? ( - - ) : Receipts ? ( - - ) : ( -

    No transactions found.

    - )} + ); } diff --git a/tools/tenscan/frontend/src/components/modules/personal/personal-txn-details.tsx b/tools/tenscan/frontend/src/components/modules/personal/personal-txn-details.tsx new file mode 100644 index 0000000000..00e174f8e6 --- /dev/null +++ b/tools/tenscan/frontend/src/components/modules/personal/personal-txn-details.tsx @@ -0,0 +1,233 @@ +import TruncatedAddress from "../common/truncated-address"; +import KeyValueItem, { KeyValueList } from "@/src/components/ui/key-value"; +import { Badge } from "@/src/components/ui/badge"; +import { + PersonalTransactionType, + TransactionReceipt, + TransactionType, +} from "@/src/types/interfaces/TransactionInterfaces"; +import { BadgeType } from "@/src/types/interfaces"; +import Link from "next/link"; + +export function PersonalTxnDetailsComponent({ + transactionDetails, +}: { + transactionDetails: TransactionReceipt; +}) { + const getTransactionType = (type: TransactionType) => { + switch (type) { + case PersonalTransactionType.Legacy: + return "Legacy"; + case PersonalTransactionType.AccessList: + return "Access List"; + case PersonalTransactionType.DynamicFee: + return "Dynamic Fee"; + case PersonalTransactionType.Blob: + return "Blob"; + default: + return "Unknown"; + } + }; + + return ( +
    + + + } + /> + + } + /> + + {Number(transactionDetails?.transactionIndex)} + + } + /> + + {getTransactionType(transactionDetails?.type)} + + } + /> + + {transactionDetails?.status ? "Success" : "Failed"} + + } + /> + + + {Number(transactionDetails?.blockNumber)} + + } + /> + + {Number(transactionDetails?.gasUsed)}{" "} + + } + /> + + {Number(transactionDetails?.cumulativeGasUsed)} + + } + /> + + {Number(transactionDetails?.effectiveGasPrice)} + + } + /> + } + /> + } + /> + + } + /> + + } + /> + + 0 ? ( +
    + {transactionDetails?.logs.map((log, index) => ( +
    + + } + /> + } + /> + + } + /> + + + {log.removed ? "Yes" : "No"} + + } + /> + + {log.topics.map((topic, index) => ( +
    + } + /> +
    + ))} +
    + } + /> + + } + /> + + {Number(transactionDetails?.transactionIndex)} + + } + isLastItem + /> + +
    + ))} +
    + ) : ( + "No logs found" + ) + } + isLastItem + /> + +
    + ); +} diff --git a/tools/tenscan/frontend/src/components/modules/rollups/columns.tsx b/tools/tenscan/frontend/src/components/modules/rollups/columns.tsx new file mode 100644 index 0000000000..fe76d6fa3c --- /dev/null +++ b/tools/tenscan/frontend/src/components/modules/rollups/columns.tsx @@ -0,0 +1,128 @@ +"use client"; + +import { ColumnDef } from "@tanstack/react-table"; +import { DataTableColumnHeader } from "../common/data-table/data-table-column-header"; +import TruncatedAddress from "../common/truncated-address"; +import { formatTimeAgo } from "@/src/lib/utils"; +import Link from "next/link"; +import { EyeOpenIcon } from "@radix-ui/react-icons"; +import { Rollup } from "@/src/types/interfaces/RollupInterfaces"; + +export const columns: ColumnDef[] = [ + { + accessorKey: "ID", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return ( +
    + {row.getValue("ID")} +
    + ); + }, + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "Hash", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return ( + + ); + }, + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "Timestamp", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return ( +
    + + {formatTimeAgo(row.getValue("Timestamp"))} + +
    + ); + }, + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "L1Hash", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return ; + }, + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "FirstSeq", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return ( +
    + + + {row.getValue("FirstSeq")} + + +
    + ); + }, + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "LastSeq", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return ( +
    + + + {row.getValue("LastSeq")} + + +
    + ); + }, + enableSorting: false, + enableHiding: false, + }, + { + id: "actions", + cell: ({ row }) => { + return ( + + + + ); + }, + }, +]; diff --git a/tools/tenscan/frontend/src/components/modules/rollups/constants.tsx b/tools/tenscan/frontend/src/components/modules/rollups/constants.tsx new file mode 100644 index 0000000000..2ab030f09f --- /dev/null +++ b/tools/tenscan/frontend/src/components/modules/rollups/constants.tsx @@ -0,0 +1,45 @@ +import { + ArrowDownIcon, + ArrowRightIcon, + ArrowUpIcon, + CheckCircledIcon, + ClockIcon, +} from "@radix-ui/react-icons"; + +export const labels = [ + { + value: "Final", + label: "Final", + }, +]; + +export const statuses = [ + { + value: "Final", + label: "Final", + icon: CheckCircledIcon, + }, + { + value: "Pending", + label: "Pending", + icon: ClockIcon, + }, +]; + +export const priorities = [ + { + label: "Low", + value: "low", + icon: ArrowDownIcon, + }, + { + label: "Medium", + value: "medium", + icon: ArrowRightIcon, + }, + { + label: "High", + value: "high", + icon: ArrowUpIcon, + }, +]; diff --git a/tools/tenscan/frontend/src/components/modules/rollups/rollup-details.tsx b/tools/tenscan/frontend/src/components/modules/rollups/rollup-details.tsx new file mode 100644 index 0000000000..b53eb0e81e --- /dev/null +++ b/tools/tenscan/frontend/src/components/modules/rollups/rollup-details.tsx @@ -0,0 +1,125 @@ +import TruncatedAddress from "../common/truncated-address"; +import KeyValueItem, { KeyValueList } from "@/src/components/ui/key-value"; +import { formatTimeAgo } from "@/src/lib/utils"; +import { Rollup } from "@/src/types/interfaces/RollupInterfaces"; +import Link from "next/link"; + +export function RollupDetailsComponent({ + rollupDetails, +}: { + rollupDetails: Rollup; +}) { + return ( +
    + + + + + } + /> + + } + /> + } + /> + + {"#" + rollupDetails?.FirstSeq} + + } + /> + + {"#" + rollupDetails?.LastSeq} + + } + /> + + } + /> + + } + /> + + } + /> + 0 + ? rollupDetails?.Header?.crossChainMessages?.map((msg, index) => ( +
    + + + + {"#" + msg.Sequence} + + } + /> + + + ( +
    {payload}
    + ))} + /> + +
    +
    + )) + : "No cross chain messages found." + } + isLastItem + /> +
    +
    + ); +} diff --git a/tools/tenscan/frontend/src/components/modules/transactions/columns.tsx b/tools/tenscan/frontend/src/components/modules/transactions/columns.tsx index fda982927c..a615dad2cb 100644 --- a/tools/tenscan/frontend/src/components/modules/transactions/columns.tsx +++ b/tools/tenscan/frontend/src/components/modules/transactions/columns.tsx @@ -7,7 +7,8 @@ import { statuses } from "./constants"; import { DataTableColumnHeader } from "../common/data-table/data-table-column-header"; import { Transaction } from "@/src/types/interfaces/TransactionInterfaces"; import TruncatedAddress from "../common/truncated-address"; -import { formatNumber, formatTimeAgo } from "@/src/lib/utils"; +import { formatTimeAgo } from "@/src/lib/utils"; +import Link from "next/link"; export const columns: ColumnDef[] = [ { @@ -19,12 +20,14 @@ export const columns: ColumnDef[] = [ return (
    - #{formatNumber(row.getValue("BatchHeight"))} + #{row.getValue("BatchHeight")}
    ); }, - enableSorting: false, + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)); + }, enableHiding: false, }, @@ -42,7 +45,9 @@ export const columns: ColumnDef[] = [
    ); }, - enableSorting: false, + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)); + }, enableHiding: false, }, @@ -52,7 +57,12 @@ export const columns: ColumnDef[] = [ ), cell: ({ row }) => { - return ; + return ( + + ); }, enableSorting: false, enableHiding: false, diff --git a/tools/tenscan/frontend/src/components/modules/transactions/transaction-details.tsx b/tools/tenscan/frontend/src/components/modules/transactions/transaction-details.tsx new file mode 100644 index 0000000000..9b1ba535b8 --- /dev/null +++ b/tools/tenscan/frontend/src/components/modules/transactions/transaction-details.tsx @@ -0,0 +1,59 @@ +import TruncatedAddress from "../common/truncated-address"; +import KeyValueItem, { KeyValueList } from "@/src/components/ui/key-value"; +import { formatTimeAgo } from "@/src/lib/utils"; +import { Badge } from "@/src/components/ui/badge"; +import { Transaction } from "@/src/types/interfaces/TransactionInterfaces"; +import { BadgeType } from "@/src/types/interfaces"; +import Link from "next/link"; + +export function TransactionDetailsComponent({ + transactionDetails, +}: { + transactionDetails: Transaction; +}) { + return ( +
    + + + {"#" + Number(transactionDetails?.BatchHeight)} + + } + /> + + } + /> + + + {transactionDetails?.Finality} + + } + isLastItem + /> + +
    + ); +} diff --git a/tools/tenscan/frontend/src/components/ui/key-value.tsx b/tools/tenscan/frontend/src/components/ui/key-value.tsx index d75ca31aea..540e3a7ce3 100644 --- a/tools/tenscan/frontend/src/components/ui/key-value.tsx +++ b/tools/tenscan/frontend/src/components/ui/key-value.tsx @@ -10,7 +10,7 @@ export const KeyValueItem = ({ value, isLastItem, }: { - label: string; + label?: string; value: string | number | React.ReactNode; isLastItem?: boolean; }) => ( @@ -19,7 +19,7 @@ export const KeyValueItem = ({ ${isLastItem ? "" : "mb-2"}`} >
    - {label} + {label && {label}} {value}
    {!isLastItem && } diff --git a/tools/tenscan/frontend/src/lib/constants.ts b/tools/tenscan/frontend/src/lib/constants.ts index ebff60209e..8c0808fdbd 100644 --- a/tools/tenscan/frontend/src/lib/constants.ts +++ b/tools/tenscan/frontend/src/lib/constants.ts @@ -1,6 +1,6 @@ export const socialLinks = { github: "https://github.com/ten-protocol", - discord: "https://discord.gg/QJZ39Den7d", + discord: "https://discord.gg/tenprotocol", twitter: "https://twitter.com/tenprotocol", twitterHandle: "@tenprotocol", }; @@ -11,25 +11,22 @@ export const pricePollingInterval = 60 * 1000; export const RESET_COPIED_TIMEOUT = 2000; -export const getOptions = (query: { - page?: string | string[]; - size?: string | string[]; -}) => { - const offset = - query.page && query.size - ? (parseInt(query.page as string, 10) - 1) * - parseInt(query.size as string, 10) - : 0; - const options = { - offset: Number.isNaN(offset) ? 0 : offset, - size: Number.isNaN(parseInt(query.size as string, 10)) - ? 10 - : parseInt(query.size as string, 10), - // sort: query.sort ? (query.sort as string) : "blockNumber", - // order: query.order ? (query.order as string) : "desc", - // filter: query.filter ? (query.filter as string) : "", +const calculateOffset = (page: number, size: number) => { + if (page <= 0) return 0; + return (page - 1) * size; +}; + +export const getOptions = (query: { page?: number; size?: number }) => { + const defaultSize = 20; + const size = query.size + ? +(query.size > 100 ? 100 : query.size) + : defaultSize; + const page = query.page || 1; + const offset = calculateOffset(page, size); + return { + offset: Number.isNaN(offset) || offset < 0 ? 0 : offset, + size, }; - return options; }; export const version = process.env.NEXT_PUBLIC_FE_VERSION; diff --git a/tools/tenscan/frontend/src/lib/utils.ts b/tools/tenscan/frontend/src/lib/utils.ts index 83fbff0f8f..a07a957028 100644 --- a/tools/tenscan/frontend/src/lib/utils.ts +++ b/tools/tenscan/frontend/src/lib/utils.ts @@ -1,6 +1,7 @@ import { type ClassValue, clsx } from "clsx"; import { formatDistanceToNow } from "date-fns"; import { twMerge } from "tailwind-merge"; +import { ItemPosition } from "../types/interfaces"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); @@ -25,3 +26,30 @@ export const formatNumber = (number: string | number) => { const num = Number(number); return num.toLocaleString(); }; + +export const getItem = ( + arr: T[], + key: string, + position: ItemPosition = ItemPosition.FIRST +) => { + if (!arr || !arr.length) { + return null; + } + + const keys = key.split("."); + const item = position === ItemPosition.FIRST ? arr[0] : arr[arr.length - 1]; + let value: any = item; + + for (const k of keys) { + if (value[k] === undefined) { + return null; + } + value = value[k]; + } + + return value; +}; + +export function jsonHexToObj(hex: string) { + return JSON.parse(Buffer.from(hex.slice(2), "hex").toString()); +} diff --git a/tools/tenscan/frontend/src/routes/index.ts b/tools/tenscan/frontend/src/routes/index.ts index b0516e895a..a85623ce77 100644 --- a/tools/tenscan/frontend/src/routes/index.ts +++ b/tools/tenscan/frontend/src/routes/index.ts @@ -3,8 +3,11 @@ import { NavLink } from "../types/interfaces"; export const apiRoutes = { // **** BATCHES **** getLatestBatch: "/items/batch/latest/", - getBatches: "/items/batches/", + getBatches: "/items/v2/batches/", getBatchByHash: "/items/batch/:hash", + getBatchByHeight: "/items/batch/height/:height", + getBatchTransactions: "/items/batch/:fullHash/transactions", + getBatchesInRollup: "/items/rollup/:hash/batches", // **** BLOCKS **** getBlocks: "/items/blocks/", @@ -16,6 +19,7 @@ export const apiRoutes = { // **** TRANSACTIONS **** getTransactions: "/items/transactions/", getTransactionCount: "/count/transactions/", + getTransactionByHash: "/items/transaction/:hash", getEtherPrice: "https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd", @@ -23,6 +27,9 @@ export const apiRoutes = { // **** ROLLUPS **** getLatestRollup: "/items/rollup/latest/", decryptEncryptedRollup: "/actions/decryptTxBlob/", + getRollups: "/items/rollups/", + getRollupByHash: "/items/rollup/:hash", + getRollupByBatchSequence: "/items/rollup/batch/:seq", // **** INFO **** getHealthStatus: "/info/health/", @@ -30,6 +37,12 @@ export const apiRoutes = { export const ethMethods = { getStorageAt: "eth_getStorageAt", + getTransactionReceipt: "eth_getTransactionReceipt", +}; +// to send TEN Custom Queries (CQ) through the provider we call eth_getStorageAt and use these addresses to identify the TEN CQ method +export const tenCustomQueryMethods = { + getUserID: "0x0000000000000000000000000000000000000001", + listPersonalTransactions: "0x0000000000000000000000000000000000000002", }; export const NavLinks: NavLink[] = [ @@ -56,13 +69,13 @@ export const NavLinks: NavLink[] = [ isExternal: false, }, { - href: "/blocks", - label: "Blocks", + href: "/batches", + label: "Batches", isExternal: false, }, { - href: "/batches", - label: "Batches", + href: "/rollups", + label: "Rollups", isExternal: false, }, ], diff --git a/tools/tenscan/frontend/src/services/useRollupsService.ts b/tools/tenscan/frontend/src/services/useRollupsService.ts index e75fc02771..2853b44491 100644 --- a/tools/tenscan/frontend/src/services/useRollupsService.ts +++ b/tools/tenscan/frontend/src/services/useRollupsService.ts @@ -1,26 +1,67 @@ -import { decryptEncryptedRollup, fetchRollups } from "@/api/rollups"; +import { + decryptEncryptedRollup, + fetchBatchesInRollups, + fetchLatestRollups, + fetchRollups, +} from "@/api/rollups"; import { toast } from "@/src/components/ui/use-toast"; import { useMutation, useQuery } from "@tanstack/react-query"; import { useState } from "react"; +import { getOptions, pollingInterval } from "../lib/constants"; +import { useRouter } from "next/router"; export const useRollupsService = () => { + const { query } = useRouter(); + + const [noPolling, setNoPolling] = useState(false); const [decryptedRollup, setDecryptedRollup] = useState(); - const { data: rollups, isLoading: isRollupsLoading } = useQuery({ - queryKey: ["rollups"], - queryFn: () => fetchRollups(), + const options = getOptions(query); + + const { data: latestRollups } = useQuery({ + queryKey: ["latestRollups"], + queryFn: () => fetchLatestRollups(), + }); + + const { + data: rollups, + isLoading: isRollupsLoading, + refetch: refetchRollups, + } = useQuery({ + queryKey: ["rollups", options], + queryFn: () => fetchRollups(options), + refetchInterval: noPolling ? false : pollingInterval, }); const { mutate: decryptEncryptedData } = useMutation({ mutationFn: decryptEncryptedRollup, onSuccess: (data: any) => { setDecryptedRollup(data); - toast({ description: "Decryption successful!" }); }, onError: (error: any) => { toast({ description: error.message }); }, }); - return { rollups, isRollupsLoading, decryptEncryptedData, decryptedRollup }; + const { + data: rollupBatches, + isLoading: isRollupBatchesLoading, + refetch: refetchRollupBatches, + } = useQuery({ + queryKey: ["rollupBatches", { hash: query.hash, options }], + queryFn: () => fetchBatchesInRollups(query.hash as string, options), + }); + + return { + rollups, + latestRollups, + refetchRollups, + isRollupsLoading, + decryptEncryptedData, + decryptedRollup, + setNoPolling, + rollupBatches, + isRollupBatchesLoading, + refetchRollupBatches, + }; }; diff --git a/tools/tenscan/frontend/src/services/useTransactionsService.ts b/tools/tenscan/frontend/src/services/useTransactionsService.ts index f51f7d7f79..5284c073f3 100644 --- a/tools/tenscan/frontend/src/services/useTransactionsService.ts +++ b/tools/tenscan/frontend/src/services/useTransactionsService.ts @@ -2,34 +2,22 @@ import { fetchEtherPrice, fetchTransactions, fetchTransactionCount, + personalTransactionsData, } from "@/api/transactions"; import { useWalletConnection } from "@/src/components/providers/wallet-provider"; import { useQuery } from "@tanstack/react-query"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { getOptions, pollingInterval, pricePollingInterval, } from "../lib/constants"; -import { PersonalTransactionsResponse } from "../types/interfaces/TransactionInterfaces"; import { useRouter } from "next/router"; -import { showToast } from "../components/ui/use-toast"; -import { ToastType } from "../types/interfaces"; -import { ethMethods } from "../routes"; export const useTransactionsService = () => { const { query } = useRouter(); const { walletAddress, provider } = useWalletConnection(); - const [personalTxnsLoading, setPersonalTxnsLoading] = useState(false); - const [personalTxns, setPersonalTxns] = - useState(); - - useEffect(() => { - personalTransactions(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [walletAddress]); - const [noPolling, setNoPolling] = useState(false); const options = getOptions(query); @@ -51,32 +39,11 @@ export const useTransactionsService = () => { refetchInterval: noPolling ? false : pollingInterval, }); - const personalTransactions = async () => { - try { - setPersonalTxnsLoading(true); - if (provider) { - const requestPayload = { - address: walletAddress, - pagination: { - ...options, - }, - }; - const personalTxData = await provider.send(ethMethods.getStorageAt, [ - "listPersonalTransactions", - requestPayload, - null, - ]); - setPersonalTxns(personalTxData); - } - } catch (error) { - console.error("Error fetching personal transactions:", error); - setPersonalTxns(undefined); - showToast(ToastType.DESTRUCTIVE, "Error fetching personal transactions"); - throw error; - } finally { - setPersonalTxnsLoading(false); - } - }; + const { data: personalTxns, isLoading: personalTxnsLoading } = useQuery({ + queryKey: ["personalTxns", options], + queryFn: () => personalTransactionsData(provider, walletAddress, options), + enabled: !!walletAddress && !!provider, + }); const { data: price, isLoading: isPriceLoading } = useQuery({ queryKey: ["price"], @@ -94,5 +61,6 @@ export const useTransactionsService = () => { personalTxns, personalTxnsLoading, price, + isPriceLoading, }; }; diff --git a/tools/tenscan/frontend/src/types/interfaces/BatchInterfaces.ts b/tools/tenscan/frontend/src/types/interfaces/BatchInterfaces.ts index a8fa9c7717..45773a008d 100644 --- a/tools/tenscan/frontend/src/types/interfaces/BatchInterfaces.ts +++ b/tools/tenscan/frontend/src/types/interfaces/BatchInterfaces.ts @@ -1,31 +1,34 @@ +import { CrossChainMessage } from "./RollupInterfaces"; + export type Batch = { - parentHash: string; - stateRoot: string; - transactionsRoot: string; - receiptsRoot: string; - number: number; - sequencerOrderNo: number; - gasLimit: number; - gasUsed: number; - timestamp: string; - extraData: string; - baseFee: number; - coinbase: string; - l1Proof: string; - R: number; - S: number; - crossChainMessages: any[]; - inboundCrossChainHash: string; - inboundCrossChainHeight: number; - transfersTree: string; + sequence: number; hash: string; - sha3Uncles: string; - miner: string; - logsBloom: string; - difficulty: string; - nonce: string; - baseFeePerGas: number; - EncryptedTxBlob: string; + fullHash: string; + height: number; + txCount: number; + header: { + hash: string; + parentHash: string; + stateRoot: string; + transactionsRoot: string; + receiptsRoot: string; + number: string; + sequencerOrderNo: string; + gasLimit: string; + gasUsed: string; + timestamp: string; + extraData: string; + baseFeePerGas: string; + miner: string; + l1Proof: string; + signature: string; + crossChainMessages: []; + inboundCrossChainHash: string; + inboundCrossChainHeight: string; + TransfersTree: string; + crossChainTree: string; + }; + encryptedTxBlob: string; }; export type BatchDetails = { @@ -48,7 +51,7 @@ export type BatchDetails = { crossChainMessages: any[]; inboundCrossChainHash: string; inboundCrossChainHeight: number; - transfersTree: string; + TransfersTree: string; hash: string; sha3Uncles: string; miner: string; @@ -57,10 +60,33 @@ export type BatchDetails = { nonce: string; baseFeePerGas: number; }; - TxHashes: []; + TxHashes: string[]; EncryptedTxBlob: string; }; +export interface LatestBatch { + hash: string; + parentHash: string; + stateRoot: string; + transactionsRoot: string; + receiptsRoot: string; + number: string; + sequencerOrderNo: string; + gasLimit: string; + gasUsed: string; + timestamp: string; + extraData: string; + baseFeePerGas: string; + miner: string; + l1Proof: string; + signature: string; + crossChainMessages: CrossChainMessage[]; + inboundCrossChainHash: string; + inboundCrossChainHeight: string; + TransfersTree: string; + crossChainTree: string; +} + export type BatchResponse = { BatchesData: Batch[]; Total: string; diff --git a/tools/tenscan/frontend/src/types/interfaces/RollupInterfaces.ts b/tools/tenscan/frontend/src/types/interfaces/RollupInterfaces.ts new file mode 100644 index 0000000000..e99caabc5c --- /dev/null +++ b/tools/tenscan/frontend/src/types/interfaces/RollupInterfaces.ts @@ -0,0 +1,32 @@ +export interface RollupsResponse { + RollupsData: Rollup[]; + Total: number; +} + +export interface Rollup { + ID: number; + Hash: string; + FirstSeq: number; + LastSeq: number; + Timestamp: number; + Header: Header; + L1Hash: string; +} + +export interface Header { + CompressionL1Head: string; + crossChainMessages: CrossChainMessage[]; + PayloadHash: string; + Signature: string; + LastBatchSeqNo: number; + hash: string; +} + +export type CrossChainMessage = { + Sender: string; + Sequence: number; + Nonce: number; + Topic: number; + Payload: string[]; + ConsistencyLevel: number; +}; diff --git a/tools/tenscan/frontend/src/types/interfaces/TransactionInterfaces.ts b/tools/tenscan/frontend/src/types/interfaces/TransactionInterfaces.ts index 8d468f3daa..25b8ef80c4 100644 --- a/tools/tenscan/frontend/src/types/interfaces/TransactionInterfaces.ts +++ b/tools/tenscan/frontend/src/types/interfaces/TransactionInterfaces.ts @@ -27,7 +27,14 @@ export type PersonalTransactionsResponse = { Total: number; }; -export type TransactionType = 0x0 | 0x1 | 0x2 | 0x3; +export type TransactionType = "0x0" | "0x1" | "0x2" | "0x3"; + +export enum PersonalTransactionType { + Legacy = "0x0", + AccessList = "0x1", + DynamicFee = "0x2", + Blob = "0x3", +} export type PersonalTransactions = { id: number; @@ -45,3 +52,32 @@ export type PersonalTransactions = { transactionIndex: string; type: TransactionType; }; + +export type TransactionReceipt = { + blockHash: string; + blockNumber: string; + contractAddress: string; + cumulativeGasUsed: string; + effectiveGasPrice: string; + from: string; + gasUsed: string; + logs: Log[]; + logsBloom: string; + status: string; + to: string; + transactionHash: string; + transactionIndex: string; + type: TransactionType; +}; + +export type Log = { + address: string; + blockHash: string; + blockNumber: string; + data: string; + logIndex: string; + removed: boolean; + topics: string[]; + transactionHash: string; + transactionIndex: string; +}; diff --git a/tools/tenscan/frontend/src/types/interfaces/index.ts b/tools/tenscan/frontend/src/types/interfaces/index.ts index 923786fedc..48b87edb9e 100644 --- a/tools/tenscan/frontend/src/types/interfaces/index.ts +++ b/tools/tenscan/frontend/src/types/interfaces/index.ts @@ -79,3 +79,24 @@ export enum ToastType { DESTRUCTIVE = "destructive", DEFAULT = "default", } + +export enum BadgeType { + SUCCESS = "success", + SECONDARY = "secondary", + DESTRUCTIVE = "destructive", + DEFAULT = "default", + OUTLINE = "outline", +} + +export interface DashboardAnalyticsData { + title: string; + value: string | number | JSX.Element; + change?: string; + icon: any; + loading?: boolean; +} + +export enum ItemPosition { + FIRST = "first", + LAST = "last", +} diff --git a/tools/tenscan/frontend/styles/globals.css b/tools/tenscan/frontend/styles/globals.css index 442bbd2700..598e71e97e 100644 --- a/tools/tenscan/frontend/styles/globals.css +++ b/tools/tenscan/frontend/styles/globals.css @@ -76,7 +76,9 @@ body { @apply bg-background text-foreground; - font-feature-settings: "rlig" 1, "calt" 1; + font-feature-settings: + "rlig" 1, + "calt" 1; font-family: "Quicksand", sans-serif; } @@ -90,6 +92,17 @@ font-family: "DMSans", sans-serif; } + input[type="number"] { + -webkit-appearance: textfield; + -moz-appearance: textfield; + appearance: textfield; + } + input[type="number"]::-webkit-inner-spin-button, + input[type="number"]::-webkit-outer-spin-button { + appearance: none; + -webkit-appearance: none; + } + /* styles for docs */ .prose ul { list-style: disc; diff --git a/tools/walletextension/common/constants.go b/tools/walletextension/common/constants.go index bf14a59f5f..3fdb4d63a2 100644 --- a/tools/walletextension/common/constants.go +++ b/tools/walletextension/common/constants.go @@ -15,26 +15,25 @@ const ( ) const ( - PathStatic = "/static/" - PathReady = "/ready/" - PathJoin = "/join/" - PathGetMessage = "/getmessage/" - PathAuthenticate = "/authenticate/" - PathQuery = "/query/" - PathRevoke = "/revoke/" - PathHealth = "/health/" - PathNetworkHealth = "/network-health/" - WSProtocol = "ws://" - HTTPProtocol = "http://" - EncryptedTokenQueryParameter = "token" - AddressQueryParameter = "a" - MessageUserIDLen = 40 - MessageUserIDLenWithPrefix = 42 - EthereumAddressLen = 42 - GetStorageAtUserIDRequestMethodName = "0x0000000000000000000000000000000000000000" - SuccessMsg = "success" - APIVersion1 = "/v1" - PathVersion = "/version/" - DeduplicationBufferSize = 20 - DefaultGatewayAuthMessageType = "EIP712" + PathStatic = "/static/" + PathReady = "/ready/" + PathJoin = "/join/" + PathGetMessage = "/getmessage/" + PathAuthenticate = "/authenticate/" + PathQuery = "/query/" + PathRevoke = "/revoke/" + PathHealth = "/health/" + PathNetworkHealth = "/network-health/" + WSProtocol = "ws://" + HTTPProtocol = "http://" + EncryptedTokenQueryParameter = "token" + AddressQueryParameter = "a" + MessageUserIDLen = 40 + MessageUserIDLenWithPrefix = 42 + EthereumAddressLen = 42 + SuccessMsg = "success" + APIVersion1 = "/v1" + PathVersion = "/version/" + DeduplicationBufferSize = 20 + DefaultGatewayAuthMessageType = "EIP712" ) diff --git a/tools/walletextension/frontend/public/docs/terms.json b/tools/walletextension/frontend/public/docs/terms.json index ff0769351e..6becc3b6ed 100644 --- a/tools/walletextension/frontend/public/docs/terms.json +++ b/tools/walletextension/frontend/public/docs/terms.json @@ -55,8 +55,8 @@ { "heading": "8. LIMITATIONS OF LIABILITY", "content": [ - "TO THE FULLEST EXTENT ALLOWED BY APPLICABLE LAW, UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, TORT, CONTRACT, STRICT LIABILITY, OR OTHERWISE) SHALL THE INDEMNIFIED PARTIES OR ANY OF THEM BE LIABLE TO YOU OR TO ANY OTHER PERSON FOR:
    • ANY INDIRECT, SPECIAL, INCIDENTAL, PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING DAMAGES FOR LOST PROFITS, BUSINESS, OR REVENUE, BUSINESS INTERRUPTION, LOSS OF DATA, LOSS OF BUSINESS OPPORTUNITY, GOODWILL OR REPUTATION, WORK STOPPAGE, ACCURACY OF RESULTS, OR COMPUTER FAILURE OR MALFUNCTION;
    • ANY SUBSTITUTE GOODS, SERVICES OR TECHNOLOGY;
    • ANY AMOUNT, IN THE AGGREGATE, IN EXCESS OF ONE-HUNDRED POUNDS (£100); OR
    • ANY MATTER BEYOND THE REASONABLE CONTROL OF THE INDEMNIFIED PARTIES OR ANY OF THEM.
    ", - "SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL OR CERTAIN OTHER DAMAGES, SO THE FOREGOING LIMITATIONS AND EXCLUSIONS MAY NOT APPLY TO YOU.", + "To the fullest extent allowed by applicable law, under no circumstances and under no legal theory (including, without limitation, tort, contract, strict liability, or otherwise) shall the indemnified parties or any of them be liable to you or to any other person for:
    • any indirect, special, incidental, punitive or consequential damages of any kind, including damages for lost profits, business, or revenue, business interruption, loss of data, loss of business opportunity, goodwill or reputation, work stoppage, accuracy of results, or computer failure or malfunction;
    • any substitute goods, services or technology;
    • any amount, in the aggregate, in excess of one-hundred pounds (£100); or
    • any matter beyond the reasonable control of the indemnified parties or any of them.
    ", + "Some jurisdictions do not allow the exclusion or limitation of incidental or consequential or certain other damages, so the foregoing limitations and exclusions may not apply to you.", "Nothing in these Terms is intended to exclude or limit our liability for death or personal injury caused by our negligence, or for fraud or fraudulent misrepresentation, or to affect your statutory rights." ] }, diff --git a/tools/walletextension/frontend/src/lib/constants.ts b/tools/walletextension/frontend/src/lib/constants.ts index bd8814dba9..20e064c61a 100644 --- a/tools/walletextension/frontend/src/lib/constants.ts +++ b/tools/walletextension/frontend/src/lib/constants.ts @@ -5,7 +5,7 @@ export const tenscanLink = "https://testnet.tenscan.io"; export const socialLinks = { github: "https://github.com/ten-protocol", - discord: "https://discord.gg/fVXstswaJY", + discord: "https://discord.gg/tenprotocol", twitter: "https://twitter.com/tenprotocol", twitterHandle: "@tenprotocol", }; @@ -43,7 +43,7 @@ export const tenChainIDDecimal = 443; export const tenChainIDHex = "0x" + tenChainIDDecimal.toString(16); // Convert to hexadecimal and prefix with '0x' export const METAMASK_CONNECTION_TIMEOUT = 3000; -export const userStorageAddress = "0x0000000000000000000000000000000000000000"; +export const userStorageAddress = "0x0000000000000000000000000000000000000001"; export const nativeCurrency = { name: "Sepolia Ether", diff --git a/tools/walletextension/rpcapi/blockchain_api.go b/tools/walletextension/rpcapi/blockchain_api.go index 7993231108..aeb6a01ed9 100644 --- a/tools/walletextension/rpcapi/blockchain_api.go +++ b/tools/walletextension/rpcapi/blockchain_api.go @@ -2,23 +2,30 @@ package rpcapi import ( "context" + "encoding/base64" "encoding/json" + "fmt" - "github.com/ethereum/go-ethereum/core/types" - - "github.com/ethereum/go-ethereum/common" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/common/gethapi" + "github.com/ten-protocol/go-ten/go/common/privacy" "github.com/ten-protocol/go-ten/lib/gethfork/rpc" - wecommon "github.com/ten-protocol/go-ten/tools/walletextension/common" ) type BlockChainAPI struct { - we *Services + we *Services + storageWhitelist *privacy.Whitelist } func NewBlockChainAPI(we *Services) *BlockChainAPI { - return &BlockChainAPI{we} + whitelist := privacy.NewWhitelist() + return &BlockChainAPI{ + we: we, + storageWhitelist: whitelist, + } } func (api *BlockChainAPI) ChainId() *hexutil.Big { //nolint:stylecheck @@ -34,7 +41,7 @@ func (api *BlockChainAPI) BlockNumber() hexutil.Uint64 { return *nr } -func (api *BlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { +func (api *BlockChainAPI) GetBalance(ctx context.Context, address gethcommon.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { return ExecAuthRPC[hexutil.Big]( ctx, api.we, @@ -55,13 +62,13 @@ func (api *BlockChainAPI) GetBalance(ctx context.Context, address common.Address // Result structs for GetProof type AccountResult struct { - Address common.Address `json:"address"` - AccountProof []string `json:"accountProof"` - Balance *hexutil.Big `json:"balance"` - CodeHash common.Hash `json:"codeHash"` - Nonce hexutil.Uint64 `json:"nonce"` - StorageHash common.Hash `json:"storageHash"` - StorageProof []StorageResult `json:"storageProof"` + Address gethcommon.Address `json:"address"` + AccountProof []string `json:"accountProof"` + Balance *hexutil.Big `json:"balance"` + CodeHash gethcommon.Hash `json:"codeHash"` + Nonce hexutil.Uint64 `json:"nonce"` + StorageHash gethcommon.Hash `json:"storageHash"` + StorageProof []StorageResult `json:"storageProof"` } type StorageResult struct { @@ -70,7 +77,7 @@ type StorageResult struct { Proof []string `json:"proof"` } -func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) { +func (s *BlockChainAPI) GetProof(ctx context.Context, address gethcommon.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) { return nil, rpcNotImplemented } @@ -84,7 +91,7 @@ func (api *BlockChainAPI) GetHeaderByNumber(ctx context.Context, number rpc.Bloc return *resp, err } -func (api *BlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.Hash) map[string]interface{} { +func (api *BlockChainAPI) GetHeaderByHash(ctx context.Context, hash gethcommon.Hash) map[string]interface{} { resp, _ := UnauthenticatedTenRPCCall[map[string]interface{}](ctx, api.we, &CacheCfg{CacheType: LongLiving}, "eth_getHeaderByHash", hash) if resp == nil { return nil @@ -107,7 +114,7 @@ func (api *BlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.Block return *resp, err } -func (api *BlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Hash, fullTx bool) (map[string]interface{}, error) { +func (api *BlockChainAPI) GetBlockByHash(ctx context.Context, hash gethcommon.Hash, fullTx bool) (map[string]interface{}, error) { resp, err := UnauthenticatedTenRPCCall[map[string]interface{}](ctx, api.we, &CacheCfg{CacheType: LongLiving}, "eth_getBlockByHash", hash, fullTx) if resp == nil { return nil, err @@ -115,7 +122,7 @@ func (api *BlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Hash, return *resp, err } -func (api *BlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { +func (api *BlockChainAPI) GetCode(ctx context.Context, address gethcommon.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { // todo - must be authenticated resp, err := UnauthenticatedTenRPCCall[hexutil.Bytes]( ctx, @@ -135,9 +142,19 @@ func (api *BlockChainAPI) GetCode(ctx context.Context, address common.Address, b return *resp, err } -func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, hexKey string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { - // GetStorageAt is repurposed to return the userID - if address.Hex() == wecommon.GetStorageAtUserIDRequestMethodName { +// GetStorageAt is not compatible with ETH RPC tooling. Ten network does not getStorageAt because it would +// violate the privacy guarantees of the network. +// +// However, we can repurpose this method to be able to route Ten-specific requests through from an ETH RPC client. +// We call these requests Custom Queries. +// +// This method signature matches eth_getStorageAt, but we use the address field to specify the custom query method, +// the hex-encoded position field to specify the parameters json, and nil for the block number. +// +// In future, we can support both CustomQueries and some debug version of eth_getStorageAt if needed. +func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address gethcommon.Address, params string, _ rpc.BlockNumberOrHash) (hexutil.Bytes, error) { + switch address.Hex() { + case common.UserIDRequestCQMethod: userID, err := extractUserID(ctx, api.we) if err != nil { return nil, err @@ -148,13 +165,38 @@ func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Addre return nil, err } return userID, nil - } + case common.ListPrivateTransactionsCQMethod: + // sensitive CustomQuery methods use the convention of having "address" at the top level of the params json + userAddr, err := extractCustomQueryAddress(params) + if err != nil { + return nil, fmt.Errorf("unable to extract address from custom query params: %w", err) + } + resp, err := ExecAuthRPC[any](ctx, api.we, &ExecCfg{account: userAddr}, "scan_getPersonalTransactions", params) + if err != nil { + return nil, fmt.Errorf("unable to execute custom query: %w", err) + } + // turn resp object into hexutil.Bytes + serialised, err := json.Marshal(resp) + if err != nil { + return nil, fmt.Errorf("unable to marshal response object: %w", err) + } + return serialised, nil + default: // address was not a recognised custom query method address + resp, err := ExecAuthRPC[any](ctx, api.we, &ExecCfg{tryUntilAuthorised: true}, "eth_getStorageAt", address, params, nil) + if err != nil { + return nil, fmt.Errorf("unable to execute eth_getStorageAt: %w", err) + } + if resp == nil { + return nil, nil + } - resp, err := ExecAuthRPC[hexutil.Bytes](ctx, api.we, &ExecCfg{account: &address}, "eth_getStorageAt", address, hexKey, blockNrOrHash) - if resp == nil { - return nil, err + respHex, ok := (*resp).(string) + if !ok { + return nil, fmt.Errorf("unable to decode response") + } + // turn resp object into hexutil.Bytes + return hexutil.MustDecode(respHex), nil } - return *resp, err } func (s *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { @@ -162,21 +204,21 @@ func (s *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc. } type OverrideAccount struct { - Nonce *hexutil.Uint64 `json:"nonce"` - Code *hexutil.Bytes `json:"code"` - Balance **hexutil.Big `json:"balance"` - State *map[common.Hash]common.Hash `json:"state"` - StateDiff *map[common.Hash]common.Hash `json:"stateDiff"` + Nonce *hexutil.Uint64 `json:"nonce"` + Code *hexutil.Bytes `json:"code"` + Balance **hexutil.Big `json:"balance"` + State *map[gethcommon.Hash]gethcommon.Hash `json:"state"` + StateDiff *map[gethcommon.Hash]gethcommon.Hash `json:"stateDiff"` } type ( - StateOverride map[common.Address]OverrideAccount + StateOverride map[gethcommon.Address]OverrideAccount BlockOverrides struct { Number *hexutil.Big Difficulty *hexutil.Big Time *hexutil.Uint64 GasLimit *hexutil.Uint64 - Coinbase *common.Address - Random *common.Hash + Coinbase *gethcommon.Address + Random *gethcommon.Hash BaseFee *hexutil.Big } ) @@ -188,7 +230,7 @@ func (api *BlockChainAPI) Call(ctx context.Context, args gethapi.TransactionArgs return cacheBlockNumberOrHash(blockNrOrHash) }, }, - computeFromCallback: func(user *GWUser) *common.Address { + computeFromCallback: func(user *GWUser) *gethcommon.Address { return searchFromAndData(user.GetAllAddresses(), args) }, adjustArgs: func(acct *GWAccount) []any { @@ -213,7 +255,7 @@ func (api *BlockChainAPI) EstimateGas(ctx context.Context, args gethapi.Transact return LatestBatch }, }, - computeFromCallback: func(user *GWUser) *common.Address { + computeFromCallback: func(user *GWUser) *gethcommon.Address { return searchFromAndData(user.GetAllAddresses(), args) }, adjustArgs: func(acct *GWAccount) []any { @@ -233,7 +275,7 @@ func populateFrom(acct *GWAccount, args gethapi.TransactionArgs) gethapi.Transac // clone the args argsClone := cloneArgs(args) // set the from - if args.From == nil || args.From.Hex() == (common.Address{}).Hex() { + if args.From == nil || args.From.Hex() == (gethcommon.Address{}).Hex() { argsClone.From = acct.address } return argsClone @@ -258,3 +300,41 @@ type accessListResult struct { func (s *BlockChainAPI) CreateAccessList(ctx context.Context, args gethapi.TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*accessListResult, error) { return nil, rpcNotImplemented } + +func extractCustomQueryAddress(params any) (*gethcommon.Address, error) { + // sensitive CustomQuery methods use the convention of having "address" at the top level of the params json + // we don't care about the params struct overall, just want to extract the address string field + paramsStr, ok := params.(string) + if !ok { + return nil, fmt.Errorf("params must be a json string") + } + var paramsJSON map[string]json.RawMessage + err := json.Unmarshal([]byte(paramsStr), ¶msJSON) + if err != nil { + // try to base64 decode the params string and then unmarshal before giving up + bytesStr, err64 := base64.StdEncoding.DecodeString(paramsStr) + if err64 != nil { + // was not base64 encoded, give up + return nil, fmt.Errorf("unable to unmarshal params string: %w", err) + } + // was base64 encoded, try to unmarshal + err = json.Unmarshal(bytesStr, ¶msJSON) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal params string: %w", err) + } + } + // Extract the RawMessage for the key "address" + addressRaw, ok := paramsJSON["address"] + if !ok { + return nil, fmt.Errorf("params must contain an 'address' field") + } + + // Unmarshal the RawMessage to a string + var addressStr string + err = json.Unmarshal(addressRaw, &addressStr) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal address field to string: %w", err) + } + address := gethcommon.HexToAddress(addressStr) + return &address, nil +} diff --git a/tools/walletextension/rpcapi/filter_api.go b/tools/walletextension/rpcapi/filter_api.go index b7f049833c..b144160ae8 100644 --- a/tools/walletextension/rpcapi/filter_api.go +++ b/tools/walletextension/rpcapi/filter_api.go @@ -3,9 +3,12 @@ package rpcapi import ( "context" "fmt" + "sort" "sync/atomic" "time" + "github.com/status-im/keycard-go/hexutils" + "github.com/ethereum/go-ethereum/log" subscriptioncommon "github.com/ten-protocol/go-ten/go/common/subscription" @@ -186,32 +189,80 @@ func (api *FilterAPI) NewFilter(crit common.FilterCriteria) (rpc.ID, error) { } func (api *FilterAPI) GetLogs(ctx context.Context, crit common.FilterCriteria) ([]*types.Log, error) { - logs, err := ExecAuthRPC[[]*types.Log]( - ctx, - api.we, - &ExecCfg{ - cacheCfg: &CacheCfg{ - CacheTypeDynamic: func() CacheStrategy { - // when the toBlock is not specified, the request is open-ended - if crit.ToBlock != nil && crit.ToBlock.Int64() > 0 { - return LongLiving - } - return LatestBatch - }, - }, - tryUntilAuthorised: true, - adjustArgs: func(acct *GWAccount) []any { - // convert to something serializable - return []any{common.FromCriteria(crit)} + method := "eth_getLogs" + audit(api.we, "RPC start method=%s args=%v", method, ctx) + requestStartTime := time.Now() + userID, err := extractUserID(ctx, api.we) + if err != nil { + return nil, err + } + + res, err := withCache( + api.we.Cache, + &CacheCfg{ + CacheTypeDynamic: func() CacheStrategy { + if crit.ToBlock != nil && crit.ToBlock.Int64() > 0 { + return LongLiving + } + if crit.BlockHash != nil { + return LongLiving + } + // when the toBlock or the block Hash are not specified, the request is open-ended + return LatestBatch }, }, - "eth_getLogs", - crit, - ) - if logs != nil { - return *logs, err + generateCacheKey([]any{userID, method, common.SerializableFilterCriteria(crit)}), + func() (*[]*types.Log, error) { // called when there is no entry in the cache + user, err := getUser(userID, api.we) + if err != nil { + return nil, err + } + + allEventLogsMap := make(map[LogKey]*types.Log) + // for each account registered for the current user + // execute the get_Logs function + // dedupe and concatenate the results + for _, acct := range user.accounts { + eventLogs, err := withEncRPCConnection(ctx, api.we, acct, func(rpcClient *tenrpc.EncRPCClient) (*[]*types.Log, error) { + var result []*types.Log + + // wrap the context with a timeout to prevent long executions + timeoutContext, cancelCtx := context.WithTimeout(ctx, maximumRPCCallDuration) + defer cancelCtx() + + err := rpcClient.CallContext(timeoutContext, &result, method, common.SerializableFilterCriteria(crit)) + return &result, err + }) + if err != nil { + return nil, fmt.Errorf("could not read logs. cause: %w", err) + } + // dedupe event logs + for _, eventLog := range *eventLogs { + allEventLogsMap[LogKey{ + BlockHash: eventLog.BlockHash, + TxHash: eventLog.TxHash, + Index: eventLog.Index, + }] = eventLog + } + } + + result := make([]*types.Log, 0) + for _, eventLog := range allEventLogsMap { + result = append(result, eventLog) + } + sort.Slice(result, func(i, j int) bool { + if result[i].BlockNumber == result[j].BlockNumber { + return result[i].Index < result[j].Index + } + return result[i].BlockNumber < result[j].BlockNumber + }) + return &result, nil + }) + if err != nil { + return nil, err } - return nil, err + audit(api.we, "RPC call. uid=%s, method=%s args=%v result=%v error=%v time=%d", hexutils.BytesToHex(userID), method, crit, res, err, time.Since(requestStartTime).Milliseconds()) + return *res, err } func (api *FilterAPI) UninstallFilter(id rpc.ID) bool { diff --git a/tools/walletextension/rpcapi/utils.go b/tools/walletextension/rpcapi/utils.go index 9dbca7148a..2bab927659 100644 --- a/tools/walletextension/rpcapi/utils.go +++ b/tools/walletextension/rpcapi/utils.go @@ -101,15 +101,15 @@ func ExecAuthRPC[R any](ctx context.Context, w *Services, cfg *ExecCfg, method s return nil, err } - user, err := getUser(userID, w) - if err != nil { - return nil, err - } - cacheArgs := []any{userID, method} cacheArgs = append(cacheArgs, args...) res, err := withCache(w.Cache, cfg.cacheCfg, generateCacheKey(cacheArgs), func() (*R, error) { + user, err := getUser(userID, w) + if err != nil { + return nil, err + } + // determine candidate "from" candidateAccts, err := getCandidateAccounts(user, w, cfg) if err != nil {