diff --git a/index.d.ts b/index.d.ts index 4a3a5f3..58eabe9 100644 --- a/index.d.ts +++ b/index.d.ts @@ -7,9 +7,13 @@ export interface UTXO { script: Buffer, value: number } + redeemScript?: Buffer, + witnessScript?: Buffer, + isTaproot?: boolean } export interface Target { - address: string, + address?: string, + script?: Buffer, value?: number } export interface SelectedUTXO { diff --git a/test/fixtures/accumulative.json b/test/fixtures/accumulative.json index 23505d5..73e761f 100644 --- a/test/fixtures/accumulative.json +++ b/test/fixtures/accumulative.json @@ -163,7 +163,7 @@ "feeRate": 5, "inputs": [ { - "script": { + "redeemScript": { "length": 1000 }, "value": 3000 @@ -220,7 +220,7 @@ "feeRate": 5, "inputs": [ { - "script": { + "redeemScript": { "length": 500 }, "value": 3000 @@ -239,7 +239,7 @@ "inputs": [ { "i": 0, - "script": { + "redeemScript": { "length": 500 }, "value": 3000 @@ -272,7 +272,7 @@ "value": 3000 }, { - "script": { + "redeemScript": { "length": 400 }, "value": 3000 @@ -672,7 +672,7 @@ "inputs": [ 20000, { - "script": { + "redeemScript": { "length": 300 }, "value": 10000 @@ -691,7 +691,7 @@ }, { "i": 1, - "script": { + "redeemScript": { "length": 300 }, "value": 10000 diff --git a/test/fixtures/index.json b/test/fixtures/index.json index 9ebcdbf..3cff1dc 100644 --- a/test/fixtures/index.json +++ b/test/fixtures/index.json @@ -136,7 +136,7 @@ "feeRate": 5, "inputs": [ { - "script": { + "redeemScript": { "length": 1000 }, "value": 3000 @@ -175,7 +175,7 @@ "feeRate": 5, "inputs": [ { - "script": { + "redeemScript": { "length": 500 }, "value": 3000 @@ -220,7 +220,7 @@ "value": 3000 }, { - "script": { + "redeemScript": { "length": 400 }, "value": 3000 @@ -620,7 +620,7 @@ "inputs": [ 20000, { - "script": { + "redeemScript": { "length": 300 }, "value": 10000 @@ -804,6 +804,315 @@ 10000 ], "expected": {} + }, + { + "description": "segwit mainnet input and output", + "feeRate": 10, + "inputs": [{ + "value": 103430, + "witnessUtxo": { + "script": "script public key", + "value": 103430 + } + }], + "outputs": [ + { + "value": 100000, + "address": "bc1q34aq5drpuwy3wgl9lhup9892qp6svr8ldzyy7c" + } + ], + "expected": { + "inputs": [ + { + "i": 0, + "value": 103430, + "witnessUtxo": { + "script": "script public key", + "value": 103430 + } + } + ], + "outputs": [ + { + "value": 100000, + "address": "bc1q34aq5drpuwy3wgl9lhup9892qp6svr8ldzyy7c" + }, + { + "value": 2000 + } + ], + "fee": 1430 + } + }, + { + "description": "segwit testnet input and output", + "feeRate": 10, + "inputs": [{ + "value": 103430, + "witnessUtxo": { + "script": "script public key", + "value": 103430 + } + }], + "outputs": [ + { + "value": 100000, + "address": "tb1q34aq5drpuwy3wgl9lhup9892qp6svr8ldzyy7c" + } + ], + "expected": { + "inputs": [ + { + "i": 0, + "value": 103430, + "witnessUtxo": { + "script": "script public key", + "value": 103430 + } + } + ], + "outputs": [ + { + "value": 100000, + "address": "tb1q34aq5drpuwy3wgl9lhup9892qp6svr8ldzyy7c" + }, + { + "value": 2000 + } + ], + "fee": 1430 + } + }, + { + "description": "p2sh mainnet output", + "feeRate": 10, + "inputs": [ + 105000 + ], + "outputs": [ + { + "value": 100000, + "address": "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy" + } + ], + "expected": { + "inputs": [ + { + "i": 0, + "value": 105000 + } + ], + "outputs": [ + { + "value": 100000, + "address": "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy" + }, + { + "value": 2760 + } + ], + "fee": 2240 + } + }, + { + "description": "p2sh testnet output", + "feeRate": 10, + "inputs": [ + 105000 + ], + "outputs": [ + { + "value": 100000, + "address": "2J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy" + } + ], + "expected": { + "inputs": [ + { + "i": 0, + "value": 105000 + } + ], + "outputs": [ + { + "value": 100000, + "address": "2J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy" + }, + { + "value": 2760 + } + ], + "fee": 2240 + } + }, + { + "description": "p2wsh or taproot mainnet output", + "feeRate": 10, + "inputs": [ + 104010 + ], + "outputs": [ + { + "value": 100000, + "address": "bc1qeklep85ntjz4605drds6aww9u0qr46qzrv5xswd35uhjuj8ahfcqgf6hak" + } + ], + "expected": { + "inputs": [ + { + "i": 0, + "value": 104010 + } + ], + "outputs": [ + { + "value": 100000, + "address": "bc1qeklep85ntjz4605drds6aww9u0qr46qzrv5xswd35uhjuj8ahfcqgf6hak" + }, + { + "value": 1660 + } + ], + "fee": 2350 + } + }, + { + "description": "p2wsh or taproot testnet output", + "feeRate": 10, + "inputs": [ + 104010 + ], + "outputs": [ + { + "value": 100000, + "address": "tb1qeklep85ntjz4605drds6aww9u0qr46qzrv5xswd35uhjuj8ahfcqgf6hak" + } + ], + "expected": { + "inputs": [ + { + "i": 0, + "value": 104010 + } + ], + "outputs": [ + { + "value": 100000, + "address": "tb1qeklep85ntjz4605drds6aww9u0qr46qzrv5xswd35uhjuj8ahfcqgf6hak" + }, + { + "value": 1660 + } + ], + "fee": 2350 + } + }, + { + "description": "taproot input", + "feeRate": 10, + "inputs": [{ + "value": 103360, + "isTaproot": true + }], + "outputs": [ + 100000 + ], + "expected": { + "inputs": [ + { + "i": 0, + "value": 103360, + "isTaproot": true + } + ], + "outputs": [ + { + "value": 100000 + }, + { + "value": 2000 + } + ], + "fee": 1360 + } + }, + { + "description": "1 output, passes, skipped detrimental input", + "feeRate": 5, + "inputs": [ + { + "redeemScript": { + "length": 1000 + }, + "value": 3000 + }, + { + "value": 3000 + }, + { + "value": 3000 + } + ], + "outputs": [ + 4000 + ], + "expected": { + "fee": 2000, + "inputs": [ + { + "i": 1, + "value": 3000 + }, + { + "i": 2, + "value": 3000 + } + ], + "outputs": [ + { + "value": 4000 + } + ] + } + }, + { + "description": "1 output, passes, skipped detrimental input", + "feeRate": 5, + "inputs": [ + { + "witnessScript": { + "length": 4000 + }, + "value": 3000 + }, + { + "value": 3000 + }, + { + "value": 3000 + } + ], + "outputs": [ + 4000 + ], + "expected": { + "fee": 2000, + "inputs": [ + { + "i": 1, + "value": 3000 + }, + { + "i": 2, + "value": 3000 + } + ], + "outputs": [ + { + "value": 4000 + } + ] + } } ] diff --git a/utils.js b/utils.js index 687fe66..af55a19 100644 --- a/utils.js +++ b/utils.js @@ -2,15 +2,29 @@ var TX_EMPTY_SIZE = 4 + 1 + 1 + 4 var TX_INPUT_BASE = 32 + 4 + 1 + 4 var TX_INPUT_PUBKEYHASH = 107 +var TX_INPUT_SEGWIT = 27 +var TX_INPUT_TAPROOT = 17 // round up 16.5 bytes var TX_OUTPUT_BASE = 8 + 1 var TX_OUTPUT_PUBKEYHASH = 25 +var TX_OUTPUT_SCRIPTHASH = 23 +var TX_OUTPUT_SEGWIT = 22 +var TX_OUTPUT_SEGWIT_SCRIPTHASH = 34 function inputBytes (input) { - return TX_INPUT_BASE + (input.script ? input.script.length : TX_INPUT_PUBKEYHASH) + return TX_INPUT_BASE + (input.redeemScript ? input.redeemScript.length : 0) + + (input.witnessScript ? parseInt(input.witnessScript.length / 4) + : input.isTaproot ? TX_INPUT_TAPROOT + : input.witnessUtxo ? TX_INPUT_SEGWIT + : !input.redeemScript ? TX_INPUT_PUBKEYHASH : 0) } function outputBytes (output) { - return TX_OUTPUT_BASE + (output.script ? output.script.length : TX_OUTPUT_PUBKEYHASH) + return TX_OUTPUT_BASE + (output.script ? output.script.length + : output.address?.startsWith('bc1') || output.address?.startsWith('tb1') + ? output.address?.length === 42 ? TX_OUTPUT_SEGWIT : TX_OUTPUT_SEGWIT_SCRIPTHASH + : output.address?.startsWith('3') || output.address?.startsWith('2') + ? TX_OUTPUT_SCRIPTHASH : TX_OUTPUT_PUBKEYHASH + ) } function dustThreshold (output, feeRate) {