mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2025-01-03 09:37:38 +00:00
c51573f333
Implemented polygon->eth flow
330 lines
11 KiB
Solidity
330 lines
11 KiB
Solidity
|
|
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.0;
|
|
|
|
import {RLPReader} from "lib/rlpreader.sol";
|
|
import {SafeMath} from "lib/safemath.sol";
|
|
|
|
interface IRootChain {
|
|
function slash() external;
|
|
|
|
function submitHeaderBlock(bytes calldata data, bytes calldata sigs)
|
|
external;
|
|
|
|
function submitCheckpoint(bytes calldata data, uint[3][] calldata sigs)
|
|
external;
|
|
|
|
function getLastChildBlock() external view returns (uint256);
|
|
|
|
function currentHeaderBlock() external view returns (uint256);
|
|
}
|
|
|
|
contract RootChainHeader {
|
|
event NewHeaderBlock(
|
|
address indexed proposer,
|
|
uint256 indexed headerBlockId,
|
|
uint256 indexed reward,
|
|
uint256 start,
|
|
uint256 end,
|
|
bytes32 root
|
|
);
|
|
// housekeeping event
|
|
event ResetHeaderBlock(address indexed proposer, uint256 indexed headerBlockId);
|
|
struct HeaderBlock {
|
|
bytes32 root;
|
|
uint256 start;
|
|
uint256 end;
|
|
uint256 createdAt;
|
|
address proposer;
|
|
}
|
|
}
|
|
|
|
contract ProxyStorage {
|
|
address internal proxyTo;
|
|
}
|
|
|
|
contract ChainIdMixin {
|
|
bytes constant public networkId = hex"0539";
|
|
uint256 constant public CHAINID = 1337;
|
|
}
|
|
|
|
interface IGovernance {
|
|
function update(address target, bytes calldata data) external;
|
|
}
|
|
|
|
contract Governable {
|
|
IGovernance public governance;
|
|
|
|
constructor(address _governance) {
|
|
governance = IGovernance(_governance);
|
|
}
|
|
|
|
modifier onlyGovernance() {
|
|
_assertGovernance();
|
|
_;
|
|
}
|
|
|
|
function _assertGovernance() private view {
|
|
require(
|
|
msg.sender == address(governance),
|
|
"Only governance contract is authorized"
|
|
);
|
|
}
|
|
}
|
|
|
|
contract Registry is Governable {
|
|
// @todo hardcode constants
|
|
bytes32 private constant WETH_TOKEN = keccak256("wethToken");
|
|
bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager");
|
|
bytes32 private constant STAKE_MANAGER = keccak256("stakeManager");
|
|
bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare");
|
|
bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager");
|
|
bytes32 private constant CHILD_CHAIN = keccak256("childChain");
|
|
bytes32 private constant STATE_SENDER = keccak256("stateSender");
|
|
bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager");
|
|
|
|
address public erc20Predicate;
|
|
address public erc721Predicate;
|
|
|
|
mapping(bytes32 => address) public contractMap;
|
|
mapping(address => address) public rootToChildToken;
|
|
mapping(address => address) public childToRootToken;
|
|
mapping(address => bool) public proofValidatorContracts;
|
|
mapping(address => bool) public isERC721;
|
|
|
|
enum Type {Invalid, ERC20, ERC721, Custom}
|
|
struct Predicate {
|
|
Type _type;
|
|
}
|
|
mapping(address => Predicate) public predicates;
|
|
|
|
event TokenMapped(address indexed rootToken, address indexed childToken);
|
|
event ProofValidatorAdded(address indexed validator, address indexed from);
|
|
event ProofValidatorRemoved(address indexed validator, address indexed from);
|
|
event PredicateAdded(address indexed predicate, address indexed from);
|
|
event PredicateRemoved(address indexed predicate, address indexed from);
|
|
event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract);
|
|
|
|
constructor(address _governance) Governable(_governance) {}
|
|
|
|
function updateContractMap(bytes32 _key, address _address) external onlyGovernance {
|
|
emit ContractMapUpdated(_key, contractMap[_key], _address);
|
|
contractMap[_key] = _address;
|
|
}
|
|
|
|
/**
|
|
* @dev Map root token to child token
|
|
* @param _rootToken Token address on the root chain
|
|
* @param _childToken Token address on the child chain
|
|
* @param _isERC721 Is the token being mapped ERC721
|
|
*/
|
|
function mapToken(
|
|
address _rootToken,
|
|
address _childToken,
|
|
bool _isERC721
|
|
) external onlyGovernance {
|
|
require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS");
|
|
rootToChildToken[_rootToken] = _childToken;
|
|
childToRootToken[_childToken] = _rootToken;
|
|
isERC721[_rootToken] = _isERC721;
|
|
//IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken);
|
|
emit TokenMapped(_rootToken, _childToken);
|
|
}
|
|
|
|
function addErc20Predicate(address predicate) public onlyGovernance {
|
|
require(predicate != address(0x0), "Can not add null address as predicate");
|
|
erc20Predicate = predicate;
|
|
addPredicate(predicate, Type.ERC20);
|
|
}
|
|
|
|
function addErc721Predicate(address predicate) public onlyGovernance {
|
|
erc721Predicate = predicate;
|
|
addPredicate(predicate, Type.ERC721);
|
|
}
|
|
|
|
function addPredicate(address predicate, Type _type) public onlyGovernance {
|
|
require(predicates[predicate]._type == Type.Invalid, "Predicate already added");
|
|
predicates[predicate]._type = _type;
|
|
emit PredicateAdded(predicate, msg.sender);
|
|
}
|
|
|
|
function removePredicate(address predicate) public onlyGovernance {
|
|
require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist");
|
|
delete predicates[predicate];
|
|
emit PredicateRemoved(predicate, msg.sender);
|
|
}
|
|
|
|
function getValidatorShareAddress() public view returns (address) {
|
|
return contractMap[VALIDATOR_SHARE];
|
|
}
|
|
|
|
function getWethTokenAddress() public view returns (address) {
|
|
return contractMap[WETH_TOKEN];
|
|
}
|
|
|
|
function getDepositManagerAddress() public view returns (address) {
|
|
return contractMap[DEPOSIT_MANAGER];
|
|
}
|
|
|
|
function getStakeManagerAddress() public view returns (address) {
|
|
return contractMap[STAKE_MANAGER];
|
|
}
|
|
|
|
function getSlashingManagerAddress() public view returns (address) {
|
|
return contractMap[SLASHING_MANAGER];
|
|
}
|
|
|
|
function getWithdrawManagerAddress() public view returns (address) {
|
|
return contractMap[WITHDRAW_MANAGER];
|
|
}
|
|
|
|
function getChildChainAndStateSender() public view returns (address, address) {
|
|
return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]);
|
|
}
|
|
|
|
function isTokenMapped(address _token) public view returns (bool) {
|
|
return rootToChildToken[_token] != address(0x0);
|
|
}
|
|
|
|
function isTokenMappedAndIsErc721(address _token) public view returns (bool) {
|
|
require(isTokenMapped(_token), "TOKEN_NOT_MAPPED");
|
|
return isERC721[_token];
|
|
}
|
|
|
|
function isTokenMappedAndGetPredicate(address _token) public view returns (address) {
|
|
if (isTokenMappedAndIsErc721(_token)) {
|
|
return erc721Predicate;
|
|
}
|
|
return erc20Predicate;
|
|
}
|
|
|
|
function isChildTokenErc721(address childToken) public view returns (bool) {
|
|
address rootToken = childToRootToken[childToken];
|
|
require(rootToken != address(0x0), "Child token is not mapped");
|
|
return isERC721[rootToken];
|
|
}
|
|
}
|
|
|
|
contract RootChainStorage is ProxyStorage, RootChainHeader, ChainIdMixin {
|
|
bytes32 public heimdallId;
|
|
uint8 public constant VOTE_TYPE = 2;
|
|
|
|
uint16 internal constant MAX_DEPOSITS = 10000;
|
|
uint256 public _nextHeaderBlock = MAX_DEPOSITS;
|
|
uint256 internal _blockDepositId = 1;
|
|
mapping(uint256 => HeaderBlock) public headerBlocks;
|
|
Registry internal registry;
|
|
}
|
|
|
|
contract TestRootChain is RootChainStorage, IRootChain {
|
|
using SafeMath for uint256;
|
|
using RLPReader for bytes;
|
|
using RLPReader for RLPReader.RLPItem;
|
|
|
|
modifier onlyDepositManager() {
|
|
require(msg.sender == registry.getDepositManagerAddress(), "UNAUTHORIZED_DEPOSIT_MANAGER_ONLY");
|
|
_;
|
|
}
|
|
|
|
function submitHeaderBlock(bytes calldata /*data*/, bytes calldata /*sigs*/) external pure {
|
|
revert();
|
|
}
|
|
|
|
function submitCheckpoint(bytes calldata data, uint[3][] calldata /*sigs*/) external {
|
|
(address proposer, uint256 start, uint256 end, bytes32 rootHash, bytes32 accountHash, uint256 borChainID) =
|
|
abi.decode(data, (address, uint256, uint256, bytes32, bytes32, uint256));
|
|
require(CHAINID == borChainID, "Invalid bor chain id");
|
|
|
|
require(_buildHeaderBlock(proposer, start, end, rootHash), "INCORRECT_HEADER_DATA");
|
|
|
|
// check if it is better to keep it in local storage instead
|
|
/*IStakeManager stakeManager = IStakeManager(registry.getStakeManagerAddress());
|
|
uint256 _reward = stakeManager.checkSignatures(
|
|
end.sub(start).add(1),
|
|
*//**
|
|
prefix 01 to data
|
|
01 represents positive vote on data and 00 is negative vote
|
|
malicious validator can try to send 2/3 on negative vote so 01 is appended
|
|
*//*
|
|
keccak256(abi.encodePacked(bytes(hex"01"), data)),
|
|
accountHash,
|
|
proposer,
|
|
sigs
|
|
);*/
|
|
|
|
//require(_reward != 0, "Invalid checkpoint");
|
|
emit NewHeaderBlock(proposer, _nextHeaderBlock, 0 /*_reward*/, start, end, rootHash);
|
|
_nextHeaderBlock = _nextHeaderBlock.add(MAX_DEPOSITS);
|
|
_blockDepositId = 1;
|
|
}
|
|
|
|
function updateDepositId(uint256 numDeposits) external onlyDepositManager returns (uint256 depositId) {
|
|
depositId = currentHeaderBlock().add(_blockDepositId);
|
|
// deposit ids will be (_blockDepositId, _blockDepositId + 1, .... _blockDepositId + numDeposits - 1)
|
|
_blockDepositId = _blockDepositId.add(numDeposits);
|
|
require(
|
|
// Since _blockDepositId is initialized to 1; only (MAX_DEPOSITS - 1) deposits per header block are allowed
|
|
_blockDepositId <= MAX_DEPOSITS,
|
|
"TOO_MANY_DEPOSITS"
|
|
);
|
|
}
|
|
|
|
function getLastChildBlock() external view returns (uint256) {
|
|
return headerBlocks[currentHeaderBlock()].end;
|
|
}
|
|
|
|
function slash() external {
|
|
//TODO: future implementation
|
|
}
|
|
|
|
function currentHeaderBlock() public view returns (uint256) {
|
|
return _nextHeaderBlock.sub(MAX_DEPOSITS);
|
|
}
|
|
|
|
function _buildHeaderBlock(
|
|
address proposer,
|
|
uint256 start,
|
|
uint256 end,
|
|
bytes32 rootHash
|
|
) private returns (bool) {
|
|
uint256 nextChildBlock;
|
|
/*
|
|
The ID of the 1st header block is MAX_DEPOSITS.
|
|
if _nextHeaderBlock == MAX_DEPOSITS, then the first header block is yet to be submitted, hence nextChildBlock = 0
|
|
*/
|
|
if (_nextHeaderBlock > MAX_DEPOSITS) {
|
|
nextChildBlock = headerBlocks[currentHeaderBlock()].end + 1;
|
|
}
|
|
if (nextChildBlock != start) {
|
|
return false;
|
|
}
|
|
|
|
HeaderBlock memory headerBlock = HeaderBlock({
|
|
root: rootHash,
|
|
start: nextChildBlock,
|
|
end: end,
|
|
createdAt: block.timestamp,
|
|
proposer: proposer
|
|
});
|
|
|
|
headerBlocks[_nextHeaderBlock] = headerBlock;
|
|
return true;
|
|
}
|
|
|
|
// Housekeeping function. @todo remove later
|
|
function setNextHeaderBlock(uint256 _value) public /*onlyOwner*/ {
|
|
require(_value % MAX_DEPOSITS == 0, "Invalid value");
|
|
for (uint256 i = _value; i < _nextHeaderBlock; i += MAX_DEPOSITS) {
|
|
delete headerBlocks[i];
|
|
}
|
|
_nextHeaderBlock = _value;
|
|
_blockDepositId = 1;
|
|
emit ResetHeaderBlock(msg.sender, _nextHeaderBlock);
|
|
}
|
|
|
|
// Housekeeping function. @todo remove later
|
|
function setHeimdallId(string memory _heimdallId) public /*onlyOwner*/ {
|
|
heimdallId = keccak256(abi.encodePacked(_heimdallId));
|
|
}
|
|
}
|