From 79f9d1eee19a4a4508132cd4b42725c14caed889 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Wed, 17 Jan 2024 12:15:20 +0100 Subject: [PATCH 1/4] Bump up contract packages used by the SDK ABIs of some tBTC contracts changed recently. Here we bump up relevant contract packages to fetch those changes and adjust the SDK around. --- typescript/yarn.lock | 60 ++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/typescript/yarn.lock b/typescript/yarn.lock index bf93f537f..5e86c6ab9 100644 --- a/typescript/yarn.lock +++ b/typescript/yarn.lock @@ -1516,16 +1516,16 @@ resolved "https://registry.yarnpkg.com/@keep-network/bitcoin-spv-sol/-/bitcoin-spv-sol-3.4.0-solc-0.8.tgz#8b44c246ffab8ea993efe196f6bf385b1a3b84dc" integrity sha512-KlpY9BbasyLvYXSS7dsJktgRChu/yjdFLOX8ldGA/pltLicCm/l0F4oqxL8wSws9XD12vq9x0B5qzPygVLB2TQ== -"@keep-network/ecdsa@2.1.0-dev.13", "@keep-network/ecdsa@development": - version "2.1.0-dev.13" - resolved "https://registry.yarnpkg.com/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.13.tgz#31f2f28e74485dcbe03f782f5f67ac299203f3f1" - integrity sha512-Gv9nNQQkE/VTitFSiJqQUXczIbHWOBVG7UH6h3MDo7Pcgl4iuXhM5nmQdRd9vqjU42rI+TyPuSErWjg2+QEEEw== +"@keep-network/ecdsa@2.1.0-dev.17", "@keep-network/ecdsa@development": + version "2.1.0-dev.17" + resolved "https://registry.yarnpkg.com/@keep-network/ecdsa/-/ecdsa-2.1.0-dev.17.tgz#2e6abea11c094adfa9f52dfe0e7d3befa5fe2dfb" + integrity sha512-48zLogWDObqf3uGffDd8N2iP7m3rBinF+tAl7DidY/87eqWNydhpbO4j1H5i9vZ+Axi6nOQcuvpLlS7p9pjOFw== dependencies: - "@keep-network/random-beacon" "2.1.0-dev.13" + "@keep-network/random-beacon" "2.1.0-dev.17" "@keep-network/sortition-pools" "^2.0.0-pre.16" "@openzeppelin/contracts" "^4.6.0" "@openzeppelin/contracts-upgradeable" "^4.6.0" - "@threshold-network/solidity-contracts" "1.3.0-dev.5" + "@threshold-network/solidity-contracts" "1.3.0-dev.11" "@keep-network/keep-core@1.8.0-dev.5": version "1.8.0-dev.5" @@ -1557,25 +1557,15 @@ version "0.0.1" resolved "https://codeload.github.com/keep-network/prettier-config-keep/tar.gz/a1a333e7ac49928a0f6ed39421906dd1e46ab0f3" -"@keep-network/random-beacon@2.1.0-dev.13": - version "2.1.0-dev.13" - resolved "https://registry.yarnpkg.com/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.13.tgz#8b4d20456e17cb76531a25c98370d3a6da8c8be5" - integrity sha512-o5+LvzQB5Sqnpbu5Wr97HvU63rlw9v/O5ZGxDiWe4XwzFhC/FEnza+uWgWm1IJkFVrQj/DzYokqkzgANx/lBnA== +"@keep-network/random-beacon@2.1.0-dev.17": + version "2.1.0-dev.17" + resolved "https://registry.yarnpkg.com/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.17.tgz#5fb2621948aa2fe07ceb134ba76f737b7e6d85cd" + integrity sha512-alfd2sHdMrX15qKzM4zwkZ3l/CXboLoeos4l3WvChW978VJIwUPm2ZIXd8tNTaHlykQ57eSSX7esaLfIjeO3Kg== dependencies: "@keep-network/sortition-pools" "^2.0.0-pre.16" "@openzeppelin/contracts" "4.7.3" "@thesis/solidity-contracts" "github:thesis/solidity-contracts#4985bcf" - "@threshold-network/solidity-contracts" "1.3.0-dev.5" - -"@keep-network/random-beacon@2.1.0-dev.14": - version "2.1.0-dev.14" - resolved "https://registry.yarnpkg.com/@keep-network/random-beacon/-/random-beacon-2.1.0-dev.14.tgz#d9fac9fa8a5a06ea0985114c4ca79e4805c16d55" - integrity sha512-FdVSW2VtUIcwPCrnrWUudbXOFi+SKZ6cEz7P3+gO+49DFas4ApH6lkRILD/DUHQDMV7D56TxAdw/DHt0dbA+wg== - dependencies: - "@keep-network/sortition-pools" "^2.0.0-pre.16" - "@openzeppelin/contracts" "4.7.3" - "@thesis/solidity-contracts" "github:thesis/solidity-contracts#4985bcf" - "@threshold-network/solidity-contracts" "1.3.0-dev.5" + "@threshold-network/solidity-contracts" "1.3.0-dev.8" "@keep-network/sortition-pools@1.2.0-dev.1": version "1.2.0-dev.1" @@ -1593,13 +1583,13 @@ "@thesis/solidity-contracts" "github:thesis/solidity-contracts#4985bcf" "@keep-network/tbtc-v2@development": - version "1.5.0-dev.3" - resolved "https://registry.yarnpkg.com/@keep-network/tbtc-v2/-/tbtc-v2-1.5.0-dev.3.tgz#814682bf9f627780137430c3ad5a6eaf5638eb3c" - integrity sha512-Vf/NOBf0ybN5cPP/R0LXJbZeMWVEkga7mWqie1HyktqJi8pp4XbHoJ6WIro+qrH47AymWv/S2mGYlU4Smsihrw== + version "1.6.0-dev.14" + resolved "https://registry.yarnpkg.com/@keep-network/tbtc-v2/-/tbtc-v2-1.6.0-dev.14.tgz#45e5ec1ca44bd47c8beb246b9718ce93b191b4e7" + integrity sha512-opUFWtQ3tSPCG0z9FcsOVTp4ATA+XyL2nQK9GgJ6BRfVv3RAYKM9Z97NFcAE9bjcbfwvYKg8P64YY680e9XFvA== dependencies: "@keep-network/bitcoin-spv-sol" "3.4.0-solc-0.8" - "@keep-network/ecdsa" "2.1.0-dev.13" - "@keep-network/random-beacon" "2.1.0-dev.14" + "@keep-network/ecdsa" "2.1.0-dev.17" + "@keep-network/random-beacon" "2.1.0-dev.17" "@keep-network/tbtc" "1.1.2-dev.1" "@openzeppelin/contracts" "^4.8.1" "@openzeppelin/contracts-upgradeable" "^4.8.1" @@ -1955,10 +1945,20 @@ dependencies: "@openzeppelin/contracts" "^4.1.0" -"@threshold-network/solidity-contracts@1.3.0-dev.5": - version "1.3.0-dev.5" - resolved "https://registry.yarnpkg.com/@threshold-network/solidity-contracts/-/solidity-contracts-1.3.0-dev.5.tgz#f7a2727d627a10218f0667bc0d33e19ed8f87fdc" - integrity sha512-AInTKQkJ0PKa32q2m8GnZFPYEArsnvOwhIFdBFaHdq9r4EGyqHMf4YY1WjffkheBZ7AQ0DNA8Lst30kBoQd0SA== +"@threshold-network/solidity-contracts@1.3.0-dev.11": + version "1.3.0-dev.11" + resolved "https://registry.yarnpkg.com/@threshold-network/solidity-contracts/-/solidity-contracts-1.3.0-dev.11.tgz#849f20a5094c93359bbdea0c42780c318f985ee0" + integrity sha512-QQJB17BvuU/7UaitneoD7zFmIA3fZQ3FAvOAP2q+FkWEBZPYtAMf3+vB7y+Y+QlrcUl1kcA9wXD5auirsdxCBQ== + dependencies: + "@keep-network/keep-core" ">1.8.1-dev <1.8.1-goerli" + "@openzeppelin/contracts" "~4.5.0" + "@openzeppelin/contracts-upgradeable" "~4.5.2" + "@thesis/solidity-contracts" "github:thesis/solidity-contracts#4985bcf" + +"@threshold-network/solidity-contracts@1.3.0-dev.8": + version "1.3.0-dev.8" + resolved "https://registry.yarnpkg.com/@threshold-network/solidity-contracts/-/solidity-contracts-1.3.0-dev.8.tgz#6de25dc6ce374cfbdf3b67c72097044631222f3a" + integrity sha512-s6SFZyf1xXgOdMK1zYnjsURnVz7Xxzf0z/34vH+hDg8n/G8L0jPR6Iz4laWSSL2y1P3ffFAFTUMvwfJMJitfVw== dependencies: "@keep-network/keep-core" ">1.8.1-dev <1.8.1-goerli" "@openzeppelin/contracts" "~4.5.0" From b240a9756e13afa5c671671b305977fe500a07a7 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Wed, 17 Jan 2024 13:35:01 +0100 Subject: [PATCH 2/4] Expose `getCoinbaseTxHash` function This function allows to get the BTC coinbase transaction for the given block height. --- typescript/src/lib/bitcoin/client.ts | 6 ++++++ typescript/src/lib/electrum/client.ts | 18 ++++++++++++++++++ typescript/test/lib/electrum.test.ts | 9 +++++++++ typescript/test/utils/mock-bitcoin-client.ts | 4 ++++ 4 files changed, 37 insertions(+) diff --git a/typescript/src/lib/bitcoin/client.ts b/typescript/src/lib/bitcoin/client.ts index 7befcb074..ca7699a16 100644 --- a/typescript/src/lib/bitcoin/client.ts +++ b/typescript/src/lib/bitcoin/client.ts @@ -101,4 +101,10 @@ export interface BitcoinClient { * @param transaction - Transaction to broadcast. */ broadcast(transaction: BitcoinRawTx): Promise + + /** + * Gets the hash of the coinbase transaction for the given block height. + * @param blockHeight - Height of the block. + */ + getCoinbaseTxHash(blockHeight: number): Promise } diff --git a/typescript/src/lib/electrum/client.ts b/typescript/src/lib/electrum/client.ts index a773cb86f..0d3d177c5 100644 --- a/typescript/src/lib/electrum/client.ts +++ b/typescript/src/lib/electrum/client.ts @@ -638,6 +638,24 @@ export class ElectrumClient implements BitcoinClient { }) }) } + + // eslint-disable-next-line valid-jsdoc + /** + * @see {BitcoinClient#getCoinbaseTxHash} + */ + getCoinbaseTxHash(blockHeight: number): Promise { + return this.withElectrum(async (electrum: Electrum) => { + const txHash = await this.withBackoffRetrier()(async () => { + return await electrum.request("blockchain.transaction.id_from_pos", [ + blockHeight, + 0, + false, + ]) + }) + + return BitcoinTxHash.from(txHash) + }) + } } /** diff --git a/typescript/test/lib/electrum.test.ts b/typescript/test/lib/electrum.test.ts index 3de68b213..c6b5d47e9 100644 --- a/typescript/test/lib/electrum.test.ts +++ b/typescript/test/lib/electrum.test.ts @@ -274,6 +274,15 @@ describe("Electrum", () => { ) }) }) + + describe("getCoinbaseTxHash", () => { + it("should return proper coinbase tx hash", async () => { + const result = await electrumClient.getCoinbaseTxHash(2135502) + expect(result.toString()).to.be.equal( + "1f523d1ce7553ec609bae104812dede95aa38eb13d2c2c6b64ffe868bbc1a54c" + ) + }) + }) }) }) diff --git a/typescript/test/utils/mock-bitcoin-client.ts b/typescript/test/utils/mock-bitcoin-client.ts index 35bd6a9b3..399803331 100644 --- a/typescript/test/utils/mock-bitcoin-client.ts +++ b/typescript/test/utils/mock-bitcoin-client.ts @@ -159,4 +159,8 @@ export class MockBitcoinClient implements BitcoinClient { resolve() }) } + + getCoinbaseTxHash(blockHeight: number): Promise { + throw new Error("not implemented") + } } From bc385f85abd5407cdfbd74f9474a54753d82ee28 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Wed, 17 Jan 2024 16:31:46 +0100 Subject: [PATCH 3/4] Include coinbase data in the `assembleBitcoinSpvProof` function --- typescript/src/lib/bitcoin/spv.ts | 54 ++++- typescript/src/lib/ethereum/bridge.ts | 8 +- typescript/test/data/deposit-sweep.ts | 44 ++++ typescript/test/data/proof.ts | 229 +++++++++++++++++++ typescript/test/data/redemption.ts | 43 ++++ typescript/test/lib/bitcoin.test.ts | 102 ++++++++- typescript/test/lib/ethereum.test.ts | 20 ++ typescript/test/services/maintenance.test.ts | 93 +++++++- typescript/test/utils/mock-bitcoin-client.ts | 23 +- 9 files changed, 597 insertions(+), 19 deletions(-) diff --git a/typescript/src/lib/bitcoin/spv.ts b/typescript/src/lib/bitcoin/spv.ts index 596fd4056..417e683a0 100644 --- a/typescript/src/lib/bitcoin/spv.ts +++ b/typescript/src/lib/bitcoin/spv.ts @@ -1,4 +1,4 @@ -import { BitcoinTx, BitcoinTxHash } from "./tx" +import { BitcoinTx, BitcoinTxHash, extractBitcoinRawTxVectors } from "./tx" import { BitcoinClient } from "./client" import { BigNumber } from "ethers" import { @@ -29,6 +29,17 @@ export interface BitcoinSpvProof { * 80-byte-long. The block header with the lowest height is first. */ bitcoinHeaders: Hex + + /** + * The sha256 preimage of the coinbase transaction hash i.e., + * the sha256 hash of the coinbase transaction. + */ + coinbasePreimage: Hex + + /** + * Merkle proof of coinbase transaction inclusion in a block. + */ + coinbaseProof: Hex } /** @@ -101,10 +112,34 @@ export async function assembleBitcoinSpvProof( const merkleProof = createMerkleProof(merkleBranch) + const coinbaseTxHash = await bitcoinClient.getCoinbaseTxHash(txBlockHeight) + + const coinbaseTx = extractBitcoinRawTxVectors( + await bitcoinClient.getRawTransaction(coinbaseTxHash) + ) + + const coinbasePreimage = BitcoinHashUtils.computeSha256( + Hex.from( + `${coinbaseTx.version.toString()}` + + `${coinbaseTx.inputs.toString()}` + + `${coinbaseTx.outputs.toString()}` + + `${coinbaseTx.locktime.toString()}` + ) + ) + + const coinbaseMerkleBranch = await bitcoinClient.getTransactionMerkle( + coinbaseTxHash, + txBlockHeight + ) + + const coinbaseMerkleProof = createMerkleProof(coinbaseMerkleBranch) + const proof = { merkleProof: merkleProof, txIndexInBlock: merkleBranch.position, bitcoinHeaders: headersChain, + coinbasePreimage: coinbasePreimage, + coinbaseProof: coinbaseMerkleProof, } return { ...transaction, ...proof } @@ -159,6 +194,13 @@ export async function validateBitcoinSpvProof( bitcoinClient ) + if ( + proof.merkleProof.toBuffer().length !== + proof.coinbaseProof.toBuffer().length + ) { + throw new Error("Tx not on same level of merkle tree as coinbase") + } + const bitcoinHeaders: BitcoinHeader[] = BitcoinHeaderSerializer.deserializeHeadersChain(proof.bitcoinHeaders) if (bitcoinHeaders.length != requiredConfirmations) { @@ -166,12 +208,11 @@ export async function validateBitcoinSpvProof( } const merkleRootHash: Hex = bitcoinHeaders[0].merkleRootHash - const intermediateNodeHashes: Hex[] = splitMerkleProof(proof.merkleProof) validateMerkleTree( transactionHash, merkleRootHash, - intermediateNodeHashes, + splitMerkleProof(proof.merkleProof), proof.txIndexInBlock ) @@ -180,6 +221,13 @@ export async function validateBitcoinSpvProof( previousDifficulty, currentDifficulty ) + + validateMerkleTree( + BitcoinHashUtils.computeSha256(proof.coinbasePreimage).reverse(), + merkleRootHash, + splitMerkleProof(proof.coinbaseProof), + 0 + ) } /** diff --git a/typescript/src/lib/ethereum/bridge.ts b/typescript/src/lib/ethereum/bridge.ts index 43a7c2d6e..9d1bcb7c9 100644 --- a/typescript/src/lib/ethereum/bridge.ts +++ b/typescript/src/lib/ethereum/bridge.ts @@ -286,6 +286,8 @@ export class EthereumBridge merkleProof: sweepProof.merkleProof.toPrefixedString(), txIndexInBlock: sweepProof.txIndexInBlock, bitcoinHeaders: sweepProof.bitcoinHeaders.toPrefixedString(), + coinbasePreimage: sweepProof.coinbasePreimage.toPrefixedString(), + coinbaseProof: sweepProof.coinbaseProof.toPrefixedString(), } const mainUtxoParam = { @@ -391,9 +393,11 @@ export class EthereumBridge } const redemptionProofParam = { - merkleProof: `0x${redemptionProof.merkleProof}`, + merkleProof: redemptionProof.merkleProof.toPrefixedString(), txIndexInBlock: redemptionProof.txIndexInBlock, - bitcoinHeaders: `0x${redemptionProof.bitcoinHeaders}`, + bitcoinHeaders: redemptionProof.bitcoinHeaders.toPrefixedString(), + coinbasePreimage: redemptionProof.coinbasePreimage.toPrefixedString(), + coinbaseProof: redemptionProof.coinbaseProof.toPrefixedString(), } const mainUtxoParam = { diff --git a/typescript/test/data/deposit-sweep.ts b/typescript/test/data/deposit-sweep.ts index 35b551e43..0d61d9386 100644 --- a/typescript/test/data/deposit-sweep.ts +++ b/typescript/test/data/deposit-sweep.ts @@ -430,6 +430,8 @@ export interface DepositSweepProofTestData { latestBlockHeight: number headersChain: Hex transactionMerkleBranch: BitcoinTxMerkleBranch + coinbaseRawTransaction: BitcoinRawTx + coinbaseMerkleBranch: BitcoinTxMerkleBranch } expectedSweepProof: { sweepTx: BitcoinRawTxVectors @@ -563,6 +565,38 @@ export const depositSweepProof: DepositSweepProofTestData = { ], position: 6, }, + coinbaseRawTransaction: { + transactionHex: + "02000000000101000000000000000000000000000000000000000000000000000" + + "0000000000000ffffffff4803bb05210445c21c62425443506f6f6cfabe6d6d97" + + "92ca7580b0dccdb4465ab26f697e165c536838032a466ef25b25bb7bebb3ae040" + + "000000d6531d503040db15755000000000000ffffffff0212304d000000000017" + + "a9147bef0b4a4dafa77b2ec52b81659cbcf0d9a91487870000000000000000266" + + "a24aa21a9ed8a716e1e3e8691cfc14df968505d88fae2e240b7f4ca4d59871992" + + "a63c9900640120000000000000000000000000000000000000000000000000000" + + "000000000000000000000", + }, + coinbaseMerkleBranch: { + blockHeight: 2164155, + merkle: [ + Hex.from( + "d654db76daa53b61becba19a542c6156a725d0bce3d0f3a57d713c9a9498ab95" + ), + Hex.from( + "df2a8c9cded17679ecacc119d92bfa5dc795bb80284e320e80e4f9f93d0e5ba4" + ), + Hex.from( + "b7d63f60c09609472aa68fbb6380cffc87b4d921a26edc8b2626154145d603be" + ), + Hex.from( + "a51612d3f3f857e95803a4d86aa6dbbe2e756dc2ed6cc0e04630e8baf597e377" + ), + Hex.from( + "a00501650e0c4f8a1e07a5d6d5bc5e75e4c75de61a65f0410cce354bbae78686" + ), + ], + position: 0, + }, }, expectedSweepProof: { sweepTx: { @@ -613,6 +647,16 @@ export const depositSweepProof: DepositSweepProofTestData = { "99b8e9db517e36f000000000000a494b8034039e7855b75563ab83c9410dd67e89b" + "b58e6cd93b85290a885dd749f4d61c62ed3e031ad9a83746" ), + coinbasePreimage: Hex.from( + "0248a67dec36c38a485ad920afd89dcf082e7e570390380890e3a9465662f449" + ), + coinbaseProof: Hex.from( + "95ab98949a3c717da5f3d0e3bcd025a756612c549aa1cbbe613ba5da76db54d6a45" + + "b0e3df9f9e4800e324e2880bb95c75dfa2bd919c1acec7976d1de9c8c2adfbe03d6" + + "45411526268bdc6ea221d9b487fccf8063bb8fa62a470996c0603fd6b777e397f5b" + + "ae83046e0c06cedc26d752ebedba66ad8a40358e957f8f3d31216a58686e7ba4b35" + + "ce0c41f0651ae65dc7e4755ebcd5d6a5071e8a4f0c0e650105a0" + ), }, mainUtxo: NO_MAIN_UTXO, }, diff --git a/typescript/test/data/proof.ts b/typescript/test/data/proof.ts index 6d3e114c0..8ff28b560 100644 --- a/typescript/test/data/proof.ts +++ b/typescript/test/data/proof.ts @@ -20,6 +20,8 @@ export interface ProofTestData { latestBlockHeight: number headersChain: Hex transactionMerkleBranch: BitcoinTxMerkleBranch + coinbaseRawTransaction: BitcoinRawTx + coinbaseMerkleBranch: BitcoinTxMerkleBranch } expectedProof: BitcoinSpvProof & BitcoinTx } @@ -115,6 +117,44 @@ export const singleInputProofTestData: ProofTestData = { ], position: 11, }, + coinbaseRawTransaction: { + transactionHex: + "02000000000101000000000000000000000000000000000000000000000000000" + + "0000000000000ffffffff4803b8052104e0c01c62425443506f6f6cfabe6d6d56" + + "06828fa0322cc81efd5fdbf7f100c7a8497e88dbc102097b30e08f13465bd9040" + + "000000d6531d503040db19f2e020000000000ffffffff0297814e000000000017" + + "a9147bef0b4a4dafa77b2ec52b81659cbcf0d9a91487870000000000000000266" + + "a24aa21a9ed4a673a4a13930bcc67727968c29960bf8ba07b218ff3811edf3ef3" + + "cc196707880120000000000000000000000000000000000000000000000000000" + + "000000000000000000000", + }, + coinbaseMerkleBranch: { + blockHeight: 2164152, + merkle: [ + Hex.from( + "095b539f725018c6afd940281d22f523ae74f9b07fe685b4f3decf130a6e3d7a" + ), + Hex.from( + "ad6a634865b1e61051b6ecf819d98110db0cf5747eaf2205fe9e82db47318b95" + ), + Hex.from( + "0c99d031690fa507e27c4068516c752297ea7df9a5b7482bfc0c44705c059024" + ), + Hex.from( + "352a81a4e75ea8c39060a6a383a4bd19245d86cf38918d9d49cafd4e54dfba7e" + ), + Hex.from( + "43ad3aadad675e398c59eb846a8e037cf7de8ba3b38f3388175f25d84b777c80" + ), + Hex.from( + "6969c227128793b3c9e99c05f20fb9b91fdb73458fd53151b5fe29d30c10cf9a" + ), + Hex.from( + "0a76bc4d8c3d532357be4d188ba89e9ae364a7d3c365e690e3cb07359b86129c" + ), + ], + position: 0, + }, }, expectedProof: { transactionHash: BitcoinTxHash.from( @@ -165,6 +205,18 @@ export const singleInputProofTestData: ProofTestData = { "d225028aa08ab6139eee31f4f67a010000000000004cda79bc48b970de2fb29c3f" + "38626eb9d70d8bae7b92aad09f2a0ad2d2f334d35bca1c62ffff001d048fc217" ), + coinbasePreimage: Hex.from( + "8e690235847a80c4d300542a2d27b90bfd13d77b3421c1b5590f4220718cd3fd" + ), + coinbaseProof: Hex.from( + "7a3d6e0a13cfdef3b485e67fb0f974ae23f5221d2840d9afc61850729f535b0995" + + "8b3147db829efe0522af7e74f50cdb1081d919f8ecb65110e6b16548636aad2490" + + "055c70440cfc2b48b7a5f97dea9722756c5168407ce207a50f6931d0990c7ebadf" + + "544efdca499d8d9138cf865d2419bda483a3a66090c3a85ee7a4812a35807c774b" + + "d8255f1788338fb3a38bdef77c038e6a84eb598c395e67adad3aad439acf100cd3" + + "29feb55131d58f4573db1fb9b90ff2059ce9c9b393871227c269699c12869b3507" + + "cbe390e665c3d3a764e39a9ea88b184dbe5723533d8c4dbc760a" + ), }, } @@ -295,6 +347,38 @@ export const multipleInputsProofTestData: ProofTestData = { ], position: 6, }, + coinbaseRawTransaction: { + transactionHex: + "02000000000101000000000000000000000000000000000000000000000000000" + + "0000000000000ffffffff4803bb05210445c21c62425443506f6f6cfabe6d6d97" + + "92ca7580b0dccdb4465ab26f697e165c536838032a466ef25b25bb7bebb3ae040" + + "000000d6531d503040db15755000000000000ffffffff0212304d000000000017" + + "a9147bef0b4a4dafa77b2ec52b81659cbcf0d9a91487870000000000000000266" + + "a24aa21a9ed8a716e1e3e8691cfc14df968505d88fae2e240b7f4ca4d59871992" + + "a63c9900640120000000000000000000000000000000000000000000000000000" + + "000000000000000000000", + }, + coinbaseMerkleBranch: { + blockHeight: 2164155, + merkle: [ + Hex.from( + "d654db76daa53b61becba19a542c6156a725d0bce3d0f3a57d713c9a9498ab95" + ), + Hex.from( + "df2a8c9cded17679ecacc119d92bfa5dc795bb80284e320e80e4f9f93d0e5ba4" + ), + Hex.from( + "b7d63f60c09609472aa68fbb6380cffc87b4d921a26edc8b2626154145d603be" + ), + Hex.from( + "a51612d3f3f857e95803a4d86aa6dbbe2e756dc2ed6cc0e04630e8baf597e377" + ), + Hex.from( + "a00501650e0c4f8a1e07a5d6d5bc5e75e4c75de61a65f0410cce354bbae78686" + ), + ], + position: 0, + }, }, expectedProof: { transactionHash: BitcoinTxHash.from( @@ -364,6 +448,16 @@ export const multipleInputsProofTestData: ProofTestData = { "99b8e9db517e36f000000000000a494b8034039e7855b75563ab83c9410dd67e89b" + "b58e6cd93b85290a885dd749f4d61c62ed3e031ad9a83746" ), + coinbasePreimage: Hex.from( + "0248a67dec36c38a485ad920afd89dcf082e7e570390380890e3a9465662f449" + ), + coinbaseProof: Hex.from( + "95ab98949a3c717da5f3d0e3bcd025a756612c549aa1cbbe613ba5da76db54d6a45" + + "b0e3df9f9e4800e324e2880bb95c75dfa2bd919c1acec7976d1de9c8c2adfbe03d6" + + "45411526268bdc6ea221d9b487fccf8063bb8fa62a470996c0603fd6b777e397f5b" + + "ae83046e0c06cedc26d752ebedba66ad8a40358e957f8f3d31216a58686e7ba4b35" + + "ce0c41f0651ae65dc7e4755ebcd5d6a5071e8a4f0c0e650105a0" + ), }, } @@ -378,6 +472,8 @@ export interface TransactionProofData { latestBlockHeight: number headersChain: Hex transactionMerkleBranch: BitcoinTxMerkleBranch + coinbaseRawTransaction: BitcoinRawTx + coinbaseMerkleBranch: BitcoinTxMerkleBranch previousDifficulty: BigNumber currentDifficulty: BigNumber } @@ -499,6 +595,56 @@ export const transactionConfirmationsInOneEpochData: TransactionProofData = { ], position: 17, }, + coinbaseRawTransaction: { + transactionHex: + "010000000001010000000000000000000000000000000000000000000000000000000" + + "000000000ffffffff6003e6d70b192f5669614254432f4d696e656420627920736368" + + "756c747a2f2cfabe6d6dcb674c098d80e8456c9cebe631a3ae9ec16e47185c0684b0c" + + "2fbab45827d4b79100000000000000010548e591b518d91e94608772cd43615000000" + + "0000ffffffff0290519d25000000001976a914536ffa992491508dca0354e52f32a3a" + + "7a679a53a88ac0000000000000000266a24aa21a9edca238b73df37d6eba6f22606c9" + + "38f46786f95c861e96acf85fdc6b61d4b3ecc10120000000000000000000000000000" + + "000000000000000000000000000000000000000000000", + }, + coinbaseMerkleBranch: { + blockHeight: 776166, + merkle: [ + Hex.from( + "438e7c04ee74dd2e79ffd3ace87c45a7b275e3f95783954ba321081e3f92fe99" + ), + Hex.from( + "02c8dcc4cb7b50857853ff31610700cd538e435002a433b318f4efc976912002" + ), + Hex.from( + "43e1894ff1469105951c529a622370460b666c722c84d05d73712e5af84bfd09" + ), + Hex.from( + "bdd6e6d69540a45185b4ad6dbfd108b7dfebc4d6ae7030d2d3bd139e9b2c1e28" + ), + Hex.from( + "cffed70dd62cab291f132fb90c5d6cd11af589db122df862585c6dd43c667d4f" + ), + Hex.from( + "13bdefbf92421aa7861528e16e7046b569d25ee0f4b7649492e42e9ea2331c39" + ), + Hex.from( + "df429494c5eef971a7ab80c8a0f7f9cdfa30148afef706f07923bd93d5a7e22a" + ), + Hex.from( + "c8a3f1bc73146bd4a1a0e848f2b0b4a21be86e4930f239d856af8e9646014236" + ), + Hex.from( + "1f514df87fe2c400e508e01cd8967657ef76db9681f65dc82b0bc6d4004b575f" + ), + Hex.from( + "e463950c8efd9114237189f07ddf1cfdb72658bad23bce667c269652bd0ade3c" + ), + Hex.from( + "3d7ae6df787807320fdc397a7055e86c932a7c36ab1d1f942b92c53bf2a1d2f9" + ), + ], + position: 0, + }, previousDifficulty: BigNumber.from(39156400059293), currentDifficulty: BigNumber.from(39350942467772), }, @@ -596,6 +742,54 @@ export const transactionConfirmationsInTwoEpochsData: TransactionProofData = { ], position: 262, }, + coinbaseRawTransaction: { + transactionHex: + "010000000001010000000000000000000000000000000000000000000000000000000" + + "000000000ffffffff5003fecf0b1362696e616e63652f383235f10213007dfea192fa" + + "be6d6def5afc5d3971282911709ed7cb7d9142bb531ae9cc453036b2b77afa95dc878" + + "304000000000000000000d483e700000000000000ffffffff03599476250000000017" + + "a914ca35b1f4d02907314852f09935b9604507f8d700870000000000000000266a24a" + + "a21a9eddfad2eae7dcecafd0a22651c8e9d197143b7811e503dc77d7fe70931d2fd7f" + + "7600000000000000002b6a2952534b424c4f434b3aed6626f157d5eaf68037ee382bd" + + "47af82d62aab3b7aab4b79058b234004c6cf401200000000000000000000000000000" + + "00000000000000000000000000000000000000000000", + }, + coinbaseMerkleBranch: { + blockHeight: 774142, + merkle: [ + Hex.from( + "294dca224d25b5637e2520e07a5b5942a1e56a91532927f9611b49ce73e8e674" + ), + Hex.from( + "69e58cb5e60eafa3faff8a4a691bb80d6bf541c40eba3d06868a28873b29089d" + ), + Hex.from( + "12b7ab7aae471f8a2208ffb88e2624a9107e99dfe52d653e0237149c08505794" + ), + Hex.from( + "11df9abc347cb8ae1f61e86b5713966780cc052704b145e26ec9591c3c6b819e" + ), + Hex.from( + "19ca1d35c20828cde2e933e0058f7666c2f5b428d135392bc4c444046f06831f" + ), + Hex.from( + "d112040dc1d4952db1f6079f9a79cd18e695495adac9e742b8b28f0adb30f2eb" + ), + Hex.from( + "ea5cbc9f966c8c69e0ecca2719d26fe834dd308e6fe620036d200f0c46454804" + ), + Hex.from( + "5bd7afaed89f37bf36978b52d634c57451215eecdf1780de3b5274f27ce5508b" + ), + Hex.from( + "73a7ab1687e76798fb48089d3965f678b72c74d8dee7d814761f08fa489e15f9" + ), + Hex.from( + "e7e530e181683d272293f19fe18a33f1dc05eded12ec27945b49311b2e14ee42" + ), + ], + position: 0, + }, previousDifficulty: BigNumber.from(37590453655497), currentDifficulty: BigNumber.from(39350942467772), }, @@ -683,6 +877,41 @@ export const testnetTransactionData: TransactionProofData = { ], position: 4, }, + coinbaseRawTransaction: { + transactionHex: + "010000000001010000000000000000000000000000000000000000000000000000000" + + "000000000ffffffff0403bdf124ffffffff02c92638000000000016001416761a41f7" + + "452e32f987702b6deb87f68cd7aa7a0000000000000000266a24aa21a9ed270d869bd" + + "7697384d2e4810cb6a24ba31390e2cc3c02fd60fd3f70e1997d63ec01200000000000" + + "00000000000000000000000000000000000000000000000000000000000000", + }, + coinbaseMerkleBranch: { + blockHeight: 2421181, + merkle: [ + Hex.from( + "9f6f47ff0bc14890fe99236695c3cf5dde83ee93f196737618a81480a3232bc3" + ), + Hex.from( + "6938bd629f8e0fc641d6438ee0d0f5100931cb68f48abf28a8b9a34321904d8b" + ), + Hex.from( + "27598cb2cc1e2131cd718bc8ecc1388d3b2f04c0e641bbb59876267ea465d5c7" + ), + Hex.from( + "0eebd6daa03f6db4a27541a91bcf86612c97d100bc37c3eb321d64d943adb2a5" + ), + Hex.from( + "b25854f31fc046eb0f53cddbf2b6de3d54d52710acd79a796c78c3be235f031a" + ), + Hex.from( + "1fc5ab77039f59ac2494791fc05c75fb53e2dacf57a20f67e7d6727b38778825" + ), + Hex.from( + "5b0acfdbb89af64a583a88e92252b8634bd4da06ee102ecd34c2662955e9f1c7" + ), + ], + position: 0, + }, previousDifficulty: BigNumber.from(1), currentDifficulty: BigNumber.from(1), }, diff --git a/typescript/test/data/redemption.ts b/typescript/test/data/redemption.ts index deeea11a3..e1dcf3aaf 100644 --- a/typescript/test/data/redemption.ts +++ b/typescript/test/data/redemption.ts @@ -546,6 +546,8 @@ export interface RedemptionProofTestData { latestBlockHeight: number headersChain: Hex transactionMerkleBranch: BitcoinTxMerkleBranch + coinbaseRawTransaction: BitcoinRawTx + coinbaseMerkleBranch: BitcoinTxMerkleBranch } expectedRedemptionProof: { redemptionTx: BitcoinRawTxVectors @@ -666,6 +668,37 @@ export const redemptionProof: RedemptionProofTestData = { ], position: 4, }, + coinbaseRawTransaction: { + transactionHex: + "0200000000010100000000000000000000000000000000000000000000000000000000" + + "00000000ffffffff480359832104f6905e62425443506f6f6cfabe6d6d44b54ffb3a8b" + + "00ab84e3907bfb9fe78157ce719a54630a1321f50b37116f9f71040000002a8dcca602" + + "04dad94668000000000000ffffffff028d644b000000000017a9147bef0b4a4dafa77b" + + "2ec52b81659cbcf0d9a91487870000000000000000266a24aa21a9ed533404630c2732" + + "6df0f77bad5822ad39e6ef184f61ac334eb6264af532bee74801200000000000000000" + + "00000000000000000000000000000000000000000000000000000000", + }, + coinbaseMerkleBranch: { + blockHeight: 2196313, + merkle: [ + Hex.from( + "f0ba82de74f59444ebe27dc395ecf2f6c769d29050f455ad1017f3dcdcd2dd59" + ), + Hex.from( + "1e7b3d5fb5b83ecd12bfe89d9bbd9de747ab544f111063339f11b57f08228ba3" + ), + Hex.from( + "983edacf13f754bf8b6f8c584b7fe12d0d0c0e890e7ce2d29e0c7335021d8b71" + ), + Hex.from( + "65ea59172f35ee6db6e4194227bea23daedbda8299bea94710f21c97f3e9cc17" + ), + Hex.from( + "8c5b4ce089d0c450bf6125e7d342114246802bf4c9638d222aa9fcbe8e06024e" + ), + ], + position: 0, + }, }, expectedRedemptionProof: { redemptionTx: { @@ -712,6 +745,16 @@ export const redemptionProof: RedemptionProofTestData = { "eed9f700b9c2b00000000000000465ec2f30447552a4a30ee63964aaebcb0406492" + "69eab449fb51823d58835a4aed9a5e62341f5c192fd94baa" ), + coinbasePreimage: Hex.from( + "5fb81be0d06e1573231f6b5f4eba3055f3160e1b45a7db6c9cb816b0c184b419" + ), + coinbaseProof: Hex.from( + "59ddd2dcdcf31710ad55f45090d269c7f6f2ec95c37de2eb4494f574de82baf0a38" + + "b22087fb5119f336310114f54ab47e79dbd9b9de8bf12cd3eb8b55f3d7b1e718b1d" + + "0235730c9ed2e27c0e890e0c0d2de17f4b588c6f8bbf54f713cfda3e9817cce9f39" + + "71cf21047a9be9982dadbae3da2be274219e4b66dee352f1759ea654e02068ebefc" + + "a92a228d63c9f42b8046421142d3e72561bf50c4d089e04c5b8c" + ), }, mainUtxo: { transactionHash: BitcoinTxHash.from( diff --git a/typescript/test/lib/bitcoin.test.ts b/typescript/test/lib/bitcoin.test.ts index 11dacfaac..fd57a9e25 100644 --- a/typescript/test/lib/bitcoin.test.ts +++ b/typescript/test/lib/bitcoin.test.ts @@ -18,6 +18,8 @@ import { BitcoinSpvProof, assembleBitcoinSpvProof, validateBitcoinSpvProof, + BitcoinRawTx, + BitcoinTxMerkleBranch, } from "../../src" import { BigNumber } from "ethers" import { btcAddresses, btcAddressFromPublicKey } from "../data/bitcoin" @@ -924,6 +926,10 @@ describe("Bitcoin", () => { expect(proof.merkleProof).to.deep.equal(expectedProof.merkleProof) expect(proof.txIndexInBlock).to.equal(expectedProof.txIndexInBlock) expect(proof.bitcoinHeaders).to.deep.equal(expectedProof.bitcoinHeaders) + expect(proof.coinbasePreimage).to.deep.equal( + expectedProof.coinbasePreimage + ) + expect(proof.coinbaseProof).to.deep.equal(expectedProof.coinbaseProof) }) }) @@ -944,6 +950,10 @@ describe("Bitcoin", () => { expect(proof.merkleProof).to.deep.equal(expectedProof.merkleProof) expect(proof.txIndexInBlock).to.equal(expectedProof.txIndexInBlock) expect(proof.bitcoinHeaders).to.deep.equal(expectedProof.bitcoinHeaders) + expect(proof.coinbasePreimage).to.deep.equal( + expectedProof.coinbasePreimage + ) + expect(proof.coinbaseProof).to.deep.equal(expectedProof.coinbaseProof) }) }) @@ -974,10 +984,22 @@ describe("Bitcoin", () => { data.bitcoinChainData.transaction ) bitcoinClient.transactions = transactions + bitcoinClient.latestHeight = data.bitcoinChainData.latestBlockHeight + bitcoinClient.headersChain = data.bitcoinChainData.headersChain - bitcoinClient.transactionMerkle = + + const txBlockHeight = + data.bitcoinChainData.latestBlockHeight - + data.bitcoinChainData.accumulatedTxConfirmations + + 1 + + const transactionMerkle = new Map() + transactionMerkle.set( + `${transactionHash.toString()}${txBlockHeight.toString(16)}`, data.bitcoinChainData.transactionMerkleBranch + ) + const confirmations = new Map() confirmations.set( transactionHash.toString(), @@ -985,6 +1007,29 @@ describe("Bitcoin", () => { ) bitcoinClient.confirmations = confirmations + const coinbaseTxHash = BitcoinTxHash.from( + BitcoinHashUtils.computeHash256( + Hex.from(data.bitcoinChainData.coinbaseRawTransaction.transactionHex) + ).toString() + ) + + const coinbaseHashes = new Map() + coinbaseHashes.set(txBlockHeight, coinbaseTxHash) + bitcoinClient.coinbaseHashes = coinbaseHashes + + const rawTransactions = new Map() + rawTransactions.set( + coinbaseTxHash.toString(), + data.bitcoinChainData.coinbaseRawTransaction + ) + bitcoinClient.rawTransactions = rawTransactions + + transactionMerkle.set( + `${coinbaseTxHash.toString()}${txBlockHeight.toString(16)}`, + data.bitcoinChainData.coinbaseMerkleBranch + ) + bitcoinClient.transactionMerkle = transactionMerkle + const proof = await assembleBitcoinSpvProof( transactionHash, data.requiredConfirmations, @@ -1089,6 +1134,14 @@ describe("Bitcoin", () => { merkle[merkle.length - 1].toString() + "ff" ) + const coinbaseMerkle = [ + ...transactionConfirmationsInOneEpochData.bitcoinChainData + .coinbaseMerkleBranch.merkle, + ] + coinbaseMerkle[coinbaseMerkle.length - 1] = Hex.from( + coinbaseMerkle[coinbaseMerkle.length - 1].toString() + "ff" + ) + const corruptedProofData: TransactionProofData = { ...transactionConfirmationsInOneEpochData, bitcoinChainData: { @@ -1098,6 +1151,11 @@ describe("Bitcoin", () => { .transactionMerkleBranch, merkle: merkle, }, + coinbaseMerkleBranch: { + ...transactionConfirmationsInOneEpochData.bitcoinChainData + .coinbaseMerkleBranch, + merkle: merkle, + }, }, } @@ -1119,6 +1177,11 @@ describe("Bitcoin", () => { .transactionMerkleBranch, merkle: [], }, + coinbaseMerkleBranch: { + ...transactionConfirmationsInOneEpochData.bitcoinChainData + .coinbaseMerkleBranch, + merkle: [], + }, }, } @@ -1252,10 +1315,22 @@ describe("Bitcoin", () => { data.bitcoinChainData.transaction ) bitcoinClient.transactions = transactions + bitcoinClient.latestHeight = data.bitcoinChainData.latestBlockHeight + bitcoinClient.headersChain = data.bitcoinChainData.headersChain - bitcoinClient.transactionMerkle = + + const txBlockHeight = + data.bitcoinChainData.latestBlockHeight - + data.bitcoinChainData.accumulatedTxConfirmations + + 1 + + const transactionMerkle = new Map() + transactionMerkle.set( + `${transactionHash.toString()}${txBlockHeight.toString(16)}`, data.bitcoinChainData.transactionMerkleBranch + ) + const confirmations = new Map() confirmations.set( transactionHash.toString(), @@ -1263,6 +1338,29 @@ describe("Bitcoin", () => { ) bitcoinClient.confirmations = confirmations + const coinbaseTxHash = BitcoinTxHash.from( + BitcoinHashUtils.computeHash256( + Hex.from(data.bitcoinChainData.coinbaseRawTransaction.transactionHex) + ).toString() + ) + + const coinbaseHashes = new Map() + coinbaseHashes.set(txBlockHeight, coinbaseTxHash) + bitcoinClient.coinbaseHashes = coinbaseHashes + + const rawTransactions = new Map() + rawTransactions.set( + coinbaseTxHash.toString(), + data.bitcoinChainData.coinbaseRawTransaction + ) + bitcoinClient.rawTransactions = rawTransactions + + transactionMerkle.set( + `${coinbaseTxHash.toString()}${txBlockHeight.toString(16)}`, + data.bitcoinChainData.coinbaseMerkleBranch + ) + bitcoinClient.transactionMerkle = transactionMerkle + await validateBitcoinSpvProof( data.bitcoinChainData.transaction.transactionHash, data.requiredConfirmations, diff --git a/typescript/test/lib/ethereum.test.ts b/typescript/test/lib/ethereum.test.ts index 0cda5f971..1b89d253f 100644 --- a/typescript/test/lib/ethereum.test.ts +++ b/typescript/test/lib/ethereum.test.ts @@ -203,6 +203,10 @@ describe("Ethereum", () => { merkleProof: Hex.from("44444444"), txIndexInBlock: 5, bitcoinHeaders: Hex.from("66666666"), + coinbasePreimage: BitcoinHashUtils.computeSha256( + Hex.from("77777777") + ), + coinbaseProof: Hex.from("88888888"), }, { transactionHash: BitcoinTxHash.from( @@ -227,6 +231,10 @@ describe("Ethereum", () => { merkleProof: "0x44444444", txIndexInBlock: 5, bitcoinHeaders: "0x66666666", + coinbasePreimage: BitcoinHashUtils.computeSha256( + Hex.from("77777777") + ).toPrefixedString(), + coinbaseProof: "0x88888888", }, { txHash: @@ -301,6 +309,10 @@ describe("Ethereum", () => { merkleProof: Hex.from("44444444"), txIndexInBlock: 5, bitcoinHeaders: Hex.from("66666666"), + coinbasePreimage: BitcoinHashUtils.computeSha256( + Hex.from("77777777") + ), + coinbaseProof: Hex.from("88888888"), }, { transactionHash: BitcoinTxHash.from( @@ -327,6 +339,10 @@ describe("Ethereum", () => { merkleProof: "0x44444444", txIndexInBlock: 5, bitcoinHeaders: "0x66666666", + coinbasePreimage: BitcoinHashUtils.computeSha256( + Hex.from("77777777") + ).toPrefixedString(), + coinbaseProof: "0x88888888", }, { txHash: @@ -357,6 +373,8 @@ describe("Ethereum", () => { revealedAt: 1654774330, sweptAt: 1655033516, treasuryFee: BigNumber.from(200), + extraData: + "0x0000000000000000000000000000000000000000000000000000000000000000", } as any) }) @@ -400,6 +418,8 @@ describe("Ethereum", () => { revealedAt: 1654774330, sweptAt: 1655033516, treasuryFee: BigNumber.from(200), + extraData: + "0x0000000000000000000000000000000000000000000000000000000000000000", } as any) }) diff --git a/typescript/test/services/maintenance.test.ts b/typescript/test/services/maintenance.test.ts index 8b0f3d7bd..18c89f6bc 100644 --- a/typescript/test/services/maintenance.test.ts +++ b/typescript/test/services/maintenance.test.ts @@ -2,11 +2,14 @@ import { BigNumber, BigNumberish } from "ethers" import { MockTBTCContracts } from "../utils/mock-tbtc-contracts" import { MockBitcoinClient } from "../utils/mock-bitcoin-client" import { + BitcoinHashUtils, BitcoinNetwork, BitcoinRawTx, BitcoinTx, BitcoinTxHash, + BitcoinTxMerkleBranch, BitcoinUtxo, + Hex, MaintenanceService, RedemptionRequest, WalletTx, @@ -2439,20 +2442,55 @@ describe("Maintenance", () => { transactionHash.toString(), depositSweepProof.bitcoinChainData.rawTransaction ) - bitcoinClient.rawTransactions = rawTransactions bitcoinClient.latestHeight = depositSweepProof.bitcoinChainData.latestBlockHeight bitcoinClient.headersChain = depositSweepProof.bitcoinChainData.headersChain - bitcoinClient.transactionMerkle = + + const txBlockHeight = + depositSweepProof.bitcoinChainData.latestBlockHeight - + depositSweepProof.bitcoinChainData.accumulatedTxConfirmations + + 1 + + const transactionMerkle = new Map() + transactionMerkle.set( + `${transactionHash.toString()}${txBlockHeight.toString(16)}`, depositSweepProof.bitcoinChainData.transactionMerkleBranch + ) + const confirmations = new Map() confirmations.set( transactionHash.toString(), depositSweepProof.bitcoinChainData.accumulatedTxConfirmations ) bitcoinClient.confirmations = confirmations + + const coinbaseTxHash = BitcoinTxHash.from( + BitcoinHashUtils.computeHash256( + Hex.from( + depositSweepProof.bitcoinChainData.coinbaseRawTransaction + .transactionHex + ) + ).toString() + ) + + const coinbaseHashes = new Map() + coinbaseHashes.set(txBlockHeight, coinbaseTxHash) + bitcoinClient.coinbaseHashes = coinbaseHashes + + rawTransactions.set( + coinbaseTxHash.toString(), + depositSweepProof.bitcoinChainData.coinbaseRawTransaction + ) + bitcoinClient.rawTransactions = rawTransactions + + transactionMerkle.set( + `${coinbaseTxHash.toString()}${txBlockHeight.toString(16)}`, + depositSweepProof.bitcoinChainData.coinbaseMerkleBranch + ) + bitcoinClient.transactionMerkle = transactionMerkle + await maintenanceService.spv.submitDepositSweepProof( transactionHash, NO_MAIN_UTXO @@ -2475,6 +2513,12 @@ describe("Maintenance", () => { expect(bridgeLog[0].sweepProof.bitcoinHeaders).to.deep.equal( depositSweepProof.expectedSweepProof.sweepProof.bitcoinHeaders ) + expect(bridgeLog[0].sweepProof.coinbasePreimage).to.deep.equal( + depositSweepProof.expectedSweepProof.sweepProof.coinbasePreimage + ) + expect(bridgeLog[0].sweepProof.coinbaseProof).to.deep.equal( + depositSweepProof.expectedSweepProof.sweepProof.coinbaseProof + ) }) }) @@ -2515,14 +2559,23 @@ describe("Maintenance", () => { transactionHash.toString(), redemptionProof.bitcoinChainData.rawTransaction ) - bitcoinClient.rawTransactions = rawTransactions bitcoinClient.latestHeight = redemptionProof.bitcoinChainData.latestBlockHeight bitcoinClient.headersChain = redemptionProof.bitcoinChainData.headersChain - bitcoinClient.transactionMerkle = + + const txBlockHeight = + redemptionProof.bitcoinChainData.latestBlockHeight - + redemptionProof.bitcoinChainData.accumulatedTxConfirmations + + 1 + + const transactionMerkle = new Map() + transactionMerkle.set( + `${transactionHash.toString()}${txBlockHeight.toString(16)}`, redemptionProof.bitcoinChainData.transactionMerkleBranch + ) + const confirmations = new Map() confirmations.set( transactionHash.toString(), @@ -2530,6 +2583,31 @@ describe("Maintenance", () => { ) bitcoinClient.confirmations = confirmations + const coinbaseTxHash = BitcoinTxHash.from( + BitcoinHashUtils.computeHash256( + Hex.from( + redemptionProof.bitcoinChainData.coinbaseRawTransaction + .transactionHex + ) + ).toString() + ) + + const coinbaseHashes = new Map() + coinbaseHashes.set(txBlockHeight, coinbaseTxHash) + bitcoinClient.coinbaseHashes = coinbaseHashes + + rawTransactions.set( + coinbaseTxHash.toString(), + redemptionProof.bitcoinChainData.coinbaseRawTransaction + ) + bitcoinClient.rawTransactions = rawTransactions + + transactionMerkle.set( + `${coinbaseTxHash.toString()}${txBlockHeight.toString(16)}`, + redemptionProof.bitcoinChainData.coinbaseMerkleBranch + ) + bitcoinClient.transactionMerkle = transactionMerkle + await maintenanceService.spv.submitRedemptionProof( transactionHash, mainUtxo, @@ -2556,6 +2634,13 @@ describe("Maintenance", () => { expect(bridgeLog[0].redemptionProof.bitcoinHeaders).to.deep.equal( redemptionProof.expectedRedemptionProof.redemptionProof.bitcoinHeaders ) + expect(bridgeLog[0].redemptionProof.coinbasePreimage).to.deep.equal( + redemptionProof.expectedRedemptionProof.redemptionProof + .coinbasePreimage + ) + expect(bridgeLog[0].redemptionProof.coinbaseProof).to.deep.equal( + redemptionProof.expectedRedemptionProof.redemptionProof.coinbaseProof + ) }) }) }) diff --git a/typescript/test/utils/mock-bitcoin-client.ts b/typescript/test/utils/mock-bitcoin-client.ts index 399803331..98bf77958 100644 --- a/typescript/test/utils/mock-bitcoin-client.ts +++ b/typescript/test/utils/mock-bitcoin-client.ts @@ -21,13 +21,10 @@ export class MockBitcoinClient implements BitcoinClient { private _transactionHashes = new Map() private _latestHeight = 0 private _headersChain = Hex.from("") - private _transactionMerkle: BitcoinTxMerkleBranch = { - blockHeight: 0, - merkle: [], - position: 0, - } + private _transactionMerkle = new Map() private _broadcastLog: BitcoinRawTx[] = [] private _transactionHistory = new Map() + private _coinbaseHashes = new Map() set network(value: BitcoinNetwork) { this._network = value @@ -61,7 +58,7 @@ export class MockBitcoinClient implements BitcoinClient { this._headersChain = value } - set transactionMerkle(value: BitcoinTxMerkleBranch) { + set transactionMerkle(value: Map) { this._transactionMerkle = value } @@ -69,6 +66,10 @@ export class MockBitcoinClient implements BitcoinClient { this._transactionHistory = value } + set coinbaseHashes(value: Map) { + this._coinbaseHashes = value + } + get broadcastLog(): BitcoinRawTx[] { return this._broadcastLog } @@ -149,7 +150,11 @@ export class MockBitcoinClient implements BitcoinClient { blockHeight: number ): Promise { return new Promise((resolve, _) => { - resolve(this._transactionMerkle) + resolve( + this._transactionMerkle.get( + `${transactionHash.toString()}${blockHeight.toString(16)}` + ) as BitcoinTxMerkleBranch + ) }) } @@ -161,6 +166,8 @@ export class MockBitcoinClient implements BitcoinClient { } getCoinbaseTxHash(blockHeight: number): Promise { - throw new Error("not implemented") + return new Promise((resolve, _) => { + resolve(this._coinbaseHashes.get(blockHeight) as BitcoinTxHash) + }) } } From e8ca5b89aed687d1d3b51862bfcc239a1af575c9 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Wed, 17 Jan 2024 16:32:39 +0100 Subject: [PATCH 4/4] Re-generate API reference --- typescript/api-reference/README.md | 6 ++-- .../api-reference/classes/ElectrumClient.md | 27 ++++++++++++++++++ .../api-reference/classes/EthereumBridge.md | 28 +++++++++---------- .../api-reference/interfaces/BitcoinClient.md | 23 +++++++++++++++ .../interfaces/BitcoinSpvProof.md | 27 ++++++++++++++++++ .../interfaces/BitcoinTxMerkleBranch.md | 6 ++-- 6 files changed, 97 insertions(+), 20 deletions(-) diff --git a/typescript/api-reference/README.md b/typescript/api-reference/README.md index 4d28190ab..c2ca73552 100644 --- a/typescript/api-reference/README.md +++ b/typescript/api-reference/README.md @@ -611,7 +611,7 @@ Bitcoin transaction along with the inclusion proof. #### Defined in -[src/lib/bitcoin/spv.ts:64](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/bitcoin/spv.ts#L64) +[src/lib/bitcoin/spv.ts:75](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/bitcoin/spv.ts#L75) ___ @@ -686,7 +686,7 @@ Electrum script hash as a hex string. #### Defined in -[src/lib/electrum/client.ts:649](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/electrum/client.ts#L649) +[src/lib/electrum/client.ts:667](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/electrum/client.ts#L667) ___ @@ -957,7 +957,7 @@ The function should be used within a try-catch block. #### Defined in -[src/lib/bitcoin/spv.ts:145](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/bitcoin/spv.ts#L145) +[src/lib/bitcoin/spv.ts:180](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/bitcoin/spv.ts#L180) ___ diff --git a/typescript/api-reference/classes/ElectrumClient.md b/typescript/api-reference/classes/ElectrumClient.md index ca7c460ce..2b4bc55ba 100644 --- a/typescript/api-reference/classes/ElectrumClient.md +++ b/typescript/api-reference/classes/ElectrumClient.md @@ -24,6 +24,7 @@ Electrum-based implementation of the Bitcoin client. - [broadcast](ElectrumClient.md#broadcast) - [findAllUnspentTransactionOutputs](ElectrumClient.md#findallunspenttransactionoutputs) +- [getCoinbaseTxHash](ElectrumClient.md#getcoinbasetxhash) - [getHeadersChain](ElectrumClient.md#getheaderschain) - [getNetwork](ElectrumClient.md#getnetwork) - [getRawTransaction](ElectrumClient.md#getrawtransaction) @@ -167,6 +168,32 @@ ___ ___ +### getCoinbaseTxHash + +▸ **getCoinbaseTxHash**(`blockHeight`): `Promise`\<[`BitcoinTxHash`](BitcoinTxHash.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `blockHeight` | `number` | + +#### Returns + +`Promise`\<[`BitcoinTxHash`](BitcoinTxHash.md)\> + +**`See`** + +#### Implementation of + +[BitcoinClient](../interfaces/BitcoinClient.md).[getCoinbaseTxHash](../interfaces/BitcoinClient.md#getcoinbasetxhash) + +#### Defined in + +[src/lib/electrum/client.ts:646](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/electrum/client.ts#L646) + +___ + ### getHeadersChain ▸ **getHeadersChain**(`blockHeight`, `chainLength`): `Promise`\<[`Hex`](Hex.md)\> diff --git a/typescript/api-reference/classes/EthereumBridge.md b/typescript/api-reference/classes/EthereumBridge.md index 0a14d40e3..bc84ce9b5 100644 --- a/typescript/api-reference/classes/EthereumBridge.md +++ b/typescript/api-reference/classes/EthereumBridge.md @@ -148,7 +148,7 @@ EthersContractHandle.\_totalRetryAttempts #### Defined in -[src/lib/ethereum/bridge.ts:494](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L494) +[src/lib/ethereum/bridge.ts:498](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L498) ___ @@ -177,7 +177,7 @@ Builds the UTXO hash based on the UTXO components. UTXO hash is computed as #### Defined in -[src/lib/ethereum/bridge.ts:618](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L618) +[src/lib/ethereum/bridge.ts:622](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L622) ___ @@ -204,7 +204,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:429](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L429) +[src/lib/ethereum/bridge.ts:433](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L433) ___ @@ -334,7 +334,7 @@ Bridge.getNewWalletRegisteredEvents #### Defined in -[src/lib/ethereum/bridge.ts:530](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L530) +[src/lib/ethereum/bridge.ts:534](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L534) ___ @@ -361,7 +361,7 @@ Bridge.getRedemptionRequestedEvents #### Defined in -[src/lib/ethereum/bridge.ts:635](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L635) +[src/lib/ethereum/bridge.ts:639](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L639) ___ @@ -381,7 +381,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:515](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L515) +[src/lib/ethereum/bridge.ts:519](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L519) ___ @@ -405,7 +405,7 @@ Parsed deposit request. #### Defined in -[src/lib/ethereum/bridge.ts:474](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L474) +[src/lib/ethereum/bridge.ts:478](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L478) ___ @@ -454,7 +454,7 @@ Parsed wallet data. #### Defined in -[src/lib/ethereum/bridge.ts:589](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L589) +[src/lib/ethereum/bridge.ts:593](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L593) ___ @@ -510,7 +510,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:336](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L336) +[src/lib/ethereum/bridge.ts:338](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L338) ___ @@ -597,7 +597,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:380](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L380) +[src/lib/ethereum/bridge.ts:382](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L382) ___ @@ -644,7 +644,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:322](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L322) +[src/lib/ethereum/bridge.ts:324](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L324) ___ @@ -664,7 +664,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:555](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L555) +[src/lib/ethereum/bridge.ts:559](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L559) ___ @@ -690,7 +690,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:572](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L572) +[src/lib/ethereum/bridge.ts:576](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L576) ___ @@ -715,7 +715,7 @@ Deposit key. #### Defined in -[src/lib/ethereum/bridge.ts:455](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L455) +[src/lib/ethereum/bridge.ts:459](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L459) ___ diff --git a/typescript/api-reference/interfaces/BitcoinClient.md b/typescript/api-reference/interfaces/BitcoinClient.md index d09386d9e..5f5faa90b 100644 --- a/typescript/api-reference/interfaces/BitcoinClient.md +++ b/typescript/api-reference/interfaces/BitcoinClient.md @@ -12,6 +12,7 @@ Represents a Bitcoin client. - [broadcast](BitcoinClient.md#broadcast) - [findAllUnspentTransactionOutputs](BitcoinClient.md#findallunspenttransactionoutputs) +- [getCoinbaseTxHash](BitcoinClient.md#getcoinbasetxhash) - [getHeadersChain](BitcoinClient.md#getheaderschain) - [getNetwork](BitcoinClient.md#getnetwork) - [getRawTransaction](BitcoinClient.md#getrawtransaction) @@ -72,6 +73,28 @@ List of UTXOs. ___ +### getCoinbaseTxHash + +▸ **getCoinbaseTxHash**(`blockHeight`): `Promise`\<[`BitcoinTxHash`](../classes/BitcoinTxHash.md)\> + +Gets the hash of the coinbase transaction for the given block height. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `blockHeight` | `number` | Height of the block. | + +#### Returns + +`Promise`\<[`BitcoinTxHash`](../classes/BitcoinTxHash.md)\> + +#### Defined in + +[src/lib/bitcoin/client.ts:109](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/bitcoin/client.ts#L109) + +___ + ### getHeadersChain ▸ **getHeadersChain**(`blockHeight`, `chainLength`): `Promise`\<[`Hex`](../classes/Hex.md)\> diff --git a/typescript/api-reference/interfaces/BitcoinSpvProof.md b/typescript/api-reference/interfaces/BitcoinSpvProof.md index 81058b45f..8141fc10c 100644 --- a/typescript/api-reference/interfaces/BitcoinSpvProof.md +++ b/typescript/api-reference/interfaces/BitcoinSpvProof.md @@ -8,6 +8,8 @@ the Bitcoin blockchain. ### Properties - [bitcoinHeaders](BitcoinSpvProof.md#bitcoinheaders) +- [coinbasePreimage](BitcoinSpvProof.md#coinbasepreimage) +- [coinbaseProof](BitcoinSpvProof.md#coinbaseproof) - [merkleProof](BitcoinSpvProof.md#merkleproof) - [txIndexInBlock](BitcoinSpvProof.md#txindexinblock) @@ -26,6 +28,31 @@ Concatenated block headers in hexadecimal format. Each block header is ___ +### coinbasePreimage + +• **coinbasePreimage**: [`Hex`](../classes/Hex.md) + +The sha256 preimage of the coinbase transaction hash i.e., +the sha256 hash of the coinbase transaction. + +#### Defined in + +[src/lib/bitcoin/spv.ts:37](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/bitcoin/spv.ts#L37) + +___ + +### coinbaseProof + +• **coinbaseProof**: [`Hex`](../classes/Hex.md) + +Merkle proof of coinbase transaction inclusion in a block. + +#### Defined in + +[src/lib/bitcoin/spv.ts:42](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/bitcoin/spv.ts#L42) + +___ + ### merkleProof • **merkleProof**: [`Hex`](../classes/Hex.md) diff --git a/typescript/api-reference/interfaces/BitcoinTxMerkleBranch.md b/typescript/api-reference/interfaces/BitcoinTxMerkleBranch.md index 94c74a9c9..aca610752 100644 --- a/typescript/api-reference/interfaces/BitcoinTxMerkleBranch.md +++ b/typescript/api-reference/interfaces/BitcoinTxMerkleBranch.md @@ -20,7 +20,7 @@ The height of the block the transaction was confirmed in. #### Defined in -[src/lib/bitcoin/spv.ts:41](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/bitcoin/spv.ts#L41) +[src/lib/bitcoin/spv.ts:52](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/bitcoin/spv.ts#L52) ___ @@ -34,7 +34,7 @@ the deepest pairing first. Each hash is an unprefixed hex string. #### Defined in -[src/lib/bitcoin/spv.ts:48](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/bitcoin/spv.ts#L48) +[src/lib/bitcoin/spv.ts:59](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/bitcoin/spv.ts#L59) ___ @@ -46,4 +46,4 @@ The 0-based index of the transaction's position in the block. #### Defined in -[src/lib/bitcoin/spv.ts:53](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/bitcoin/spv.ts#L53) +[src/lib/bitcoin/spv.ts:64](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/bitcoin/spv.ts#L64)