Skip to content

Commit b526e50

Browse files
authored
Merge pull request #79 from maticnetwork/feat/pip36-forge
PIP36: Replay Failed StateSyncs (on forge)
2 parents 96a19dd + c562fe8 commit b526e50

37 files changed

+2318
-178
lines changed

.github/workflows/ci.yml

+25-22
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,36 @@ name: CI
33
on:
44
push:
55
branches:
6-
- develop
6+
- dev
77
- master
88
pull_request:
9-
branches:
10-
- develop
9+
branches:
10+
- dev
1111
- master
1212

1313
jobs:
1414
build:
1515
runs-on: ubuntu-latest
1616
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: Install solc
21-
run: |
22-
set -x
23-
wget -c https://github.com/ethereum/solidity/releases/download/v0.5.17/solc-static-linux
24-
mv solc-static-linux solc
25-
chmod +x solc
26-
solc --version
27-
- name: Setup Node.js environment
28-
uses: actions/setup-node@v3
29-
with:
30-
node-version: '16'
31-
registry-url: 'https://registry.npmjs.org'
32-
- name: Generate genesis file
33-
run: bash generate.sh 15001 heimdall-15001
34-
- name: Run tests
35-
run: npm run test:ci
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+
- uses: dtolnay/rust-toolchain@nightly
26+
- name: Install svm
27+
run: |
28+
cargo install svm-rs
29+
svm install 0.5.17
30+
svm install 0.6.12
31+
- name: Install Foundry
32+
uses: foundry-rs/foundry-toolchain@v1
33+
with:
34+
version: nightly
35+
- name: Generate genesis file
36+
run: bash generate.sh 15001 heimdall-15001
37+
- name: Run tests
38+
run: forge build && forge test -vvv

.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,11 @@ logs
77
genesis.json
88

99
.idea
10+
11+
/out
12+
/target
13+
/cache
14+
/coverage
15+
lcov.info
16+
.env
17+
.password

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "matic-contracts"]
22
path = matic-contracts
33
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

.prettierrc

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
}

README.md

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
# genesis-contracts
22

3+
> NOTE (IMPORTANT!)
4+
> If you modify contracts, run `forge build` before testing/deploying/etc, or Foundry may not recompile them!
5+
36
#### Setup genesis
47

58
Setup genesis whenever contracts get changed
69
### 1. Install dependencies and submodules
710
```bash
8-
$ npm install
9-
$ git submodule init
10-
$ git submodule update
11+
npm install
12+
git submodule init
13+
git submodule update
1114
```
1215

1316
### 2. Compile Matic contracts
1417
```bash
15-
$ cd matic-contracts
16-
$ npm install
17-
$ node scripts/process-templates.js --bor-chain-id <bor-chain-id>
18-
$ npm run truffle:compile
19-
$ cd ..
18+
cd matic-contracts
19+
npm install
20+
node scripts/process-templates.js --bor-chain-id <bor-chain-id>
21+
npm run truffle:compile
22+
cd ..
2023
```
2124

2225
### 3. Generate Bor validator set sol file
@@ -44,7 +47,7 @@ Following command will generate `genesis.json` file from `genesis-template.json`
4447

4548
```bash
4649
# Generate genesis file
47-
$ node generate-genesis.js --bor-chain-id <bor-chain-id> --heimdall-chain-id <heimdall-chain-id>
50+
node generate-genesis.js --bor-chain-id <bor-chain-id> --heimdall-chain-id <heimdall-chain-id>
4851
```
4952

5053
### 7. Run Tests

contracts/BorValidatorSet.template

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ pragma solidity ^0.5.11;
22
pragma experimental ABIEncoderV2;
33

44
import { SafeMath } from "openzeppelin-solidity/contracts/math/SafeMath.sol";
5-
import { RLPReader } from "solidity-rlp/contracts/RLPReader.sol";
5+
import { RLPReader } from "./utils/RLPReader.sol";
66

77
import { BytesLib } from "../matic-contracts/contracts/common/lib/BytesLib.sol";
88
import { System } from "./System.sol";

contracts/IStateReceiver.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pragma solidity ^0.5.11;
1+
pragma solidity >0.5.11;
22

33
// IStateReceiver represents interface to receive state
44
interface IStateReceiver {

contracts/StateReceiver.sol

+75-11
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,107 @@
1-
pragma solidity ^0.5.11;
1+
pragma solidity 0.6.12;
22

3-
import { RLPReader } from "solidity-rlp/contracts/RLPReader.sol";
4-
5-
import { System } from "./System.sol";
3+
import {RLPReader} from "./utils/RLPReader.sol";
4+
import {System} from "./System.sol";
5+
import {IStateReceiver} from "./IStateReceiver.sol";
66

77
contract StateReceiver is System {
88
using RLPReader for bytes;
99
using RLPReader for RLPReader.RLPItem;
1010

1111
uint256 public lastStateId;
1212

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+
1323
event StateCommitted(uint256 indexed stateId, bool success);
24+
event StateSyncReplay(uint256 indexed stateId);
25+
26+
constructor(address _rootSetter) public {
27+
rootSetter = _rootSetter;
28+
}
1429

15-
function commitState(uint256 syncTime, bytes calldata recordBytes) external onlySystem returns(bool success) {
30+
function commitState(uint256 syncTime, bytes calldata recordBytes) external onlySystem returns (bool success) {
1631
// parse state data
1732
RLPReader.RLPItem[] memory dataList = recordBytes.toRlpItem().toList();
1833
uint256 stateId = dataList[0].toUint();
19-
require(
20-
lastStateId + 1 == stateId,
21-
"StateIds are not sequential"
22-
);
34+
require(lastStateId + 1 == stateId, "StateIds are not sequential");
2335
lastStateId++;
24-
2536
address receiver = dataList[1].toAddress();
2637
bytes memory stateData = dataList[2].toBytes();
2738
// notify state receiver contract, in a non-revert manner
2839
if (isContract(receiver)) {
2940
uint256 txGas = 5000000;
41+
3042
bytes memory data = abi.encodeWithSignature("onStateReceive(uint256,bytes)", stateId, stateData);
3143
// solium-disable-next-line security/no-inline-assembly
3244
assembly {
3345
success := call(txGas, receiver, 0, add(data, 0x20), mload(data), 0, 0)
3446
}
3547
emit StateCommitted(stateId, success);
48+
if (!success) failedStateSyncs[stateId] = abi.encode(receiver, stateData);
3649
}
3750
}
3851

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+
39103
// check if address is contract
40-
function isContract(address _addr) private view returns (bool){
104+
function isContract(address _addr) private view returns (bool) {
41105
uint32 size;
42106
// solium-disable-next-line security/no-inline-assembly
43107
assembly {

contracts/System.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pragma solidity ^0.5.11;
1+
pragma solidity >0.5.11;
22

33
contract System {
44
address public constant SYSTEM_ADDRESS = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE;

contracts/test/TestStateReceiver.sol

-7
This file was deleted.

0 commit comments

Comments
 (0)