├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── .prettierrc ├── .soliumignore ├── .soliumrc.json ├── README.md ├── blocks.js ├── contracts ├── BorValidatorSet.template ├── ECVerify.sol ├── IStateReceiver.sol ├── IterableMapping.sol ├── Migrations.sol ├── StateReceiver.sol ├── System.sol ├── ValidatorSet.sol ├── ValidatorVerifier.sol ├── test │ ├── TestBorValidatorSet.sol │ ├── TestCommitState.sol │ └── TestSystem.sol └── utils │ └── RLPReader.sol ├── foundry.toml ├── generate-borvalidatorset.js ├── generate-genesis.js ├── generate.sh ├── genesis-template.json ├── migrations ├── 1_initial_migration.js └── 2_genesis_contracts_deploy.js ├── package-lock.json ├── package.json ├── scripts └── run-test.sh ├── sprintSizes.js ├── test ├── BorValidatorSet.t.sol ├── BorValidatorSet.test.js ├── ECVerify.t.sol ├── IterableMapping.t.sol ├── Migrations.t.sol ├── StateReceiver.t.sol ├── StateReceiver.test.js ├── System.t.sol ├── ValidatorVerifier.t.sol └── helpers │ ├── ECVerifyWrapper.sol │ ├── IBorValidatorSet.sol │ ├── IECVerifyWrapper.sol │ ├── IIterableMappingWrapper.sol │ ├── IMigrations.sol │ ├── IStateReceiver.sol │ ├── ISystem.sol │ ├── IValidatorVerifier.sol │ ├── IterableMappingWrapper.sol │ ├── TestReenterer.sol │ ├── TestRevertingReceiver.sol │ ├── merkle.js │ └── rlpEncodeValidatorsAndProducers.js ├── truffle-config.js └── validators.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - dev 7 | - master 8 | pull_request: 9 | branches: 10 | - dev 11 | - master 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Update Path 19 | run: echo "$RUNNER_WORKSPACE/$(basename $GITHUB_REPOSITORY)" >> $GITHUB_PATH # Make it accessible from runner 20 | - name: Setup Node.js environment 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: '16' 24 | registry-url: 'https://registry.npmjs.org' 25 | - name: Install solc-select 26 | run: | 27 | sudo apt update 28 | sudo apt install python3 python3-pip -y 29 | pip install solc-select 30 | - name: Install Solidity Version 31 | run: | 32 | solc-select install 0.5.17 33 | solc-select install 0.6.12 34 | solc-select use 0.5.17 35 | solc --version 36 | - name: Install Foundry 37 | uses: foundry-rs/foundry-toolchain@v1 38 | with: 39 | version: nightly 40 | - name: Generate genesis file 41 | run: bash generate.sh 15001 heimdall-15001 42 | - name: Run tests 43 | run: forge build && forge test -vvv 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | contracts/BorValidatorSet.sol 4 | 5 | pids 6 | logs 7 | genesis.json 8 | 9 | .idea 10 | 11 | /out 12 | /target 13 | /cache 14 | /coverage 15 | lcov.info 16 | .env 17 | .password -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "matic-contracts"] 2 | path = matic-contracts 3 | url = https://github.com/maticnetwork/contracts.git 4 | [submodule "lib/forge-std"] 5 | path = lib/forge-std 6 | url = https://github.com/foundry-rs/forge-std 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "semi": false, 5 | "singleQuote": true, 6 | "trailingComma": "none", 7 | "overrides": [ 8 | { 9 | "files": "*.sol", 10 | "options": { 11 | "printWidth": 120, 12 | "singleQuote": false 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | contracts/Migrations.sol 3 | -------------------------------------------------------------------------------- /.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:recommended", 3 | "plugins": ["security"], 4 | "rules": { 5 | "quotes": ["error", "double"], 6 | "indentation": ["error", 2], 7 | "linebreak-style": ["error", "unix"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # genesis-contracts 2 | 3 | > NOTE (IMPORTANT!) 4 | > If you modify contracts, run `forge build` before testing/deploying/etc, or Foundry may not recompile them! 5 | 6 | #### Setup genesis 7 | 8 | Setup genesis whenever contracts get changed 9 | ### 1. Install dependencies and submodules 10 | ```bash 11 | npm install 12 | git submodule init 13 | git submodule update 14 | ``` 15 | 16 | ### 2. Compile Matic contracts 17 | ```bash 18 | cd matic-contracts 19 | npm install 20 | node scripts/process-templates.js --bor-chain-id 21 | npm run truffle:compile 22 | cd .. 23 | ``` 24 | 25 | ### 3. Generate Bor validator set sol file 26 | 27 | Following command will generate `BorValidatorSet.sol` file from `BorValidatorSet.template` file. 28 | 29 | ```bash 30 | # Generate bor validator set using stake and balance 31 | # Modify validators.json before as per your need 32 | $ node generate-borvalidatorset.js --bor-chain-id --heimdall-chain-id 33 | ``` 34 | 35 | ### 4. Compile contracts 36 | ```bash 37 | $ npm run truffle:compile 38 | ``` 39 | 40 | ### 5. Configure Block times 41 | 42 | To add custom block time and associated block no.s in genesis, edit the `blocks.js` file 43 | 44 | ### 6. Generate genesis file 45 | 46 | Following command will generate `genesis.json` file from `genesis-template.json` file. 47 | 48 | ```bash 49 | # Generate genesis file 50 | node generate-genesis.js --bor-chain-id --heimdall-chain-id 51 | ``` 52 | 53 | ### 7. Run Tests 54 | ```bash 55 | $ npm run testrpc 56 | $ npm test 57 | ``` 58 | -------------------------------------------------------------------------------- /blocks.js: -------------------------------------------------------------------------------- 1 | const blocks = [ 2 | { 3 | number: "0", 4 | time: "2" 5 | }, 6 | { 7 | number: "10", 8 | time: "5" 9 | } 10 | ] 11 | 12 | exports = module.exports = blocks -------------------------------------------------------------------------------- /contracts/BorValidatorSet.template: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import { SafeMath } from "openzeppelin-solidity/contracts/math/SafeMath.sol"; 5 | import { RLPReader } from "./utils/RLPReader.sol"; 6 | 7 | import { BytesLib } from "../matic-contracts/contracts/common/lib/BytesLib.sol"; 8 | import { System } from "./System.sol"; 9 | import { ECVerify } from "./ECVerify.sol"; 10 | import { ValidatorSet } from "./ValidatorSet.sol"; 11 | 12 | contract BorValidatorSet is System { 13 | using SafeMath for uint256; 14 | using RLPReader for bytes; 15 | using RLPReader for RLPReader.RLPItem; 16 | using ECVerify for bytes32; 17 | 18 | bytes32 public constant CHAIN = keccak256("{{heimdallChainId}}"); 19 | bytes32 public constant ROUND_TYPE = keccak256("vote"); 20 | bytes32 public constant BOR_ID = keccak256("{{borChainId}}"); 21 | uint8 public constant VOTE_TYPE = 2; 22 | uint256 public constant FIRST_END_BLOCK = {{firstEndBlock}}; 23 | uint256 public constant SPRINT = {{sprintSize}}; 24 | 25 | struct Validator { 26 | uint256 id; 27 | uint256 power; 28 | address signer; 29 | } 30 | 31 | // span details 32 | struct Span { 33 | uint256 number; 34 | uint256 startBlock; 35 | uint256 endBlock; 36 | } 37 | 38 | mapping(uint256 => Validator[]) public validators; 39 | mapping(uint256 => Validator[]) public producers; 40 | 41 | mapping (uint256 => Span) public spans; // span number => span 42 | uint256[] public spanNumbers; // recent span numbers 43 | 44 | // event 45 | event NewSpan(uint256 indexed id, uint256 indexed startBlock, uint256 indexed endBlock); 46 | 47 | constructor() public {} 48 | 49 | modifier onlyValidator() { 50 | uint256 span = currentSpanNumber(); 51 | require(isValidator(span, msg.sender)); 52 | _; 53 | } 54 | 55 | function setInitialValidators() internal { 56 | address[] memory d; 57 | uint256[] memory p; 58 | 59 | // get initial validators 60 | (d, p) = getInitialValidators(); 61 | 62 | // initial span 63 | uint256 span = 0; 64 | spans[span] = Span({ 65 | number: span, 66 | startBlock: 0, 67 | endBlock: FIRST_END_BLOCK 68 | }); 69 | spanNumbers.push(span); 70 | validators[span].length = 0; 71 | producers[span].length = 0; 72 | 73 | for (uint256 i = 0; i < d.length; i++) { 74 | validators[span].length++; 75 | validators[span][i] = Validator({ 76 | id: i, 77 | power: p[i], 78 | signer: d[i] 79 | }); 80 | } 81 | 82 | for (uint256 i = 0; i < d.length; i++) { 83 | producers[span].length++; 84 | producers[span][i] = Validator({ 85 | id: i, 86 | power: p[i], 87 | signer: d[i] 88 | }); 89 | } 90 | } 91 | 92 | function currentSprint() public view returns (uint256) { 93 | return block.number / SPRINT; 94 | } 95 | 96 | function getSpan(uint256 span) public view returns (uint256 number, uint256 startBlock, uint256 endBlock) { 97 | return (spans[span].number, spans[span].startBlock, spans[span].endBlock); 98 | } 99 | 100 | function getCurrentSpan() public view returns (uint256 number, uint256 startBlock, uint256 endBlock) { 101 | uint256 span = currentSpanNumber(); 102 | return (spans[span].number, spans[span].startBlock, spans[span].endBlock); 103 | } 104 | 105 | function getNextSpan() public view returns (uint256 number, uint256 startBlock, uint256 endBlock) { 106 | uint256 span = currentSpanNumber().add(1); 107 | return (spans[span].number, spans[span].startBlock, spans[span].endBlock); 108 | } 109 | 110 | function getValidatorsBySpan(uint256 span) internal view returns (Validator[] memory) { 111 | return validators[span]; 112 | } 113 | 114 | function getProducersBySpan(uint256 span) internal view returns (Validator[] memory) { 115 | return producers[span]; 116 | } 117 | 118 | // get span number by block 119 | function getSpanByBlock(uint256 number) public view returns (uint256) { 120 | for (uint256 i = spanNumbers.length; i > 0; i--) { 121 | Span memory span = spans[spanNumbers[i - 1]]; 122 | if (span.startBlock <= number && span.endBlock != 0 && number <= span.endBlock) { 123 | return span.number; 124 | } 125 | } 126 | 127 | // if cannot find matching span, return latest span 128 | if (spanNumbers.length > 0) { 129 | return spanNumbers[spanNumbers.length - 1]; 130 | } 131 | 132 | // return default if not found any thing 133 | return 0; 134 | } 135 | 136 | function currentSpanNumber() public view returns (uint256) { 137 | return getSpanByBlock(block.number); 138 | } 139 | 140 | function getValidatorsTotalStakeBySpan(uint256 span) public view returns (uint256) { 141 | Validator[] memory vals = validators[span]; 142 | uint256 result = 0; 143 | for (uint256 i = 0; i < vals.length; i++) { 144 | result = result.add(vals[i].power); 145 | } 146 | return result; 147 | } 148 | 149 | function getProducersTotalStakeBySpan(uint256 span) public view returns (uint256) { 150 | Validator[] memory vals = producers[span]; 151 | uint256 result = 0; 152 | for (uint256 i = 0; i < vals.length; i++) { 153 | result = result.add(vals[i].power); 154 | } 155 | return result; 156 | } 157 | 158 | function getValidatorBySigner(uint256 span, address signer) public view returns (Validator memory result) { 159 | Validator[] memory vals = validators[span]; 160 | for (uint256 i = 0; i < vals.length; i++) { 161 | if (vals[i].signer == signer) { 162 | result = vals[i]; 163 | break; 164 | } 165 | } 166 | } 167 | 168 | function isValidator(uint256 span, address signer) public view returns (bool) { 169 | Validator[] memory vals = validators[span]; 170 | for (uint256 i = 0; i < vals.length; i++) { 171 | if (vals[i].signer == signer) { 172 | return true; 173 | } 174 | } 175 | return false; 176 | } 177 | 178 | function isProducer(uint256 span, address signer) public view returns (bool) { 179 | Validator[] memory vals = producers[span]; 180 | for (uint256 i = 0; i < vals.length; i++) { 181 | if (vals[i].signer == signer) { 182 | return true; 183 | } 184 | } 185 | return false; 186 | } 187 | 188 | function isCurrentValidator(address signer) public view returns (bool) { 189 | return isValidator(currentSpanNumber(), signer); 190 | } 191 | 192 | function isCurrentProducer(address signer) public view returns (bool) { 193 | return isProducer(currentSpanNumber(), signer); 194 | } 195 | 196 | // get bor validator 197 | function getBorValidators(uint256 number) public view returns (address[] memory, uint256[] memory) { 198 | if (number <= FIRST_END_BLOCK) { 199 | return getInitialValidators(); 200 | } 201 | 202 | // span number by block 203 | uint256 span = getSpanByBlock(number); 204 | 205 | address[] memory addrs = new address[](producers[span].length); 206 | uint256[] memory powers = new uint256[](producers[span].length); 207 | for (uint256 i = 0; i < producers[span].length; i++) { 208 | addrs[i] = producers[span][i].signer; 209 | powers[i] = producers[span][i].power; 210 | } 211 | 212 | return (addrs, powers); 213 | } 214 | 215 | /// Get current validator set (last enacted or initial if no changes ever made) with current stake. 216 | function getInitialValidators() public view returns (address[] memory, uint256[] memory) { 217 | address[] memory addrs = new address[]({{validators.length}});{% for v in validators %} 218 | addrs[{{ loop.index0 }}] = {{ v.address }};{% endfor %} 219 | uint256[] memory powers = new uint256[]({{validators.length}});{% for v in validators %} 220 | powers[{{ loop.index0 }}] = {{ v.stake }};{% endfor %} 221 | return (addrs, powers); 222 | } 223 | 224 | /// Get current validator set (last enacted or initial if no changes ever made) with current stake. 225 | function getValidators() public view returns (address[] memory, uint256[] memory) { 226 | return getBorValidators(block.number); 227 | } 228 | 229 | function commitSpan( 230 | uint256 newSpan, 231 | uint256 startBlock, 232 | uint256 endBlock, 233 | bytes calldata validatorBytes, 234 | bytes calldata producerBytes 235 | ) external onlySystem { 236 | // current span 237 | uint256 span = currentSpanNumber(); 238 | // set initial validators if current span is zero 239 | if (span == 0) { 240 | setInitialValidators(); 241 | } 242 | 243 | // check conditions 244 | require(newSpan == span.add(1), "Invalid span id"); 245 | require(endBlock > startBlock, "End block must be greater than start block"); 246 | require((endBlock - startBlock + 1) % SPRINT == 0, "Difference between start and end block must be in multiples of sprint"); 247 | require(spans[span].startBlock <= startBlock, "Start block must be greater than current span"); 248 | 249 | // check if already in the span 250 | require(spans[newSpan].number == 0, "Span already exists"); 251 | 252 | // store span 253 | spans[newSpan] = Span({ 254 | number: newSpan, 255 | startBlock: startBlock, 256 | endBlock: endBlock 257 | }); 258 | spanNumbers.push(newSpan); 259 | validators[newSpan].length = 0; 260 | producers[newSpan].length = 0; 261 | 262 | // set validators 263 | RLPReader.RLPItem[] memory validatorItems = validatorBytes.toRlpItem().toList(); 264 | for (uint256 i = 0; i < validatorItems.length; i++) { 265 | RLPReader.RLPItem[] memory v = validatorItems[i].toList(); 266 | validators[newSpan].length++; 267 | validators[newSpan][i] = Validator({ 268 | id: v[0].toUint(), 269 | power: v[1].toUint(), 270 | signer: v[2].toAddress() 271 | }); 272 | } 273 | 274 | // set producers 275 | RLPReader.RLPItem[] memory producerItems = producerBytes.toRlpItem().toList(); 276 | for (uint256 i = 0; i < producerItems.length; i++) { 277 | RLPReader.RLPItem[] memory v = producerItems[i].toList(); 278 | producers[newSpan].length++; 279 | producers[newSpan][i] = Validator({ 280 | id: v[0].toUint(), 281 | power: v[1].toUint(), 282 | signer: v[2].toAddress() 283 | }); 284 | } 285 | } 286 | 287 | // Get stake power by sigs and data hash 288 | function getStakePowerBySigs(uint256 span, bytes32 dataHash, bytes memory sigs) public view returns (uint256) { 289 | uint256 stakePower = 0; 290 | address lastAdd = address(0x0); // cannot have address(0x0) as an owner 291 | 292 | for (uint64 i = 0; i < sigs.length; i += 65) { 293 | bytes memory sigElement = BytesLib.slice(sigs, i, 65); 294 | address signer = dataHash.ecrecovery(sigElement); 295 | 296 | // check if signer is stacker and not proposer 297 | Validator memory validator = getValidatorBySigner(span, signer); 298 | if (isValidator(span, signer) && signer > lastAdd) { 299 | lastAdd = signer; 300 | stakePower = stakePower.add(validator.power); 301 | } 302 | } 303 | 304 | return stakePower; 305 | } 306 | 307 | // 308 | // Utility functions 309 | // 310 | 311 | function checkMembership( 312 | bytes32 rootHash, 313 | bytes32 leaf, 314 | bytes memory proof 315 | ) public pure returns (bool) { 316 | bytes32 proofElement; 317 | byte direction; 318 | bytes32 computedHash = leaf; 319 | 320 | uint256 len = (proof.length / 33) * 33; 321 | if (len > 0) { 322 | computedHash = leafNode(leaf); 323 | } 324 | 325 | for (uint256 i = 33; i <= len; i += 33) { 326 | bytes32 tempBytes; 327 | assembly { 328 | // Get a location of some free memory and store it in tempBytes as 329 | // Solidity does for memory variables. 330 | tempBytes := mload(add(proof, sub(i, 1))) 331 | proofElement := mload(add(proof, i)) 332 | } 333 | 334 | direction = tempBytes[0]; 335 | if (direction == 0) { 336 | computedHash = innerNode(proofElement, computedHash); 337 | } else { 338 | computedHash = innerNode(computedHash, proofElement); 339 | } 340 | } 341 | 342 | return computedHash == rootHash; 343 | } 344 | 345 | function leafNode(bytes32 d) public pure returns (bytes32) { 346 | return sha256(abi.encodePacked(byte(uint8(0)), d)); 347 | } 348 | 349 | function innerNode(bytes32 left, bytes32 right) public pure returns (bytes32) { 350 | return sha256(abi.encodePacked(byte(uint8(1)), left, right)); 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /contracts/ECVerify.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | 4 | library ECVerify { 5 | function ecrecovery( 6 | bytes32 hash, 7 | bytes memory sig 8 | ) internal pure returns (address) { 9 | bytes32 r; 10 | bytes32 s; 11 | uint8 v; 12 | 13 | if (sig.length != 65) { 14 | return address(0x0); 15 | } 16 | 17 | assembly { 18 | r := mload(add(sig, 32)) 19 | s := mload(add(sig, 64)) 20 | v := and(mload(add(sig, 65)), 255) 21 | } 22 | 23 | // https://github.com/ethereum/go-ethereum/issues/2053 24 | if (v < 27) { 25 | v += 27; 26 | } 27 | 28 | if (v != 27 && v != 28) { 29 | return address(0x0); 30 | } 31 | 32 | // get address out of hash and signature 33 | address result = ecrecover(hash, v, r, s); 34 | 35 | // ecrecover returns zero on error 36 | require(result != address(0x0)); 37 | 38 | return result; 39 | } 40 | 41 | function ecrecovery( 42 | bytes32 hash, 43 | uint8 v, 44 | bytes32 r, 45 | bytes32 s 46 | ) internal pure returns (address) { 47 | // get address out of hash and signature 48 | address result = ecrecover(hash, v, r, s); 49 | 50 | // ecrecover returns zero on error 51 | require(result != address(0x0)); 52 | 53 | return result; 54 | } 55 | 56 | function ecverify( 57 | bytes32 hash, 58 | bytes memory sig, 59 | address signer 60 | ) internal pure returns (bool) { 61 | return signer == ecrecovery(hash, sig); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /contracts/IStateReceiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.11; 2 | 3 | // IStateReceiver represents interface to receive state 4 | interface IStateReceiver { 5 | function onStateReceive(uint256 stateId, bytes calldata data) external; 6 | } -------------------------------------------------------------------------------- /contracts/IterableMapping.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | library IterableMapping { 4 | struct Map { 5 | mapping(uint256 => IndexValue) data; 6 | KeyFlag[] keys; 7 | uint256 size; 8 | } 9 | 10 | struct IndexValue { uint256 keyIndex; bool value; } 11 | struct KeyFlag { uint256 key; bool deleted; } 12 | 13 | function insert(Map storage self, uint256 key, bool value) internal returns (bool replaced) { 14 | uint256 keyIndex = self.data[key].keyIndex; 15 | self.data[key].value = value; 16 | if (keyIndex > 0) { 17 | return true; 18 | } else { 19 | keyIndex = self.keys.length++; 20 | self.data[key].keyIndex = keyIndex + 1; 21 | self.keys[keyIndex].key = key; 22 | self.size++; 23 | return false; 24 | } 25 | } 26 | 27 | function remove(Map storage self, uint256 key) internal returns (bool success) { 28 | uint256 keyIndex = self.data[key].keyIndex; 29 | if (keyIndex == 0) { 30 | return false; 31 | } 32 | delete self.data[key]; 33 | self.keys[keyIndex - 1].deleted = true; 34 | self.size--; 35 | } 36 | 37 | function contains(Map storage self, uint256 key) internal view returns (bool) { 38 | return self.data[key].keyIndex > 0; 39 | } 40 | 41 | function start(Map storage self) internal view returns (uint256 keyIndex) { 42 | return next(self, uint(-1)); 43 | } 44 | 45 | function valid(Map storage self, uint256 keyIndex) internal view returns (bool) { 46 | return keyIndex < self.keys.length; 47 | } 48 | 49 | // Iterate next 50 | function next(Map storage self, uint256 keyIndex) internal view returns (uint r_keyIndex) { 51 | keyIndex++; 52 | while (keyIndex < self.keys.length && self.keys[keyIndex].deleted) { 53 | keyIndex++; 54 | } 55 | return keyIndex; 56 | } 57 | 58 | // Get value from key index 59 | function get(Map storage self, uint256 keyIndex) internal view returns (uint256 key, bool value) { 60 | key = self.keys[keyIndex].key; 61 | value = self.data[key].value; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint256 public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | constructor() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint256 completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/StateReceiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.6.12; 2 | 3 | import {RLPReader} from "./utils/RLPReader.sol"; 4 | import {System} from "./System.sol"; 5 | import {IStateReceiver} from "./IStateReceiver.sol"; 6 | 7 | contract StateReceiver is System { 8 | using RLPReader for bytes; 9 | using RLPReader for RLPReader.RLPItem; 10 | 11 | uint256 public lastStateId; 12 | 13 | bytes32 public failedStateSyncsRoot; 14 | mapping(bytes32 => bool) public nullifier; 15 | 16 | mapping(uint256 => bytes) public failedStateSyncs; 17 | 18 | address public immutable rootSetter; 19 | uint256 public leafCount; 20 | uint256 public replayCount; 21 | uint256 public constant TREE_DEPTH = 16; 22 | 23 | event StateCommitted(uint256 indexed stateId, bool success); 24 | event StateSyncReplay(uint256 indexed stateId); 25 | 26 | constructor(address _rootSetter) public { 27 | rootSetter = _rootSetter; 28 | } 29 | 30 | function commitState(uint256 syncTime, bytes calldata recordBytes) external onlySystem returns (bool success) { 31 | // parse state data 32 | RLPReader.RLPItem[] memory dataList = recordBytes.toRlpItem().toList(); 33 | uint256 stateId = dataList[0].toUint(); 34 | require(lastStateId + 1 == stateId, "StateIds are not sequential"); 35 | lastStateId++; 36 | address receiver = dataList[1].toAddress(); 37 | bytes memory stateData = dataList[2].toBytes(); 38 | // notify state receiver contract, in a non-revert manner 39 | if (isContract(receiver)) { 40 | uint256 txGas = 5000000; 41 | 42 | bytes memory data = abi.encodeWithSignature("onStateReceive(uint256,bytes)", stateId, stateData); 43 | // solium-disable-next-line security/no-inline-assembly 44 | assembly { 45 | success := call(txGas, receiver, 0, add(data, 0x20), mload(data), 0, 0) 46 | } 47 | emit StateCommitted(stateId, success); 48 | if (!success) failedStateSyncs[stateId] = abi.encode(receiver, stateData); 49 | } 50 | } 51 | 52 | function replayFailedStateSync(uint256 stateId) external { 53 | bytes memory stateSyncData = failedStateSyncs[stateId]; 54 | require(stateSyncData.length != 0, "!found"); 55 | delete failedStateSyncs[stateId]; 56 | 57 | (address receiver, bytes memory stateData) = abi.decode(stateSyncData, (address, bytes)); 58 | emit StateSyncReplay(stateId); 59 | IStateReceiver(receiver).onStateReceive(stateId, stateData); // revertable 60 | } 61 | 62 | function setRootAndLeafCount(bytes32 _root, uint256 _leafCount) external { 63 | require(msg.sender == rootSetter, "!rootSetter"); 64 | require(failedStateSyncsRoot == bytes32(0), "!zero"); 65 | failedStateSyncsRoot = _root; 66 | leafCount = _leafCount; 67 | } 68 | 69 | function replayHistoricFailedStateSync( 70 | bytes32[TREE_DEPTH] calldata proof, 71 | uint256 leafIndex, 72 | uint256 stateId, 73 | address receiver, 74 | bytes calldata data 75 | ) external { 76 | require(leafIndex < 2 ** TREE_DEPTH, "invalid leafIndex"); 77 | require(++replayCount <= leafCount, "end"); 78 | bytes32 root = failedStateSyncsRoot; 79 | require(root != bytes32(0), "!root"); 80 | 81 | bytes32 leafHash = keccak256(abi.encode(stateId, receiver, data)); 82 | bytes32 zeroHash = 0x28cf91ac064e179f8a42e4b7a20ba080187781da55fd4f3f18870b7a25bacb55; // keccak256(abi.encode(uint256(0), address(0), new bytes(0))); 83 | require(leafHash != zeroHash && !nullifier[leafHash], "used"); 84 | nullifier[leafHash] = true; 85 | 86 | require(root == _getRoot(proof, leafIndex, leafHash), "!proof"); 87 | 88 | emit StateSyncReplay(stateId); 89 | IStateReceiver(receiver).onStateReceive(stateId, data); 90 | } 91 | 92 | function _getRoot(bytes32[TREE_DEPTH] memory proof, uint256 index, bytes32 leafHash) private pure returns (bytes32) { 93 | bytes32 node = leafHash; 94 | 95 | for (uint256 height = 0; height < TREE_DEPTH; height++) { 96 | if (((index >> height) & 1) == 1) node = keccak256(abi.encodePacked(proof[height], node)); 97 | else node = keccak256(abi.encodePacked(node, proof[height])); 98 | } 99 | 100 | return node; 101 | } 102 | 103 | // check if address is contract 104 | function isContract(address _addr) private view returns (bool) { 105 | uint32 size; 106 | // solium-disable-next-line security/no-inline-assembly 107 | assembly { 108 | size := extcodesize(_addr) 109 | } 110 | return (size > 0); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /contracts/System.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.11; 2 | 3 | contract System { 4 | address public constant SYSTEM_ADDRESS = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE; 5 | 6 | modifier onlySystem() { 7 | require(msg.sender == SYSTEM_ADDRESS, "Not System Addess!"); 8 | _; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /contracts/ValidatorSet.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | interface ValidatorSet { 4 | // Get initial validator set 5 | function getInitialValidators() 6 | external 7 | view 8 | returns (address[] memory, uint256[] memory); 9 | 10 | // Get current validator set (last enacted or initial if no changes ever made) with current stake. 11 | function getValidators() 12 | external 13 | view 14 | returns (address[] memory, uint256[] memory); 15 | 16 | // Check if signer is validator 17 | function isValidator(uint256 span, address signer) 18 | external 19 | view 20 | returns (bool); 21 | 22 | // Check if signer is producer 23 | function isProducer(uint256 span, address signer) 24 | external 25 | view 26 | returns (bool); 27 | 28 | // Check if signer is current validator 29 | function isCurrentValidator(address signer) 30 | external 31 | view 32 | returns (bool); 33 | 34 | // Check if signer is current producer 35 | function isCurrentProducer(address signer) 36 | external 37 | view 38 | returns (bool); 39 | 40 | // Propose new span 41 | function proposeSpan() 42 | external; 43 | 44 | // Pending span proposal 45 | function spanProposalPending() 46 | external 47 | view 48 | returns (bool); 49 | 50 | // Commit span 51 | function commitSpan( 52 | uint256 newSpan, 53 | uint256 startBlock, 54 | uint256 endBlock, 55 | bytes calldata validatorBytes, 56 | bytes calldata producerBytes 57 | ) external; 58 | 59 | function getSpan(uint256 span) 60 | external 61 | view 62 | returns (uint256 number, uint256 startBlock, uint256 endBlock); 63 | 64 | function getCurrentSpan() 65 | external 66 | view 67 | returns (uint256 number, uint256 startBlock, uint256 endBlock); 68 | 69 | function getNextSpan() 70 | external 71 | view 72 | returns (uint256 number, uint256 startBlock, uint256 endBlock); 73 | 74 | function currentSpanNumber() 75 | external 76 | view 77 | returns (uint256); 78 | 79 | function getSpanByBlock(uint256 number) 80 | external 81 | view 82 | returns (uint256); 83 | 84 | function getBorValidators(uint256 number) 85 | external 86 | view 87 | returns (address[] memory, uint256[] memory); 88 | } 89 | -------------------------------------------------------------------------------- /contracts/ValidatorVerifier.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | import { ValidatorSet } from "./ValidatorSet.sol"; 4 | 5 | 6 | contract ValidatorVerifier { 7 | address constant public validatorSet = 0x0000000000000000000000000000000000001000; 8 | 9 | /** 10 | * @dev Throws if called by any account other than the validator set. 11 | */ 12 | modifier onlyValidatorSetContract() { 13 | require(isValidatorSetContract(), "Verifiable: caller is not the verifiable contract"); 14 | _; 15 | } 16 | 17 | constructor () public { 18 | // Doesn't work since contract is in genesis 19 | } 20 | 21 | /** 22 | * @dev Returns true if the caller is the current validator set contract. 23 | */ 24 | function isValidatorSetContract() public view returns (bool) { 25 | return msg.sender == validatorSet; 26 | } 27 | 28 | // check if signer is validator 29 | function isValidator(address signer) public view returns (bool) { 30 | return ValidatorSet(validatorSet).isCurrentValidator(signer); 31 | } 32 | 33 | // check if signer is producer 34 | function isProducer(address signer) public view returns (bool) { 35 | return ValidatorSet(validatorSet).isCurrentProducer(signer); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /contracts/test/TestBorValidatorSet.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import {BorValidatorSet} from "../BorValidatorSet.sol"; 5 | import {TestSystem} from "./TestSystem.sol"; 6 | 7 | contract TestBorValidatorSet is BorValidatorSet, TestSystem {} 8 | -------------------------------------------------------------------------------- /contracts/test/TestCommitState.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | pragma experimental ABIEncoderV2; 3 | 4 | 5 | contract TestCommitState { 6 | 7 | uint256 public id; 8 | bytes public data; 9 | 10 | function onStateReceive( 11 | uint256 _id, /* id */ 12 | bytes calldata _data 13 | ) external { 14 | (,,,uint256 num) = abi.decode(_data, (address, address, uint256, uint256)); 15 | // dummy loop 16 | for (uint256 i = 0; i < num; i++) { 17 | id = i; 18 | } 19 | id = _id; 20 | data = _data; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/test/TestSystem.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | contract TestSystem { 4 | address public dummySystem; 5 | 6 | function setSystemAddress(address _system) public { 7 | dummySystem = _system; 8 | } 9 | 10 | modifier onlySystem() { 11 | require(msg.sender == dummySystem, "Not System Addess!"); 12 | _; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /contracts/utils/RLPReader.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | /* 4 | * @author Hamdi Allam hamdi.allam97@gmail.com 5 | * Please reach out with any questions or concerns 6 | */ 7 | pragma solidity >=0.5.10 <0.9.0; 8 | 9 | library RLPReader { 10 | uint8 constant STRING_SHORT_START = 0x80; 11 | uint8 constant STRING_LONG_START = 0xb8; 12 | uint8 constant LIST_SHORT_START = 0xc0; 13 | uint8 constant LIST_LONG_START = 0xf8; 14 | uint8 constant WORD_SIZE = 32; 15 | 16 | struct RLPItem { 17 | uint256 len; 18 | uint256 memPtr; 19 | } 20 | 21 | struct Iterator { 22 | RLPItem item; // Item that's being iterated over. 23 | uint256 nextPtr; // Position of the next item in the list. 24 | } 25 | 26 | /* 27 | * @dev Returns the next element in the iteration. Reverts if it has not next element. 28 | * @param self The iterator. 29 | * @return The next element in the iteration. 30 | */ 31 | function next(Iterator memory self) internal pure returns (RLPItem memory) { 32 | require(hasNext(self)); 33 | 34 | uint256 ptr = self.nextPtr; 35 | uint256 itemLength = _itemLength(ptr); 36 | self.nextPtr = ptr + itemLength; 37 | 38 | return RLPItem(itemLength, ptr); 39 | } 40 | 41 | /* 42 | * @dev Returns true if the iteration has more elements. 43 | * @param self The iterator. 44 | * @return true if the iteration has more elements. 45 | */ 46 | function hasNext(Iterator memory self) internal pure returns (bool) { 47 | RLPItem memory item = self.item; 48 | return self.nextPtr < item.memPtr + item.len; 49 | } 50 | 51 | /* 52 | * @param item RLP encoded bytes 53 | */ 54 | function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { 55 | uint256 memPtr; 56 | assembly { 57 | memPtr := add(item, 0x20) 58 | } 59 | 60 | return RLPItem(item.length, memPtr); 61 | } 62 | 63 | /* 64 | * @dev Create an iterator. Reverts if item is not a list. 65 | * @param self The RLP item. 66 | * @return An 'Iterator' over the item. 67 | */ 68 | function iterator(RLPItem memory self) internal pure returns (Iterator memory) { 69 | require(isList(self)); 70 | 71 | uint256 ptr = self.memPtr + _payloadOffset(self.memPtr); 72 | return Iterator(self, ptr); 73 | } 74 | 75 | /* 76 | * @param the RLP item. 77 | */ 78 | function rlpLen(RLPItem memory item) internal pure returns (uint256) { 79 | return item.len; 80 | } 81 | 82 | /* 83 | * @param the RLP item. 84 | * @return (memPtr, len) pair: location of the item's payload in memory. 85 | */ 86 | function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) { 87 | uint256 offset = _payloadOffset(item.memPtr); 88 | uint256 memPtr = item.memPtr + offset; 89 | uint256 len = item.len - offset; // data length 90 | return (memPtr, len); 91 | } 92 | 93 | /* 94 | * @param the RLP item. 95 | */ 96 | function payloadLen(RLPItem memory item) internal pure returns (uint256) { 97 | (, uint256 len) = payloadLocation(item); 98 | return len; 99 | } 100 | 101 | /* 102 | * @param the RLP item containing the encoded list. 103 | */ 104 | function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { 105 | require(isList(item)); 106 | 107 | uint256 items = numItems(item); 108 | RLPItem[] memory result = new RLPItem[](items); 109 | 110 | uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr); 111 | uint256 dataLen; 112 | for (uint256 i = 0; i < items; i++) { 113 | dataLen = _itemLength(memPtr); 114 | result[i] = RLPItem(dataLen, memPtr); 115 | memPtr = memPtr + dataLen; 116 | } 117 | require(memPtr - item.memPtr == item.len, "Wrong total length."); 118 | 119 | return result; 120 | } 121 | 122 | // @return indicator whether encoded payload is a list. negate this function call for isData. 123 | function isList(RLPItem memory item) internal pure returns (bool) { 124 | if (item.len == 0) return false; 125 | 126 | uint8 byte0; 127 | uint256 memPtr = item.memPtr; 128 | assembly { 129 | byte0 := byte(0, mload(memPtr)) 130 | } 131 | 132 | if (byte0 < LIST_SHORT_START) return false; 133 | return true; 134 | } 135 | 136 | /* 137 | * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory. 138 | * @return keccak256 hash of RLP encoded bytes. 139 | */ 140 | function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) { 141 | uint256 ptr = item.memPtr; 142 | uint256 len = item.len; 143 | bytes32 result; 144 | assembly { 145 | result := keccak256(ptr, len) 146 | } 147 | return result; 148 | } 149 | 150 | /* 151 | * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory. 152 | * @return keccak256 hash of the item payload. 153 | */ 154 | function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) { 155 | (uint256 memPtr, uint256 len) = payloadLocation(item); 156 | bytes32 result; 157 | assembly { 158 | result := keccak256(memPtr, len) 159 | } 160 | return result; 161 | } 162 | 163 | /** RLPItem conversions into data types **/ 164 | 165 | // @returns raw rlp encoding in bytes 166 | function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) { 167 | bytes memory result = new bytes(item.len); 168 | if (result.length == 0) return result; 169 | 170 | uint256 ptr; 171 | assembly { 172 | ptr := add(0x20, result) 173 | } 174 | 175 | copy(item.memPtr, ptr, item.len); 176 | return result; 177 | } 178 | 179 | // any non-zero byte except "0x80" is considered true 180 | function toBoolean(RLPItem memory item) internal pure returns (bool) { 181 | require(item.len == 1); 182 | uint256 result; 183 | uint256 memPtr = item.memPtr; 184 | assembly { 185 | result := byte(0, mload(memPtr)) 186 | } 187 | 188 | // SEE Github Issue #5. 189 | // Summary: Most commonly used RLP libraries (i.e Geth) will encode 190 | // "0" as "0x80" instead of as "0". We handle this edge case explicitly 191 | // here. 192 | if (result == 0 || result == STRING_SHORT_START) { 193 | return false; 194 | } else { 195 | return true; 196 | } 197 | } 198 | 199 | function toAddress(RLPItem memory item) internal pure returns (address) { 200 | // 1 byte for the length prefix 201 | require(item.len == 21); 202 | 203 | return address(uint160(toUint(item))); 204 | } 205 | 206 | function toUint(RLPItem memory item) internal pure returns (uint256) { 207 | require(item.len > 0 && item.len <= 33); 208 | 209 | (uint256 memPtr, uint256 len) = payloadLocation(item); 210 | 211 | uint256 result; 212 | assembly { 213 | result := mload(memPtr) 214 | 215 | // shift to the correct location if neccesary 216 | if lt(len, 32) { 217 | result := div(result, exp(256, sub(32, len))) 218 | } 219 | } 220 | 221 | return result; 222 | } 223 | 224 | // enforces 32 byte length 225 | function toUintStrict(RLPItem memory item) internal pure returns (uint256) { 226 | // one byte prefix 227 | require(item.len == 33); 228 | 229 | uint256 result; 230 | uint256 memPtr = item.memPtr + 1; 231 | assembly { 232 | result := mload(memPtr) 233 | } 234 | 235 | return result; 236 | } 237 | 238 | function toBytes(RLPItem memory item) internal pure returns (bytes memory) { 239 | require(item.len > 0); 240 | 241 | (uint256 memPtr, uint256 len) = payloadLocation(item); 242 | bytes memory result = new bytes(len); 243 | 244 | uint256 destPtr; 245 | assembly { 246 | destPtr := add(0x20, result) 247 | } 248 | 249 | copy(memPtr, destPtr, len); 250 | return result; 251 | } 252 | 253 | /* 254 | * Private Helpers 255 | */ 256 | 257 | // @return number of payload items inside an encoded list. 258 | function numItems(RLPItem memory item) private pure returns (uint256) { 259 | if (item.len == 0) return 0; 260 | 261 | uint256 count = 0; 262 | uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr); 263 | uint256 endPtr = item.memPtr + item.len; 264 | while (currPtr < endPtr) { 265 | currPtr = currPtr + _itemLength(currPtr); // skip over an item 266 | count++; 267 | } 268 | 269 | return count; 270 | } 271 | 272 | // @return entire rlp item byte length 273 | function _itemLength(uint256 memPtr) private pure returns (uint256) { 274 | uint256 itemLen; 275 | uint256 byte0; 276 | assembly { 277 | byte0 := byte(0, mload(memPtr)) 278 | } 279 | 280 | if (byte0 < STRING_SHORT_START) { 281 | itemLen = 1; 282 | } else if (byte0 < STRING_LONG_START) { 283 | itemLen = byte0 - STRING_SHORT_START + 1; 284 | } else if (byte0 < LIST_SHORT_START) { 285 | assembly { 286 | let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is 287 | memPtr := add(memPtr, 1) // skip over the first byte 288 | 289 | /* 32 byte word size */ 290 | let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len 291 | itemLen := add(dataLen, add(byteLen, 1)) 292 | } 293 | } else if (byte0 < LIST_LONG_START) { 294 | itemLen = byte0 - LIST_SHORT_START + 1; 295 | } else { 296 | assembly { 297 | let byteLen := sub(byte0, 0xf7) 298 | memPtr := add(memPtr, 1) 299 | 300 | let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length 301 | itemLen := add(dataLen, add(byteLen, 1)) 302 | } 303 | } 304 | 305 | return itemLen; 306 | } 307 | 308 | // @return number of bytes until the data 309 | function _payloadOffset(uint256 memPtr) private pure returns (uint256) { 310 | uint256 byte0; 311 | assembly { 312 | byte0 := byte(0, mload(memPtr)) 313 | } 314 | 315 | if (byte0 < STRING_SHORT_START) { 316 | return 0; 317 | } else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) { 318 | return 1; 319 | } else if (byte0 < LIST_SHORT_START) { 320 | // being explicit 321 | return byte0 - (STRING_LONG_START - 1) + 1; 322 | } else { 323 | return byte0 - (LIST_LONG_START - 1) + 1; 324 | } 325 | } 326 | 327 | /* 328 | * @param src Pointer to source 329 | * @param dest Pointer to destination 330 | * @param len Amount of memory to copy from the source 331 | */ 332 | function copy(uint256 src, uint256 dest, uint256 len) private pure { 333 | if (len == 0) return; 334 | 335 | // copy as many word sizes as possible 336 | for (; len >= WORD_SIZE; len -= WORD_SIZE) { 337 | assembly { 338 | mstore(dest, mload(src)) 339 | } 340 | 341 | src += WORD_SIZE; 342 | dest += WORD_SIZE; 343 | } 344 | 345 | if (len > 0) { 346 | // left over bytes. Mask is used to remove unwanted bytes from the word 347 | uint256 mask = 256 ** (WORD_SIZE - len) - 1; 348 | assembly { 349 | let srcpart := and(mload(src), not(mask)) // zero out src 350 | let destpart := and(mload(dest), mask) // retrieve the bytes 351 | mstore(dest, or(destpart, srcpart)) 352 | } 353 | } 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = "contracts" 3 | out = "out" 4 | libs = ["lib"] 5 | optimizer = true 6 | optimizer_runs = 999999 7 | via_ir = true 8 | verbosity = 2 9 | ffi = true 10 | fs_permissions = [{ access = "read", path = "./out/"}] 11 | 12 | remappings = [ 13 | "forge-std=lib/forge-std/src", 14 | "openzeppelin-solidity/=node_modules/openzeppelin-solidity/", 15 | ] 16 | 17 | [profile.intense.fuzz] 18 | runs = 10000 19 | max_test_rejects = 999999 20 | 21 | [fmt] 22 | line_length = 160 23 | number_underscore = "thousands" 24 | 25 | [rpc_endpoints] 26 | anvil = "http://127.0.0.1:8545" 27 | mainnet = "https://mainnet.infura.io/v3/${INFURA_KEY}" 28 | goerli = "https://goerli.infura.io/v3/${INFURA_KEY}" 29 | sepolia = "https://sepolia.infura.io/v3/${INFURA_KEY}" 30 | polygon_pos = "https://polygon-mainnet.infura.io/v3/${INFURA_KEY}" 31 | mumbai = "https://polygon-mumbai.infura.io/v3/${INFURA_KEY}" 32 | polygon_zkevm = "https://zkevm-rpc.com" 33 | polygon_zkevm_testnet = "https://rpc.public.zkevm-test.net" 34 | 35 | [etherscan] 36 | mainnet = { key = "${ETHERSCAN_API_KEY}" } 37 | goerli = { key = "${ETHERSCAN_API_KEY}" } 38 | sepolia = { key = "${ETHERSCAN_API_KEY}" } 39 | polygon_pos = { key = "${POLYGONSCAN_API_KEY}" } 40 | mumbai = { key = "${POLYGONSCAN_API_KEY}" } 41 | polygon_zkevm = { key = "${POLYGONSCAN_ZKEVM_API_KEY}" } 42 | polygon_zkevm_testnet = { key = "${POLYGONSCAN_ZKEVM_API_KEY}" } 43 | 44 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config -------------------------------------------------------------------------------- /generate-borvalidatorset.js: -------------------------------------------------------------------------------- 1 | const program = require("commander") 2 | const fs = require("fs") 3 | const nunjucks = require("nunjucks") 4 | const web3 = require("web3") 5 | const validators = require("./validators") 6 | 7 | program.version("0.0.1") 8 | program.option("--bor-chain-id ", "Bor chain id", "15001") 9 | program.option( 10 | "--heimdall-chain-id ", 11 | "Heimdall chain id", 12 | "heimdall-P5rXwg" 13 | ) 14 | program.option("--sprint-size ", "Sprint size", "64") 15 | program.option( 16 | "--first-end-block ", 17 | "End block for first span", 18 | "255" 19 | ) 20 | program.option( 21 | "-o, --output ", 22 | "BorValidatorSet.sol", 23 | "./contracts/BorValidatorSet.sol" 24 | ) 25 | program.option( 26 | "-t, --template