From 11f2862b97d397ebeb7ed167f0dd453aed526d29 Mon Sep 17 00:00:00 2001 From: Mark Tan Date: Thu, 1 Dec 2022 14:20:12 +0800 Subject: [PATCH 01/10] Shift code from dftx PR (WIP) --- .../txn/txn_builder_masternode_update.test.ts | 976 ++++++++++++++++++ .../src/txn/txn_builder_masternode.ts | 21 + 2 files changed, 997 insertions(+) create mode 100644 packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts diff --git a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts new file mode 100644 index 0000000000..09935c0a9b --- /dev/null +++ b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts @@ -0,0 +1,976 @@ +import { JsonRpcClient } from '@defichain/jellyfish-api-jsonrpc' +import { RegTest, RegTestFoundationKeys } from '@defichain/jellyfish-network' +import { OP_CODES, Script, TransactionSegWit, UpdateMasternode, Vin, Vout } from '@defichain/jellyfish-transaction' +import { fromAddress, P2PKH, P2SH, P2WPKH } from '@defichain/jellyfish-address' +import { DeFiDRpcError, MasterNodeRegTestContainer } from '@defichain/testcontainers' +import { getProviders, MockProviders } from '../provider.mock' +import { P2WPKHTransactionBuilder, Prevout } from '../../src' +import { fundEllipticPair, sendTransaction } from '../test.utils' +import { Bech32 } from '@defichain/jellyfish-crypto' +import { BigNumber } from '@defichain/jellyfish-json' +import { Testing, TestingGroup } from '@defichain/jellyfish-testing' + +describe('UpdateMasternode', () => { + const container = new MasterNodeRegTestContainer() + let providers: MockProviders + let builder: P2WPKHTransactionBuilder + let jsonRpc: JsonRpcClient + + beforeAll(async () => { + await container.start() + await container.waitForWalletCoinbaseMaturity() + await container.waitForWalletBalanceGTE(1000) + + jsonRpc = new JsonRpcClient(await container.getCachedRpcUrl()) + providers = await getProviders(container) + }) + + afterAll(async () => { + await container.stop() + }) + + beforeEach(async () => { + await providers.randomizeEllipticPair() + builder = new P2WPKHTransactionBuilder(providers.fee, providers.prevout, providers.elliptic, RegTest) + await fundEllipticPair(container, providers.ellipticPair, 10) + await providers.setupMocks() + + // enable updating + await jsonRpc.masternode.setGov({ + ATTRIBUTES: { + 'v0/params/feature/mn-setowneraddress': 'true', + 'v0/params/feature/mn-setoperatoraddress': 'true', + 'v0/params/feature/mn-setrewardaddress': 'true' + } + }) + await container.generate(1) + }) + + it('should update owner address with P2WPKH address', async () => { + const pubKey = await providers.ellipticPair.publicKey() + const script1 = await providers.elliptic.script() + + const initialAddress = Bech32.fromPubKey(pubKey, 'bcrt') + const masternodeId = await jsonRpc.masternode.createMasternode(initialAddress) + await container.generate(20) + + const newAddress = await container.getNewAddress('', 'bech32') + const addressDest: P2WPKH = P2WPKH.fromAddress(RegTest, newAddress, P2WPKH) + const addressDestKeyHash = addressDest.pubKeyHash + const script2 = await fromAddress(newAddress, 'regtest')?.script as Script + + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [ + { + updateType: 0x01, + address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash } + } + ] + } + + const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) + const collateralPrevout: Prevout = { + txid: masternodeId, + vout: 1, + script: script1, + value: new BigNumber(rawCollateralTx.vout[1].value), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVout: Vout = { + script: script2, + value: new BigNumber(rawCollateralTx.vout[1].value), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVin: Vin = { + txid: masternodeId, + index: 1, + script: { stack: [] }, + sequence: 0xffffffff + } + const customVinVout = { + prevout: collateralPrevout, + vin: collateralVin, + vout: collateralVout + } + + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, [customVinVout]) + const outs = await sendTransaction(container, txn) + + const encoded: string = OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode).asBuffer().toString('hex') + const expectedRedeemScript = `6a${encoded}` + + expect(outs.length).toStrictEqual(3) + expect(outs[0]).toStrictEqual({ + n: 0, + tokenId: 0, + value: 0, + scriptPubKey: { + asm: expect.stringContaining('OP_RETURN 446654786d'), + hex: expectedRedeemScript, + type: 'nulldata' + } + }) + expect(outs[1]).toStrictEqual({ + n: 1, + tokenId: 0, + value: 2, + scriptPubKey: { + addresses: [newAddress], + asm: expect.any(String), + hex: expect.any(String), + reqSigs: 1, + type: 'witness_v0_keyhash' + } + }) + expect(outs[2]).toStrictEqual({ + n: 2, + tokenId: 0, + value: expect.any(Number), + scriptPubKey: { + addresses: [await providers.getAddress()], + asm: expect.any(String), + hex: expect.any(String), + reqSigs: 1, + type: 'witness_v0_keyhash' + } + }) + expect(outs[2].value).toBeGreaterThan(6.99) + expect(outs[2].value).toBeLessThan(7) + }) + + it('should update operator address with P2WPKH address', async () => { + const pubKey = await providers.ellipticPair.publicKey() + const collateralAddress = Bech32.fromPubKey(pubKey, 'bcrt') + const txid = await jsonRpc.masternode.createMasternode(collateralAddress) + await container.generate(20) + + const address = await container.getNewAddress('', 'bech32') + const addressDest: P2WPKH = P2WPKH.fromAddress(RegTest, address, P2WPKH) + const addressDestKeyHash = addressDest.pubKeyHash + + const updateMasternode: UpdateMasternode = { + nodeId: txid, + updates: [ + { + updateType: 0x02, + address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash } + } + ] + } + + const script = await providers.elliptic.script() + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script) + const outs = await sendTransaction(container, txn) + + const encoded: string = OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode).asBuffer().toString('hex') + const expectedRedeemScript = `6a${encoded}` + + expect(outs.length).toStrictEqual(2) + expect(outs[0]).toStrictEqual({ + n: 0, + tokenId: 0, + value: 0, + scriptPubKey: { + asm: expect.stringContaining('OP_RETURN 446654786d'), + hex: expectedRedeemScript, + type: 'nulldata' + } + }) + expect(outs[1]).toStrictEqual({ + n: 1, + tokenId: 0, + value: expect.any(Number), + scriptPubKey: { + addresses: [await providers.getAddress()], + asm: expect.any(String), + hex: expect.any(String), + reqSigs: 1, + type: 'witness_v0_keyhash' + } + }) + expect(outs[1].value).toBeGreaterThan(6.99) + expect(outs[1].value).toBeLessThan(7) + }) + + it('should update reward address with P2WPKH address', async () => { + const pubKey = await providers.ellipticPair.publicKey() + const collateralAddress = Bech32.fromPubKey(pubKey, 'bcrt') + const txid = await jsonRpc.masternode.createMasternode(collateralAddress) + await container.generate(20) + + const address = await container.getNewAddress('', 'bech32') + const addressDest: P2WPKH = P2WPKH.fromAddress(RegTest, address, P2WPKH) + const addressDestKeyHash = addressDest.pubKeyHash + + const updateMasternode: UpdateMasternode = { + nodeId: txid, + updates: [ + { + updateType: 0x03, + address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash } + } + ] + } + + const script = await providers.elliptic.script() + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script) + const outs = await sendTransaction(container, txn) + + const encoded: string = OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode).asBuffer().toString('hex') + const expectedRedeemScript = `6a${encoded}` + + expect(outs.length).toStrictEqual(2) + expect(outs[0]).toStrictEqual({ + n: 0, + tokenId: 0, + value: 0, + scriptPubKey: { + asm: expect.stringContaining('OP_RETURN 446654786d'), + hex: expectedRedeemScript, + type: 'nulldata' + } + }) + expect(outs[1]).toStrictEqual({ + n: 1, + tokenId: 0, + value: expect.any(Number), + scriptPubKey: { + addresses: [await providers.getAddress()], + asm: expect.any(String), + hex: expect.any(String), + reqSigs: 1, + type: 'witness_v0_keyhash' + } + }) + expect(outs[1].value).toBeGreaterThan(6.99) + expect(outs[1].value).toBeLessThan(7) + }) + + it('should update multiple addresses simultaneously with P2WPKH address', async () => { + const pubKey = await providers.ellipticPair.publicKey() + const script1 = await providers.elliptic.script() + + const collateralAddress = Bech32.fromPubKey(pubKey, 'bcrt') + const masternodeId = await jsonRpc.masternode.createMasternode(collateralAddress) + await container.generate(20) + + const ownerAddress = await container.getNewAddress('', 'bech32') + const ownerAddressDest: P2WPKH = P2WPKH.fromAddress(RegTest, ownerAddress, P2WPKH) + const ownerAddressDestKeyHash = ownerAddressDest.pubKeyHash + const script2 = await fromAddress(ownerAddress, 'regtest')?.script as Script + + const operatorAddress = await container.getNewAddress('', 'bech32') + const operatorAddressDest: P2WPKH = P2WPKH.fromAddress(RegTest, operatorAddress, P2WPKH) + const operatorAddressDestKeyHash = operatorAddressDest.pubKeyHash + + const rewardAddress = await container.getNewAddress('', 'bech32') + const rewardAddressDest: P2WPKH = P2WPKH.fromAddress(RegTest, rewardAddress, P2WPKH) + const rewardAddressDestKeyHash = rewardAddressDest.pubKeyHash + + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [ + { + updateType: 0x01, + address: { addressType: 0x04, addressPubKeyHash: ownerAddressDestKeyHash } + }, + { + updateType: 0x02, + address: { addressType: 0x04, addressPubKeyHash: operatorAddressDestKeyHash } + }, + { + updateType: 0x03, + address: { addressType: 0x04, addressPubKeyHash: rewardAddressDestKeyHash } + } + ] + } + + const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) + const collateralPrevout: Prevout = { + txid: masternodeId, + vout: 1, + script: script1, + value: new BigNumber(rawCollateralTx.vout[1].value), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVout: Vout = { + script: script2, + value: new BigNumber(rawCollateralTx.vout[1].value), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVin: Vin = { + txid: masternodeId, + index: 1, + script: { stack: [] }, + sequence: 0xffffffff + } + const customVinVout = { + prevout: collateralPrevout, + vin: collateralVin, + vout: collateralVout + } + + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, [customVinVout]) + const outs = await sendTransaction(container, txn) + + const encoded: string = OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode).asBuffer().toString('hex') + const expectedRedeemScript = `6a${encoded}` + + expect(outs.length).toStrictEqual(3) + expect(outs[0]).toStrictEqual({ + n: 0, + tokenId: 0, + value: 0, + scriptPubKey: { + asm: expect.stringContaining('OP_RETURN 446654786d'), + hex: expectedRedeemScript, + type: 'nulldata' + } + }) + expect(outs[1]).toStrictEqual({ + n: 1, + tokenId: 0, + value: 2, + scriptPubKey: { + addresses: [ownerAddress], + asm: expect.any(String), + hex: expect.any(String), + reqSigs: 1, + type: 'witness_v0_keyhash' + } + }) + expect(outs[2]).toStrictEqual({ + n: 2, + tokenId: 0, + value: expect.any(Number), + scriptPubKey: { + addresses: [await providers.getAddress()], + asm: expect.any(String), + hex: expect.any(String), + reqSigs: 1, + type: 'witness_v0_keyhash' + } + }) + expect(outs[2].value).toBeGreaterThan(6.99) + expect(outs[2].value).toBeLessThan(7) + }) + + it('should update multiple addresses simultaneously with P2PKH address', async () => { + const pubKey = await providers.ellipticPair.publicKey() + const script1 = await providers.elliptic.script() + + const collateralAddress = Bech32.fromPubKey(pubKey, 'bcrt') + const masternodeId = await jsonRpc.masternode.createMasternode(collateralAddress) + await container.generate(20) + + const ownerAddress = await container.getNewAddress('', 'legacy') + const ownerAddressDest: P2PKH = P2PKH.fromAddress(RegTest, ownerAddress, P2PKH) + const ownerAddressDestHex = ownerAddressDest.hex + const script2 = await fromAddress(ownerAddress, 'regtest')?.script as Script + + const operatorAddress = await container.getNewAddress('', 'legacy') + const operatorAddressDest: P2PKH = P2PKH.fromAddress(RegTest, operatorAddress, P2PKH) + const operatorAddressDestHex = operatorAddressDest.hex + + const rewardAddress = await container.getNewAddress('', 'legacy') + const rewardAddressDest: P2PKH = P2PKH.fromAddress(RegTest, rewardAddress, P2PKH) + const rewardAddressDestHex = rewardAddressDest.hex + + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [ + { + updateType: 0x01, + address: { addressType: 0x01, addressPubKeyHash: ownerAddressDestHex } + }, + { + updateType: 0x02, + address: { addressType: 0x01, addressPubKeyHash: operatorAddressDestHex } + }, + { + updateType: 0x03, + address: { addressType: 0x01, addressPubKeyHash: rewardAddressDestHex } + } + ] + } + + const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) + const collateralPrevout: Prevout = { + txid: masternodeId, + vout: 1, + script: script1, + value: new BigNumber(rawCollateralTx.vout[1].value), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVout: Vout = { + script: script2, + value: new BigNumber(rawCollateralTx.vout[1].value), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVin: Vin = { + txid: masternodeId, + index: 1, + script: { stack: [] }, + sequence: 0xffffffff + } + const customVinVout = { + prevout: collateralPrevout, + vin: collateralVin, + vout: collateralVout + } + + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, [customVinVout]) + const outs = await sendTransaction(container, txn) + + const encoded: string = OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode).asBuffer().toString('hex') + const expectedRedeemScript = `6a${encoded}` + + expect(outs.length).toStrictEqual(3) + expect(outs[0]).toStrictEqual({ + n: 0, + tokenId: 0, + value: 0, + scriptPubKey: { + asm: expect.stringContaining('OP_RETURN 446654786d'), + hex: expectedRedeemScript, + type: 'nulldata' + } + }) + expect(outs[1]).toStrictEqual({ + n: 1, + tokenId: 0, + value: 2, + scriptPubKey: { + addresses: [ownerAddress], + asm: expect.any(String), + hex: expect.any(String), + reqSigs: 1, + type: 'pubkeyhash' + } + }) + expect(outs[2]).toStrictEqual({ + n: 2, + tokenId: 0, + value: expect.any(Number), + scriptPubKey: { + addresses: [await providers.getAddress()], + asm: expect.any(String), + hex: expect.any(String), + reqSigs: 1, + type: 'witness_v0_keyhash' + } + }) + expect(outs[2].value).toBeGreaterThan(6.99) + expect(outs[2].value).toBeLessThan(7) + }) + + it('should update remove reward address', async () => { + const pubKey = await providers.ellipticPair.publicKey() + const collateralAddress = Bech32.fromPubKey(pubKey, 'bcrt') + const txid = await jsonRpc.masternode.createMasternode(collateralAddress) + await container.generate(20) + + const rewardAddress = await container.getNewAddress('', 'bech32') + const rewardAddressDest: P2WPKH = P2WPKH.fromAddress(RegTest, rewardAddress, P2WPKH) + const rewardAddressDestKeyHash = rewardAddressDest.pubKeyHash + + const setRewardAddressUpdate: UpdateMasternode = { + nodeId: txid, + updates: [ + { + updateType: 0x03, + address: { addressType: 0x04, addressPubKeyHash: rewardAddressDestKeyHash } + } + ] + } + + const script = await providers.elliptic.script() + const setRewardTxn: TransactionSegWit = await builder.masternode.update(setRewardAddressUpdate, script) + await sendTransaction(container, setRewardTxn) + + await container.generate(50) + + const updateMasternode: UpdateMasternode = { + nodeId: txid, + updates: [ + { + updateType: 0x04, + address: { addressType: 0x00 } + } + ] + } + + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script) + const outs = await sendTransaction(container, txn) + + const encoded: string = OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode).asBuffer().toString('hex') + const expectedRedeemScript = `6a${encoded}` + + expect(outs.length).toStrictEqual(2) + expect(outs[0]).toStrictEqual({ + n: 0, + tokenId: 0, + value: 0, + scriptPubKey: { + asm: expect.stringContaining('OP_RETURN 446654786d'), + hex: expectedRedeemScript, + type: 'nulldata' + } + }) + expect(outs[1]).toStrictEqual({ + n: 1, + tokenId: 0, + value: expect.any(Number), + scriptPubKey: { + addresses: [await providers.getAddress()], + asm: expect.any(String), + hex: expect.any(String), + reqSigs: 1, + type: 'witness_v0_keyhash' + } + }) + expect(outs[1].value).toBeGreaterThan(6.99) + expect(outs[1].value).toBeLessThan(7) + }) + + it('should fail if address is P2SH', async () => { + const pubKey = await providers.ellipticPair.publicKey() + const collateralAddress = Bech32.fromPubKey(pubKey, 'bcrt') + const masternodeId = await jsonRpc.masternode.createMasternode(collateralAddress) + await container.generate(20) + + const script = await providers.elliptic.script() + + { + const address = await container.getNewAddress('', 'p2sh-segwit') + const addressDest: P2SH = P2SH.fromAddress(RegTest, address, P2SH) + const addressDestHex = addressDest.hex + const script2 = fromAddress(address, 'regtest')?.script as Script + + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [{ updateType: 0x01, address: { addressType: 0x02, addressPubKeyHash: addressDestHex } }] + } + + const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) + const collateralPrevout: Prevout = { + txid: masternodeId, + vout: 1, + script: script, + value: new BigNumber(rawCollateralTx.vout[1].value), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVout: Vout = { + script: script2, + value: new BigNumber(rawCollateralTx.vout[1].value), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVin: Vin = { + txid: masternodeId, + index: 1, + script: { stack: [] }, + sequence: 0xffffffff + } + const customVinVout = { + prevout: collateralPrevout, + vin: collateralVin, + vout: collateralVout + } + + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script, [customVinVout]) + const promise = sendTransaction(container, txn) + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow("DeFiDRpcError: 'bad-txns-customtx, UpdateMasternodeTx: Owner address must be P2PKH or P2WPKH type (code 16)', code: -26") + } + { + const address = await container.getNewAddress('', 'p2sh-segwit') + const addressDest: P2SH = P2SH.fromAddress(RegTest, address, P2SH) + const addressDestHex = addressDest.hex + + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [{ updateType: 0x02, address: { addressType: 0x02, addressPubKeyHash: addressDestHex } }] + } + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script) + const promise = sendTransaction(container, txn) + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow("DeFiDRpcError: 'bad-txns-customtx, UpdateMasternodeTx: Operator address must be P2PKH or P2WPKH type (code 16)', code: -26") + } + { + const address = await container.getNewAddress('', 'p2sh-segwit') + const addressDest: P2SH = P2SH.fromAddress(RegTest, address, P2SH) + const addressDestHex = addressDest.hex + + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [{ updateType: 0x03, address: { addressType: 0x02, addressPubKeyHash: addressDestHex } }] + } + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script) + const promise = sendTransaction(container, txn) + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow("DeFiDRpcError: 'bad-txns-customtx, UpdateMasternodeTx: Reward address must be P2PKH or P2WPKH type (code 16)', code: -26") + } + }) + + it('should be failed as invalid address is not allowed', async () => { + const pubKey = await providers.ellipticPair.publicKey() + const collateralAddress = Bech32.fromPubKey(pubKey, 'bcrt') + const masternodeId = await jsonRpc.masternode.createMasternode(collateralAddress) + + await container.generate(20) + + const script = await providers.elliptic.script() + + { + const invalidAddress = 'INVALID_ADDRESS' + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [{ updateType: 0x02, address: { addressType: 0x02, addressPubKeyHash: invalidAddress } }] + } + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script) + const promise = sendTransaction(container, txn) + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow('DeFiDRpcError: \'bad-txns-customtx, UpdateMasternodeTx: Operator address must be P2PKH or P2WPKH type (code 16)\', code: -26') + } + + { + const invalidAddress = 'INVALID_ADDRESS' + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [{ updateType: 0x03, address: { addressType: 0x02, addressPubKeyHash: invalidAddress } }] + } + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script) + const promise = sendTransaction(container, txn) + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow('DeFiDRpcError: \'bad-txns-customtx, UpdateMasternodeTx: Reward address must be P2PKH or P2WPKH type (code 16)\', code: -26') + } + }) + + it('should fail to update owner address without collateral transaction inputs', async () => { + const pubKey = await providers.ellipticPair.publicKey() + const initialAddress = Bech32.fromPubKey(pubKey, 'bcrt') + + const masternodeId = await jsonRpc.masternode.createMasternode(initialAddress) + await container.generate(20) + + const address = await container.getNewAddress('', 'bech32') + const addressDest: P2WPKH = P2WPKH.fromAddress(RegTest, address, P2WPKH) + const addressDestKeyHash = addressDest.pubKeyHash + + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [ + { + updateType: 0x01, + address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash } + } + ] + } + + const script = await providers.elliptic.script() + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script) + const promise = sendTransaction(container, txn) + + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow("DeFiDRpcError: 'bad-txns-customtx, UpdateMasternodeTx: Missing previous collateral from transaction inputs (code 16)', code: -26") + }) + + it('should fail to update owner address with same address', async () => { + const pubKey = await providers.ellipticPair.publicKey() + const initialAddress = Bech32.fromPubKey(pubKey, 'bcrt') + const script1 = await providers.elliptic.script() + + const masternodeId = await jsonRpc.masternode.createMasternode(initialAddress) + await container.generate(20) + + const newAddress = await container.getNewAddress('', 'bech32') + const addressDest: P2WPKH = P2WPKH.fromAddress(RegTest, newAddress, P2WPKH) + const addressDestKeyHash = addressDest.pubKeyHash + + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [ + { + updateType: 0x01, + address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash } + } + ] + } + + const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) + const collateralPrevout: Prevout = { + txid: masternodeId, + vout: 1, + script: script1, + value: new BigNumber(rawCollateralTx.vout[1].value), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVout: Vout = { + script: script1, + value: new BigNumber(rawCollateralTx.vout[1].value), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVin: Vin = { + txid: masternodeId, + index: 1, + script: { stack: [] }, + sequence: 0xffffffff + } + const customVinVout = { + prevout: collateralPrevout, + vin: collateralVin, + vout: collateralVout + } + + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, [customVinVout]) + const promise = sendTransaction(container, txn) + + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow("DeFiDRpcError: 'bad-txns-customtx, UpdateMasternodeTx: Masternode with collateral address as operator or owner already exists (code 16)', code: -26") + }) + + it('should fail to update another node with operator address that is already used', async () => { + const pubKey1 = await providers.ellipticPair.publicKey() + const collateralAddress1 = Bech32.fromPubKey(pubKey1, 'bcrt') + const addressDest1: P2WPKH = P2WPKH.fromAddress(RegTest, collateralAddress1, P2WPKH) + const addressDestKeyHash1 = addressDest1.pubKeyHash + + await jsonRpc.masternode.createMasternode(collateralAddress1) + await container.generate(20) + + await providers.randomizeEllipticPair() + await providers.setupMocks() + const pubKey2 = await providers.ellipticPair.publicKey() + const collateralAddress2 = Bech32.fromPubKey(pubKey2, 'bcrt') + + const masternodeId2 = await jsonRpc.masternode.createMasternode(collateralAddress2) + await container.generate(20) + + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId2, + updates: [ + { + updateType: 0x02, + address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash1 } + } + ] + } + + const script = await providers.elliptic.script() + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script) + const promise = sendTransaction(container, txn) + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow("DeFiDRpcError: 'bad-txns-customtx, UpdateMasternodeTx: Masternode with that operator address already exists (code 16)', code: -26") + }) + + it('should fail with incorrect collateral amount', async () => { + const pubKey = await providers.ellipticPair.publicKey() + const script1 = await providers.elliptic.script() + + const initialAddress = Bech32.fromPubKey(pubKey, 'bcrt') + const masternodeId = await jsonRpc.masternode.createMasternode(initialAddress) + await container.generate(20) + + const newAddress = await container.getNewAddress('', 'bech32') + const addressDest: P2WPKH = P2WPKH.fromAddress(RegTest, newAddress, P2WPKH) + const addressDestKeyHash = addressDest.pubKeyHash + const script2 = await fromAddress(newAddress, 'regtest')?.script as Script + + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [ + { + updateType: 0x01, + address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash } + } + ] + } + + const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) + const collateralPrevout: Prevout = { + txid: masternodeId, + vout: 1, + script: script1, + value: new BigNumber(5), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVout: Vout = { + script: script2, + value: new BigNumber(5), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVin: Vin = { + txid: masternodeId, + index: 1, + script: { stack: [] }, + sequence: 0xffffffff + } + const customVinVout = { + prevout: collateralPrevout, + vin: collateralVin, + vout: collateralVout + } + + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, [customVinVout]) + const promise = sendTransaction(container, txn) + + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow('DeFiDRpcError: \'bad-txns-customtx, UpdateMasternodeTx: Incorrect collateral amount (code 16)\', code: -26') + }) + + it('should not update masternode while in PRE_ENABLED or TRANSFERRING state', async () => { + const pubKey = await providers.ellipticPair.publicKey() + const script = await providers.elliptic.script() + const collateralAddress = Bech32.fromPubKey(pubKey, 'bcrt') + const masternodeId = await jsonRpc.masternode.createMasternode(collateralAddress) + await container.generate(1) + + { + const masternodes = await jsonRpc.masternode.listMasternodes() + expect(masternodes[masternodeId]).toStrictEqual(expect.objectContaining({ + state: 'PRE_ENABLED', + ownerAuthAddress: collateralAddress + })) + } + + const address = await container.getNewAddress('', 'bech32') + const addressDest: P2WPKH = P2WPKH.fromAddress(RegTest, address, P2WPKH) + const addressDestKeyHash = addressDest.pubKeyHash + + { + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [ + { + updateType: 0x02, + address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash } + } + ] + } + + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script) + const promise = sendTransaction(container, txn) + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow(`DeFiDRpcError: 'bad-txns-customtx, UpdateMasternodeTx: Masternode ${masternodeId} is not in 'ENABLED' state (code 16)', code: -26`) + } + + await container.generate(20) + + { + const masternodes = await jsonRpc.masternode.listMasternodes() + expect(masternodes[masternodeId]).toStrictEqual(expect.objectContaining({ + state: 'ENABLED', + ownerAuthAddress: collateralAddress + })) + } + + { + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [ + { + updateType: 0x02, + address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash } + } + ] + } + + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script) + const outs = await sendTransaction(container, txn) + + const encoded: string = OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode).asBuffer().toString('hex') + const expectedRedeemScript = `6a${encoded}` + expect(outs.length).toStrictEqual(2) + expect(outs[0].scriptPubKey.hex).toStrictEqual(expectedRedeemScript) + } + + { + const masternodes = await jsonRpc.masternode.listMasternodes() + expect(masternodes[masternodeId]).toStrictEqual(expect.objectContaining({ + state: 'TRANSFERRING', + ownerAuthAddress: collateralAddress + })) + } + + await container.generate(45) + + { + const masternodes = await jsonRpc.masternode.listMasternodes() + expect(masternodes[masternodeId]).toStrictEqual(expect.objectContaining({ + state: 'ENABLED', + ownerAuthAddress: collateralAddress + })) + } + }) +}) + +describe('Update Masternode (Multi-containers)', () => { + let tGroup: TestingGroup + const node: Testing[] = [] + let builderNode1: P2WPKHTransactionBuilder + let providersNode1: MockProviders + + beforeAll(async () => { + tGroup = TestingGroup.create(2, i => new MasterNodeRegTestContainer(RegTestFoundationKeys[i])) + await tGroup.start() + node.push(tGroup.get(0)) + node.push(tGroup.get(1)) + await node[0].container.waitForWalletCoinbaseMaturity() + await node[0].container.waitForWalletBalanceGTE(1000) + providersNode1 = await getProviders(node[1].container) + + await providersNode1.randomizeEllipticPair() + builderNode1 = new P2WPKHTransactionBuilder(providersNode1.fee, providersNode1.prevout, providersNode1.elliptic, RegTest) + await fundEllipticPair(node[1].container, providersNode1.ellipticPair, 10) + await providersNode1.setupMocks() + + await node[0].container.generate(1) + await node[1].container.generate(1) + }) + + afterAll(async () => { + await tGroup.stop() + }) + + it('should throw error if incorrect authorization is provided', async () => { + await node[0].container.waitForWalletCoinbaseMaturity() + await node[1].container.waitForWalletCoinbaseMaturity() + + // enable updating + await node[0].rpc.masternode.setGov({ + ATTRIBUTES: { + 'v0/params/feature/mn-setowneraddress': 'true', + 'v0/params/feature/mn-setoperatoraddress': 'true', + 'v0/params/feature/mn-setrewardaddress': 'true' + } + }) + await node[0].generate(1) + + const masternodeOwnerAddress0 = await node[0].rpc.wallet.getNewAddress() + const masternodeId0 = await node[0].rpc.masternode.createMasternode(masternodeOwnerAddress0) + await node[0].generate(100) // create masternode and wait for it to be enabled + + const operatorAddress0 = await node[0].rpc.wallet.getNewAddress() + const operatorAddressDest0: P2WPKH = P2WPKH.fromAddress(RegTest, operatorAddress0, P2WPKH) + const operatorAddressDestKeyHash0 = operatorAddressDest0.pubKeyHash + const operatorScript = await fromAddress(operatorAddress0, 'regtest')?.script as Script + await node[0].generate(4) + await tGroup.waitForSync() // container2 should know about the new masternode + + const updateMasternode0: UpdateMasternode = { + nodeId: masternodeId0, + updates: [ + { + updateType: 0x02, + address: { addressType: 0x04, addressPubKeyHash: operatorAddressDestKeyHash0 } + } + ] + } + const txn: TransactionSegWit = await builderNode1.masternode.update(updateMasternode0, operatorScript) + const promise = sendTransaction(node[1].container, txn) + + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow('DeFiDRpcError: \'bad-txns-customtx, UpdateMasternodeTx: tx must have at least one input from the owner (code 16)\', code: -26') + }) +}) \ No newline at end of file diff --git a/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts b/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts index b689cc758f..6d4f716d64 100644 --- a/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts +++ b/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts @@ -41,4 +41,25 @@ export class TxnBuilderMasternode extends P2WPKHTxnBuilder { changeScript ) } + + /** + * Build update masternode transaction + * + * @param {UpdateMasternode} updateMasternode transaction to create + * @param {Script} changeScript to send unspent to after deducting the (converted + fees) + * @param {Array<{ vin: Vin, vout: Vout, prevout: Prevout }>} [customVinVout = []] for custom vin and vout when updating owner address + * @return {Promise} + */ + async update ( + updateMasternode: UpdateMasternode, + changeScript: Script, + customVinVout?: Array<{ vin: Vin, vout: Vout, prevout: Prevout }> + ): Promise { + return await this.createDeFiTx( + OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode), + changeScript, + undefined, + customVinVout + ) + } } From 55049d6b130dc60826324036bcacef9eff4211f2 Mon Sep 17 00:00:00 2001 From: Mark Tan Date: Thu, 1 Dec 2022 18:30:09 +0800 Subject: [PATCH 02/10] Update masternode update function to manually build transaction --- .../txn/txn_builder_masternode_update.test.ts | 207 ++---------------- .../src/txn/txn_builder_masternode.ts | 89 +++++++- 2 files changed, 102 insertions(+), 194 deletions(-) diff --git a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts index 09935c0a9b..34405b0abe 100644 --- a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts +++ b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts @@ -1,13 +1,12 @@ import { JsonRpcClient } from '@defichain/jellyfish-api-jsonrpc' import { RegTest, RegTestFoundationKeys } from '@defichain/jellyfish-network' -import { OP_CODES, Script, TransactionSegWit, UpdateMasternode, Vin, Vout } from '@defichain/jellyfish-transaction' +import { OP_CODES, Script, TransactionSegWit, UpdateMasternode } from '@defichain/jellyfish-transaction' import { fromAddress, P2PKH, P2SH, P2WPKH } from '@defichain/jellyfish-address' import { DeFiDRpcError, MasterNodeRegTestContainer } from '@defichain/testcontainers' import { getProviders, MockProviders } from '../provider.mock' -import { P2WPKHTransactionBuilder, Prevout } from '../../src' +import { P2WPKHTransactionBuilder } from '../../src' import { fundEllipticPair, sendTransaction } from '../test.utils' import { Bech32 } from '@defichain/jellyfish-crypto' -import { BigNumber } from '@defichain/jellyfish-json' import { Testing, TestingGroup } from '@defichain/jellyfish-testing' describe('UpdateMasternode', () => { @@ -70,31 +69,10 @@ describe('UpdateMasternode', () => { } const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) - const collateralPrevout: Prevout = { - txid: masternodeId, - vout: 1, - script: script1, - value: new BigNumber(rawCollateralTx.vout[1].value), - tokenId: rawCollateralTx.vout[1].tokenId - } - const collateralVout: Vout = { - script: script2, - value: new BigNumber(rawCollateralTx.vout[1].value), - tokenId: rawCollateralTx.vout[1].tokenId - } - const collateralVin: Vin = { - txid: masternodeId, - index: 1, - script: { stack: [] }, - sequence: 0xffffffff - } - const customVinVout = { - prevout: collateralPrevout, - vin: collateralVin, - vout: collateralVout - } - - const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, [customVinVout]) + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, { + rawCollateralTx: rawCollateralTx, + newOwnerScript: script2 + }) const outs = await sendTransaction(container, txn) const encoded: string = OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode).asBuffer().toString('hex') @@ -287,31 +265,10 @@ describe('UpdateMasternode', () => { } const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) - const collateralPrevout: Prevout = { - txid: masternodeId, - vout: 1, - script: script1, - value: new BigNumber(rawCollateralTx.vout[1].value), - tokenId: rawCollateralTx.vout[1].tokenId - } - const collateralVout: Vout = { - script: script2, - value: new BigNumber(rawCollateralTx.vout[1].value), - tokenId: rawCollateralTx.vout[1].tokenId - } - const collateralVin: Vin = { - txid: masternodeId, - index: 1, - script: { stack: [] }, - sequence: 0xffffffff - } - const customVinVout = { - prevout: collateralPrevout, - vin: collateralVin, - vout: collateralVout - } - - const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, [customVinVout]) + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, { + rawCollateralTx: rawCollateralTx, + newOwnerScript: script2 + }) const outs = await sendTransaction(container, txn) const encoded: string = OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode).asBuffer().toString('hex') @@ -396,31 +353,10 @@ describe('UpdateMasternode', () => { } const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) - const collateralPrevout: Prevout = { - txid: masternodeId, - vout: 1, - script: script1, - value: new BigNumber(rawCollateralTx.vout[1].value), - tokenId: rawCollateralTx.vout[1].tokenId - } - const collateralVout: Vout = { - script: script2, - value: new BigNumber(rawCollateralTx.vout[1].value), - tokenId: rawCollateralTx.vout[1].tokenId - } - const collateralVin: Vin = { - txid: masternodeId, - index: 1, - script: { stack: [] }, - sequence: 0xffffffff - } - const customVinVout = { - prevout: collateralPrevout, - vin: collateralVin, - vout: collateralVout - } - - const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, [customVinVout]) + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, { + rawCollateralTx: rawCollateralTx, + newOwnerScript: script2 + }) const outs = await sendTransaction(container, txn) const encoded: string = OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode).asBuffer().toString('hex') @@ -554,31 +490,10 @@ describe('UpdateMasternode', () => { } const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) - const collateralPrevout: Prevout = { - txid: masternodeId, - vout: 1, - script: script, - value: new BigNumber(rawCollateralTx.vout[1].value), - tokenId: rawCollateralTx.vout[1].tokenId - } - const collateralVout: Vout = { - script: script2, - value: new BigNumber(rawCollateralTx.vout[1].value), - tokenId: rawCollateralTx.vout[1].tokenId - } - const collateralVin: Vin = { - txid: masternodeId, - index: 1, - script: { stack: [] }, - sequence: 0xffffffff - } - const customVinVout = { - prevout: collateralPrevout, - vin: collateralVin, - vout: collateralVout - } - - const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script, [customVinVout]) + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script, { + rawCollateralTx: rawCollateralTx, + newOwnerScript: script2 + }) const promise = sendTransaction(container, txn) await expect(promise).rejects.toThrow(DeFiDRpcError) await expect(promise).rejects.toThrow("DeFiDRpcError: 'bad-txns-customtx, UpdateMasternodeTx: Owner address must be P2PKH or P2WPKH type (code 16)', code: -26") @@ -699,31 +614,10 @@ describe('UpdateMasternode', () => { } const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) - const collateralPrevout: Prevout = { - txid: masternodeId, - vout: 1, - script: script1, - value: new BigNumber(rawCollateralTx.vout[1].value), - tokenId: rawCollateralTx.vout[1].tokenId - } - const collateralVout: Vout = { - script: script1, - value: new BigNumber(rawCollateralTx.vout[1].value), - tokenId: rawCollateralTx.vout[1].tokenId - } - const collateralVin: Vin = { - txid: masternodeId, - index: 1, - script: { stack: [] }, - sequence: 0xffffffff - } - const customVinVout = { - prevout: collateralPrevout, - vin: collateralVin, - vout: collateralVout - } - - const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, [customVinVout]) + const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, { + rawCollateralTx: rawCollateralTx, + newOwnerScript: script1 + }) const promise = sendTransaction(container, txn) await expect(promise).rejects.toThrow(DeFiDRpcError) @@ -764,61 +658,6 @@ describe('UpdateMasternode', () => { await expect(promise).rejects.toThrow("DeFiDRpcError: 'bad-txns-customtx, UpdateMasternodeTx: Masternode with that operator address already exists (code 16)', code: -26") }) - it('should fail with incorrect collateral amount', async () => { - const pubKey = await providers.ellipticPair.publicKey() - const script1 = await providers.elliptic.script() - - const initialAddress = Bech32.fromPubKey(pubKey, 'bcrt') - const masternodeId = await jsonRpc.masternode.createMasternode(initialAddress) - await container.generate(20) - - const newAddress = await container.getNewAddress('', 'bech32') - const addressDest: P2WPKH = P2WPKH.fromAddress(RegTest, newAddress, P2WPKH) - const addressDestKeyHash = addressDest.pubKeyHash - const script2 = await fromAddress(newAddress, 'regtest')?.script as Script - - const updateMasternode: UpdateMasternode = { - nodeId: masternodeId, - updates: [ - { - updateType: 0x01, - address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash } - } - ] - } - - const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) - const collateralPrevout: Prevout = { - txid: masternodeId, - vout: 1, - script: script1, - value: new BigNumber(5), - tokenId: rawCollateralTx.vout[1].tokenId - } - const collateralVout: Vout = { - script: script2, - value: new BigNumber(5), - tokenId: rawCollateralTx.vout[1].tokenId - } - const collateralVin: Vin = { - txid: masternodeId, - index: 1, - script: { stack: [] }, - sequence: 0xffffffff - } - const customVinVout = { - prevout: collateralPrevout, - vin: collateralVin, - vout: collateralVout - } - - const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, [customVinVout]) - const promise = sendTransaction(container, txn) - - await expect(promise).rejects.toThrow(DeFiDRpcError) - await expect(promise).rejects.toThrow('DeFiDRpcError: \'bad-txns-customtx, UpdateMasternodeTx: Incorrect collateral amount (code 16)\', code: -26') - }) - it('should not update masternode while in PRE_ENABLED or TRANSFERRING state', async () => { const pubKey = await providers.ellipticPair.publicKey() const script = await providers.elliptic.script() @@ -973,4 +812,4 @@ describe('Update Masternode (Multi-containers)', () => { await expect(promise).rejects.toThrow(DeFiDRpcError) await expect(promise).rejects.toThrow('DeFiDRpcError: \'bad-txns-customtx, UpdateMasternodeTx: tx must have at least one input from the owner (code 16)\', code: -26') }) -}) \ No newline at end of file +}) diff --git a/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts b/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts index 6d4f716d64..ab5e092ba9 100644 --- a/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts +++ b/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts @@ -1,11 +1,18 @@ +import { RawTransaction } from '@defichain/jellyfish-api-core/dist/category/rawtx' import { BigNumber } from '@defichain/jellyfish-json' import { CreateMasternode, + DeFiTransactionConstants, OP_CODES, ResignMasternode, Script, - TransactionSegWit + Transaction, + TransactionSegWit, + UpdateMasternode, + Vin, + Vout } from '@defichain/jellyfish-transaction' +import { Prevout } from '../provider' import { P2WPKHTxnBuilder } from './txn_builder' export class TxnBuilderMasternode extends P2WPKHTxnBuilder { @@ -47,19 +54,81 @@ export class TxnBuilderMasternode extends P2WPKHTxnBuilder { * * @param {UpdateMasternode} updateMasternode transaction to create * @param {Script} changeScript to send unspent to after deducting the (converted + fees) - * @param {Array<{ vin: Vin, vout: Vout, prevout: Prevout }>} [customVinVout = []] for custom vin and vout when updating owner address - * @return {Promise} + * @param {Object} [collateralInfo] needed when updating owner address + * @param {RawTransaction} collateralInfo.rawCollateralTx to get relevant data needed for collateral vin and vout + * @param {Script} collateralInfo.newOwnerScript for new owner address + * @return {Promise} */ async update ( updateMasternode: UpdateMasternode, changeScript: Script, - customVinVout?: Array<{ vin: Vin, vout: Vout, prevout: Prevout }> + collateralInfo?: { + rawCollateralTx: RawTransaction + newOwnerScript: Script + } ): Promise { - return await this.createDeFiTx( - OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode), - changeScript, - undefined, - customVinVout - ) + const minFee = new BigNumber(0.001) + const { prevouts, vin, total } = await this.collectPrevouts(minFee) + + const deFiOut: Vout = { + value: new BigNumber(0), + script: { + stack: [ + OP_CODES.OP_RETURN, + OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode) + ] + }, + tokenId: 0x00 + } + + const change: Vout = { + value: total, + script: changeScript, + tokenId: 0x00 + } + + const mergedVin = [...vin] + const mergedVout = [deFiOut, change] + const mergedPrevouts = [...prevouts] + + if (collateralInfo != null) { + const { rawCollateralTx, newOwnerScript } = collateralInfo + const { txid } = rawCollateralTx + + const collateralPrevout: Prevout = { + txid: txid, + vout: 1, + script: changeScript, + value: new BigNumber(rawCollateralTx.vout[1].value), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVout: Vout = { + script: newOwnerScript, + value: new BigNumber(rawCollateralTx.vout[1].value), + tokenId: rawCollateralTx.vout[1].tokenId + } + const collateralVin: Vin = { + txid: txid, + index: 1, + script: { stack: [] }, + sequence: 0xffffffff + } + + mergedVin.push(collateralVin) + mergedVout.splice(1, 0, collateralVout) + mergedPrevouts.push(collateralPrevout) + } + + const txn: Transaction = { + version: DeFiTransactionConstants.Version, + vin: mergedVin, + vout: mergedVout, + lockTime: 0x00000000 + } + + const fee = await this.calculateFee(txn) + change.value = total.minus(fee) + + return await this.sign(txn, mergedPrevouts) } } From 76209cd676d02b9006000fdb57579759ad9a15c9 Mon Sep 17 00:00:00 2001 From: Mark Tan Date: Thu, 1 Dec 2022 18:37:03 +0800 Subject: [PATCH 03/10] Check reward address after updating --- .../txn/txn_builder_masternode_update.test.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts index 34405b0abe..65d4bca375 100644 --- a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts +++ b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts @@ -404,7 +404,7 @@ describe('UpdateMasternode', () => { it('should update remove reward address', async () => { const pubKey = await providers.ellipticPair.publicKey() const collateralAddress = Bech32.fromPubKey(pubKey, 'bcrt') - const txid = await jsonRpc.masternode.createMasternode(collateralAddress) + const masternodeId = await jsonRpc.masternode.createMasternode(collateralAddress) await container.generate(20) const rewardAddress = await container.getNewAddress('', 'bech32') @@ -412,7 +412,7 @@ describe('UpdateMasternode', () => { const rewardAddressDestKeyHash = rewardAddressDest.pubKeyHash const setRewardAddressUpdate: UpdateMasternode = { - nodeId: txid, + nodeId: masternodeId, updates: [ { updateType: 0x03, @@ -427,8 +427,14 @@ describe('UpdateMasternode', () => { await container.generate(50) + { + const masternodes = await jsonRpc.masternode.listMasternodes() + const mn = masternodes[masternodeId] + expect(mn.rewardAddress).toStrictEqual(rewardAddress) + } + const updateMasternode: UpdateMasternode = { - nodeId: txid, + nodeId: masternodeId, updates: [ { updateType: 0x04, @@ -468,6 +474,14 @@ describe('UpdateMasternode', () => { }) expect(outs[1].value).toBeGreaterThan(6.99) expect(outs[1].value).toBeLessThan(7) + + await container.generate(50) + + { + const masternodes = await jsonRpc.masternode.listMasternodes() + const mn = masternodes[masternodeId] + expect(mn.rewardAddress).toStrictEqual('') + } }) it('should fail if address is P2SH', async () => { From dd9fffee18de345c94ad8cda300fcb18b50f0be1 Mon Sep 17 00:00:00 2001 From: Mark Tan Date: Fri, 2 Dec 2022 13:11:55 +0800 Subject: [PATCH 04/10] Add new test (WIP) --- .../txn/txn_builder_masternode_update.test.ts | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts index 65d4bca375..adbc72473f 100644 --- a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts +++ b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts @@ -756,6 +756,95 @@ describe('UpdateMasternode', () => { })) } }) + + it.only('should update owner address twice', async () => { + const pubKey = await providers.ellipticPair.publicKey() + const script1 = await providers.elliptic.script() + + const address1 = Bech32.fromPubKey(pubKey, 'bcrt') + const masternodeId = await jsonRpc.masternode.createMasternode(address1) + await container.generate(20) + + const address2 = await container.getNewAddress('', 'bech32') + const addressDestKeyHash2 = P2WPKH.fromAddress(RegTest, address2, P2WPKH).pubKeyHash + const script2 = await fromAddress(address2, 'regtest')?.script as Script + + const updateMasternode2: UpdateMasternode = { + nodeId: masternodeId, + updates: [{ updateType: 0x01, address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash2 }}] + } + + const rawCollateralTx2 = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) + const txn2: TransactionSegWit = await builder.masternode.update(updateMasternode2, script1, { + rawCollateralTx: rawCollateralTx2, + newOwnerScript: script2, + }) + const outs2 = await sendTransaction(container, txn2) + + await container.generate(70) + + const address3 = await container.getNewAddress('', 'bech32') + const addressDestKeyHash3 = P2WPKH.fromAddress(RegTest, address3, P2WPKH).pubKeyHash + const script3 = await fromAddress(address3, 'regtest')?.script as Script + + const updateMasternode: UpdateMasternode = { + nodeId: masternodeId, + updates: [{ updateType: 0x01, address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash3 }}] + } + + const masternodes = await jsonRpc.masternode.listMasternodes() + const mn = masternodes[masternodeId] + const rawCollateralTx3 = await jsonRpc.rawtx.getRawTransaction(mn.collateralTx, true) + + // fails here because collateralPrevout needs to use script1? + // after that a new error will appear non-mandatory-script-verify-flag (Script failed an OP_EQUALVERIFY operation) + const txn3: TransactionSegWit = await builder.masternode.update(updateMasternode, script2, { + rawCollateralTx: rawCollateralTx3, + newOwnerScript: script3, + }) + const outs3 = await sendTransaction(container, txn3) + + const encoded: string = OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode).asBuffer().toString('hex') + const expectedRedeemScript = `6a${encoded}` + + // expect(outs.length).toStrictEqual(3) + // expect(outs[0]).toStrictEqual({ + // n: 0, + // tokenId: 0, + // value: 0, + // scriptPubKey: { + // asm: expect.stringContaining('OP_RETURN 446654786d'), + // hex: expectedRedeemScript, + // type: 'nulldata' + // } + // }) + // expect(outs[1]).toStrictEqual({ + // n: 1, + // tokenId: 0, + // value: 2, + // scriptPubKey: { + // addresses: [newAddress], + // asm: expect.any(String), + // hex: expect.any(String), + // reqSigs: 1, + // type: 'witness_v0_keyhash' + // } + // }) + // expect(outs[2]).toStrictEqual({ + // n: 2, + // tokenId: 0, + // value: expect.any(Number), + // scriptPubKey: { + // addresses: [await providers.getAddress()], + // asm: expect.any(String), + // hex: expect.any(String), + // reqSigs: 1, + // type: 'witness_v0_keyhash' + // } + // }) + // expect(outs[2].value).toBeGreaterThan(6.99) + // expect(outs[2].value).toBeLessThan(7) + }) }) describe('Update Masternode (Multi-containers)', () => { From e8d640ad4884b8a181d9a6e1c4cde13bbeeafef2 Mon Sep 17 00:00:00 2001 From: Mark Tan Date: Fri, 2 Dec 2022 14:55:32 +0800 Subject: [PATCH 05/10] Update test to update owner address twice --- .../txn/txn_builder_masternode_update.test.ts | 111 ++++++++++-------- 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts index adbc72473f..a24404b8eb 100644 --- a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts +++ b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts @@ -6,7 +6,7 @@ import { DeFiDRpcError, MasterNodeRegTestContainer } from '@defichain/testcontai import { getProviders, MockProviders } from '../provider.mock' import { P2WPKHTransactionBuilder } from '../../src' import { fundEllipticPair, sendTransaction } from '../test.utils' -import { Bech32 } from '@defichain/jellyfish-crypto' +import { Bech32, Elliptic, WIF } from '@defichain/jellyfish-crypto' import { Testing, TestingGroup } from '@defichain/jellyfish-testing' describe('UpdateMasternode', () => { @@ -757,93 +757,106 @@ describe('UpdateMasternode', () => { } }) - it.only('should update owner address twice', async () => { + it('should update owner address twice', async () => { const pubKey = await providers.ellipticPair.publicKey() const script1 = await providers.elliptic.script() const address1 = Bech32.fromPubKey(pubKey, 'bcrt') const masternodeId = await jsonRpc.masternode.createMasternode(address1) + await container.generate(20) - const address2 = await container.getNewAddress('', 'bech32') + // create new elliptic pair for address, script and for updating provider's pair for signing later + const ellipticPair2 = Elliptic.fromPrivKey(Buffer.alloc(32, Math.random().toString(), 'ascii')) + const address2 = Bech32.fromPubKey(await ellipticPair2.publicKey(), 'bcrt') const addressDestKeyHash2 = P2WPKH.fromAddress(RegTest, address2, P2WPKH).pubKeyHash const script2 = await fromAddress(address2, 'regtest')?.script as Script + // add private key to container + const wif2 = WIF.encode(0xef, await ellipticPair2.privateKey()) + await container.call('importprivkey', [wif2]) + const updateMasternode2: UpdateMasternode = { nodeId: masternodeId, - updates: [{ updateType: 0x01, address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash2 }}] + updates: [{ updateType: 0x01, address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash2 } }] } + // masternodeId is used as collateral transaction id because collateralTx is empty after creating masternode const rawCollateralTx2 = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) - const txn2: TransactionSegWit = await builder.masternode.update(updateMasternode2, script1, { + const txn2: TransactionSegWit = await builder.masternode.update(updateMasternode2, script1, { rawCollateralTx: rawCollateralTx2, - newOwnerScript: script2, + newOwnerScript: script2 }) - const outs2 = await sendTransaction(container, txn2) + await sendTransaction(container, txn2) await container.generate(70) + // update provider's elliptic pair for signing next transaction with address2 + await providers.setEllipticPair(ellipticPair2) + await fundEllipticPair(container, providers.ellipticPair, 10) + + await container.generate(1) + const address3 = await container.getNewAddress('', 'bech32') const addressDestKeyHash3 = P2WPKH.fromAddress(RegTest, address3, P2WPKH).pubKeyHash const script3 = await fromAddress(address3, 'regtest')?.script as Script const updateMasternode: UpdateMasternode = { nodeId: masternodeId, - updates: [{ updateType: 0x01, address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash3 }}] + updates: [{ updateType: 0x01, address: { addressType: 0x04, addressPubKeyHash: addressDestKeyHash3 } }] } + // collateral transaction has id, search in listMasternodes const masternodes = await jsonRpc.masternode.listMasternodes() const mn = masternodes[masternodeId] const rawCollateralTx3 = await jsonRpc.rawtx.getRawTransaction(mn.collateralTx, true) - // fails here because collateralPrevout needs to use script1? - // after that a new error will appear non-mandatory-script-verify-flag (Script failed an OP_EQUALVERIFY operation) - const txn3: TransactionSegWit = await builder.masternode.update(updateMasternode, script2, { + const txn3: TransactionSegWit = await builder.masternode.update(updateMasternode, script2, { rawCollateralTx: rawCollateralTx3, - newOwnerScript: script3, + newOwnerScript: script3 }) const outs3 = await sendTransaction(container, txn3) const encoded: string = OP_CODES.OP_DEFI_TX_UPDATE_MASTER_NODE(updateMasternode).asBuffer().toString('hex') const expectedRedeemScript = `6a${encoded}` - // expect(outs.length).toStrictEqual(3) - // expect(outs[0]).toStrictEqual({ - // n: 0, - // tokenId: 0, - // value: 0, - // scriptPubKey: { - // asm: expect.stringContaining('OP_RETURN 446654786d'), - // hex: expectedRedeemScript, - // type: 'nulldata' - // } - // }) - // expect(outs[1]).toStrictEqual({ - // n: 1, - // tokenId: 0, - // value: 2, - // scriptPubKey: { - // addresses: [newAddress], - // asm: expect.any(String), - // hex: expect.any(String), - // reqSigs: 1, - // type: 'witness_v0_keyhash' - // } - // }) - // expect(outs[2]).toStrictEqual({ - // n: 2, - // tokenId: 0, - // value: expect.any(Number), - // scriptPubKey: { - // addresses: [await providers.getAddress()], - // asm: expect.any(String), - // hex: expect.any(String), - // reqSigs: 1, - // type: 'witness_v0_keyhash' - // } - // }) - // expect(outs[2].value).toBeGreaterThan(6.99) - // expect(outs[2].value).toBeLessThan(7) + expect(outs3.length).toStrictEqual(3) + expect(outs3[0]).toStrictEqual({ + n: 0, + tokenId: 0, + value: 0, + scriptPubKey: { + asm: expect.stringContaining('OP_RETURN 446654786d'), + hex: expectedRedeemScript, + type: 'nulldata' + } + }) + expect(outs3[1]).toStrictEqual({ + n: 1, + tokenId: 0, + value: 2, + scriptPubKey: { + addresses: [address3], + asm: expect.any(String), + hex: expect.any(String), + reqSigs: 1, + type: 'witness_v0_keyhash' + } + }) + expect(outs3[2]).toStrictEqual({ + n: 2, + tokenId: 0, + value: expect.any(Number), + scriptPubKey: { + addresses: [await providers.getAddress()], + asm: expect.any(String), + hex: expect.any(String), + reqSigs: 1, + type: 'witness_v0_keyhash' + } + }) + expect(outs3[2].value).toBeGreaterThan(9.99) + expect(outs3[2].value).toBeLessThan(10) }) }) From 6e8d56f06cdc9916fcf4971e8bcdf75764eec88c Mon Sep 17 00:00:00 2001 From: Mark Tan Date: Fri, 2 Dec 2022 15:00:12 +0800 Subject: [PATCH 06/10] Minor fix --- .../src/txn/txn_builder_masternode.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts b/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts index ab5e092ba9..91c9143f5c 100644 --- a/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts +++ b/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts @@ -91,7 +91,7 @@ export class TxnBuilderMasternode extends P2WPKHTxnBuilder { const mergedVout = [deFiOut, change] const mergedPrevouts = [...prevouts] - if (collateralInfo != null) { + if (collateralInfo !== null && collateralInfo !== undefined) { const { rawCollateralTx, newOwnerScript } = collateralInfo const { txid } = rawCollateralTx From 6f4813bbca81f761589900b3c540f9cad18cb7f5 Mon Sep 17 00:00:00 2001 From: Mark Tan Date: Fri, 9 Dec 2022 11:19:35 +0800 Subject: [PATCH 07/10] Update tests to check address after update --- .../txn/txn_builder_masternode_update.test.ts | 72 +++++++++++++++++-- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts index a24404b8eb..637f481b89 100644 --- a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts +++ b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts @@ -115,12 +115,20 @@ describe('UpdateMasternode', () => { }) expect(outs[2].value).toBeGreaterThan(6.99) expect(outs[2].value).toBeLessThan(7) + + await container.generate(60) + + const masternodes = await jsonRpc.masternode.listMasternodes() + expect(masternodes[masternodeId]).toStrictEqual(expect.objectContaining({ + state: 'ENABLED', + ownerAuthAddress: newAddress + })) }) it('should update operator address with P2WPKH address', async () => { const pubKey = await providers.ellipticPair.publicKey() const collateralAddress = Bech32.fromPubKey(pubKey, 'bcrt') - const txid = await jsonRpc.masternode.createMasternode(collateralAddress) + const masternodeId = await jsonRpc.masternode.createMasternode(collateralAddress) await container.generate(20) const address = await container.getNewAddress('', 'bech32') @@ -128,7 +136,7 @@ describe('UpdateMasternode', () => { const addressDestKeyHash = addressDest.pubKeyHash const updateMasternode: UpdateMasternode = { - nodeId: txid, + nodeId: masternodeId, updates: [ { updateType: 0x02, @@ -169,12 +177,20 @@ describe('UpdateMasternode', () => { }) expect(outs[1].value).toBeGreaterThan(6.99) expect(outs[1].value).toBeLessThan(7) + + await container.generate(40) + + const masternodes = await jsonRpc.masternode.listMasternodes() + expect(masternodes[masternodeId]).toStrictEqual(expect.objectContaining({ + state: 'ENABLED', + operatorAuthAddress: address + })) }) it('should update reward address with P2WPKH address', async () => { const pubKey = await providers.ellipticPair.publicKey() const collateralAddress = Bech32.fromPubKey(pubKey, 'bcrt') - const txid = await jsonRpc.masternode.createMasternode(collateralAddress) + const masternodeId = await jsonRpc.masternode.createMasternode(collateralAddress) await container.generate(20) const address = await container.getNewAddress('', 'bech32') @@ -182,7 +198,7 @@ describe('UpdateMasternode', () => { const addressDestKeyHash = addressDest.pubKeyHash const updateMasternode: UpdateMasternode = { - nodeId: txid, + nodeId: masternodeId, updates: [ { updateType: 0x03, @@ -223,6 +239,14 @@ describe('UpdateMasternode', () => { }) expect(outs[1].value).toBeGreaterThan(6.99) expect(outs[1].value).toBeLessThan(7) + + await container.generate(40) + + const masternodes = await jsonRpc.masternode.listMasternodes() + expect(masternodes[masternodeId]).toStrictEqual(expect.objectContaining({ + state: 'ENABLED', + rewardAddress: address + })) }) it('should update multiple addresses simultaneously with P2WPKH address', async () => { @@ -311,6 +335,16 @@ describe('UpdateMasternode', () => { }) expect(outs[2].value).toBeGreaterThan(6.99) expect(outs[2].value).toBeLessThan(7) + + await container.generate(60) + + const masternodes = await jsonRpc.masternode.listMasternodes() + expect(masternodes[masternodeId]).toStrictEqual(expect.objectContaining({ + state: 'ENABLED', + ownerAuthAddress: ownerAddress, + operatorAuthAddress: operatorAddress, + rewardAddress: rewardAddress + })) }) it('should update multiple addresses simultaneously with P2PKH address', async () => { @@ -399,6 +433,16 @@ describe('UpdateMasternode', () => { }) expect(outs[2].value).toBeGreaterThan(6.99) expect(outs[2].value).toBeLessThan(7) + + await container.generate(60) + + const masternodes = await jsonRpc.masternode.listMasternodes() + expect(masternodes[masternodeId]).toStrictEqual(expect.objectContaining({ + state: 'ENABLED', + ownerAuthAddress: ownerAddress, + operatorAuthAddress: operatorAddress, + rewardAddress: rewardAddress + })) }) it('should update remove reward address', async () => { @@ -789,7 +833,15 @@ describe('UpdateMasternode', () => { }) await sendTransaction(container, txn2) - await container.generate(70) + await container.generate(60) + + { + const masternodes = await jsonRpc.masternode.listMasternodes() + expect(masternodes[masternodeId]).toStrictEqual(expect.objectContaining({ + state: 'ENABLED', + ownerAuthAddress: address2 + })) + } // update provider's elliptic pair for signing next transaction with address2 await providers.setEllipticPair(ellipticPair2) @@ -857,6 +909,16 @@ describe('UpdateMasternode', () => { }) expect(outs3[2].value).toBeGreaterThan(9.99) expect(outs3[2].value).toBeLessThan(10) + + await container.generate(60) + + { + const masternodes = await jsonRpc.masternode.listMasternodes() + expect(masternodes[masternodeId]).toStrictEqual(expect.objectContaining({ + state: 'ENABLED', + ownerAuthAddress: address3 + })) + } }) }) From 088cf00895e2e185c68370751f09a22f4fe44084 Mon Sep 17 00:00:00 2001 From: Mark Tan Date: Fri, 9 Dec 2022 11:21:08 +0800 Subject: [PATCH 08/10] Minor change and throw error if raw collateral tx does not have vout 1 --- .../src/txn/txn_builder_masternode.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts b/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts index 91c9143f5c..26cc05168d 100644 --- a/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts +++ b/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts @@ -93,10 +93,13 @@ export class TxnBuilderMasternode extends P2WPKHTxnBuilder { if (collateralInfo !== null && collateralInfo !== undefined) { const { rawCollateralTx, newOwnerScript } = collateralInfo - const { txid } = rawCollateralTx + + if (rawCollateralTx.vout?.length < 2) { + throw new Error('Invalid collateral tx, no vout for collateral') + } const collateralPrevout: Prevout = { - txid: txid, + txid: rawCollateralTx.txid, vout: 1, script: changeScript, value: new BigNumber(rawCollateralTx.vout[1].value), @@ -108,7 +111,7 @@ export class TxnBuilderMasternode extends P2WPKHTxnBuilder { tokenId: rawCollateralTx.vout[1].tokenId } const collateralVin: Vin = { - txid: txid, + txid: rawCollateralTx.txid, index: 1, script: { stack: [] }, sequence: 0xffffffff From 35dcc9e4f79b493a6ad82150ed13106112517cb4 Mon Sep 17 00:00:00 2001 From: Mark Tan Date: Wed, 11 Jan 2023 10:58:44 +0800 Subject: [PATCH 09/10] Remove raw transaction type --- .../txn/txn_builder_masternode_update.test.ts | 28 +++++++++++---- .../src/txn/txn_builder_masternode.ts | 35 +++++++++---------- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts index 637f481b89..0e0ca88fc6 100644 --- a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts +++ b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts @@ -70,7 +70,9 @@ describe('UpdateMasternode', () => { const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, { - rawCollateralTx: rawCollateralTx, + txid: rawCollateralTx.txid, + value: rawCollateralTx.vout[1].value, + tokenId: rawCollateralTx.vout[1].tokenId, newOwnerScript: script2 }) const outs = await sendTransaction(container, txn) @@ -290,7 +292,9 @@ describe('UpdateMasternode', () => { const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, { - rawCollateralTx: rawCollateralTx, + txid: rawCollateralTx.txid, + value: rawCollateralTx.vout[1].value, + tokenId: rawCollateralTx.vout[1].tokenId, newOwnerScript: script2 }) const outs = await sendTransaction(container, txn) @@ -388,7 +392,9 @@ describe('UpdateMasternode', () => { const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, { - rawCollateralTx: rawCollateralTx, + txid: rawCollateralTx.txid, + value: rawCollateralTx.vout[1].value, + tokenId: rawCollateralTx.vout[1].tokenId, newOwnerScript: script2 }) const outs = await sendTransaction(container, txn) @@ -549,7 +555,9 @@ describe('UpdateMasternode', () => { const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script, { - rawCollateralTx: rawCollateralTx, + txid: rawCollateralTx.txid, + value: rawCollateralTx.vout[1].value, + tokenId: rawCollateralTx.vout[1].tokenId, newOwnerScript: script2 }) const promise = sendTransaction(container, txn) @@ -673,7 +681,9 @@ describe('UpdateMasternode', () => { const rawCollateralTx = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, { - rawCollateralTx: rawCollateralTx, + txid: rawCollateralTx.txid, + value: rawCollateralTx.vout[1].value, + tokenId: rawCollateralTx.vout[1].tokenId, newOwnerScript: script1 }) const promise = sendTransaction(container, txn) @@ -828,7 +838,9 @@ describe('UpdateMasternode', () => { // masternodeId is used as collateral transaction id because collateralTx is empty after creating masternode const rawCollateralTx2 = await jsonRpc.rawtx.getRawTransaction(masternodeId, true) const txn2: TransactionSegWit = await builder.masternode.update(updateMasternode2, script1, { - rawCollateralTx: rawCollateralTx2, + txid: rawCollateralTx2.txid, + value: rawCollateralTx2.vout[1].value, + tokenId: rawCollateralTx2.vout[1].tokenId, newOwnerScript: script2 }) await sendTransaction(container, txn2) @@ -864,7 +876,9 @@ describe('UpdateMasternode', () => { const rawCollateralTx3 = await jsonRpc.rawtx.getRawTransaction(mn.collateralTx, true) const txn3: TransactionSegWit = await builder.masternode.update(updateMasternode, script2, { - rawCollateralTx: rawCollateralTx3, + txid: rawCollateralTx3.txid, + value: rawCollateralTx3.vout[1].value, + tokenId: rawCollateralTx3.vout[1].tokenId, newOwnerScript: script3 }) const outs3 = await sendTransaction(container, txn3) diff --git a/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts b/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts index 26cc05168d..41e738c493 100644 --- a/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts +++ b/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts @@ -1,4 +1,3 @@ -import { RawTransaction } from '@defichain/jellyfish-api-core/dist/category/rawtx' import { BigNumber } from '@defichain/jellyfish-json' import { CreateMasternode, @@ -54,16 +53,20 @@ export class TxnBuilderMasternode extends P2WPKHTxnBuilder { * * @param {UpdateMasternode} updateMasternode transaction to create * @param {Script} changeScript to send unspent to after deducting the (converted + fees) - * @param {Object} [collateralInfo] needed when updating owner address - * @param {RawTransaction} collateralInfo.rawCollateralTx to get relevant data needed for collateral vin and vout - * @param {Script} collateralInfo.newOwnerScript for new owner address + * @param {Object} [collateral] needed when updating owner address + * @param {string} collateral.txid collateral txid + * @param {BigNumber} collateral.value collateral amount + * @param {number} collateral.tokenId collateral tokenId + * @param {Script} collateral.newOwnerScript for new owner address * @return {Promise} */ async update ( updateMasternode: UpdateMasternode, changeScript: Script, - collateralInfo?: { - rawCollateralTx: RawTransaction + collateral?: { + txid: string + value: BigNumber + tokenId: number newOwnerScript: Script } ): Promise { @@ -91,27 +94,23 @@ export class TxnBuilderMasternode extends P2WPKHTxnBuilder { const mergedVout = [deFiOut, change] const mergedPrevouts = [...prevouts] - if (collateralInfo !== null && collateralInfo !== undefined) { - const { rawCollateralTx, newOwnerScript } = collateralInfo - - if (rawCollateralTx.vout?.length < 2) { - throw new Error('Invalid collateral tx, no vout for collateral') - } + if (collateral !== null && collateral !== undefined) { + const { txid, value, tokenId, newOwnerScript } = collateral const collateralPrevout: Prevout = { - txid: rawCollateralTx.txid, + txid, vout: 1, script: changeScript, - value: new BigNumber(rawCollateralTx.vout[1].value), - tokenId: rawCollateralTx.vout[1].tokenId + value: new BigNumber(value), + tokenId } const collateralVout: Vout = { script: newOwnerScript, - value: new BigNumber(rawCollateralTx.vout[1].value), - tokenId: rawCollateralTx.vout[1].tokenId + value: new BigNumber(value), + tokenId } const collateralVin: Vin = { - txid: rawCollateralTx.txid, + txid, index: 1, script: { stack: [] }, sequence: 0xffffffff From 8953a709766f83c102350ad31be7bca33c6ab0cf Mon Sep 17 00:00:00 2001 From: Mark Tan Date: Fri, 13 Jan 2023 10:54:55 +0800 Subject: [PATCH 10/10] Remove token id param and use 0x00 for input --- .../__tests__/txn/txn_builder_masternode_update.test.ts | 7 ------- .../src/txn/txn_builder_masternode.ts | 8 +++----- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts index 0e0ca88fc6..239f741b30 100644 --- a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts +++ b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_masternode_update.test.ts @@ -72,7 +72,6 @@ describe('UpdateMasternode', () => { const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, { txid: rawCollateralTx.txid, value: rawCollateralTx.vout[1].value, - tokenId: rawCollateralTx.vout[1].tokenId, newOwnerScript: script2 }) const outs = await sendTransaction(container, txn) @@ -294,7 +293,6 @@ describe('UpdateMasternode', () => { const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, { txid: rawCollateralTx.txid, value: rawCollateralTx.vout[1].value, - tokenId: rawCollateralTx.vout[1].tokenId, newOwnerScript: script2 }) const outs = await sendTransaction(container, txn) @@ -394,7 +392,6 @@ describe('UpdateMasternode', () => { const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, { txid: rawCollateralTx.txid, value: rawCollateralTx.vout[1].value, - tokenId: rawCollateralTx.vout[1].tokenId, newOwnerScript: script2 }) const outs = await sendTransaction(container, txn) @@ -557,7 +554,6 @@ describe('UpdateMasternode', () => { const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script, { txid: rawCollateralTx.txid, value: rawCollateralTx.vout[1].value, - tokenId: rawCollateralTx.vout[1].tokenId, newOwnerScript: script2 }) const promise = sendTransaction(container, txn) @@ -683,7 +679,6 @@ describe('UpdateMasternode', () => { const txn: TransactionSegWit = await builder.masternode.update(updateMasternode, script1, { txid: rawCollateralTx.txid, value: rawCollateralTx.vout[1].value, - tokenId: rawCollateralTx.vout[1].tokenId, newOwnerScript: script1 }) const promise = sendTransaction(container, txn) @@ -840,7 +835,6 @@ describe('UpdateMasternode', () => { const txn2: TransactionSegWit = await builder.masternode.update(updateMasternode2, script1, { txid: rawCollateralTx2.txid, value: rawCollateralTx2.vout[1].value, - tokenId: rawCollateralTx2.vout[1].tokenId, newOwnerScript: script2 }) await sendTransaction(container, txn2) @@ -878,7 +872,6 @@ describe('UpdateMasternode', () => { const txn3: TransactionSegWit = await builder.masternode.update(updateMasternode, script2, { txid: rawCollateralTx3.txid, value: rawCollateralTx3.vout[1].value, - tokenId: rawCollateralTx3.vout[1].tokenId, newOwnerScript: script3 }) const outs3 = await sendTransaction(container, txn3) diff --git a/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts b/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts index 41e738c493..221e6f3f2a 100644 --- a/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts +++ b/packages/jellyfish-transaction-builder/src/txn/txn_builder_masternode.ts @@ -56,7 +56,6 @@ export class TxnBuilderMasternode extends P2WPKHTxnBuilder { * @param {Object} [collateral] needed when updating owner address * @param {string} collateral.txid collateral txid * @param {BigNumber} collateral.value collateral amount - * @param {number} collateral.tokenId collateral tokenId * @param {Script} collateral.newOwnerScript for new owner address * @return {Promise} */ @@ -66,7 +65,6 @@ export class TxnBuilderMasternode extends P2WPKHTxnBuilder { collateral?: { txid: string value: BigNumber - tokenId: number newOwnerScript: Script } ): Promise { @@ -95,19 +93,19 @@ export class TxnBuilderMasternode extends P2WPKHTxnBuilder { const mergedPrevouts = [...prevouts] if (collateral !== null && collateral !== undefined) { - const { txid, value, tokenId, newOwnerScript } = collateral + const { txid, value, newOwnerScript } = collateral const collateralPrevout: Prevout = { txid, vout: 1, script: changeScript, value: new BigNumber(value), - tokenId + tokenId: 0x00 } const collateralVout: Vout = { script: newOwnerScript, value: new BigNumber(value), - tokenId + tokenId: 0x00 } const collateralVin: Vin = { txid,