From 29a71cd1bd32c7a92df16bffef9640aa39c858dd Mon Sep 17 00:00:00 2001 From: maikelmclauflin Date: Wed, 19 Feb 2020 21:54:19 -0600 Subject: [PATCH 1/6] added ability to define which utxos to use --- packages/cashscript/src/Instance.ts | 36 +++++++++++++++++++++-- packages/cashscript/src/Transaction.ts | 8 +++-- packages/cashscript/src/interfaces.ts | 13 ++++++++ packages/cashscript/src/util.ts | 18 +++++++++++- packages/cashscript/test/Instance.test.ts | 16 ++++++++++ 5 files changed, 86 insertions(+), 5 deletions(-) diff --git a/packages/cashscript/src/Instance.ts b/packages/cashscript/src/Instance.ts index a7f0159d..f5eaa4f1 100644 --- a/packages/cashscript/src/Instance.ts +++ b/packages/cashscript/src/Instance.ts @@ -1,6 +1,14 @@ import { BITBOX } from 'bitbox-sdk'; -import { AddressDetailsResult } from 'bitcoin-com-rest'; +import { + AddressDetailsResult, + AddressUtxoResult, + AddressUnconfirmedResult, +} from 'bitcoin-com-rest'; import { Artifact, Script, AbiFunction } from 'cashc'; +import { + Utxo, + UnconfirmedUtxo, +} from './interfaces'; import { bitbox, AddressUtil, @@ -10,7 +18,11 @@ import { import { Transaction } from './Transaction'; import { ContractFunction } from './Contract'; import { Parameter, encodeParameter } from './Parameter'; -import { countOpcodes, calculateBytesize } from './util'; +import { + countOpcodes, + calculateBytesize, + unconfirmedToUtxo, +} from './util'; export class Instance { name: string; @@ -29,6 +41,26 @@ export class Instance { return details.balanceSat + details.unconfirmedBalanceSat; } + async getUnconfirmed(): Promise { + const { utxos } = await this.bitbox.Address + .unconfirmed(this.address) as AddressUnconfirmedResult; + return utxos; + } + + async getUtxo(): Promise { + const { utxos } = await this.bitbox.Address.utxo(this.address) as AddressUtxoResult; + return utxos; + } + + async getUtxos(excludeUnconfirmed?: boolean): Promise { + const promises = [this.getUtxo()]; + if (!excludeUnconfirmed) { + promises.push(this.getUnconfirmed().then(unconfirmed => unconfirmed.map(unconfirmedToUtxo))); + } + const results = await Promise.all(promises); + return results.reduce((memo, utxos) => memo.concat(utxos), []); + } + constructor( artifact: Artifact, private redeemScript: Script, diff --git a/packages/cashscript/src/Transaction.ts b/packages/cashscript/src/Transaction.ts index a7a51339..c2e33212 100644 --- a/packages/cashscript/src/Transaction.ts +++ b/packages/cashscript/src/Transaction.ts @@ -105,7 +105,7 @@ export class Transaction { private async createTransaction( outs: Output[], options?: TxOptions, - ): Promise<{ tx: any, inputs: Utxo[]}> { + ): Promise<{ tx: any, inputs: Utxo[] }> { const sequence = options?.age ? this.builder.bip68.encode({ blocks: options.age }) : 0xfffffffe; @@ -169,8 +169,12 @@ export class Transaction { hardcodedFee?: number, minChange: number = DUST_LIMIT, satsPerByte: number = 1.0, + _utxos?: Utxo[], ): Promise<{ inputs: Utxo[], outputs: OutputForBuilder[] }> { - const { utxos } = await this.bitbox.Address.utxo(this.address) as AddressUtxoResult; + let utxos = _utxos; + if (!utxos) { + ({ utxos } = await this.bitbox.Address.utxo(this.address) as AddressUtxoResult); + } // Use a placeholder script with 65-length Buffer in the place of signatures // and a correctly sized preimage Buffer if the function is a covenant diff --git a/packages/cashscript/src/interfaces.ts b/packages/cashscript/src/interfaces.ts index 94972251..d7941002 100644 --- a/packages/cashscript/src/interfaces.ts +++ b/packages/cashscript/src/interfaces.ts @@ -7,6 +7,18 @@ export interface Utxo { confirmations: number; } +export interface UnconfirmedUtxo { + txid: string; + vout: number; + scriptPubKey: string; + amount: number; + satoshis: number; + confirmations: number; + ts: number; + legacyAddress: string; + cashAddress: string; +} + export type Output = Recipient | OpReturn; export interface OutputForBuilder { @@ -31,6 +43,7 @@ export interface TxOptions { age?: number; fee?: number; minChange?: number; + inputs?: Utxo[]; } export enum SignatureAlgorithm { diff --git a/packages/cashscript/src/util.ts b/packages/cashscript/src/util.ts index 7593d2a9..85795b1a 100644 --- a/packages/cashscript/src/util.ts +++ b/packages/cashscript/src/util.ts @@ -3,7 +3,12 @@ import { Data, Op, } from 'cashc'; -import { Utxo, OpReturn, OutputForBuilder } from './interfaces'; +import { + Utxo, + OpReturn, + OutputForBuilder, + UnconfirmedUtxo, +} from './interfaces'; import { ScriptUtil, CryptoUtil } from './BITBOX'; import { P2PKH_OUTPUT_SIZE, VERSION_SIZE, LOCKTIME_SIZE } from './constants'; import { @@ -184,3 +189,14 @@ function getPushDataOpcode(data: Buffer): Buffer { throw Error('Pushdata too large'); } // //////////////////////////////////////////////////////////////////////////// + +export function unconfirmedToUtxo(unconfirmed: UnconfirmedUtxo): Utxo { + return { + txid: unconfirmed.txid, + vout: unconfirmed.vout, + amount: unconfirmed.amount, + satoshis: unconfirmed.satoshis, + confirmations: 0, + height: 0, + }; +} diff --git a/packages/cashscript/test/Instance.test.ts b/packages/cashscript/test/Instance.test.ts index 6ef5e99c..3eb679c3 100644 --- a/packages/cashscript/test/Instance.test.ts +++ b/packages/cashscript/test/Instance.test.ts @@ -1,5 +1,8 @@ import path from 'path'; import { Contract, Instance, Sig } from '../src'; +import { + unconfirmedToUtxo, +} from '../src/util'; import { alicePk, alice, @@ -27,6 +30,19 @@ describe('Instance', () => { }); }); + describe('Contract Utxo inputs', () => { + it('should return an address\'s utxos', async () => { + const P2PKH = Contract.import(path.join(__dirname, 'fixture', 'p2pkh.json'), 'testnet'); + const instance = P2PKH.new(alicePkh); + + const unconfirmed = await instance.getUnconfirmed(); + const utxos = await instance.getUtxo(); + expect(await instance.getUtxos()).toEqual(utxos.concat( + unconfirmed.map(unconfirmedToUtxo), + )); + }); + }); + describe('Contract functions', () => { let instance: Instance; let bbInstance: Instance; From 387a25a04f747bee5ac75341e9ae84c7de771200 Mon Sep 17 00:00:00 2001 From: maikelmclauflin Date: Thu, 20 Feb 2020 20:45:37 -0600 Subject: [PATCH 2/6] finds utxo that was passed into send --- packages/cashscript/src/Instance.ts | 4 ++- packages/cashscript/src/Transaction.ts | 8 ++++- packages/cashscript/src/interfaces.ts | 33 +++++++++++++++++ packages/cashscript/test/e2e/P2PKH.test.ts | 41 +++++++++++++++++++++- 4 files changed, 83 insertions(+), 3 deletions(-) diff --git a/packages/cashscript/src/Instance.ts b/packages/cashscript/src/Instance.ts index f5eaa4f1..60c0ac36 100644 --- a/packages/cashscript/src/Instance.ts +++ b/packages/cashscript/src/Instance.ts @@ -55,7 +55,9 @@ export class Instance { async getUtxos(excludeUnconfirmed?: boolean): Promise { const promises = [this.getUtxo()]; if (!excludeUnconfirmed) { - promises.push(this.getUnconfirmed().then(unconfirmed => unconfirmed.map(unconfirmedToUtxo))); + promises.push(this.getUnconfirmed().then(unconfirmed => + unconfirmed.map(unconfirmedToUtxo) + )); } const results = await Promise.all(promises); return results.reduce((memo, utxos) => memo.concat(utxos), []); diff --git a/packages/cashscript/src/Transaction.ts b/packages/cashscript/src/Transaction.ts index c2e33212..bd4b4969 100644 --- a/packages/cashscript/src/Transaction.ts +++ b/packages/cashscript/src/Transaction.ts @@ -115,7 +115,13 @@ export class Transaction { this.builder.setLockTime(locktime); - const { inputs, outputs } = await this.getInputsAndOutputs(outs, options?.fee); + const { inputs, outputs } = await this.getInputsAndOutputs( + outs, + options?.fee, + DUST_LIMIT, + 1.0, + options?.inputs, + ); inputs.forEach((utxo) => { this.builder.addInput(utxo.txid, utxo.vout, sequence); diff --git a/packages/cashscript/src/interfaces.ts b/packages/cashscript/src/interfaces.ts index d7941002..afa0cb54 100644 --- a/packages/cashscript/src/interfaces.ts +++ b/packages/cashscript/src/interfaces.ts @@ -38,6 +38,39 @@ export interface OpReturn { opReturn: string[]; } +export interface ScriptSigDetails { + asm: string; + hex: string; +} + +export interface TxnDetailValueIn { + cashAddress: string; + legacyAddress: string; + n: number; + scriptSig: ScriptSigDetails; + sequence: number; + txid: string; + value: number; + vout: number; +} + +export interface ScriptPubKeyDetails { + addresses: string[]; + cashAddrs: string[]; + asm: string; + hex: string; + type: string; +} + +export interface TxnDetailValueOut { + n: number; + scriptPubKey: ScriptPubKeyDetails; + spendHeight: null | number; + spendIndex: null | number; + spendTxId: null | number; + value: string; +} + export interface TxOptions { time?: number; age?: number; diff --git a/packages/cashscript/test/e2e/P2PKH.test.ts b/packages/cashscript/test/e2e/P2PKH.test.ts index d20186c5..d0d3c4d0 100644 --- a/packages/cashscript/test/e2e/P2PKH.test.ts +++ b/packages/cashscript/test/e2e/P2PKH.test.ts @@ -7,7 +7,12 @@ import { bob, } from '../fixture/vars'; import { getTxOutputs } from '../test-util'; -import { isOpReturn } from '../../src/interfaces'; +import { + isOpReturn, + Utxo, + TxnDetailValueOut, + TxnDetailValueIn, +} from '../../src/interfaces'; import { createOpReturnOutput } from '../../src/util'; import { FailedSigCheckError, Reason } from '../../src/Errors'; @@ -50,6 +55,40 @@ describe('P2PKH', () => { const txOutputs = getTxOutputs(tx); expect(txOutputs).toEqual(expect.arrayContaining([{ to, amount }])); }); + + it('should succeed when defining its own utxos', async () => { + expect.hasAssertions(); + + // given + const to = p2pkhInstance.address; + const amount = 1000; + const utxos = await p2pkhInstance.getUtxos(); + utxos.sort((a, b) => a.satoshis > b.satoshis ? 1 : -1); + const targetUtxos: Utxo[] = []; + let available = 0; + for (const utxo of utxos) { + // 1000 for fees + if (available - 1000 > amount) break; + available += utxo.satoshis; + targetUtxos.push(utxo); + } + + // when + const tx = await p2pkhInstance.functions + .spend(alicePk, new Sig(alice)) + .send(to, amount, { + inputs: targetUtxos, + }); + + for (const _input of tx.vin) { + const input = _input as TxnDetailValueIn; + expect(targetUtxos.find((utxo) => + utxo.txid === input.txid && + utxo.vout === input.vout && + utxo.satoshis === input.value + )).toBeTruthy() + } + }); }); describe('send (to many)', () => { From 5a631da3c92de8e2918ab55e47fdc5ca9452e98b Mon Sep 17 00:00:00 2001 From: maikelmclauflin Date: Thu, 20 Feb 2020 21:10:25 -0600 Subject: [PATCH 3/6] linted and adjusted tes --- packages/cashscript/src/Instance.ts | 8 ++++--- packages/cashscript/test/e2e/P2PKH.test.ts | 25 ++++++++++++++-------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/packages/cashscript/src/Instance.ts b/packages/cashscript/src/Instance.ts index 60c0ac36..46f2dab5 100644 --- a/packages/cashscript/src/Instance.ts +++ b/packages/cashscript/src/Instance.ts @@ -1,4 +1,6 @@ -import { BITBOX } from 'bitbox-sdk'; +import { + BITBOX, +} from 'bitbox-sdk'; import { AddressDetailsResult, AddressUtxoResult, @@ -55,9 +57,9 @@ export class Instance { async getUtxos(excludeUnconfirmed?: boolean): Promise { const promises = [this.getUtxo()]; if (!excludeUnconfirmed) { - promises.push(this.getUnconfirmed().then(unconfirmed => + promises.push(this.getUnconfirmed().then(unconfirmed => ( unconfirmed.map(unconfirmedToUtxo) - )); + ))); } const results = await Promise.all(promises); return results.reduce((memo, utxos) => memo.concat(utxos), []); diff --git a/packages/cashscript/test/e2e/P2PKH.test.ts b/packages/cashscript/test/e2e/P2PKH.test.ts index d0d3c4d0..74ee8947 100644 --- a/packages/cashscript/test/e2e/P2PKH.test.ts +++ b/packages/cashscript/test/e2e/P2PKH.test.ts @@ -10,7 +10,6 @@ import { getTxOutputs } from '../test-util'; import { isOpReturn, Utxo, - TxnDetailValueOut, TxnDetailValueIn, } from '../../src/interfaces'; import { createOpReturnOutput } from '../../src/util'; @@ -63,30 +62,38 @@ describe('P2PKH', () => { const to = p2pkhInstance.address; const amount = 1000; const utxos = await p2pkhInstance.getUtxos(); - utxos.sort((a, b) => a.satoshis > b.satoshis ? 1 : -1); + utxos.sort((a, b) => (a.satoshis > b.satoshis ? 1 : -1)); const targetUtxos: Utxo[] = []; let available = 0; + let tooHigh = 0; for (const utxo of utxos) { // 1000 for fees + tooHigh += utxo.satoshis; if (available - 1000 > amount) break; available += utxo.satoshis; targetUtxos.push(utxo); } // when - const tx = await p2pkhInstance.functions - .spend(alicePk, new Sig(alice)) + const transaction = p2pkhInstance.functions + .spend(alicePk, new Sig(alice)); + + expect(transaction.send(to, tooHigh, { + inputs: targetUtxos, + })).rejects.toThrow(); + + const tx = await transaction .send(to, amount, { inputs: targetUtxos, }); for (const _input of tx.vin) { const input = _input as TxnDetailValueIn; - expect(targetUtxos.find((utxo) => - utxo.txid === input.txid && - utxo.vout === input.vout && - utxo.satoshis === input.value - )).toBeTruthy() + expect(targetUtxos.find(utxo => ( + utxo.txid === input.txid + && utxo.vout === input.vout + && utxo.satoshis === input.value + ))).toBeTruthy(); } }); }); From 4ae924203e6d033e829fb532750c2bb695f935f2 Mon Sep 17 00:00:00 2001 From: maikelmclauflin Date: Sun, 23 Feb 2020 14:09:20 -0600 Subject: [PATCH 4/6] incorporated changes --- packages/cashscript/src/Instance.ts | 10 +-- packages/cashscript/src/Transaction.ts | 7 +- packages/cashscript/src/interfaces.ts | 15 +--- packages/cashscript/src/util.ts | 12 --- packages/cashscript/test/Instance.test.ts | 16 ---- packages/cashscript/test/e2e/P2PKH.test.ts | 91 +++++++++++++++------- 6 files changed, 73 insertions(+), 78 deletions(-) diff --git a/packages/cashscript/src/Instance.ts b/packages/cashscript/src/Instance.ts index 46f2dab5..83efb82d 100644 --- a/packages/cashscript/src/Instance.ts +++ b/packages/cashscript/src/Instance.ts @@ -9,7 +9,6 @@ import { import { Artifact, Script, AbiFunction } from 'cashc'; import { Utxo, - UnconfirmedUtxo, } from './interfaces'; import { bitbox, @@ -23,7 +22,6 @@ import { Parameter, encodeParameter } from './Parameter'; import { countOpcodes, calculateBytesize, - unconfirmedToUtxo, } from './util'; export class Instance { @@ -43,13 +41,13 @@ export class Instance { return details.balanceSat + details.unconfirmedBalanceSat; } - async getUnconfirmed(): Promise { + private async getUnconfirmed(): Promise { const { utxos } = await this.bitbox.Address .unconfirmed(this.address) as AddressUnconfirmedResult; return utxos; } - async getUtxo(): Promise { + private async getUtxo(): Promise { const { utxos } = await this.bitbox.Address.utxo(this.address) as AddressUtxoResult; return utxos; } @@ -57,9 +55,7 @@ export class Instance { async getUtxos(excludeUnconfirmed?: boolean): Promise { const promises = [this.getUtxo()]; if (!excludeUnconfirmed) { - promises.push(this.getUnconfirmed().then(unconfirmed => ( - unconfirmed.map(unconfirmedToUtxo) - ))); + promises.push(this.getUnconfirmed()); } const results = await Promise.all(promises); return results.reduce((memo, utxos) => memo.concat(utxos), []); diff --git a/packages/cashscript/src/Transaction.ts b/packages/cashscript/src/Transaction.ts index bd4b4969..12df796d 100644 --- a/packages/cashscript/src/Transaction.ts +++ b/packages/cashscript/src/Transaction.ts @@ -118,8 +118,7 @@ export class Transaction { const { inputs, outputs } = await this.getInputsAndOutputs( outs, options?.fee, - DUST_LIMIT, - 1.0, + options?.minChange, options?.inputs, ); @@ -174,10 +173,10 @@ export class Transaction { outs: Output[], hardcodedFee?: number, minChange: number = DUST_LIMIT, + hardcodedUtxos?: Utxo[], satsPerByte: number = 1.0, - _utxos?: Utxo[], ): Promise<{ inputs: Utxo[], outputs: OutputForBuilder[] }> { - let utxos = _utxos; + let utxos = hardcodedUtxos; if (!utxos) { ({ utxos } = await this.bitbox.Address.utxo(this.address) as AddressUtxoResult); } diff --git a/packages/cashscript/src/interfaces.ts b/packages/cashscript/src/interfaces.ts index afa0cb54..159bfc00 100644 --- a/packages/cashscript/src/interfaces.ts +++ b/packages/cashscript/src/interfaces.ts @@ -3,20 +3,9 @@ export interface Utxo { vout: number; amount: number; satoshis: number; - height: number; confirmations: number; -} - -export interface UnconfirmedUtxo { - txid: string; - vout: number; - scriptPubKey: string; - amount: number; - satoshis: number; - confirmations: number; - ts: number; - legacyAddress: string; - cashAddress: string; + height?: number; + ts?: number; } export type Output = Recipient | OpReturn; diff --git a/packages/cashscript/src/util.ts b/packages/cashscript/src/util.ts index 85795b1a..d66ae2c1 100644 --- a/packages/cashscript/src/util.ts +++ b/packages/cashscript/src/util.ts @@ -7,7 +7,6 @@ import { Utxo, OpReturn, OutputForBuilder, - UnconfirmedUtxo, } from './interfaces'; import { ScriptUtil, CryptoUtil } from './BITBOX'; import { P2PKH_OUTPUT_SIZE, VERSION_SIZE, LOCKTIME_SIZE } from './constants'; @@ -189,14 +188,3 @@ function getPushDataOpcode(data: Buffer): Buffer { throw Error('Pushdata too large'); } // //////////////////////////////////////////////////////////////////////////// - -export function unconfirmedToUtxo(unconfirmed: UnconfirmedUtxo): Utxo { - return { - txid: unconfirmed.txid, - vout: unconfirmed.vout, - amount: unconfirmed.amount, - satoshis: unconfirmed.satoshis, - confirmations: 0, - height: 0, - }; -} diff --git a/packages/cashscript/test/Instance.test.ts b/packages/cashscript/test/Instance.test.ts index 3eb679c3..6ef5e99c 100644 --- a/packages/cashscript/test/Instance.test.ts +++ b/packages/cashscript/test/Instance.test.ts @@ -1,8 +1,5 @@ import path from 'path'; import { Contract, Instance, Sig } from '../src'; -import { - unconfirmedToUtxo, -} from '../src/util'; import { alicePk, alice, @@ -30,19 +27,6 @@ describe('Instance', () => { }); }); - describe('Contract Utxo inputs', () => { - it('should return an address\'s utxos', async () => { - const P2PKH = Contract.import(path.join(__dirname, 'fixture', 'p2pkh.json'), 'testnet'); - const instance = P2PKH.new(alicePkh); - - const unconfirmed = await instance.getUnconfirmed(); - const utxos = await instance.getUtxo(); - expect(await instance.getUtxos()).toEqual(utxos.concat( - unconfirmed.map(unconfirmedToUtxo), - )); - }); - }); - describe('Contract functions', () => { let instance: Instance; let bbInstance: Instance; diff --git a/packages/cashscript/test/e2e/P2PKH.test.ts b/packages/cashscript/test/e2e/P2PKH.test.ts index 74ee8947..8f73d61b 100644 --- a/packages/cashscript/test/e2e/P2PKH.test.ts +++ b/packages/cashscript/test/e2e/P2PKH.test.ts @@ -54,6 +54,32 @@ describe('P2PKH', () => { const txOutputs = getTxOutputs(tx); expect(txOutputs).toEqual(expect.arrayContaining([{ to, amount }])); }); + + it('should fail when not enough satoshis are provided in utoxos', async () => { + // when + const to = p2pkhInstance.address; + const amount = 1000; + const utxos = await p2pkhInstance.getUtxos(true); + utxos.sort((a, b) => (a.satoshis > b.satoshis ? 1 : -1)) + const { utxos: gathered, failureAmount } = gatherUtxos(utxos, { + amount + }); + + // send + expect( + p2pkhInstance.functions + .spend(alicePk, new Sig(alice)) + .send(to, failureAmount, { + inputs: gathered, + }) + ).rejects.toThrow(); + + expect( + p2pkhInstance.functions + .spend(alicePk, new Sig(alice)) + .send(to, failureAmount) + ).rejects.not.toThrow() + }) it('should succeed when defining its own utxos', async () => { expect.hasAssertions(); @@ -61,35 +87,22 @@ describe('P2PKH', () => { // given const to = p2pkhInstance.address; const amount = 1000; - const utxos = await p2pkhInstance.getUtxos(); - utxos.sort((a, b) => (a.satoshis > b.satoshis ? 1 : -1)); - const targetUtxos: Utxo[] = []; - let available = 0; - let tooHigh = 0; - for (const utxo of utxos) { - // 1000 for fees - tooHigh += utxo.satoshis; - if (available - 1000 > amount) break; - available += utxo.satoshis; - targetUtxos.push(utxo); - } - + const utxos = await p2pkhInstance.getUtxos(true); + utxos.sort((a, b) => (a.satoshis > b.satoshis ? 1 : -1)) + const { utxos: gathered, total } = gatherUtxos(utxos, { + amount + }); + // when - const transaction = p2pkhInstance.functions - .spend(alicePk, new Sig(alice)); - - expect(transaction.send(to, tooHigh, { - inputs: targetUtxos, - })).rejects.toThrow(); - - const tx = await transaction - .send(to, amount, { - inputs: targetUtxos, + const receipt = await p2pkhInstance.functions + .spend(alicePk, new Sig(alice)) + .send(to, total, { + inputs: gathered, }); - for (const _input of tx.vin) { - const input = _input as TxnDetailValueIn; - expect(targetUtxos.find(utxo => ( + // then + for (const input of receipt.vin as TxnDetailValueIn[]) { + expect(gathered.find(utxo => ( utxo.txid === input.txid && utxo.vout === input.vout && utxo.satoshis === input.value @@ -188,3 +201,29 @@ describe('P2PKH', () => { }); }); }); + +function gatherUtxos(utxos, options?: { + amount?: number, + fees?: number +}): { + utxos: Utxo[], + total: number, + failureAmount: number +} { + const targetUtxos: Utxo[] = []; + let total = 0; + let failureAmount = 0; + // 1000 for fees + const { amount = 0, fees = 1000 } = options || {}; + for (const utxo of utxos) { + failureAmount += utxo.satoshis; + if (total - fees > amount) break; + total += utxo.satoshis; + targetUtxos.push(utxo); + } + return { + utxos: targetUtxos, + total, + failureAmount + } +} From 2024565a380c9e2a36e1954708a7d7a1c52d841d Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Tue, 25 Feb 2020 14:37:53 +0100 Subject: [PATCH 5/6] Changes to manual utxo inputs tests - Allow unconfirmed UTXOs to be used - Replace `total` with `amount` for "happy case" test - Add type annotation for gatherUtxos parameter --- packages/cashscript/test/e2e/P2PKH.test.ts | 40 +++++++++------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/packages/cashscript/test/e2e/P2PKH.test.ts b/packages/cashscript/test/e2e/P2PKH.test.ts index 8f73d61b..c27dbec1 100644 --- a/packages/cashscript/test/e2e/P2PKH.test.ts +++ b/packages/cashscript/test/e2e/P2PKH.test.ts @@ -54,24 +54,20 @@ describe('P2PKH', () => { const txOutputs = getTxOutputs(tx); expect(txOutputs).toEqual(expect.arrayContaining([{ to, amount }])); }); - - it('should fail when not enough satoshis are provided in utoxos', async () => { + + it('should fail when not enough satoshis are provided in utxos', async () => { // when const to = p2pkhInstance.address; const amount = 1000; - const utxos = await p2pkhInstance.getUtxos(true); + const utxos = await p2pkhInstance.getUtxos(); utxos.sort((a, b) => (a.satoshis > b.satoshis ? 1 : -1)) - const { utxos: gathered, failureAmount } = gatherUtxos(utxos, { - amount - }); + const { utxos: gathered, failureAmount } = gatherUtxos(utxos, { amount }); // send expect( p2pkhInstance.functions .spend(alicePk, new Sig(alice)) - .send(to, failureAmount, { - inputs: gathered, - }) + .send(to, failureAmount, { inputs: gathered }) ).rejects.toThrow(); expect( @@ -87,18 +83,14 @@ describe('P2PKH', () => { // given const to = p2pkhInstance.address; const amount = 1000; - const utxos = await p2pkhInstance.getUtxos(true); + const utxos = await p2pkhInstance.getUtxos(); utxos.sort((a, b) => (a.satoshis > b.satoshis ? 1 : -1)) - const { utxos: gathered, total } = gatherUtxos(utxos, { - amount - }); - + const { utxos: gathered } = gatherUtxos(utxos, { amount }); + // when const receipt = await p2pkhInstance.functions .spend(alicePk, new Sig(alice)) - .send(to, total, { - inputs: gathered, - }); + .send(to, amount, { inputs: gathered }); // then for (const input of receipt.vin as TxnDetailValueIn[]) { @@ -202,13 +194,13 @@ describe('P2PKH', () => { }); }); -function gatherUtxos(utxos, options?: { - amount?: number, - fees?: number -}): { - utxos: Utxo[], - total: number, - failureAmount: number +function gatherUtxos(utxos: Utxo[], options?: { + amount?: number, + fees?: number +}): { + utxos: Utxo[], + total: number, + failureAmount: number } { const targetUtxos: Utxo[] = []; let total = 0; From d38a9c38525d5a11c80130fd00b3c52e95debcff Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Thu, 26 Mar 2020 11:55:07 +0100 Subject: [PATCH 6/6] Small fixes to manual utxo selection tests --- packages/cashscript/test/e2e/P2PKH.test.ts | 26 +++++++++------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/cashscript/test/e2e/P2PKH.test.ts b/packages/cashscript/test/e2e/P2PKH.test.ts index c27dbec1..fba4d61b 100644 --- a/packages/cashscript/test/e2e/P2PKH.test.ts +++ b/packages/cashscript/test/e2e/P2PKH.test.ts @@ -60,22 +60,22 @@ describe('P2PKH', () => { const to = p2pkhInstance.address; const amount = 1000; const utxos = await p2pkhInstance.getUtxos(); - utxos.sort((a, b) => (a.satoshis > b.satoshis ? 1 : -1)) + utxos.sort((a, b) => (a.satoshis > b.satoshis ? 1 : -1)); const { utxos: gathered, failureAmount } = gatherUtxos(utxos, { amount }); // send - expect( + await expect( p2pkhInstance.functions .spend(alicePk, new Sig(alice)) - .send(to, failureAmount, { inputs: gathered }) + .send(to, failureAmount, { inputs: gathered }), ).rejects.toThrow(); - expect( + await expect( p2pkhInstance.functions .spend(alicePk, new Sig(alice)) - .send(to, failureAmount) - ).rejects.not.toThrow() - }) + .send(to, failureAmount), + ).resolves.toBeTruthy(); + }); it('should succeed when defining its own utxos', async () => { expect.hasAssertions(); @@ -84,7 +84,7 @@ describe('P2PKH', () => { const to = p2pkhInstance.address; const amount = 1000; const utxos = await p2pkhInstance.getUtxos(); - utxos.sort((a, b) => (a.satoshis > b.satoshis ? 1 : -1)) + utxos.sort((a, b) => (a.satoshis > b.satoshis ? 1 : -1)); const { utxos: gathered } = gatherUtxos(utxos, { amount }); // when @@ -197,11 +197,7 @@ describe('P2PKH', () => { function gatherUtxos(utxos: Utxo[], options?: { amount?: number, fees?: number -}): { - utxos: Utxo[], - total: number, - failureAmount: number -} { +}): { utxos: Utxo[], total: number, failureAmount: number } { const targetUtxos: Utxo[] = []; let total = 0; let failureAmount = 0; @@ -216,6 +212,6 @@ function gatherUtxos(utxos: Utxo[], options?: { return { utxos: targetUtxos, total, - failureAmount - } + failureAmount, + }; }