113 lines
4.7 KiB
TypeScript
113 lines
4.7 KiB
TypeScript
import algosdk from 'algosdk'
|
|
import { IPublisher, PublishInfo } from './IPublisher'
|
|
import { StatusCode } from '../common/statusCodes'
|
|
import { PythData } from 'backend/common/basetypes'
|
|
const PricecasterLib = require('../../lib/pricecaster')
|
|
const tools = require('../../tools/app-tools')
|
|
|
|
export class Pricekeeper2Publisher implements IPublisher {
|
|
private algodClient: algosdk.Algodv2
|
|
private pclib: any
|
|
private account: algosdk.Account
|
|
private vaaProcessorAppId: number
|
|
private vaaProcessorOwner: string
|
|
private numOfVerifySteps: number = 0
|
|
private guardianCount: number = 0
|
|
private stepSize: number = 0
|
|
private dumpFailedTx: boolean
|
|
private dumpFailedTxDirectory: string | undefined
|
|
private compiledVerifyProgram: { bytes: Uint8Array, hash: string } = { bytes: new Uint8Array(), hash: '' }
|
|
constructor (vaaProcessorAppId: number,
|
|
priceKeeperAppId: number,
|
|
vaaProcessorOwner: string,
|
|
verifyProgramBinary: Uint8Array,
|
|
verifyProgramHash: string,
|
|
signKey: algosdk.Account,
|
|
algoClientToken: string,
|
|
algoClientServer: string,
|
|
algoClientPort: string,
|
|
dumpFailedTx: boolean = false,
|
|
dumpFailedTxDirectory: string = './') {
|
|
this.account = signKey
|
|
this.compiledVerifyProgram.bytes = verifyProgramBinary
|
|
this.compiledVerifyProgram.hash = verifyProgramHash
|
|
this.vaaProcessorAppId = vaaProcessorAppId
|
|
this.vaaProcessorOwner = vaaProcessorOwner
|
|
this.dumpFailedTx = dumpFailedTx
|
|
this.dumpFailedTxDirectory = dumpFailedTxDirectory
|
|
this.algodClient = new algosdk.Algodv2(algoClientToken, algoClientServer, algoClientPort)
|
|
this.pclib = new PricecasterLib.PricecasterLib(this.algodClient)
|
|
this.pclib.setAppId('vaaProcessor', vaaProcessorAppId)
|
|
this.pclib.setAppId('pricekeeper', priceKeeperAppId)
|
|
this.pclib.enableDumpFailedTx(this.dumpFailedTx)
|
|
this.pclib.setDumpFailedTxDirectory(this.dumpFailedTxDirectory)
|
|
}
|
|
|
|
async start () {
|
|
}
|
|
|
|
stop () {
|
|
}
|
|
|
|
signCallback (sender: string, tx: algosdk.Transaction) {
|
|
const txSigned = tx.signTxn(this.account.sk)
|
|
return txSigned
|
|
}
|
|
|
|
async publish (data: PythData): Promise<PublishInfo> {
|
|
const publishInfo: PublishInfo = { status: StatusCode.OK }
|
|
|
|
const txParams = await this.algodClient.getTransactionParams().do()
|
|
txParams.fee = 1000
|
|
txParams.flatFee = true
|
|
|
|
this.guardianCount = await tools.readAppGlobalStateByKey(this.algodClient, this.vaaProcessorAppId, this.vaaProcessorOwner, 'gscount')
|
|
this.stepSize = await tools.readAppGlobalStateByKey(this.algodClient, this.vaaProcessorAppId, this.vaaProcessorOwner, 'vssize')
|
|
this.numOfVerifySteps = Math.ceil(this.guardianCount / this.stepSize)
|
|
if (this.guardianCount === 0 || this.stepSize === 0) {
|
|
throw new Error('cannot get guardian count and/or step-size from global state')
|
|
}
|
|
//
|
|
// (!)
|
|
// Stateless programs cannot access state nor stack from stateful programs, so
|
|
// for the VAA Verify program to use the guardian set, we pass the global state as TX argument,
|
|
// (and check it against the current global list to be sure it's ok). This way it can be read by
|
|
// VAA verifier as a stateless program CAN DO READS of call transaction arguments in a group.
|
|
// The same technique is used for the note field, where the payload is set.
|
|
//
|
|
|
|
try {
|
|
const guardianKeys = []
|
|
const buf = Buffer.alloc(8)
|
|
for (let i = 0; i < this.guardianCount; i++) {
|
|
buf.writeBigUInt64BE(BigInt(i++))
|
|
const gk = await tools.readAppGlobalStateByKey(this.algodClient, this.vaaProcessorAppId, this.vaaProcessorOwner, buf.toString())
|
|
guardianKeys.push(Buffer.from(gk, 'base64').toString('hex'))
|
|
}
|
|
|
|
const strSig = data.signatures.toString('hex')
|
|
|
|
const gid = this.pclib.beginTxGroup()
|
|
const sigSubsets = []
|
|
for (let i = 0; i < this.numOfVerifySteps; i++) {
|
|
const st = this.stepSize * i
|
|
const sigSetLen = 132 * this.stepSize
|
|
|
|
const keySubset = guardianKeys.slice(st, i < this.numOfVerifySteps - 1 ? st + this.stepSize : undefined)
|
|
|
|
sigSubsets.push(strSig.slice(i * sigSetLen, i < this.numOfVerifySteps - 1 ? ((i * sigSetLen) + sigSetLen) : undefined))
|
|
this.pclib.addVerifyTx(gid, this.compiledVerifyProgram.hash, txParams, data.vaaBody, keySubset, this.guardianCount)
|
|
}
|
|
this.pclib.addPriceStoreTx(gid, this.vaaProcessorOwner, txParams, data.symbol, data.vaaBody.slice(51))
|
|
const txId = await this.pclib.commitVerifyTxGroup(gid, this.compiledVerifyProgram.bytes, sigSubsets, this.vaaProcessorOwner, this.signCallback.bind(this))
|
|
publishInfo.txid = txId
|
|
} catch (e: any) {
|
|
publishInfo.status = StatusCode.ERROR_SUBMIT_MESSAGE
|
|
publishInfo.reason = e.response.text ? e.response.text : e.toString()
|
|
return publishInfo
|
|
}
|
|
|
|
return publishInfo
|
|
}
|
|
}
|