Transactions
Token Transfers
Tokens
Internal Transactions
Coin Balance History
Logs
Code
Read Contract
Write Contract
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- StateConnector
- Optimization enabled
- true
- Compiler version
- v0.7.6+commit.7338295f
- Optimization runs
- 200
- Verified at
- 2022-07-13T20:49:30.263090Z
./contracts/genesis/implementation/StateConnector.sol
// (c) 2021, Flare Networks Limited. All rights reserved. // Please see the file LICENSE for licensing terms. // SPDX-License-Identifier: MIT pragma solidity 0.7.6; contract StateConnector { //==================================================================== // Data Structures //==================================================================== // unused old storage slots (genesis contract upgrade) uint256[16] private gap; // Signalling block.coinbase value address public constant SIGNAL_COINBASE = address(0x00000000000000000000000000000000000DEaD1); // November 5th, 2021 uint256 public constant BUFFER_TIMESTAMP_OFFSET = 1636070400 seconds; // Amount of time a buffer is active before cycling to the next one uint256 public constant BUFFER_WINDOW = 90 seconds; // {Requests, Votes, Reveals} uint256 public constant TOTAL_STORED_BUFFERS = 3; // Store a proof for one week uint256 public constant TOTAL_STORED_PROOFS = (1 weeks) / BUFFER_WINDOW; // Cold wallet address => Hot wallet address mapping(address => address) public attestorAddressMapping; //======================= // VOTING DATA STRUCTURES //======================= // Voting round consists of 4 sequential buffer windows: collect, commit, reveal, finalize // Round ID is the buffer number of the window of the collect phase struct Vote { // Struct for Vote in buffer number 'N' // Hash of the Merkle root (+ random number and msg.sender) that contains valid requests from 'Round ID = N-1' bytes32 commitHash; // Merkle root for 'Round ID = N-2' used for commitHash in buffer number 'N-1' bytes32 merkleRoot; // Random number for 'Round ID = N-2' used for commitHash in buffer number 'N-1' bytes32 randomNumber; } struct Buffers { // {Requests, Votes, Reveals} Vote[TOTAL_STORED_BUFFERS] votes; // The latest buffer number that this account has voted on, used for determining relevant votes uint256 latestVote; } mapping(address => Buffers) public buffers; //============================= // MERKLE PROOF DATA STRUCTURES //============================= // The total number of buffers that have elapsed over time such that the latest buffer // has been proven using finaliseRound() uint256 public totalBuffers; // The proven merkle roots for each Round ID, // accessed using: Round ID % TOTAL_STORED_PROOFS // within one week of proving the merkle root. bytes32[TOTAL_STORED_PROOFS] public merkleRoots; //==================================================================== // Events //==================================================================== event AttestationRequest( address sender, uint256 timestamp, bytes data ); event RoundFinalised( uint256 indexed roundId, bytes32 merkleRoot ); //==================================================================== // Constructor //==================================================================== constructor() { /* empty block */ } //==================================================================== // Functions //==================================================================== function updateAttestorAddressMapping(address _updatedAddress) external { attestorAddressMapping[msg.sender] = _updatedAddress; } function requestAttestations(bytes calldata _data) external { emit AttestationRequest(msg.sender, block.timestamp, _data); } function submitAttestation( uint256 _bufferNumber, bytes32 _commitHash, bytes32 _merkleRoot, bytes32 _randomNumber ) external returns ( bool _isInitialBufferSlot ) { require(_bufferNumber == (block.timestamp - BUFFER_TIMESTAMP_OFFSET) / BUFFER_WINDOW, "wrong bufferNumber"); buffers[msg.sender].latestVote = _bufferNumber; buffers[msg.sender].votes[_bufferNumber % TOTAL_STORED_BUFFERS] = Vote( _commitHash, _merkleRoot, _randomNumber ); // Determine if this is the first attestation submitted in a new buffer round. // If so, the golang code will automatically finalise the previous round using finaliseRound() if (_bufferNumber > totalBuffers) { return true; } return false; } function getAttestation(uint256 _bufferNumber) external view returns (bytes32 _merkleRoot) { address attestor = attestorAddressMapping[msg.sender]; if (attestor == address(0)) { attestor = msg.sender; } require(_bufferNumber > 1); uint256 prevBufferNumber = _bufferNumber - 1; require(buffers[attestor].latestVote >= prevBufferNumber); bytes32 commitHash = buffers[attestor].votes[(prevBufferNumber - 1) % TOTAL_STORED_BUFFERS].commitHash; _merkleRoot = buffers[attestor].votes[prevBufferNumber % TOTAL_STORED_BUFFERS].merkleRoot; bytes32 randomNumber = buffers[attestor].votes[prevBufferNumber % TOTAL_STORED_BUFFERS].randomNumber; require(commitHash == keccak256(abi.encode(_merkleRoot, randomNumber, attestor))); } function finaliseRound(uint256 _bufferNumber, bytes32 _merkleRoot) external { require(_bufferNumber > 3); require(_bufferNumber == (block.timestamp - BUFFER_TIMESTAMP_OFFSET) / BUFFER_WINDOW); require(_bufferNumber > totalBuffers); // The following region can only be called from the golang code if (msg.sender == block.coinbase && block.coinbase == SIGNAL_COINBASE) { totalBuffers = _bufferNumber; merkleRoots[(_bufferNumber - 3) % TOTAL_STORED_PROOFS] = _merkleRoot; emit RoundFinalised(_bufferNumber - 3, _merkleRoot); } } function lastFinalizedRoundId() external view returns (uint256 _roundId) { require(totalBuffers >= 3, "totalBuffers < 3"); return totalBuffers - 3; } function merkleRoot(uint256 _roundId) external view returns (bytes32) { require(totalBuffers >= 3, "totalBuffers < 3"); require(_roundId <= totalBuffers - 3, "not finalized"); require(_roundId < TOTAL_STORED_PROOFS || _roundId > totalBuffers - 3 - TOTAL_STORED_PROOFS, "expired"); return merkleRoots[_roundId % TOTAL_STORED_PROOFS]; } }
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"event","name":"AttestationRequest","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false},{"type":"bytes","name":"data","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"RoundFinalised","inputs":[{"type":"uint256","name":"roundId","internalType":"uint256","indexed":true},{"type":"bytes32","name":"merkleRoot","internalType":"bytes32","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BUFFER_TIMESTAMP_OFFSET","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BUFFER_WINDOW","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"SIGNAL_COINBASE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"TOTAL_STORED_BUFFERS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"TOTAL_STORED_PROOFS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"attestorAddressMapping","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"latestVote","internalType":"uint256"}],"name":"buffers","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"finaliseRound","inputs":[{"type":"uint256","name":"_bufferNumber","internalType":"uint256"},{"type":"bytes32","name":"_merkleRoot","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"_merkleRoot","internalType":"bytes32"}],"name":"getAttestation","inputs":[{"type":"uint256","name":"_bufferNumber","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_roundId","internalType":"uint256"}],"name":"lastFinalizedRoundId","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"merkleRoot","inputs":[{"type":"uint256","name":"_roundId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"merkleRoots","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"requestAttestations","inputs":[{"type":"bytes","name":"_data","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"_isInitialBufferSlot","internalType":"bool"}],"name":"submitAttestation","inputs":[{"type":"uint256","name":"_bufferNumber","internalType":"uint256"},{"type":"bytes32","name":"_commitHash","internalType":"bytes32"},{"type":"bytes32","name":"_merkleRoot","internalType":"bytes32"},{"type":"bytes32","name":"_randomNumber","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalBuffers","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateAttestorAddressMapping","inputs":[{"type":"address","name":"_updatedAddress","internalType":"address"}]}]
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106101005760003560e01c8063c2f56d4c11610097578063ec7424a011610066578063ec7424a014610284578063f417c9d81461028c578063f5f59a4a14610294578063f64b6fda1461029c57610100565b8063c2f56d4c146101f0578063cfd1fdad14610216578063dd86215714610259578063eaebf6d31461026157610100565b80635f8c940d116100d35780635f8c940d1461018157806371c5ecb11461018957806371e24574146101a65780637ff6faa6146101cc57610100565b8063273c463b1461010557806329be4db21461012d5780633c70b3571461015c5780634b8a125f14610179575b600080fd5b61012b6004803603602081101561011b57600080fd5b50356001600160a01b031661030c565b005b61014a6004803603602081101561014357600080fd5b503561033b565b60408051918252519081900360200190f35b61014a6004803603602081101561017257600080fd5b503561049a565b61014a61059c565b61014a6105a4565b61014a6004803603602081101561019f57600080fd5b50356105a9565b61014a600480360360208110156101bc57600080fd5b50356001600160a01b03166105c1565b6101d46105d6565b604080516001600160a01b039092168252519081900360200190f35b6101d46004803603602081101561020657600080fd5b50356001600160a01b03166105dd565b6102456004803603608081101561022c57600080fd5b50803590602081013590604081013590606001356105f8565b604080519115158252519081900360200190f35b61014a6106d4565b61012b6004803603604081101561027757600080fd5b508035906020013561072c565b61014a6107d0565b61014a6107d6565b61014a6107dc565b61012b600480360360208110156102b257600080fd5b8101906020810181356401000000008111156102cd57600080fd5b8201836020820111156102df57600080fd5b8035906020019184600183028401116401000000008311171561030157600080fd5b5090925090506107e1565b33600090815260106020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b336000908152601060205260408120546001600160a01b03168061035c5750335b6001831161036957600080fd5b6001600160a01b03811660009081526011602052604090206009015460001984019081111561039757600080fd5b6001600160a01b03821660009081526011602052604081206003600019840106600381106103c157fe5b600390810291909101546001600160a01b0385166000908152601160205260409020909250908306600381106103f357fe5b60030201600101549350600060116000856001600160a01b03166001600160a01b031681526020019081526020016000206000016003848161043157fe5b066003811061043c57fe5b6003020160020154905084818560405160200180848152602001838152602001826001600160a01b03168152602001935050505060405160208183030381529060405280519060200120821461049157600080fd5b50505050919050565b6000600360125410156104e7576040805162461bcd60e51b815260206004820152601060248201526f746f74616c42756666657273203c203360801b604482015290519081900360640190fd5b600360125403821115610531576040805162461bcd60e51b815260206004820152600d60248201526c1b9bdd08199a5b985b1a5e9959609a1b604482015290519081900360640190fd5b611a408210806105475750601254611a42190182115b610582576040805162461bcd60e51b8152602060048201526007602482015266195e1c1a5c995960ca1b604482015290519081900360640190fd5b6013611a408306611a40811061059457fe5b015492915050565b636184740081565b600381565b601381611a4081106105ba57600080fd5b0154905081565b60116020526000908152604090206009015481565b620dead181565b6010602052600090815260409020546001600160a01b031681565b6000605a63618473ff19420104851461064d576040805162461bcd60e51b81526020600482015260126024820152713bb937b73390313ab33332b9273ab6b132b960711b604482015290519081900360640190fd5b336000818152601160208181526040808420600981018b905581516060810183528a81528084018a9052918201889052949093525290600387066003811061069157fe5b600302016000820151816000015560208201518160010155604082015181600201559050506012548511156106c8575060016106cc565b5060005b949350505050565b600060036012541015610721576040805162461bcd60e51b815260206004820152601060248201526f746f74616c42756666657273203c203360801b604482015290519081900360640190fd5b506012546002190190565b6003821161073957600080fd5b605a63618473ff19420104821461074f57600080fd5b601254821161075d57600080fd5b334114801561076e575041620dead1145b156107cc576012829055806013611a40600219850106611a40811061078f57fe5b01556040805182815290516002198401917f8ffd19aa79a62d0764e560d21b1245698310783be781d7d80b38233d4d7d288c919081900360200190a25b5050565b60125481565b611a4081565b605a81565b7f7cbc6812801238dea8eb58356bb62b95dbce8dc28498aa30e7d2c6873ed36cc73342848460405180856001600160a01b03168152602001848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f191690920182900397509095505050505050a1505056fea26469706673582212201835668a4aedf7b8dd6cb953219e7b126f41acff165aaa41023acd8f3dd8edf064736f6c63430007060033