Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PIP36: Replay Failed StateSyncs (on forge) #79

Open
wants to merge 16 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ name: CI
on:
push:
branches:
- develop
- dev
- master
pull_request:
branches:
- develop
- dev
- master

jobs:
Expand All @@ -17,18 +17,17 @@ jobs:
- uses: actions/checkout@v3
- name: Update Path
run: echo "$RUNNER_WORKSPACE/$(basename $GITHUB_REPOSITORY)" >> $GITHUB_PATH # Make it accessible from runner
- name: Install solc
run: |
set -x
wget -c https://github.com/ethereum/solidity/releases/download/v0.5.17/solc-static-linux
mv solc-static-linux solc
chmod +x solc
solc --version
- name: Setup Node.js environment
uses: actions/setup-node@v3
with:
node-version: '16'
registry-url: 'https://registry.npmjs.org'
- uses: dtolnay/rust-toolchain@nightly
- name: Install svm
run: |
cargo install svm-rs
svm install 0.5.17
svm install 0.6.12
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
Expand Down
16 changes: 16 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"overrides": [
{
"files": "*.sol",
"options": {
"printWidth": 120,
"singleQuote": false
}
}
]
}
2 changes: 1 addition & 1 deletion contracts/IStateReceiver.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.5.11;
pragma solidity >0.5.11;

// IStateReceiver represents interface to receive state
interface IStateReceiver {
Expand Down
85 changes: 74 additions & 11 deletions contracts/StateReceiver.sol
Original file line number Diff line number Diff line change
@@ -1,43 +1,106 @@
pragma solidity ^0.5.11;
pragma solidity 0.6.12;

import { RLPReader } from "solidity-rlp/contracts/RLPReader.sol";

import { System } from "./System.sol";
import {RLPReader} from "./utils/RLPReader.sol";
import {System} from "./System.sol";
import {IStateReceiver} from "./IStateReceiver.sol";

contract StateReceiver is System {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;

uint256 public lastStateId;

bytes32 public failedStateSyncsRoot;
mapping(bytes32 => bool) public nullifier;

mapping(uint256 => bytes) public failedStateSyncs;

address public immutable rootSetter;
uint256 public leafCount;
uint256 public replayCount;
uint256 public constant TREE_DEPTH = 16;

event StateCommitted(uint256 indexed stateId, bool success);
event StateSyncReplay(uint256 indexed stateId);

constructor(address _rootSetter) public {
rootSetter = _rootSetter;
}

function commitState(uint256 syncTime, bytes calldata recordBytes) external onlySystem returns(bool success) {
function commitState(uint256 syncTime, bytes calldata recordBytes) external onlySystem returns (bool success) {
// parse state data
RLPReader.RLPItem[] memory dataList = recordBytes.toRlpItem().toList();
uint256 stateId = dataList[0].toUint();
require(
lastStateId + 1 == stateId,
"StateIds are not sequential"
);
require(lastStateId + 1 == stateId, "StateIds are not sequential");
lastStateId++;

address receiver = dataList[1].toAddress();
bytes memory stateData = dataList[2].toBytes();
// notify state receiver contract, in a non-revert manner
if (isContract(receiver)) {
uint256 txGas = 5000000;

bytes memory data = abi.encodeWithSignature("onStateReceive(uint256,bytes)", stateId, stateData);
// solium-disable-next-line security/no-inline-assembly
assembly {
success := call(txGas, receiver, 0, add(data, 0x20), mload(data), 0, 0)
}
emit StateCommitted(stateId, success);
if (!success) failedStateSyncs[stateId] = abi.encode(receiver, stateData);
}
}

function replayFailedStateSync(uint256 stateId) external {
bytes memory stateSyncData = failedStateSyncs[stateId];
require(stateSyncData.length != 0, "!found");
delete failedStateSyncs[stateId];

(address receiver, bytes memory stateData) = abi.decode(stateSyncData, (address, bytes));
emit StateSyncReplay(stateId);
IStateReceiver(receiver).onStateReceive(stateId, stateData); // revertable
}

function setRootAndLeafCount(bytes32 _root, uint256 _leafCount) external {
require(msg.sender == rootSetter, "!rootSetter");
failedStateSyncsRoot = _root;
leafCount = _leafCount;
}

function replayHistoricFailedStateSync(
bytes32[TREE_DEPTH] calldata proof,
uint256 leafIndex,
uint256 stateId,
address receiver,
bytes calldata data
) external {
require(leafIndex < 2 ** TREE_DEPTH, "invalid leafIndex");
require(++replayCount <= leafCount, "end");
bytes32 root = failedStateSyncsRoot;
require(root != bytes32(0), "!root");

bytes32 leafHash = keccak256(abi.encode(stateId, receiver, data));
bytes32 zeroHash = 0x28cf91ac064e179f8a42e4b7a20ba080187781da55fd4f3f18870b7a25bacb55; // keccak256(abi.encode(uint256(0), address(0), new bytes(0)));
require(leafHash != zeroHash && !nullifier[leafHash], "used");
nullifier[leafHash] = true;

require(root == _getRoot(proof, leafIndex, leafHash), "!proof");

emit StateSyncReplay(stateId);
IStateReceiver(receiver).onStateReceive(stateId, data);
}

function _getRoot(bytes32[TREE_DEPTH] memory proof, uint256 index, bytes32 leafHash) private pure returns (bytes32) {
bytes32 node = leafHash;

for (uint256 height = 0; height < TREE_DEPTH; height++) {
if (((index >> height) & 1) == 1) node = keccak256(abi.encodePacked(proof[height], node));
else node = keccak256(abi.encodePacked(node, proof[height]));
}

return node;
}

// check if address is contract
function isContract(address _addr) private view returns (bool){
function isContract(address _addr) private view returns (bool) {
uint32 size;
// solium-disable-next-line security/no-inline-assembly
assembly {
Expand Down
2 changes: 1 addition & 1 deletion contracts/System.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.5.11;
pragma solidity >0.5.11;

contract System {
address public constant SYSTEM_ADDRESS = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE;
Expand Down
7 changes: 0 additions & 7 deletions contracts/test/TestStateReceiver.sol

This file was deleted.

Loading
Loading