Last active
April 13, 2022 09:54
-
-
Save Rizary/a97fa78206248f8d0a4157ea9fdc70d8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const rlp = require('rlp'); | |
const headerData = require('./headers.json'); | |
const transactions = require('./transaction.json'); | |
const { rpcWrapper, getReceiptProof } = require('../scripts/utils'); | |
const { expect } = require('chai'); | |
let MMRVerifier, HarmonyProver; | |
let prover, mmrVerifier; | |
function hexToBytes(hex) { | |
for (var bytes = [], c = 0; c < hex.length; c += 2) | |
bytes.push(parseInt(hex.substr(c, 2), 16)); | |
return bytes; | |
} | |
describe('HarmonyProver', function () { | |
// Rizary: this is where the contract deployed. | |
beforeEach(async function () { | |
MMRVerifier = await ethers.getContractFactory("MMRVerifier"); | |
mmrVerifier = await MMRVerifier.deploy(); | |
await mmrVerifier.deployed(); | |
// await HarmonyProver.link('MMRVerifier', mmrVerifier); | |
HarmonyProver = await ethers.getContractFactory( | |
"HarmonyProver", | |
{ | |
libraries: { | |
MMRVerifier: mmrVerifier.address | |
} | |
} | |
); | |
prover = await HarmonyProver.deploy(); | |
await prover.deployed(); | |
}); | |
// Rizary: doing test on parse the RLP encoded block header | |
it('parse rlp block header', async function () { | |
let header = await prover.toBlockHeader(hexToBytes(headerData.rlpheader)); | |
expect(header.hash).to.equal(headerData.hash); | |
}); | |
// Rizary: test parsing the transaction receipt proof | |
it('parse transaction receipt proof', async function () { | |
let callback = getReceiptProof; | |
let callbackArgs = [ | |
process.env.LOCALNET, | |
prover, | |
transactions.hash | |
]; | |
let isTxn = true; | |
let txProof = await rpcWrapper( | |
transactions.hash, | |
isTxn, | |
callback, | |
callbackArgs | |
); | |
console.log(txProof); | |
expect(txProof.header.hash).to.equal(transactions.header); | |
// let response = await prover.getBlockRlpData(txProof.header); | |
// console.log(response); | |
// let res = await test.bar([123, "abc", "0xD6dDd996B2d5B7DB22306654FD548bA2A58693AC"]); | |
// // console.log(res); | |
}); | |
}); | |
let TokenLockerOnEthereum, tokenLocker; | |
let HarmonyLightClient, lightclient; | |
describe('TokenLocker', function () { | |
beforeEach(async function () { | |
// Rizary: Deploy the contract and bind the TokenLockerOnEthereum contract | |
TokenLockerOnEthereum = await ethers.getContractFactory("TokenLockerOnEthereum"); | |
tokenLocker = await MMRVerifier.deploy(); | |
await tokenLocker.deployed(); | |
await tokenLocker.bind(tokenLocker.address); | |
// // await HarmonyProver.link('MMRVerifier', mmrVerifier); | |
// HarmonyProver = await ethers.getContractFactory( | |
// "HarmonyProver", | |
// { | |
// libraries: { | |
// MMRVerifier: mmrVerifier.address | |
// } | |
// } | |
// ); | |
// prover = await HarmonyProver.deploy(); | |
// await prover.deployed(); | |
}); | |
it('issue map token test', async function () { | |
}); | |
it('lock test', async function () { | |
}); | |
it('unlock test', async function () { | |
}); | |
it('light client upgrade test', async function () { | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity 0.7.3; | |
pragma experimental ABIEncoderV2; | |
import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; | |
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; | |
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; | |
import "./EthereumParser.sol"; | |
import "./lib/EthUtils.sol"; | |
import "./ethash/ethash.sol"; | |
/// Rizary: this is ethereum light client that will be deployed on harmony. | |
contract EthereumLightClient is Ethash, Initializable, PausableUpgradeable { | |
using SafeMathUpgradeable for uint256; | |
// R: Ethereum stored block header | |
struct StoredBlockHeader { | |
uint256 parentHash; | |
uint256 stateRoot; | |
uint256 transactionsRoot; | |
uint256 receiptsRoot; | |
uint256 number; | |
uint256 difficulty; | |
uint256 time; | |
uint256 hash; | |
} | |
struct HeaderInfo { | |
uint256 total_difficulty; | |
bytes32 parent_hash; | |
uint64 number; | |
} | |
// The first block header hash | |
uint256 public firstBlock; | |
// Blocks data, in the form: blockHeaderHash => BlockHeader | |
mapping(uint256 => StoredBlockHeader) public blocks; | |
// Block existing map, in the form: blockHeaderHash => bool | |
mapping(uint256 => bool) public blockExisting; | |
// Blocks in 'Verified' state | |
mapping(uint256 => bool) public verifiedBlocks; | |
// Blocks in 'Finalized' state | |
mapping(uint256 => bool) public finalizedBlocks; | |
// Valid relayed blocks for a block height, in the form: blockNumber => blockHeaderHash[] | |
mapping(uint256 => uint256[]) public blocksByHeight; | |
// Block height existing map, in the form: blockNumber => bool | |
mapping(uint256 => bool) public blocksByHeightExisting; | |
// Max block height stored | |
uint256 public blockHeightMax; | |
// Block header hash that points to longest chain head | |
// (please note that 'longest' chain is based on total difficulty) | |
// uint public longestChainHead; | |
// Longest branch head of each checkpoint, in the form: (checkpoint block hash) => (head block hash) | |
// (note that 'longest branch' means the branch which has biggest cumulative difficulty from checkpoint) | |
mapping(uint256 => uint256) public longestBranchHead; | |
uint256 private constant DEFAULT_FINALITY_CONFIRMS = 13; | |
uint256 public finalityConfirms; | |
/// R: This is the initialization function that takes RLP block header | |
function initialize(bytes memory _rlpHeader) external initializer { | |
finalityConfirms = DEFAULT_FINALITY_CONFIRMS; | |
uint256 blockHash = EthereumParser.calcBlockHeaderHash(_rlpHeader); | |
// Parse rlp-encoded block header into structure | |
EthereumParser.BlockHeader memory header = EthereumParser | |
.parseBlockHeader(_rlpHeader); | |
// Save block header info | |
StoredBlockHeader memory storedBlock = StoredBlockHeader({ | |
parentHash: header.parentHash, | |
stateRoot: header.stateRoot, | |
transactionsRoot: header.transactionsRoot, | |
receiptsRoot: header.receiptsRoot, | |
number: header.number, | |
difficulty: header.difficulty, | |
time: header.timestamp, | |
hash: blockHash | |
}); | |
_setFirstBlock(storedBlock); | |
} | |
//uint32 constant loopAccesses = 64; // Number of accesses in hashimoto loop | |
/// Rizary: this function store an Ethereum block header in this contract | |
function addBlockHeader( | |
bytes memory _rlpHeader, | |
bytes32[4][loopAccesses] memory cache, | |
bytes32[][loopAccesses] memory proofs | |
) public whenNotPaused returns (bool) { | |
// Calculate block header hash | |
uint256 blockHash = EthereumParser.calcBlockHeaderHash(_rlpHeader); | |
// Check block existing | |
require( | |
!blockExisting[blockHash], | |
"Relay block failed: block already relayed" | |
); | |
// Parse rlp-encoded block header into structure | |
EthereumParser.BlockHeader memory header = EthereumParser | |
.parseBlockHeader(_rlpHeader); | |
// Check the existence of parent block | |
require( | |
blockExisting[header.parentHash], | |
"Relay block failed: parent block not relayed yet" | |
); | |
// Check block height | |
require( | |
header.number == blocks[header.parentHash].number.add(1), | |
"Relay block failed: invalid block blockHeightMax" | |
); | |
// Check timestamp | |
require( | |
header.timestamp > blocks[header.parentHash].time, | |
"Relay block failed: invalid timestamp" | |
); | |
// Check difficulty | |
require( | |
_checkDiffValidity( | |
header.difficulty, | |
blocks[header.parentHash].difficulty | |
), | |
"Relay block failed: invalid difficulty" | |
); | |
// Verify block PoW | |
uint256 sealHash = EthereumParser.calcBlockSealHash(_rlpHeader); | |
bool rVerified = verifyEthash( | |
bytes32(sealHash), | |
uint64(header.nonce), | |
uint64(header.number), | |
cache, | |
proofs, | |
header.difficulty, | |
header.mixHash | |
); | |
require(rVerified, "Relay block failed: invalid PoW"); | |
// Save block header info | |
StoredBlockHeader memory storedBlock = StoredBlockHeader({ | |
parentHash: header.parentHash, | |
stateRoot: header.stateRoot, | |
transactionsRoot: header.transactionsRoot, | |
receiptsRoot: header.receiptsRoot, | |
number: header.number, | |
difficulty: header.difficulty, | |
time: header.timestamp, | |
hash: blockHash | |
}); | |
blocks[blockHash] = storedBlock; | |
blockExisting[blockHash] = true; | |
// verifiedBlocks[blockHash] = true; | |
blocksByHeight[header.number].push(blockHash); | |
blocksByHeightExisting[header.number] = true; | |
if (header.number > blockHeightMax) { | |
blockHeightMax = header.number; | |
} | |
// Return true if success | |
return true; | |
} | |
function getBlockHeightMax() public view returns (uint256) { | |
return blockHeightMax; | |
} | |
function getStateRoot(bytes32 blockHash) public view returns (bytes32) { | |
return bytes32(blocks[uint256(blockHash)].stateRoot); | |
} | |
function getTxRoot(bytes32 blockHash) public view returns (bytes32) { | |
return bytes32(blocks[uint256(blockHash)].transactionsRoot); | |
} | |
function getReceiptRoot(bytes32 blockHash) public view returns (bytes32) { | |
return bytes32(blocks[uint256(blockHash)].receiptsRoot); | |
} | |
function VerifyReceiptsHash(bytes32 blockHash, bytes32 receiptsHash) | |
external | |
view | |
returns (bool) | |
{ | |
return bytes32(blocks[uint256(blockHash)].receiptsRoot) == receiptsHash; | |
} | |
// Check the difficulty of block is valid or not | |
// (the block difficulty adjustment is described here: https://github.com/ethereum/EIPs/issues/100) | |
// Note that this is only 'minimal check' because we do not have 'block uncles' information to calculate exactly. | |
// 'Minimal check' is enough to prevent someone from spamming relaying blocks with quite small difficulties | |
function _checkDiffValidity(uint256 diff, uint256 parentDiff) | |
private | |
pure | |
returns (bool) | |
{ | |
return diff >= parentDiff.sub((parentDiff / 10000) * 99); | |
} | |
// Store first block header provided in initialize | |
function _setFirstBlock(StoredBlockHeader memory toSetBlock) private { | |
firstBlock = toSetBlock.hash; | |
blocks[toSetBlock.hash] = toSetBlock; | |
blockExisting[toSetBlock.hash] = true; | |
verifiedBlocks[toSetBlock.hash] = true; | |
finalizedBlocks[toSetBlock.hash] = true; | |
blocksByHeight[toSetBlock.number].push(toSetBlock.hash); | |
blocksByHeightExisting[toSetBlock.number] = true; | |
blockHeightMax = toSetBlock.number; | |
longestBranchHead[toSetBlock.hash] = toSetBlock.hash; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity 0.7.3; | |
pragma experimental ABIEncoderV2; | |
import "./HarmonyParser.sol"; | |
import "./lib/SafeCast.sol"; | |
import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; | |
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; | |
// import "openzeppelin-solidity/contracts/utils/Pausable.sol"; | |
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; | |
// import "openzeppelin-solidity/contracts/proxy/Initializable.sol"; | |
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; | |
/// Rizary: this is harmony light client that will be deployed on ethereum. | |
contract HarmonyLightClient is | |
Initializable, | |
PausableUpgradeable, | |
AccessControlUpgradeable | |
{ | |
using SafeCast for *; | |
using SafeMathUpgradeable for uint256; | |
// Harmony block header | |
struct BlockHeader { | |
bytes32 parentHash; | |
bytes32 stateRoot; | |
bytes32 transactionsRoot; | |
bytes32 receiptsRoot; | |
uint256 number; | |
uint256 epoch; | |
uint256 shard; | |
uint256 time; | |
bytes32 mmrRoot; // Merkle Mountain Range Root, for verifying block inclusion in block tree (i.e. existence in chain) | |
bytes32 hash; | |
} | |
/// R: Event of a checkpoint block header stored | |
event CheckPoint( | |
bytes32 stateRoot, | |
bytes32 transactionsRoot, | |
bytes32 receiptsRoot, | |
uint256 number, | |
uint256 epoch, | |
uint256 shard, | |
uint256 time, | |
bytes32 mmrRoot, | |
bytes32 hash | |
); | |
BlockHeader firstBlock; | |
BlockHeader lastCheckPointBlock; | |
// epoch to block numbers, as there could be >=1 mmr entries per epoch | |
mapping(uint256 => uint256[]) epochCheckPointBlockNumbers; | |
// block number to BlockHeader | |
mapping(uint256 => BlockHeader) checkPointBlocks; | |
mapping(uint256 => mapping(bytes32 => bool)) epochMmrRoots; | |
uint8 relayerThreshold; // max number of relayers allowed | |
event RelayerThresholdChanged(uint256 newThreshold); | |
event RelayerAdded(address relayer); | |
event RelayerRemoved(address relayer); | |
bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE"); | |
modifier onlyAdmin() { | |
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "sender doesn't have admin role"); | |
_; | |
} | |
modifier onlyRelayers() { | |
require(hasRole(RELAYER_ROLE, msg.sender), "sender doesn't have relayer role"); | |
_; | |
} | |
function adminPauseLightClient() external onlyAdmin { | |
_pause(); | |
} | |
function adminUnpauseLightClient() external onlyAdmin { | |
_unpause(); | |
} | |
function renounceAdmin(address newAdmin) external onlyAdmin { | |
require(msg.sender != newAdmin, 'cannot renounce self'); | |
grantRole(DEFAULT_ADMIN_ROLE, newAdmin); | |
renounceRole(DEFAULT_ADMIN_ROLE, msg.sender); | |
} | |
function adminChangeRelayerThreshold(uint256 newThreshold) external onlyAdmin { | |
relayerThreshold = newThreshold.toUint8(); | |
emit RelayerThresholdChanged(newThreshold); | |
} | |
function adminAddRelayer(address relayerAddress) external onlyAdmin { | |
require(!hasRole(RELAYER_ROLE, relayerAddress), "addr already has relayer role!"); | |
grantRole(RELAYER_ROLE, relayerAddress); | |
emit RelayerAdded(relayerAddress); | |
} | |
function adminRemoveRelayer(address relayerAddress) external onlyAdmin { | |
require(hasRole(RELAYER_ROLE, relayerAddress), "addr doesn't have relayer role!"); | |
revokeRole(RELAYER_ROLE, relayerAddress); | |
emit RelayerRemoved(relayerAddress); | |
} | |
/// R: This is the initialization function that takes the first RLP block header, initial relayer address, | |
/// and initial relayer threshold allowed. | |
function initialize( | |
bytes memory firstRlpHeader, | |
address[] memory initialRelayers, | |
uint8 initialRelayerThreshold | |
) external initializer { | |
// Parse firstRlpHeader to firstBlock | |
HarmonyParser.BlockHeader memory header = HarmonyParser.toBlockHeader( | |
firstRlpHeader | |
); | |
firstBlock.parentHash = header.parentHash; | |
firstBlock.stateRoot = header.stateRoot; | |
firstBlock.transactionsRoot = header.transactionsRoot; | |
firstBlock.receiptsRoot = header.receiptsRoot; | |
firstBlock.number = header.number; | |
firstBlock.epoch = header.epoch; | |
firstBlock.shard = header.shardID; | |
firstBlock.time = header.timestamp; | |
firstBlock.mmrRoot = HarmonyParser.toBytes32(header.mmrRoot); | |
firstBlock.hash = header.hash; | |
// Store firstBlock and update corresponding mappings | |
epochCheckPointBlockNumbers[header.epoch].push(header.number); | |
checkPointBlocks[header.number] = firstBlock; | |
epochMmrRoots[header.epoch][firstBlock.mmrRoot] = true; | |
// Permit relayers | |
relayerThreshold = initialRelayerThreshold; | |
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender); | |
for (uint256 i; i < initialRelayers.length; i++) { | |
grantRole(RELAYER_ROLE, initialRelayers[i]); | |
} | |
} | |
/// Rizary: this function store a Harmony checkpoint block header in this contract, called by permissioned relayers | |
function submitCheckpoint(bytes memory rlpHeader) external onlyRelayers whenNotPaused { | |
// Parse rlpHeader to checkPoint Block | |
HarmonyParser.BlockHeader memory header = HarmonyParser.toBlockHeader( | |
rlpHeader | |
); | |
BlockHeader memory checkPointBlock; | |
checkPointBlock.parentHash = header.parentHash; | |
checkPointBlock.stateRoot = header.stateRoot; | |
checkPointBlock.transactionsRoot = header.transactionsRoot; | |
checkPointBlock.receiptsRoot = header.receiptsRoot; | |
checkPointBlock.number = header.number; | |
checkPointBlock.epoch = header.epoch; | |
checkPointBlock.shard = header.shardID; | |
checkPointBlock.time = header.timestamp; | |
checkPointBlock.mmrRoot = HarmonyParser.toBytes32(header.mmrRoot); | |
checkPointBlock.hash = header.hash; | |
// Store checkPointBlock and update corresponding mappings | |
epochCheckPointBlockNumbers[header.epoch].push(header.number); | |
checkPointBlocks[header.number] = checkPointBlock; | |
epochMmrRoots[header.epoch][checkPointBlock.mmrRoot] = true; | |
// Emit event of the checkpoint block header stored | |
emit CheckPoint( | |
checkPointBlock.stateRoot, | |
checkPointBlock.transactionsRoot, | |
checkPointBlock.receiptsRoot, | |
checkPointBlock.number, | |
checkPointBlock.epoch, | |
checkPointBlock.shard, | |
checkPointBlock.time, | |
checkPointBlock.mmrRoot, | |
checkPointBlock.hash | |
); | |
} | |
/// Rizary: This function retrieve an epoch's latest checkpoint stored block header | |
function getLatestCheckPoint(uint256 blockNumber, uint256 epoch) | |
public | |
view | |
returns (BlockHeader memory checkPointBlock) | |
{ | |
// Check if any checkpoint block header is stored for the specified epoch | |
require( | |
epochCheckPointBlockNumbers[epoch].length > 0, | |
"no checkpoints for epoch" | |
); | |
// Get the epoch's latest checkpoint block header stored | |
uint256[] memory checkPointBlockNumbers = epochCheckPointBlockNumbers[epoch]; | |
uint256 nearest = 0; | |
for (uint256 i = 0; i < checkPointBlockNumbers.length; i++) { | |
uint256 checkPointBlockNumber = checkPointBlockNumbers[i]; | |
if ( | |
checkPointBlockNumber > blockNumber && | |
checkPointBlockNumber < nearest | |
) { | |
nearest = checkPointBlockNumber; | |
} | |
} | |
// Return result | |
checkPointBlock = checkPointBlocks[nearest]; | |
} | |
/// Rizary: This function check if an epoch has a checkpoint MMR Root stored in this contract | |
function isValidCheckPoint(uint256 epoch, bytes32 mmrRoot) public view returns (bool status) { | |
return epochMmrRoots[epoch][mmrRoot]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity 0.7.3; | |
pragma experimental ABIEncoderV2; | |
import "./HarmonyLightClient.sol"; | |
import "./lib/MMRVerifier.sol"; | |
import "./HarmonyProver.sol"; | |
import "./TokenLocker.sol"; | |
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; | |
/// Rizary: This contract inherit TokenLocker interface. | |
/// This is where the token on ethereum getting checked. | |
contract TokenLockerOnEthereum is TokenLocker, OwnableUpgradeable { | |
HarmonyLightClient public lightclient; | |
mapping(bytes32 => bool) public spentReceipt; | |
function initialize() external initializer { | |
__Ownable_init(); | |
} | |
function changeLightClient(HarmonyLightClient newClient) | |
external | |
onlyOwner | |
{ | |
lightclient = newClient; | |
} | |
function bind(address otherSide) external onlyOwner { | |
otherSideBridge = otherSide; | |
} | |
/// Rizary: This function try to validate and verify blockheader then execute the proof | |
function validateAndExecuteProof( | |
HarmonyParser.BlockHeader memory header, | |
MMRVerifier.MMRProof memory mmrProof, | |
MPT.MerkleProof memory receiptdata | |
) external { | |
/// R: 1. Check if the block header's epoch have a valid checkpoint in MMR root. | |
require(lightclient.isValidCheckPoint(header.epoch, mmrProof.root), "checkpoint validation failed"); | |
// R: 2. Get header block hash | |
bytes32 blockHash = HarmonyParser.getBlockHash(header); | |
// R: 3. Get root hash of header receipt transaction | |
bytes32 rootHash = header.receiptsRoot; | |
// R: 4. Verify the status of header exist by its MMR | |
(bool status, string memory message) = HarmonyProver.verifyHeader( | |
header, | |
mmrProof | |
); | |
require(status, "block header could not be verified"); | |
bytes32 receiptHash = keccak256( | |
abi.encodePacked(blockHash, rootHash, receiptdata.key) | |
); | |
// R: 5. Check if transaction is unspent | |
require(spentReceipt[receiptHash] == false, "double spent!"); | |
// R: 6. Verify receipt of header transaction by its data | |
(status, message) = HarmonyProver.verifyReceipt(header, receiptdata); | |
require(status, "receipt data could not be verified"); | |
// R: 7. Mark transaction as spent | |
spentReceipt[receiptHash] = true; | |
// R: 8. execute the receipt data | |
uint256 executedEvents = execute(receiptdata.expectedValue); | |
// R: 9. Check if executed action is performed | |
require(executedEvents > 0, "no valid event"); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity 0.7.3; | |
pragma experimental ABIEncoderV2; | |
import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; | |
import "./EthereumLightClient.sol"; | |
import "./EthereumProver.sol"; | |
import "./TokenLocker.sol"; | |
/// Rizary: This contract is deployed on Harmony. | |
contract TokenLockerOnHarmony is TokenLocker, OwnableUpgradeable { | |
using RLPReader for RLPReader.RLPItem; | |
using RLPReader for bytes; | |
using SafeMathUpgradeable for uint256; | |
using SafeERC20Upgradeable for IERC20Upgradeable; | |
EthereumLightClient public lightclient; | |
mapping(bytes32 => bool) public spentReceipt; | |
function initialize() external initializer { | |
__Ownable_init(); | |
} | |
function changeLightClient(EthereumLightClient newClient) | |
external | |
onlyOwner | |
{ | |
lightclient = newClient; | |
} | |
function bind(address otherSide) external onlyOwner { | |
otherSideBridge = otherSide; | |
} | |
/// Rizary: This function try to validate and verify blockheader then execute the proof | |
function validateAndExecuteProof( | |
uint256 blockNo, | |
bytes32 rootHash, | |
bytes calldata mptkey, | |
bytes calldata proof | |
) external { | |
// R: 1. Get header hash of block | |
bytes32 blockHash = bytes32(lightclient.blocksByHeight(blockNo, 0)); | |
// R: 2. Check if receiptsRoot of block blockHash in light client contract storage matches the rootHash provided | |
require( | |
lightclient.VerifyReceiptsHash(blockHash, rootHash), | |
"wrong receipt hash" | |
); | |
bytes32 receiptHash = keccak256( | |
abi.encodePacked(blockHash, rootHash, mptkey) | |
); | |
// R: 3. Check if transaction is unspent | |
require(spentReceipt[receiptHash] == false, "double spent!"); | |
// R: 4. validate the MPT using rootHash, MPT key and its proof. | |
bytes memory rlpdata = EthereumProver.validateMPTProof( | |
rootHash, | |
mptkey, | |
proof | |
); | |
// R: 5. Mark transaction as spent | |
spentReceipt[receiptHash] = true; | |
// R: 6. execute the receipt data | |
uint256 executedEvents = execute(rlpdata); | |
// R: 7. Check if executed action is performed | |
require(executedEvents > 0, "no valid event"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment