First VAA processor deployment :party:
This commit is contained in:
parent
1a7343b50a
commit
831b55cc2a
|
@ -4,8 +4,11 @@ const fs = require('fs')
|
||||||
const { sha512_256 } = require('js-sha512')
|
const { sha512_256 } = require('js-sha512')
|
||||||
const tools = require('../tools/app-tools')
|
const tools = require('../tools/app-tools')
|
||||||
|
|
||||||
const approvalProgramFilename = 'teal/pricekeeper.teal'
|
const approvalProgramFilename = 'teal/pricekeeper/pricekeeper.teal'
|
||||||
const clearProgramFilename = 'teal/clearstate.teal'
|
const clearProgramFilename = 'teal/pricekeeper/clearstate.teal'
|
||||||
|
const vaaProcessorApprovalProgramFilename = 'teal/wormhole/build/vaa-processor-approval.teal'
|
||||||
|
const vaaProcessorClearProgramFilename = 'teal/wormhole/build/vaa-processor-clear.teal'
|
||||||
|
const vaaVerifyStatelessProgramFilename = 'teal/wormhole/build/vaa-verify.teal'
|
||||||
|
|
||||||
class PricecasterLib {
|
class PricecasterLib {
|
||||||
constructor (algodClient, ownerAddr = undefined) {
|
constructor (algodClient, ownerAddr = undefined) {
|
||||||
|
@ -113,7 +116,17 @@ class PricecasterLib {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal function.
|
* Internal function.
|
||||||
* Compile application approval program.
|
* Compile application clear state program.
|
||||||
|
* @return {String} base64 string containing the compiled program
|
||||||
|
*/
|
||||||
|
this.compileVAAProcessorClearProgram = function () {
|
||||||
|
const program = fs.readFileSync(vaaProcessorClearProgramFilename, 'utf8')
|
||||||
|
return this.compileProgram(program)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function.
|
||||||
|
* Compile pricekeeper application approval program.
|
||||||
* @return {String} base64 string containing the compiled program
|
* @return {String} base64 string containing the compiled program
|
||||||
*/
|
*/
|
||||||
this.compileApprovalProgram = async function () {
|
this.compileApprovalProgram = async function () {
|
||||||
|
@ -123,6 +136,18 @@ class PricecasterLib {
|
||||||
return compiledApprovalProgram
|
return compiledApprovalProgram
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function.
|
||||||
|
* Compile VAA Processor application approval program.
|
||||||
|
* @return {String} base64 string containing the compiled program
|
||||||
|
*/
|
||||||
|
this.compileVAAProcessorApprovalProgram = async function () {
|
||||||
|
const program = fs.readFileSync(vaaProcessorApprovalProgramFilename, 'utf8')
|
||||||
|
const compiledApprovalProgram = await this.compileProgram(program)
|
||||||
|
this.approvalProgramHash = compiledApprovalProgram.hash
|
||||||
|
return compiledApprovalProgram
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to retrieve the application id from a createApp transaction response.
|
* Helper function to retrieve the application id from a createApp transaction response.
|
||||||
* @param {Object} txResponse object containig the transactionResponse of the createApp call
|
* @param {Object} txResponse object containig the transactionResponse of the createApp call
|
||||||
|
@ -180,6 +205,50 @@ class PricecasterLib {
|
||||||
return txId
|
return txId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the VAA Processor application based on the default approval and clearState programs or based on the specified files.
|
||||||
|
* @param {String} sender account used to sign the createApp transaction
|
||||||
|
* @param {String} gexpTime Guardian key set expiration time
|
||||||
|
* @param {String} gkeys Guardian keys listed as a single array
|
||||||
|
* @param {Function} signCallback callback with prototype signCallback(sender, tx) used to sign transactions
|
||||||
|
* @return {String} transaction id of the created application
|
||||||
|
*/
|
||||||
|
this.createVaaProcessorApp = async function (sender, gexpTime, gkeys, signCallback) {
|
||||||
|
const localInts = 0
|
||||||
|
const localBytes = 0
|
||||||
|
const globalInts = 2
|
||||||
|
const globalBytes = 20
|
||||||
|
|
||||||
|
// declare onComplete as NoOp
|
||||||
|
const onComplete = algosdk.OnApplicationComplete.NoOpOC
|
||||||
|
|
||||||
|
// get node suggested parameters
|
||||||
|
const params = await algodClient.getTransactionParams().do()
|
||||||
|
|
||||||
|
params.fee = this.minFee
|
||||||
|
params.flatFee = true
|
||||||
|
|
||||||
|
const compiledProgram = await this.compileVAAProcessorApprovalProgram()
|
||||||
|
const approvalProgramCompiled = compiledProgram.compiledBytes
|
||||||
|
const clearProgramCompiled = (await this.compileVAAProcessorClearProgram()).compiledBytes
|
||||||
|
const appArgs = [new Uint8Array(Buffer.from(gkeys, 'hex')), algosdk.encodeUint64(parseInt(gexpTime))]
|
||||||
|
|
||||||
|
// create unsigned transaction
|
||||||
|
const txApp = algosdk.makeApplicationCreateTxn(
|
||||||
|
sender, params, onComplete,
|
||||||
|
approvalProgramCompiled, clearProgramCompiled,
|
||||||
|
localInts, localBytes, globalInts, globalBytes, appArgs
|
||||||
|
)
|
||||||
|
const txId = txApp.txID().toString()
|
||||||
|
|
||||||
|
// Sign the transaction
|
||||||
|
const txAppSigned = signCallback(sender, txApp)
|
||||||
|
|
||||||
|
// Submit the transaction
|
||||||
|
await algodClient.sendRawTransaction(txAppSigned).do()
|
||||||
|
return txId
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal function.
|
* Internal function.
|
||||||
* Call application specifying args and accounts.
|
* Call application specifying args and accounts.
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"@certusone/wormhole-sdk": "^0.0.5",
|
"@certusone/wormhole-sdk": "^0.0.5",
|
||||||
"@pythnetwork/client": "^2.3.1",
|
"@pythnetwork/client": "^2.3.1",
|
||||||
"@randlabs/js-config-reader": "^1.1.0",
|
"@randlabs/js-config-reader": "^1.1.0",
|
||||||
"algosdk": "^1.11.1",
|
"algosdk": "^1.12.0",
|
||||||
"charm": "^1.0.2",
|
"charm": "^1.0.2",
|
||||||
"fastpriorityqueue": "^0.7.1",
|
"fastpriorityqueue": "^0.7.1",
|
||||||
"js-sha512": "^0.8.0"
|
"js-sha512": "^0.8.0"
|
||||||
|
@ -1321,9 +1321,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/algosdk": {
|
"node_modules/algosdk": {
|
||||||
"version": "1.11.1",
|
"version": "1.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/algosdk/-/algosdk-1.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/algosdk/-/algosdk-1.12.0.tgz",
|
||||||
"integrity": "sha512-2B7Tz8NSaME1aQDrzvhAuTm67c/2KCXTeuzgKvB61YXUU3pe0zyDzEw7bgv1sC6lbbD6BihroiUtidP+Fdh+cQ==",
|
"integrity": "sha512-Iqek0AwcCeXLywVg4E8gWWjmuPZ10P7PUmpZrlR71FSNyEtX4Ie+UgrNHWhkYnhyykRU5mjtvD4Hrb2eOepsGA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"algo-msgpack-with-bigint": "^2.1.1",
|
"algo-msgpack-with-bigint": "^2.1.1",
|
||||||
"buffer": "^6.0.2",
|
"buffer": "^6.0.2",
|
||||||
|
@ -6960,9 +6960,9 @@
|
||||||
"integrity": "sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ=="
|
"integrity": "sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ=="
|
||||||
},
|
},
|
||||||
"algosdk": {
|
"algosdk": {
|
||||||
"version": "1.11.1",
|
"version": "1.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/algosdk/-/algosdk-1.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/algosdk/-/algosdk-1.12.0.tgz",
|
||||||
"integrity": "sha512-2B7Tz8NSaME1aQDrzvhAuTm67c/2KCXTeuzgKvB61YXUU3pe0zyDzEw7bgv1sC6lbbD6BihroiUtidP+Fdh+cQ==",
|
"integrity": "sha512-Iqek0AwcCeXLywVg4E8gWWjmuPZ10P7PUmpZrlR71FSNyEtX4Ie+UgrNHWhkYnhyykRU5mjtvD4Hrb2eOepsGA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"algo-msgpack-with-bigint": "^2.1.1",
|
"algo-msgpack-with-bigint": "^2.1.1",
|
||||||
"buffer": "^6.0.2",
|
"buffer": "^6.0.2",
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
"start-doge": "npm run compile && cross-env PRICECASTER_SETTINGS=./settings/settings-doge.js node build/main.js",
|
"start-doge": "npm run compile && cross-env PRICECASTER_SETTINGS=./settings/settings-doge.js node build/main.js",
|
||||||
"test-pkeeper-sc": "mocha test/pkeeper-sc-test.js --timeout 60000",
|
"test-pkeeper-sc": "mocha test/pkeeper-sc-test.js --timeout 60000",
|
||||||
"test-wormhole-sc": "mocha test/wormhole-sc-test.js --timeout 60000"
|
"test-wormhole-sc": "mocha test/wormhole-sc-test.js --timeout 60000"
|
||||||
|
|
||||||
},
|
},
|
||||||
"author": "Randlabs inc",
|
"author": "Randlabs inc",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
@ -20,7 +19,7 @@
|
||||||
"@certusone/wormhole-sdk": "^0.0.5",
|
"@certusone/wormhole-sdk": "^0.0.5",
|
||||||
"@pythnetwork/client": "^2.3.1",
|
"@pythnetwork/client": "^2.3.1",
|
||||||
"@randlabs/js-config-reader": "^1.1.0",
|
"@randlabs/js-config-reader": "^1.1.0",
|
||||||
"algosdk": "^1.11.1",
|
"algosdk": "^1.12.0",
|
||||||
"charm": "^1.0.2",
|
"charm": "^1.0.2",
|
||||||
"fastpriorityqueue": "^0.7.1",
|
"fastpriorityqueue": "^0.7.1",
|
||||||
"js-sha512": "^0.8.0"
|
"js-sha512": "^0.8.0"
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
/* eslint-disable linebreak-style */
|
||||||
|
const algosdk = require('algosdk')
|
||||||
|
const { exit } = require('process')
|
||||||
|
const readline = require('readline')
|
||||||
|
const PricecasterLib = require('../lib/pricecaster')
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout
|
||||||
|
})
|
||||||
|
const spawnSync = require('child_process').spawnSync
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
function ask (questionText) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
rl.question(questionText, input => resolve(input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let globalMnemo = ''
|
||||||
|
|
||||||
|
function signCallback (sender, tx) {
|
||||||
|
const txSigned = tx.signTxn(algosdk.mnemonicToSecretKey(globalMnemo).sk)
|
||||||
|
return txSigned
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startOp (algodClient, fromAddress, gexpTime, gkeys) {
|
||||||
|
console.log('Compiling VAA Processor program code...')
|
||||||
|
const out = spawnSync('python', ['teal/wormhole/pyteal/vaa-processor.py'])
|
||||||
|
console.log(out.output.toString())
|
||||||
|
|
||||||
|
// console.log('Compiling VAA Verify stateless program code...')
|
||||||
|
// out = spawnSync('python', ['teal/wormhole/pyteal/vaa-verify.py'])
|
||||||
|
// console.log(out.output.toString())
|
||||||
|
|
||||||
|
const pclib = new PricecasterLib.PricecasterLib(algodClient)
|
||||||
|
console.log('Creating new app...')
|
||||||
|
const txId = await pclib.createVaaProcessorApp(fromAddress, gexpTime, gkeys.join(''), signCallback)
|
||||||
|
console.log('txId: ' + txId)
|
||||||
|
const txResponse = await pclib.waitForTransactionResponse(txId)
|
||||||
|
const appId = pclib.appIdFromCreateAppResponse(txResponse)
|
||||||
|
console.log('Deployment App Id: %d', appId)
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
console.log('\nVAA Processor for Wormhole Deployment Tool -- (c)2021-22 Randlabs, Inc.')
|
||||||
|
console.log('-----------------------------------------------------------------------\n')
|
||||||
|
|
||||||
|
if (process.argv.length !== 6) {
|
||||||
|
console.log('Usage: deploy <glistfile> <from> <network>\n')
|
||||||
|
console.log('where:\n')
|
||||||
|
console.log('glistfile File containing the initial list of guardians')
|
||||||
|
console.log('gexptime Guardian set expiration time')
|
||||||
|
console.log('from Deployer account')
|
||||||
|
console.log('network Testnet, betanet or mainnet')
|
||||||
|
console.log('\nFile must contain one guardian key per line, formatted in hex, without hex prefix.')
|
||||||
|
exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const listfile = process.argv[2]
|
||||||
|
const gexpTime = process.argv[3]
|
||||||
|
const fromAddress = process.argv[4]
|
||||||
|
const network = process.argv[5]
|
||||||
|
|
||||||
|
const config = { server: '', apiToken: '', port: '' }
|
||||||
|
if (network === 'betanet') {
|
||||||
|
config.server = 'https://api.betanet.algoexplorer.io'
|
||||||
|
} else if (network === 'mainnet') {
|
||||||
|
config.server = 'https://api.algoexplorer.io'
|
||||||
|
} else if (network === 'testnet') {
|
||||||
|
config.server = 'https://api.testnet.algoexplorer.io'
|
||||||
|
} else {
|
||||||
|
console.error('Invalid network: ' + network)
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileDataStr = fs.readFileSync(listfile).toString()
|
||||||
|
const gkeys = fileDataStr.match(/[^\r\n]+/g)
|
||||||
|
if (!algosdk.isValidAddress(fromAddress)) {
|
||||||
|
console.error('Invalid deployer address: ' + fromAddress)
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const algodClient = new algosdk.Algodv2(config.apiToken, config.server, config.port)
|
||||||
|
|
||||||
|
console.log('Parameters for deployment: ')
|
||||||
|
console.log('From: ' + fromAddress)
|
||||||
|
console.log('Network: ' + network)
|
||||||
|
console.log('Guardian expiration time: ' + gexpTime)
|
||||||
|
console.log(`Guardian Keys: (${gkeys.length}) ` + gkeys)
|
||||||
|
const answer = await ask('\nEnter YES to confirm parameters, anything else to abort. ')
|
||||||
|
if (answer !== 'YES') {
|
||||||
|
console.warn('Aborted by user.')
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
globalMnemo = await ask('\nEnter mnemonic for sender account.\nBE SURE TO DO THIS FROM A SECURED SYSTEM\n')
|
||||||
|
try {
|
||||||
|
await startOp(algodClient, fromAddress, gexpTime, gkeys)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('(!) Deployment Failed: ' + e.toString())
|
||||||
|
}
|
||||||
|
console.log('Bye.')
|
||||||
|
exit(0)
|
||||||
|
})()
|
Loading…
Reference in New Issue