Skip to content

Commit

Permalink
feat: opt out block braodcast, multisig fixes, cleanup, consider epoc…
Browse files Browse the repository at this point in the history
…h for schedule
  • Loading branch information
vaultec81 committed Mar 15, 2024
1 parent 735ee0b commit 626959b
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 116 deletions.
30 changes: 22 additions & 8 deletions contracts/btc-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ const CONFIG = {
WP_PUB: '034240ccd025374e0531945a65661aedaac5fff1b2ae46197623e594e0129e8b13',
DECIMAL_PLACES: "100000000",
ACCEPTABLE_FEE: 1, //1% to cover exchange costs %
MAX_GAS_FEE: 16_000 //Maximum allowed gas fee for redeem transactions
MAX_GAS_FEE: 16_000, //Maximum allowed gas fee for redeem transactions
//Minimum height a transaction should be created at.
//Prevents possible attacks of replaying previous TXs prior to contract being deployed.
//Deploy contract on or before block height
REPLAY_HEIGHT: 818769
}

interface Transfer {
Expand Down Expand Up @@ -131,7 +135,7 @@ function verifyLock(controller, params?:{hashLockImage?: string}): boolean {
}

function compileScript(pubKey: string, addrKey: string) {
return Buffer.from(`21${pubKey}ad20${addrKey}`)
return Buffer.from(`21${pubKey}ad20${addrKey}`, 'hex')
}

actions.applyHtlc = async (tx: HtlcTransfer) => {
Expand Down Expand Up @@ -353,7 +357,7 @@ actions.redeem = async (tx: Transfer) => {
address: tx.dest,

balance: totalRefBal,
rblance: 0,
rbalance: 0,
asset_type: "TOKEN:WBTC",

inputs: tx.inputs,
Expand Down Expand Up @@ -381,6 +385,11 @@ actions.redeemProof = async (args) => {
const {proof} = args
const tx_id = utils.bitcoin.reverseBytes(proof.tx_id)

//See above notes.
if(proof.confirming_height < CONFIG.REPLAY_HEIGHT) {
return
}

const bundleHeaders = await relayState.pull(`headers/${calcKey(proof.confirming_height)}`) || {}

const header = bundleHeaders[proof.confirming_height]
Expand Down Expand Up @@ -417,7 +426,7 @@ actions.redeemProof = async (args) => {
const btcOutput1 = utils.bitcoin.BTCUtils.extractOutputAtIndex(utils.bitcoin.SPVUtils.deserializeHex((proof as any).vout),txIndex+1)
const outHash = utils.bitcoin.BTCUtils.extractHash(btcOutput0)
const val = utils.bitcoin.BTCUtils.extractValue(btcOutput0)
const opReturnData = utils.bitcoin.BTCUtils.extractOpReturnData(btcOutput1)
const opReturnData = utils.bitcoin.BTCUtils.extractOpReturn(btcOutput1)

var a = new BigDecimal(val.toString());
var b = new BigDecimal(CONFIG.DECIMAL_PLACES);
Expand All @@ -426,18 +435,18 @@ actions.redeemProof = async (args) => {

console.log(redeemedAmount)

const hex = utils.bitcoin.ser.serializeHex(opReturnData)
const hex = utils.bitcoin.SPVUtils.serializeHex(opReturnData)
log('out hex', hex)
const redeem = await state.pull(`redeems/${hex}`) as any
const redeem = await state.pull(`redeems/${hex.slice(2)}`) as any
if(redeem) {
const hashBreak = utils.base58.decode(redeem.address)
if(redeem.status === "pending" && utils.base58.encode(outHash) === utils.base58.encode(hashBreak.slice(1))) {
redeem.status = 'complete'
redeem.tx_id = tx_id
redeem.out_index = txIndex
redeem.rbalance = redeem.rbalance + redeemedAmount
redeem.rbalance = (redeem.rbalance || 0) + redeemedAmount

await state.update(`redeems/${hex}`, redeem)
await state.update(`redeems/${hex.slice(2)}`, redeem)
}
}
} catch (ex) {
Expand All @@ -464,6 +473,11 @@ actions.mint = async (args: {
const {proof} = args
const tx_id = utils.bitcoin.reverseBytes(proof.tx_id)

//See above notes.
if(proof.confirming_height < CONFIG.REPLAY_HEIGHT) {
return
}

const bundleHeaders = await relayState.pull(`headers/${calcKey(proof.confirming_height)}`) || {}

const header = bundleHeaders[proof.confirming_height]
Expand Down
32 changes: 24 additions & 8 deletions src/modules/api/graphql/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,15 +317,31 @@ export const Resolvers = {
//TODO: Create separate API to include indicate whether a node is in the schedule or not.
return await appContainer.self.newService.chainBridge.getWitnessesAtBlock(args.height)
},
activeWitnessNodes: async (_, args) => {
if(!args.height) {
args.height = await appContainer.self.newService.chainBridge.getLatestBlock()
}
return await appContainer.self.newService.electionManager.getMembersOfBlock(args.height)
},
witnessSchedule: async (_, args) => {
if(!args.height) {
args.height = await appContainer.self.newService.chainBridge.getLatestBlock()
}
return await appContainer.self.newService.witness.getBlockSchedule(args.height)
},
nextWitnessSlot: async (_, args) => {
let node_id
if(args.local) {
node_id = appContainer.self.identity.id;
}

const nextSlot = appContainer.self.witness.witnessSchedule.find(e => {
if(node_id) {
return e.in_past === false && e.did === node_id;
let account
if(args.self) {
account = process.env.HIVE_ACCOUNT
}

if(!args.height) {
args.height = await appContainer.self.newService.chainBridge.getLatestBlock()
}

const nextSlot = (await appContainer.self.newService.witness.getBlockSchedule(args.height)).find(e => {
if(account) {
return e.in_past === false && e.account === account;
} else {
return e.in_past === false;
}
Expand Down
4 changes: 3 additions & 1 deletion src/modules/api/graphql/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ export const schema = `
localNodeInfo: LocalNodeInfo
witnessNodes(height: Int!): [WitnessNode]
nextWitnessSlot(local: Boolean): JSON
activeWitnessNodes: JSON
witnessSchedule(height: Int): JSON
nextWitnessSlot(self: Boolean): JSON
}
`
29 changes: 11 additions & 18 deletions src/services/new/chainBridgeV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ export class ChainBridgeV2 {
// }).filter(e => !!e).map(e => e.key)
const witnessSet = (await this.self.electionManager.getMembersOfBlock(blockHeight)).map(e => e.key)
// console.log('Asking for schedule at slot', blockHeight)
const witnessSchedule = await this.self.witness.roundCheck(blockHeight)
const witnessSchedule = await this.self.witness.getBlockSchedule(blockHeight)


const slotHeight = (blockHeight - (blockHeight % networks[this.self.config.get('network.id')].roundLength)) //+ networks[this.self.config.get('network.id')].roundLength
Expand Down Expand Up @@ -544,6 +544,15 @@ export class ChainBridgeV2 {

}

async getLatestBlock(): Promise<number> {
const block = await this.events.findOne({}, {
sort: {
key: -1
}
})
return Number(block.key)
}

async getWitnessesAtBlock(blk: number) {
//This is not safe as it can vary with historical records
const witnesses = await this.witnessDb.find({
Expand All @@ -565,22 +574,6 @@ export class ChainBridgeV2 {
sort = {
valid_from: -1
}
} else {
query = {
type: "witness.toggle",
account: e.account,

$or: [{
valid_to: {
$exists: false
}
}, {
valid_to: null
}]
}
sort = {
valid_from: -1
}
}
const data = await this.witnessHistoryDb.findOne(query, {
sort
Expand All @@ -599,7 +592,7 @@ export class ChainBridgeV2 {
const keys = await this.accountAuths.findOne({
account: data.account,
valid_from: {
$lte: blk
$lt: blk
},
// $or: [
// {
Expand Down
24 changes: 24 additions & 0 deletions src/services/new/transactionPool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CID } from "kubo-rpc-client";
import { verifyTx } from "./utils";
import { convertTxJws } from "@vsc.eco/client/dist/utils";
import DAGCbor from 'ipld-dag-cbor'
import NodeSchedule from 'node-schedule'

interface TxAnnounceMsg {
//ref_tx = only CID of TX useful for TXs under 2kb
Expand Down Expand Up @@ -355,6 +356,29 @@ export class TransactionPoolV2 {
this.self.p2pService.memoryPoolChannel.register('announce_tx', this.onTxAnnounce, {
loopbackOk: true
})
NodeSchedule.scheduleJob('*/5 * * * *', async () => {
const unconfirmedTxs = await this.txDb.find({
status: TransactionDbStatus.unconfirmed
}).toArray()

for(let tx of unconfirmedTxs) {
console.log('rebroadcasting tx', tx.id, {
type: 'direct_tx',
data: Buffer.from(await this.self.ipfs.block.get(CID.parse(tx.id))).toString('base64url'),
//Fill in
sig_data: Buffer.from(await this.self.ipfs.block.get(CID.parse(tx.sig_hash))).toString('base64url')
})
await this.self.p2pService.memoryPoolChannel.call('announce_tx', {
payload: {
type: 'direct_tx',
data: Buffer.from(await this.self.ipfs.block.get(CID.parse(tx.id))).toString('base64url'),
//Fill in
sig_data: Buffer.from(await this.self.ipfs.block.get(CID.parse(tx.sig_hash))).toString('base64url')
},
mode: 'basic'
})
}
})
}

async start() {
Expand Down
3 changes: 0 additions & 3 deletions src/services/new/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,6 @@ export function convertTxJws(args: {
]
}




let jwsDag = {
jws: {
signatures: [
Expand Down
6 changes: 3 additions & 3 deletions src/services/new/witness/electionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ export class ElectionManager {

console.log('electionData - holding election', electionData)

if(electionData.members.length > 8) {
if(electionData.members.length < 8) {
console.log("Minimum network config not met for election. Skipping.")
return;
}
Expand Down Expand Up @@ -314,11 +314,11 @@ export class ElectionManager {

async btHoldElection({data:block}) {
const blk = block.key
const witnessSchedule = await this.self.witness.roundCheck(blk)
const scheduleSlot = witnessSchedule.find(e => e.bn >= blk)
// const drift = blk % this.epochLength;
// const slotHeight = blk - drift
if(blk % this.epochLength === 0 && this.self.chainBridge.parseLag < 5) {
const witnessSchedule = await this.self.witness.getBlockSchedule(blk)
const scheduleSlot = witnessSchedule.find(e => e.bn >= blk)
if(scheduleSlot && scheduleSlot.account === process.env.HIVE_ACCOUNT) {
this.holdElection(blk)
}
Expand Down
Loading

0 comments on commit 626959b

Please sign in to comment.