Skip to content

P2tr psbt v1 #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 53 commits into
base: p2tr-v1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
574a287
chore: add bn.js to dependencies (previously it was present in devDep…
motorina0 Oct 28, 2021
7ae5368
feat: add liftX() function (first version)
motorina0 Oct 28, 2021
8fb2f0a
feat: update the Payment interface with taproot specific fields
motorina0 Oct 28, 2021
47eab68
feat: add first version of p2tr; basic logic for key path construct/s…
motorina0 Oct 28, 2021
38fe854
test: add first tests for p2tr
motorina0 Oct 28, 2021
1bf104e
feat: add generated files
motorina0 Oct 28, 2021
a598134
feat: compute "taproot output key" when "taoroot internal key" is kno…
motorina0 Oct 29, 2021
33c2bd9
tests: improve test coverage
motorina0 Oct 29, 2021
f04d8b2
feat: add function `computeMastRoot()`
motorina0 Nov 1, 2021
583174d
feat: compute p2tr hash based on the script tree
motorina0 Nov 1, 2021
1b96e6d
feat: add scriptsTree field to Payment interface; export p2tr
motorina0 Nov 1, 2021
fc1f1b2
feat: convert `scriptsTree` output to Buffer
motorina0 Nov 1, 2021
91ccef3
tests: add tests for script tree
motorina0 Nov 1, 2021
d840ab6
feat: add simple type for TaprootLeaf and TaprootNode
motorina0 Nov 1, 2021
fb7df4a
feat: check for hash mismatch between the input hash and the computed…
motorina0 Nov 1, 2021
af639f9
feat: validate witness data (partial)
motorina0 Nov 2, 2021
9947571
refactor: split `computeTweakFromScriptPath()` into `rootHash()` and …
motorina0 Nov 2, 2021
b8f8c91
feat: compute hash from witness control-block
motorina0 Nov 2, 2021
8fd07fc
refactor: extract taproot related logic to taproot.ts file
motorina0 Nov 2, 2021
c987b0c
chore: code format and lint
motorina0 Nov 2, 2021
2feff5d
refactor: compare GROUP_ORDER as buffer (instead of using BN.js)
motorina0 Nov 2, 2021
2f55aad
refactor: rename `rootHash` to `rootHashFromPath` and `computeMastRoo…
motorina0 Nov 3, 2021
2a4e64b
tests: add bib341 tests by @sipa; plus refactoring
motorina0 Nov 3, 2021
f7d01b8
refactor: extract `tapBranchHash()` rename `leafHash()` to `tapBranch…
motorina0 Nov 3, 2021
a3550c1
feat: build control-block as part of witness; update tests
motorina0 Nov 3, 2021
b1fca66
chore: lint & format; fix: discovered bug in findScriptPath() after lint
motorina0 Nov 3, 2021
ad2aec1
chore: code clean-up; fix o.scriptLeaf (needs tests)
motorina0 Nov 3, 2021
dcffed3
chore: update taggedHash() prefix after rebase
motorina0 Nov 12, 2021
d107161
fix: rebase issues
motorina0 Jan 12, 2022
4d2af06
chore: remove the bn.js dependency
motorina0 Jan 13, 2022
6cfbf65
refactor: use injectable ecc lib
motorina0 Jan 13, 2022
e64c2d8
chore: code format
motorina0 Jan 13, 2022
d987d8d
refactor: move taproot utils file
motorina0 Jan 13, 2022
9f51a1a
refactor: move non-exported function to the bottom
motorina0 Jan 13, 2022
bc6358f
fix: lint & gitdiff issues
motorina0 Jan 13, 2022
f61371c
chore: removed un-used exports
motorina0 Jan 13, 2022
4573e6c
chore: add docs, simplify code
motorina0 Jan 13, 2022
ddc1e8d
feat: pass the ECC library as an optional parameter to p2tr
motorina0 Jan 14, 2022
4cad59c
refactor: move eccLib to PaymentOptions
motorina0 Jan 18, 2022
c670698
fix: remove `TinySecp256k1Interface` from `PaymentCreator`
motorina0 Jan 20, 2022
b566dd7
chore: keep ecc test vectors to a minimum
motorina0 Jan 20, 2022
ef751d1
chore: just bla bla
motorina0 Jan 25, 2022
56d3064
chore: some clean-up
motorina0 Jan 25, 2022
9a3c13e
test: fix unit test for signature validation
motorina0 Jan 26, 2022
6c9b2ea
chore: more testing
motorina0 Jan 26, 2022
ea3872f
chore code clean-up
motorina0 Jan 26, 2022
6c0a6e7
test: add more tests
motorina0 Jan 26, 2022
4ce7e27
feat: add tweakSigner() to PSBT
motorina0 Jan 27, 2022
be42121
test: add tweakSigner test
motorina0 Jan 27, 2022
db7fb7d
chore: code clean-up
motorina0 Jan 27, 2022
80b5fb7
chore: fix lint
motorina0 Jan 27, 2022
04969fd
feat: add logic for validate taproot input
motorina0 Jan 27, 2022
a38d650
chore: testing leftovers
motorina0 Jan 27, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,409 changes: 3,396 additions & 13 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"bip174": "^2.0.1",
"bs58check": "^2.1.2",
"create-hash": "^1.1.0",
"tiny-secp256k1": "^2.2.0",
"typeforce": "^1.11.3",
"varuint-bitcoin": "^1.1.2",
"wif": "^2.0.1"
Expand All @@ -70,7 +71,6 @@
"bip39": "^3.0.2",
"bip65": "^1.0.1",
"bip68": "^1.0.3",
"bn.js": "^4.11.8",
"bs58": "^4.0.0",
"dhttp": "^3.0.0",
"ecpair": "^2.0.1",
Expand All @@ -84,7 +84,6 @@
"randombytes": "^2.1.0",
"regtest-client": "0.2.0",
"rimraf": "^2.6.3",
"tiny-secp256k1": "^2.1.2",
"ts-node": "^8.3.0",
"tslint": "^6.1.3",
"typescript": "^4.4.4"
Expand Down
105 changes: 105 additions & 0 deletions play.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import * as ecc from 'tiny-secp256k1';
import { ECPairFactory } from 'ecpair'

import { Psbt } from './src/psbt.js'
import { p2pkh, p2wpkh, p2tr } from './src/payments/index.js'
import { testnet as network } from './src/networks.js'

console.log(''.padEnd(100, '#'))
const ECPair = ECPairFactory(ecc);
const hex = (s) => Buffer.from(s, 'hex')

const inP2pkhKey = ECPair.fromPrivateKey(hex('82fd530c9eb33570c7e05ca5e80b740bcf1118e8f4c73d44a801fa9dd60f6449'))
const inP2wpkhKey = ECPair.fromPrivateKey(hex('35dfb4dc373860005d6f74d37064e328bc772343c354c95e31b654d0c5e22f58'))
const inP2trKey = ECPair.fromPrivateKey(hex('accaf12e04e11b08fc28f5fe75b47ea663843b698981e31f1cafa2224d6e28c0'))

const outP2trKey = ECPair.fromPrivateKey(hex('900afde76badc8914c9940379c74857d70b4d7da590097285572df6b88ad2975'))
const outP2wpkhKey = ECPair.fromPrivateKey(hex('65ba77c6052f41325d13df8c740b5e33a26d6612e1923bf3afd67ad8081227ee'))



const inP2wpkh = p2wpkh({ pubkey: inP2wpkhKey.publicKey, network })
const inP2tr = p2tr({ internalPubkey: inP2trKey.publicKey.slice(1), network }, { eccLib: ecc })
const outP2tr = p2tr({ internalPubkey: outP2trKey.publicKey.slice(1), network }, { eccLib: ecc })
const outP2wpkh = p2wpkh({ pubkey: outP2wpkhKey.publicKey, network })

const inTweakedP2trKey = Psbt.tweakSigner(inP2trKey, { network })

console.log('### inP2trKey.privateKey ', inP2trKey.privateKey.toString('hex'))
console.log('### inP2trKey.publicKey ', inP2trKey.publicKey.toString('hex'))
console.log('### inP2trKey.toWIF() ', inP2trKey.toWIF())
console.log('### inTweakedP2trKey.privateKey ', inTweakedP2trKey.privateKey.toString('hex'))
console.log('### inTweakedP2trKey.publicKey ', inTweakedP2trKey.publicKey.toString('hex'))
console.log(''.padEnd(100, '#'))


console.log('### inP2tr.script ', inP2tr.output.toString('hex'))
console.log('### inP2tr.address ', inP2tr.address)
console.log('### inP2tr.pubkey ', inP2tr.pubkey.toString('hex'))
console.log(''.padEnd(100, '#'))

console.log('### p2pkh.address ', p2pkh({ pubkey: inP2pkhKey.publicKey, network }).address)
console.log('### p2wpkh.address ', inP2wpkh.address)
console.log('### inP2tr.address ', inP2tr.address)
console.log('### outP2tr.address ', outP2tr.address)
console.log(''.padEnd(100, '#'))


const psbt = new Psbt({ network })
// spend p2pkh
psbt.addInput({
hash: hex('32833f8502f64f85674d2b637ec3ff0032d5585cd305b8d68db4b83ed977f303').reverse(),
index: 0,
}).updateInput(0, { nonWitnessUtxo: hex('0200000000010147d8d83d6dd1dc8c7841f7f42d7239d2318a0a6a9bb5c936a1d54f72dcf859220000000000feffffff02122c1b00000000001976a9149e7ef1767764ff34a0595dbc4c2b70db017ed06688ac9874aeb10000000017a914b0c19f9f547df19b5fbbbfa25c850c6d1e1b550987024730440220301ddecd390bad5f957545a8eb68bf43681d11abf0a15c3a91fa47ff35178e8402206dfa3f611f2bd3da1604c9ff1f758b0cf4881b9f26ed3d5fa44531c36ba463d001210269997f09a81ec9829043a7f407d14e1fbceb799445e96613c852a8be0c1b5132749c2000') })

// spend p2tr
psbt.addInput({
hash: hex('8b9fd7f222dfa16191a15485e241d2e94baa1bef3e6a989cf2d584bda800d066').reverse(),
index: 0,
}).updateInput(1, { witnessUtxo: { script: inP2tr.output, value: 67000 } })

// spend pwpkh
psbt.addInput({
hash: hex('2258ccf6dcb061db928b8f74c6bb74596b43935eb04f03c5d431020ba5e4877e').reverse(),
index: 0,
}).updateInput(2, { witnessUtxo: { script: inP2wpkh.output, value: 10000 } })

psbt.addOutput({
address: outP2tr.address,
value: 1780000
})
psbt.addOutput({
address: outP2wpkh.address,
value: 67000
})

console.log('### psbt 1', psbt.toBase64())
// psbt.signInput(0, inP2pkhKey)
psbt.signInput(1, inTweakedP2trKey)
// psbt.signInput(2, inP2wpkhKey)
console.log('### psbt 2', psbt.toBase64())

const validator = (
pubkey,
msghash,
signature,
) => {
console.log('### verifySchnorr', pubkey.toString('hex'))
// msghash[0]=0
return ECPair.fromPublicKey(pubkey).verifySchnorr(msghash, signature)
};

const isValid = psbt.validateSignaturesOfInput(1, validator)
console.log('### isValid', isValid)

// Serialize tx
psbt.finalizeAllInputs()
const tx = psbt.extractTransaction()
const rawTx = tx.toBuffer()

console.log('### rawTx', rawTx.toString('hex'))
/// 1b9e7e80288a059adb9da6fd7c30e7383a5a924f78d3a0b6b047e07345990f48
/// b2138ee2639c569fe7f2666635399caaa7a435b3763c079f069e194116840892
/// e7a79cc65d17bebfc4cf03d26d6d879b754afe9cdc56ce98d2f1fac29cc7f1e2
/// 8b9fd7f222dfa16191a15485e241d2e94baa1bef3e6a989cf2d584bda800d066
/// 735321667cf43ded23dc935f355c4b1b1e77d9a47d11c77d384af5094e1ad171 -> 3 inputs, 2 outputs
110 changes: 110 additions & 0 deletions play2.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { BIP32Factory } from 'bip32';
import { ECPairFactory } from 'ecpair'
import * as ecc from 'tiny-secp256k1';

import * as bitcoin from './src/index.js';
import { testnet as network } from './src/networks.js'

const ECPair = ECPairFactory(ecc);
const bip32 = BIP32Factory(ecc);
const hex = (s) => Buffer.from(s, 'hex')

// Order of the curve (N) - 1
const N_LESS_1 = Buffer.from(
'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140',
'hex',
);
// 1 represented as 32 bytes BE
const ONE = Buffer.from(
'0000000000000000000000000000000000000000000000000000000000000001',
'hex',
);


const myKey = ECPair.fromPrivateKey(hex('accaf12e04e11b08fc28f5fe75b47ea663843b698981e31f1cafa2224d6e28c0'))
const output = createKeySpendOutput(myKey.publicKey);
const address = bitcoin.address.fromOutputScript(output, network);
const amount = 100000;
const sendAmount = 75000
console.log('### output', output.toString('hex'))
console.log('### address', address)

const unspent = { txId: '8162b0310f37182049429e147b0ba7ac17eb4dd5c6d3016927e4c993b63edc2b', vout: 0}

const tx = createSigned(
myKey,
unspent.txId,
unspent.vout,
sendAmount,
[output], // public key
[amount],
);

const txHex = tx.toHex();
console.log('### txHex', txHex)
// txId: a618fdb361a6cfbc345d2b030ab7533dc88b3c097af58df6979b4d9aa4472379


function createKeySpendOutput(publicKey) {
// x-only pubkey (remove 1 byte y parity)
const myXOnlyPubkey = publicKey.slice(1, 33);
const commitHash = bitcoin.crypto.taggedHash('TapTweak', myXOnlyPubkey);
console.log('### commitHash ', commitHash.toString('hex'))
const tweakResult = ecc.xOnlyPointAddTweak(myXOnlyPubkey, commitHash);
console.log('### tweakResult xOnlyPubkey', Buffer.from(tweakResult.xOnlyPubkey).toString('hex'))
if (tweakResult === null) throw new Error('Invalid Tweak');
const { xOnlyPubkey: tweaked } = tweakResult;
// scriptPubkey
return Buffer.concat([
// witness v1, PUSH_DATA 32 bytes
Buffer.from([0x51, 0x20]),
// x-only tweaked pubkey
tweaked,
]);
}

function signTweaked(messageHash, key) {
const privateKey =
key.publicKey[0] === 2
? key.privateKey
: ecc.privateAdd(ecc.privateSub(N_LESS_1, key.privateKey), ONE);
const tweakHash = bitcoin.crypto.taggedHash(
'TapTweak',
key.publicKey.slice(1, 33),
);
const newPrivateKey = ecc.privateAdd(privateKey, tweakHash);
if (newPrivateKey === null) throw new Error('Invalid Tweak');
return ecc.signSchnorr(messageHash, newPrivateKey, Buffer.alloc(32));
}

// Function for creating signed tx
function createSigned(
key,
txid,
vout,
amountToSend,
scriptPubkeys,
values,
) {
console.log('### scriptPubkeys', scriptPubkeys.map(s => s.toString('hex')))
console.log('### values', values)
const tx = new bitcoin.Transaction();
tx.version = 2;
// Add input
tx.addInput(Buffer.from(txid, 'hex').reverse(), vout);
// Add output
tx.addOutput(scriptPubkeys[0], amountToSend);
const sighash = tx.hashForWitnessV1(
0, // which input
scriptPubkeys, // All previous outputs of all inputs
values, // All previous values of all inputs
bitcoin.Transaction.SIGHASH_DEFAULT, // sighash flag, DEFAULT is schnorr-only (DEFAULT == ALL)
);
console.log('### sighash', sighash.toString('hex'))
console.log('### unsignedTx', tx.toHex())
const signature = Buffer.from(signTweaked(sighash, key));
// witness stack for keypath spend is just the signature.
// If sighash is not SIGHASH_DEFAULT (ALL) then you must add 1 byte with sighash value
tx.ins[0].witness = [signature];
return tx;
}
11 changes: 11 additions & 0 deletions play3.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as ecc from 'tiny-secp256k1';
import { ECPairFactory } from 'ecpair'

import { p2tr } from './src/payments/index.js'
import { testnet as network } from './src/networks.js'

console.log(''.padEnd(100, '#'))

const inP2tr = p2tr({ output: Buffer.from('51209421e734b0f9d2c467ea7dd197c61acb4467cdcbc9f4cb0c571f8b63a5c40cae', 'hex'), network }, { eccLib: ecc })

console.log('### inP2tr',inP2tr.address)
11 changes: 11 additions & 0 deletions play4.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as ecc from 'tiny-secp256k1';
import { ECPairFactory } from 'ecpair'
import NETWORKS from './src/networks.js'

const ECPair = ECPairFactory(ecc);
const hex = (s) => Buffer.from(s, 'hex')

const key = ECPair.fromPrivateKey(hex('82fd530c9eb33570c7e05ca5e80b740bcf1118e8f4c73d44a801fa9dd60f6449'), { network: NETWORKS.testnet })
console.log('### key.toWIF()', key.toWIF())

ECPair.fromWIF('cRyKzLXVgTReWe7wgfEiXktTa9tf4e5DK1STha274d7BBbnucTaR', NETWORKS.testnet);
112 changes: 112 additions & 0 deletions play5.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import * as ecc from 'tiny-secp256k1';
import { ECPairFactory } from 'ecpair'

import { Psbt } from './src/psbt.js'
import { p2pkh, p2wpkh, p2tr } from './src/payments/index.js'
import { testnet as network } from './src/networks.js'
console.log(''.padEnd(100, '#'))
const ECPair = ECPairFactory(ecc);
const hex = (s) => Buffer.from(s, 'hex')

const inP2pkhKey = ECPair.fromPrivateKey(hex('82fd530c9eb33570c7e05ca5e80b740bcf1118e8f4c73d44a801fa9dd60f6449'), { network })
const inP2wpkhKey = ECPair.fromPrivateKey(hex('35dfb4dc373860005d6f74d37064e328bc772343c354c95e31b654d0c5e22f58'), { network })
const inP2trKey = ECPair.fromPrivateKey(hex('accaf12e04e11b08fc28f5fe75b47ea663843b698981e31f1cafa2224d6e28c0'), { network })

const outP2trKey = ECPair.fromPrivateKey(hex('900afde76badc8914c9940379c74857d70b4d7da590097285572df6b88ad2975'), { network })
const outP2wpkhKey = ECPair.fromPrivateKey(hex('65ba77c6052f41325d13df8c740b5e33a26d6612e1923bf3afd67ad8081227ee'), { network })

console.log('### inP2pkhKey.toWIF()', inP2pkhKey.toWIF())
console.log('### inP2trKey.toWIF()', inP2trKey.toWIF())
console.log('### inP2wpkhKey.toWIF()', inP2wpkhKey.toWIF())
console.log('### outP2trKey.toWIF()', outP2trKey.toWIF())
// 02982a2876765bb37b53a12418b9e72b8afa8d54e344a1bd585299a211fbe625f3



const inP2wpkh = p2wpkh({ pubkey: inP2wpkhKey.publicKey, network })
const inP2tr = p2tr({ internalPubkey: inP2trKey.publicKey.slice(1), network }, { eccLib: ecc })
const outP2tr = p2tr({ internalPubkey: outP2trKey.publicKey.slice(1), network }, { eccLib: ecc })
const outP2wpkh = p2wpkh({ pubkey: outP2wpkhKey.publicKey, network })


// const inTweakedP2trKey = inP2trKeyPsbt.prepareTaprootSigner(outP2trKey, { network })
// console.log('### inTweakedP2trKey.toWIF()', inTweakedP2trKey.toWIF())


console.log('### inP2trKey.privateKey ', inP2trKey.privateKey.toString('hex'))
console.log('### inP2trKey.publicKey ', inP2trKey.publicKey.toString('hex'))
console.log('### outP2trKey.publicKey ', outP2trKey.publicKey.toString('hex'))
console.log('### inP2pkhKey.publicKey ', inP2pkhKey.publicKey.toString('hex'))
console.log('### inP2wpkhKey.publicKey ', inP2wpkhKey.publicKey.toString('hex'))
// console.log('### inTweakedP2trKey.privateKey ', inTweakedP2trKey.privateKey.toString('hex'))
// console.log('### inTweakedP2trKey.publicKey ', inTweakedP2trKey.publicKey.toString('hex'))
console.log(''.padEnd(100, '#'))


console.log('### inP2tr.script ', inP2tr.output.toString('hex'))
console.log('### inP2tr.address ', inP2tr.address)
console.log('### inP2tr.pubkey ', inP2tr.pubkey.toString('hex'))
console.log(''.padEnd(100, '#'))

console.log('### p2pkh.address ', p2pkh({ pubkey: inP2pkhKey.publicKey, network }).address)
console.log('### p2wpkh.address ', inP2wpkh.address)
console.log('### inP2tr.address ', inP2tr.address)
console.log('### outP2tr.address ', outP2tr.address)

console.log(''.padEnd(100, '#'))


const psbt = new Psbt({ network })
// spend p2pkh
psbt.addInput({
hash: hex('32833f8502f64f85674d2b637ec3ff0032d5585cd305b8d68db4b83ed977f303').reverse(),
index: 0,
}).updateInput(0, { nonWitnessUtxo: hex('0200000000010147d8d83d6dd1dc8c7841f7f42d7239d2318a0a6a9bb5c936a1d54f72dcf859220000000000feffffff02122c1b00000000001976a9149e7ef1767764ff34a0595dbc4c2b70db017ed06688ac9874aeb10000000017a914b0c19f9f547df19b5fbbbfa25c850c6d1e1b550987024730440220301ddecd390bad5f957545a8eb68bf43681d11abf0a15c3a91fa47ff35178e8402206dfa3f611f2bd3da1604c9ff1f758b0cf4881b9f26ed3d5fa44531c36ba463d001210269997f09a81ec9829043a7f407d14e1fbceb799445e96613c852a8be0c1b5132749c2000') })

// spend p2tr
psbt.addInput({
hash: hex('8b9fd7f222dfa16191a15485e241d2e94baa1bef3e6a989cf2d584bda800d066').reverse(),
index: 0,
}).updateInput(1, { witnessUtxo: { script: outP2tr.output, value: 67000 } })

// spend pwpkh
psbt.addInput({
hash: hex('2258ccf6dcb061db928b8f74c6bb74596b43935eb04f03c5d431020ba5e4877e').reverse(),
index: 0,
}).updateInput(2, { witnessUtxo: { script: inP2wpkh.output, value: 10000 } })

psbt.addOutput({
address: inP2tr.address,
value: 1780000
})
psbt.addOutput({
address: outP2wpkh.address,
value: 67000
})

console.log('### psbt 1', psbt.toBase64())

psbt.signInput(0, inP2pkhKey)
psbt.signInput(1, outP2trKey)
psbt.signInput(2, inP2wpkhKey)

console.log('### psbt 2', psbt.toBase64())

// const validator = (
// pubkey,
// msghash,
// signature,
// ) => ECPair.fromPublicKey(pubkey).verify(msghash, signature);
// psbt.validateSignaturesOfAllInputs(validator)

// Serialize tx
psbt.finalizeAllInputs()
const tx = psbt.extractTransaction()
const rawTx = tx.toBuffer()

console.log('### rawTx', rawTx.toString('hex'))
/// 1b9e7e80288a059adb9da6fd7c30e7383a5a924f78d3a0b6b047e07345990f48
/// b2138ee2639c569fe7f2666635399caaa7a435b3763c079f069e194116840892
/// e7a79cc65d17bebfc4cf03d26d6d879b754afe9cdc56ce98d2f1fac29cc7f1e2
/// 8b9fd7f222dfa16191a15485e241d2e94baa1bef3e6a989cf2d584bda800d066
/// 735321667cf43ded23dc935f355c4b1b1e77d9a47d11c77d384af5094e1ad171 -> 3 inputs, 2 outputs
4 changes: 4 additions & 0 deletions published tx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
published tx
### hash 990576cefd2f30f46709a14cf0eff8fe94c9d004229b9e0ff7fca7a526e9cfea
### rawTx 0200000000010303f377d93eb8b48dd6b805d35c58d53200ffc37e632b4d67854ff602853f8332000000006a473044022069582941c9bb47bde5e4840813884f1049cccc26cf0c1ae1f82ce48d440eacfc02205a56c1df124eb4922cb338f5a99ff0840e632bdc274f3799cc3955b3f8afab670121022e68bc11f5c4ba06c4faad161e3cc141299ccf92b7549b3f34703bb90134a390ffffffff66d000a8bd84d5f29c986a3eef1baa4be9d241e28554a19161a1df22f2d79f8b0000000000ffffffff7e87e4a50b0231d4c5034fb05e93436b5974bbc6748f8b92db61b0dcf6cc58220000000000ffffffff0220291b000000000022512046f6497cb2f19d50c3e9e982a950dcc86214b01fcce437a42061c7bdb1230d33b805010000000000160014c98a3134bf8f33e599a67c40000d774205c1b41400014003694b9b7e9145af36d4faf2fca82cd913f14efbdba7cf295fa488106781d3cb6a8f52d2ef6f21f9f46029773d1289464697c2ba0abbedf5821f23fe45c5b38b02483045022100a02224b6084be798ae3009a4cf0609cdbfa1faa98d7b0657c40c7fd3a39bc484022042c2b01cffc1ddb03e59c96f6ece96ec4c9133e70b21c0d82f92c47897fc5bcf012103a54ea4405b3c933fc58b2a3b67aeb6d605a6079cf6f731820c93ca89252baeb400000000

5 changes: 5 additions & 0 deletions r1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### rawTx 0200000000010303f377d93eb8b48dd6b805d35c58d53200ffc37e632b4d67854ff602853f8332000000006a473044022069582941c9bb47bde5e4840813884f1049cccc26cf0c1ae1f82ce48d440eacfc02205a56c1df124eb4922cb338f5a99ff0840e632bdc274f3799cc3955b3f8afab670121022e68bc11f5c4ba06c4faad161e3cc141299ccf92b7549b3f34703bb90134a390ffffffff66d000a8bd84d5f29c986a3eef1baa4be9d241e28554a19161a1df22f2d79f8b0000000000ffffffff7e87e4a50b0231d4c5034fb05e93436b5974bbc6748f8b92db61b0dcf6cc58220000000000ffffffff0220291b000000000022512046f6497cb2f19d50c3e9e982a950dcc86214b01fcce437a42061c7bdb1230d33b805010000000000160014c98a3134bf8f33e599a67c40000d774205c1b41400014003694b9b7e9145af36d4faf2fca82cd913f14efbdba7cf295fa488106781d3cb6a8f52d2ef6f21f9f46029773d1289464697c2ba0abbedf5821f23fe45c5b38b02483045022100a02224b6084be798ae3009a4cf0609cdbfa1faa98d7b0657c40c7fd3a39bc484022042c2b01cffc1ddb03e59c96f6ece96ec4c9133e70b21c0d82f92c47897fc5bcf012103a54ea4405b3c933fc58b2a3b67aeb6d605a6079cf6f731820c93ca89252baeb400000000

### rawTx 0200000000010303f377d93eb8b48dd6b805d35c58d53200ffc37e632b4d67854ff602853f8332000000006a473044022069582941c9bb47bde5e4840813884f1049cccc26cf0c1ae1f82ce48d440eacfc02205a56c1df124eb4922cb338f5a99ff0840e632bdc274f3799cc3955b3f8afab670121022e68bc11f5c4ba06c4faad161e3cc141299ccf92b7549b3f34703bb90134a390ffffffff66d000a8bd84d5f29c986a3eef1baa4be9d241e28554a19161a1df22f2d79f8b0000000000ffffffff7e87e4a50b0231d4c5034fb05e93436b5974bbc6748f8b92db61b0dcf6cc58220000000000ffffffff0220291b000000000022512046f6497cb2f19d50c3e9e982a950dcc86214b01fcce437a42061c7bdb1230d33b805010000000000160014c98a3134bf8f33e599a67c40000d774205c1b41400014003694b9b7e9145af36d4faf2fca82cd913f14efbdba7cf295fa488106781d3cb6a8f52d2ef6f21f9f46029773d1289464697c2ba0abbedf5821f23fe45c5b38b02483045022100a02224b6084be798ae3009a4cf0609cdbfa1faa98d7b0657c40c7fd3a39bc484022042c2b01cffc1ddb03e59c96f6ece96ec4c9133e70b21c0d82f92c47897fc5bcf012103a54ea4405b3c933fc58b2a3b67aeb6d605a6079cf6f731820c93ca89252baeb400000000

mempool: 0200000000010303f377d93eb8b48dd6b805d35c58d53200ffc37e632b4d67854ff602853f8332000000006a473044022069582941c9bb47bde5e4840813884f1049cccc26cf0c1ae1f82ce48d440eacfc02205a56c1df124eb4922cb338f5a99ff0840e632bdc274f3799cc3955b3f8afab670121022e68bc11f5c4ba06c4faad161e3cc141299ccf92b7549b3f34703bb90134a390ffffffff66d000a8bd84d5f29c986a3eef1baa4be9d241e28554a19161a1df22f2d79f8b0000000000ffffffff7e87e4a50b0231d4c5034fb05e93436b5974bbc6748f8b92db61b0dcf6cc58220000000000ffffffff0220291b000000000022512046f6497cb2f19d50c3e9e982a950dcc86214b01fcce437a42061c7bdb1230d33b805010000000000160014c98a3134bf8f33e599a67c40000d774205c1b41400014003694b9b7e9145af36d4faf2fca82cd913f14efbdba7cf295fa488106781d3cb6a8f52d2ef6f21f9f46029773d1289464697c2ba0abbedf5821f23fe45c5b38b02483045022100a02224b6084be798ae3009a4cf0609cdbfa1faa98d7b0657c40c7fd3a39bc484022042c2b01cffc1ddb03e59c96f6ece96ec4c9133e70b21c0d82f92c47897fc5bcf012103a54ea4405b3c933fc58b2a3b67aeb6d605a6079cf6f731820c93ca89252baeb400000000
Loading