add hardhat scripts for common tasks (#37)
* feat: add hh task for deploy replica * feat: add a reportTxOutcome utils function * ref: add convenience functions to optics. allow signer passing * feat: scripts for submit-update and submit-double-update * feat: add scripts for processing messages * feat: change errors to use console.error * refactor: add utils.validateUpdate to hh tasks * fix: change to the correct new call sig for double update * fix: update hh task to conform to new proveAndProcess api * doc: slightly improve hh task docstrings for processing * feature: add a hh task to enqueue messagesbuddies-main-deployment
parent
319ebd4369
commit
d4e36f6373
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,63 @@ |
||||
// const { hre } = require('hardhat');
|
||||
const { types } = require('hardhat/config'); |
||||
const { types, task } = require('hardhat/config'); |
||||
const utils = require('./utils.js'); |
||||
|
||||
task('deploy-home') |
||||
task('deploy-home', 'Deploy a home.') |
||||
.addParam('slip44', 'The origin chain SLIP44 ID', undefined, types.int) |
||||
.addParam('updater', 'The origin chain updater', undefined, types.string) |
||||
.addParam('currentRoot', 'The current root') |
||||
.setAction(async (args) => {}); |
||||
.addParam( |
||||
'sortition', |
||||
'The updater identity handler', |
||||
undefined, |
||||
types.string, |
||||
) |
||||
.setAction(async (args) => { |
||||
let address = ethers.utils.getAddress(args.sortition); |
||||
let signer = ethers.getSigner(); |
||||
let home = await optics.deployHome(signer, args.slip44, address); |
||||
console.log(home.address); |
||||
}); |
||||
|
||||
task('deploy-replica', 'Deploy a replica.') |
||||
.addParam('origin', 'The origin chain SLIP44 ID', undefined, types.int) |
||||
.addParam( |
||||
'destination', |
||||
'The destination chain SLIP44 ID', |
||||
undefined, |
||||
types.int, |
||||
) |
||||
.addParam('updater', 'The address of the updater', undefined, types.string) |
||||
.addOptionalParam( |
||||
'wait', |
||||
'The optimistic wait period in seconds', |
||||
60 * 60 * 2, // 2 hours
|
||||
types.int, |
||||
) |
||||
.addOptionalParam( |
||||
'current', |
||||
'The current root to init with', |
||||
`0x${'00'.repeat(32)}`, |
||||
types.string, |
||||
) |
||||
.addOptionalParam( |
||||
'lastProcessed', |
||||
'The last processed message sequence', |
||||
0, |
||||
types.int, |
||||
) |
||||
.setAction(async (args) => { |
||||
let updater = ethers.utils.getAddress(args.updater); |
||||
if (!ethers.utils.isHexString(args.current, 32)) { |
||||
throw new Error('current must be a 32-byte 0x prefixed hex string'); |
||||
} |
||||
|
||||
let signer = ethers.getSigner(); |
||||
|
||||
await optics.deployReplica( |
||||
signer, |
||||
args.origin, |
||||
args.destination, |
||||
updater, |
||||
args.wait, |
||||
args.current, |
||||
args.lastProcessed, |
||||
); |
||||
}); |
||||
|
@ -1 +1,3 @@ |
||||
require('./deploy.js'); |
||||
require('./process.js'); |
||||
require('./deploy.js'); |
||||
|
@ -0,0 +1,151 @@ |
||||
const ethers = require('ethers'); |
||||
const { types, task } = require('hardhat/config'); |
||||
const utils = require('./utils.js'); |
||||
|
||||
task('prove', 'Prove a message inclusion to a replica') |
||||
.addParam( |
||||
'address', |
||||
'The address of the replica contract.', |
||||
undefined, |
||||
types.string, |
||||
) |
||||
.addParam('message', 'The message to prove.', undefined, types.string) |
||||
.addParam( |
||||
'proof', |
||||
'The 32 * 32 byte proof as a single hex string', |
||||
undefined, |
||||
types.string, |
||||
) |
||||
.addParam( |
||||
'index', |
||||
'The index of the message in the merkle tree', |
||||
undefined, |
||||
types.int, |
||||
) |
||||
.setAction(async (args) => { |
||||
let address = ethers.utils.getAddress(args.address); |
||||
let { rawProof, message, index } = args; |
||||
let proof = utils.parseProof(rawProof); |
||||
|
||||
if (!ethers.utils.isHexString(message)) { |
||||
throw new Error('newRoot must be a 0x prefixed hex string'); |
||||
} |
||||
|
||||
let signer = await ethers.getSigner(); |
||||
let replica = new optics.Replica(address, signer); |
||||
|
||||
// preflight
|
||||
if ( |
||||
await replica.callStatic.prove( |
||||
ethers.utils.keccak256(message), |
||||
proof, |
||||
index, |
||||
) |
||||
) { |
||||
let tx = await replica.prove( |
||||
ethers.utils.keccak256(message), |
||||
proof, |
||||
index, |
||||
); |
||||
await utils.reportTxOutcome(tx); |
||||
} else { |
||||
console.log('Error: Replica will reject proof'); |
||||
} |
||||
}); |
||||
|
||||
task('process', 'Process a message that has been proven to a replica') |
||||
.addParam( |
||||
'address', |
||||
'The address of the replica contract.', |
||||
undefined, |
||||
types.string, |
||||
) |
||||
.addParam('message', 'The message to prove.', undefined, types.string) |
||||
.setAction(async (args) => { |
||||
let address = ethers.utils.getAddress(args.address); |
||||
let { message } = args; |
||||
if (!ethers.utils.isHexString(message)) { |
||||
throw new Error('newRoot must be a 0x prefixed hex string'); |
||||
} |
||||
|
||||
let signer = await ethers.getSigner(); |
||||
let replica = new optics.Replica(address, signer); |
||||
|
||||
try { |
||||
await replica.callStatic.process(message); |
||||
let tx = await replica.process(message); |
||||
await utils.reportTxOutcome(tx); |
||||
} catch (e) { |
||||
console.error( |
||||
`Error: Replica will reject process with message\n\t${e.message}`, |
||||
); |
||||
} |
||||
}); |
||||
|
||||
task('prove-and-process', 'Prove and process a message') |
||||
.addParam( |
||||
'address', |
||||
'The address of the replica contract.', |
||||
undefined, |
||||
types.string, |
||||
) |
||||
.addParam('message', 'The message to prove.', undefined, types.string) |
||||
.addParam( |
||||
'proof', |
||||
'The 32 * 32 byte proof as a single hex string', |
||||
undefined, |
||||
types.string, |
||||
) |
||||
.addParam( |
||||
'index', |
||||
'The index of the message in the merkle tree', |
||||
undefined, |
||||
types.int, |
||||
) |
||||
.setAction(async (args) => { |
||||
let address = ethers.utils.getAddress(args.address); |
||||
let { rawProof, message, index } = args; |
||||
let proof = utils.parseProof(rawProof); |
||||
|
||||
if (!ethers.utils.isHexString(message)) { |
||||
throw new Error('message must be a 0x prefixed hex string'); |
||||
} |
||||
|
||||
let signer = await ethers.getSigner(); |
||||
let replica = new optics.Replica(address, signer); |
||||
|
||||
try { |
||||
// preflight and make sure it works. This throws on revert
|
||||
await replica.callStatic.proveAndProcess(message, proof, index); |
||||
await replica.proveAndProcess(message, proof, index); |
||||
} catch (e) { |
||||
console.error( |
||||
`Error: Replica will reject proveAndProcess with message\n\t${e.message}`, |
||||
); |
||||
} |
||||
}); |
||||
|
||||
task('enqueue', 'Enqueue a message on the Home chain') |
||||
.addParam( |
||||
'address', |
||||
'The address of the replica contract.', |
||||
undefined, |
||||
types.string, |
||||
) |
||||
.addParam('destination', 'The destination chain.', undefined, types.int) |
||||
.addParam('recipient', 'The message recipient.', undefined, types.string) |
||||
.addParam('body', 'The message body.', undefined, types.string) |
||||
.setAction(async (args) => { |
||||
let address = ethers.utils.getAddress(args.address); |
||||
let { destination, recipient, body } = args; |
||||
|
||||
ethers.utils.isHexString(recipient, 32); |
||||
if (!ethers.utils.isHexString(message)) { |
||||
throw new Error('body must be a 0x prefixed hex string'); |
||||
} |
||||
|
||||
let home = new optics.Home(address, signer); |
||||
|
||||
let tx = await home.enqueue(destination, recipient, body); |
||||
await utils.reportTxOutcome(tx); |
||||
}); |
@ -0,0 +1,59 @@ |
||||
const { types, task } = require('hardhat/config'); |
||||
const utils = require('./utils.js'); |
||||
|
||||
task('submit-update', 'Submit an update to a home or replica contract.') |
||||
.addParam( |
||||
'address', |
||||
'The address of the contract to update.', |
||||
undefined, |
||||
types.string, |
||||
) |
||||
.addParam('oldRoot', 'The old root', undefined, types.string) |
||||
.addParam('newRoot', 'The new root', undefined, types.string) |
||||
.addParam('signature', 'The updater signature', undefined, types.string) |
||||
.setAction(async (args) => { |
||||
let address = ethers.utils.getAddress(args.address); |
||||
let { newRoot, oldRoot, signature } = args; |
||||
let update = await utils.validateUpdate(newRoot, oldRoot, signature); |
||||
|
||||
let signer = await ethers.getSigner(); |
||||
|
||||
// we should be able to use home for either. Consider moving this to common?
|
||||
let contract = new optics.Home(address, signer); |
||||
let tx = await contract.submitSignedUpdate({ oldRoot, newRoot, signature }); |
||||
await utils.reportTxOutcome(tx); |
||||
}); |
||||
|
||||
task('submit-double-update', 'Submit a double update to a home or replica.') |
||||
.addParam( |
||||
'address', |
||||
'The address of the contract to update.', |
||||
undefined, |
||||
types.string, |
||||
) |
||||
.addParam('oldRoot1', 'The old root', undefined, types.string) |
||||
.addParam('newRoot1', 'The new root', undefined, types.string) |
||||
.addParam('signature1', 'The updater signature', undefined, types.string) |
||||
.addParam('oldRoot2', 'The old root', undefined, types.string) |
||||
.addParam('newRoot2', 'The new root', undefined, types.string) |
||||
.addParam('signature2', 'The updater signature', undefined, types.string) |
||||
.setAction(async (args) => { |
||||
let { |
||||
oldRoot1, |
||||
newRoot1, |
||||
signature1, |
||||
oldRoot2, |
||||
newRoot2, |
||||
signature2, |
||||
} = args; |
||||
|
||||
let address = ethers.utils.getAddress(args.address); |
||||
let update1 = await utils.validateUpdate(newRoot1, oldRoot1, signature1); |
||||
let update2 = await utils.validateUpdate(newRoot2, oldRoot2, signature2); |
||||
|
||||
let signer = await ethers.getSigner(); |
||||
|
||||
let contract = new optics.Common(address, signer); |
||||
let tx = await contract.submitDoubleUpdate(update1, update2); |
||||
await utils.reportTxOutcome(tx); |
||||
}); |
@ -0,0 +1,40 @@ |
||||
async function reportTxOutcome(tx, confs) { |
||||
confs = confs ? confs : 1; |
||||
console.log(`\tSent tx with ID ${tx.hash} to ${tx.to}`); |
||||
console.log(`\tWaiting for ${confs} confs`); |
||||
|
||||
return await tx.wait(confs); |
||||
} |
||||
|
||||
// turn a tightly packed proof into an array
|
||||
async function parseProof(rawProof) { |
||||
return ethers.utils.defaultAbiCoder.decode(['bytes32[32]'], rawProof); |
||||
} |
||||
|
||||
async function validateUpdate(oldRoot, newRoot, signature, slip44) { |
||||
if (!ethers.utils.isHexString(oldRoot, 32)) { |
||||
throw new Error('oldRoot must be a 32-byte 0x prefixed hex string'); |
||||
} |
||||
if (!ethers.utils.isHexString(newRoot, 32)) { |
||||
throw new Error('newRoot must be a 32-byte 0x prefixed hex string'); |
||||
} |
||||
if (!ethers.utils.isHexString(signature, 65)) { |
||||
throw new Error('signature must be a 65-byte 0x prefixed hex string'); |
||||
} |
||||
|
||||
if (slip44) { |
||||
// TODO: validate the signature
|
||||
} |
||||
|
||||
return { |
||||
oldRoot, |
||||
newRoot, |
||||
signature, |
||||
}; |
||||
} |
||||
|
||||
module.exports = { |
||||
reportTxOutcome, |
||||
parseProof, |
||||
validateUpdate, |
||||
}; |
Loading…
Reference in new issue