Skip to content
This repository was archived by the owner on Mar 1, 2024. It is now read-only.

Contract state migration #463

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
19 changes: 19 additions & 0 deletions contracts/root/RootChain.sol
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,25 @@ contract RootChain is RootChainStorage, IRootChain {
emit ResetHeaderBlock(msg.sender, _nextHeaderBlock);
}

function overrideHeaderBlock(
uint256 _headerBlockId,
address _proposer,
uint256 _start,
uint256 _end,
bytes32 _rootHash) public onlyOwner {
_nextHeaderBlock = _headerBlockId;

HeaderBlock memory headerBlock = HeaderBlock({
root: _rootHash,
start: _start,
end: _end,
createdAt: now,
proposer: _proposer
});

headerBlocks[currentHeaderBlock()] = headerBlock;
}

// Housekeeping function. @todo remove later
function setHeimdallId(string memory _heimdallId) public onlyOwner {
heimdallId = keccak256(abi.encodePacked(_heimdallId));
Expand Down
4 changes: 4 additions & 0 deletions contracts/root/stateSyncer/StateSender.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ contract StateSender is Ownable {
emit StateSynced(counter, receiver, data);
}

function setCounter(uint256 _counter) public onlyOwner {
counter = _counter;
}

// register new contract for state sync
function register(address sender, address receiver) public {
require(
Expand Down
22 changes: 22 additions & 0 deletions scripts/migrate-states/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Scripts that read contract states from a chain and write the states to another chain.

Pre-requisite: contracts are already deployed on both chains.

Steps:

1. Modify contract address in the script.
2. Run the script with custom environment variables.

Usage example
```bash
PRIVATE_KEY="0x..." NEW_CHAIN_ID=1337 npm run truffle exec scripts/migrate-states/rootChain.js
```

Environment variables:

```
PRIVATE_KEY: The private key of the account that will be used to send transactions.
NEW_CHAIN_ID: The chain id of the new chain.
OLD_CHAIN_PROVIDER: The provider of the old chain. Default: http://localhost:9546
NEW_CHAIN_PROVIDER: The provider of the new chain. Default: http://localhost:9545
```
58 changes: 58 additions & 0 deletions scripts/migrate-states/rootChain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const Web3 = require('web3');
const { oldChain, newChain, fromAccount, privateKey, newChainId } = require('./util');

const contractJson = require('../../build/contracts/RootChain.json');
const abi = contractJson.abi;

const contractAddress1 = '0x47bf9dc50D5D8d676AFBE714766dFF84C1828AE9'; // RootChain proxy contract address on Chain 1
const contractAddress2 = '0xcB6cb787A98448C586F2f74d55f1B3d0EA29F1e7'; // RootChain proxy contract address on Chain 2

const oldContract = new oldChain.eth.Contract(abi, contractAddress1);
const newContract = new newChain.eth.Contract(abi, contractAddress2);

module.exports = async function (callback) {
try {
// Read internal state from RootChain contract on Chain 1
const currentHeaderBlock = await oldContract.methods.currentHeaderBlock().call();
const headerBlock = await oldContract.methods.headerBlocks(currentHeaderBlock).call();

console.log(`Current header block on old chain: ${currentHeaderBlock}`)
console.log(`Current header block. Start: ${headerBlock.start}, end: ${headerBlock.end}`);

// Write the state to RootChain contract on Chain 2
const gasPrice = await newChain.eth.getGasPrice();

// Call overrideHeaderBlock
const overrideHeaderBlockTx = newContract.methods.overrideHeaderBlock(
parseInt(currentHeaderBlock) + 10000,
headerBlock.proposer,
headerBlock.start,
headerBlock.end,
headerBlock.root
);
const gasLimit = await overrideHeaderBlockTx.estimateGas({ from: fromAccount });

const txData = {
to: contractAddress2,
data: overrideHeaderBlockTx.encodeABI(),
gas: gasLimit,
gasPrice: gasPrice,
chainId: newChainId,
};

const signedTx = await newChain.eth.accounts.signTransaction(txData, privateKey);
const receipt = await newChain.eth.sendSignedTransaction(signedTx.rawTransaction);

console.log(`OverrideHeaderBlock transaction receipt: ${JSON.stringify(receipt)}`);

const currentHeaderBlockNewChain = parseInt(await newContract.methods.currentHeaderBlock().call());
console.log(`Current header block on new chain: ${currentHeaderBlockNewChain}`);
const headerBlockNewChain = await newContract.methods.headerBlocks(currentHeaderBlockNewChain).call();

console.log(`Current header block: Start: ${headerBlockNewChain.start}, end: ${headerBlockNewChain.end}`);
} catch (e) {
console.log(e);
}

callback();
};
62 changes: 62 additions & 0 deletions scripts/migrate-states/stakingInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const Web3 = require('web3');
const { oldChain, newChain, fromAccount, privateKey, newChainId } = require('./util');

const contractJson = require('../../build/contracts/StakingInfo.json');
const abi = contractJson.abi;

const contractAddress1 = '0x88B97C24099185FFa54DC8e78293B9D7a60463c2'; // StakingInfo contract address on Chain 1
const contractAddress2 = '0x88B97C24099185FFa54DC8e78293B9D7a60463c2'; // StakingInfo contract address on Chain 2

const oldContract = new oldChain.eth.Contract(abi, contractAddress1);
const newContract = new newChain.eth.Contract(abi, contractAddress2);

module.exports = async function (callback) {
try {

const validatorIds = [];
const nonces = [];

for (let validatorId = 1; validatorId <= 150; validatorId++) {
const nonce = await oldContract.methods.validatorNonce(validatorId).call();
if (nonce === '0' || nonce === 0) {
continue;
}
validatorIds.push(validatorId);
nonces.push(nonce);
}

console.log('validatorIds', validatorIds);
console.log('nonces', nonces);

const tx = newContract.methods.updateNonce(validatorIds, nonces);
const gasPrice = await newChain.eth.getGasPrice();
const gasLimit = await tx.estimateGas({ from: fromAccount });

const txData = {
to: contractAddress2,
data: tx.encodeABI(),
gas: gasLimit,
gasPrice: gasPrice,
chainId: newChainId,
};

const signedTx = await newChain.eth.accounts.signTransaction(txData, privateKey);
const receipt = await newChain.eth.sendSignedTransaction(signedTx.rawTransaction);

console.log(`Transaction receipt: ${JSON.stringify(receipt)}`);
for (let i = 0; i < validatorIds.length; i++) {
const validatorId = validatorIds[i];
const nonce = nonces[i];
const newNonce = await newContract.methods.validatorNonce(validatorId).call();
if (newNonce !== nonce) {
throw new Error(`Nonce for validator ${validatorId} is not updated. Expected ${nonce}, got ${newNonce}`);
}
}

console.log('All nonces are updated');
} catch (e) {
console.log(e);
}

callback();
};
42 changes: 42 additions & 0 deletions scripts/migrate-states/stateSync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const Web3 = require('web3');
const { oldChain, newChain, fromAccount, privateKey, newChainId } = require('./util');

const contractJson = require('../../build/contracts/StateSender.json');
const abi = contractJson.abi;

const contractAddress1 = '0x6E3DfF195E068A6dDc24D845014469D03bF17cb9'; // StateSender contract address on Chain 1
const contractAddress2 = '0x6E3DfF195E068A6dDc24D845014469D03bF17cb9'; // StateSender contract address on Chain 2

const oldContract = new oldChain.eth.Contract(abi, contractAddress1);
const newContract = new newChain.eth.Contract(abi, contractAddress2);

module.exports = async function (callback) {
try {
// Read internal state from StateSender contract on Chain 1
const counter = await oldContract.methods.counter().call();
console.log(`Counter value: ${counter}`);

// Write the state to StateSender contract on Chain 2
const gasPrice = await newChain.eth.getGasPrice();
const setCounterTx = newContract.methods.setCounter(counter);
const gasLimit = await setCounterTx.estimateGas({ from: fromAccount });

const txData = {
to: contractAddress2,
data: setCounterTx.encodeABI(),
gas: gasLimit,
chainId: newChainId,
};

const signedTx = await newChain.eth.accounts.signTransaction(txData, privateKey);
const receipt = await newChain.eth.sendSignedTransaction(signedTx.rawTransaction);

console.log(`Transaction receipt: ${JSON.stringify(receipt)}`);
const newCounter = await newContract.methods.counter().call();
console.log(`Counter value for new contract: ${newCounter}`);
} catch (e) {
console.log(e);
}

callback();
};
18 changes: 18 additions & 0 deletions scripts/migrate-states/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const Web3 = require('web3');

const oldChainProvider = new Web3.providers.HttpProvider(process.env.OLD_CHAIN_PROVIDER || 'http://localhost:9546');
const newChainProvider = new Web3.providers.HttpProvider(process.env.NEW_CHAIN_PROVIDER || 'http://localhost:9545');

const oldChain = new Web3(oldChainProvider);
const newChain = new Web3(newChainProvider);

const privateKey = process.env.PRIVATE_KEY; // Private key of the account that will call the functions
const fromAccount = newChain.eth.accounts.privateKeyToAccount(privateKey).address;

const newChainId = process.env.NEW_CHAIN_ID || 11155111; // Chain ID of sepolia

exports.oldChain = oldChain;
exports.newChain = newChain;
exports.fromAccount = fromAccount;
exports.privateKey = privateKey;
exports.newChainId = newChainId;