From e37a7a8fa899b8520e0e3a7272665e7ec78c648d Mon Sep 17 00:00:00 2001 From: Mohsen <56779182+mrtnetwork@users.noreply.github.com> Date: Tue, 30 Apr 2024 14:08:59 +0330 Subject: [PATCH] V2.1.0 fix issue #5 --- CHANGELOG.md | 11 +- README.md | 421 ++++++++++++++++++--------------- go.mod | 2 +- go.sum | 2 + provider/transction_builder.go | 4 +- scripts/script.go | 36 ++- scripts/transaction.go | 23 +- scripts/tx_input.go | 27 +-- scripts/tx_output.go | 15 +- test/bip_39_test.go | 9 +- test/hd_wallet_test.go | 5 +- test/keys_test.go | 3 +- test/p2pkh_test.go | 39 ++- test/p2sh_test.go | 19 +- test/p2tr_test.go | 23 +- test/p2wpkh_test.go | 39 ++- test/p2wsh_test.go | 22 +- 17 files changed, 418 insertions(+), 282 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8f6d97..2390988 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ +## 2.1.0 + +* fix issue [#5](https://github.com/mrtnetwork/bitcoin/issues/5) + ## 2.0.0 - - Updated golang.org/x/crypto to the latest version v0.18.0 - - Resolved issue with Multisignature address + +* Updated golang.org/x/crypto to the latest version v0.18.0 +* Resolved issue with Multisignature address ## 1.0.0 -* Release. \ No newline at end of file +* Release. diff --git a/README.md b/README.md index 26c1a0b..ba462fc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # BITCOIN GO Package + a comprehensive and versatile Go library for all your Bitcoin transaction needs. offers robust support for various Bitcoin transaction types, including spending transactions, Bitcoin address management, Bitcoin Schnorr signatures, BIP-39 mnemonic phrase generation, hierarchical deterministic (HD) wallet derivation, and Web3 Secret Storage Definition. This package was inspired by the [python-bitcoin-utils](https://github.com/karask/python-bitcoin-utils) package and turned into GO @@ -6,6 +7,7 @@ This package was inspired by the [python-bitcoin-utils](https://github.com/karas ## Features ### Transaction Types + This comprehensive package provides robust support for a wide array of Bitcoin transaction types, encompassing the full spectrum of Bitcoin transaction capabilities. Whether you need to execute standard payments, facilitate complex multi-signature wallets, leverage Segregated Witness (SegWit) transactions for lower fees and enhanced scalability, or embrace the privacy and flexibility of Pay-to-Taproot (P2TR) transactions, this package has you covered. Additionally, it empowers users to engage in legacy transactions, create time-locked transactions, and harness the security of multisignature (multisig) transactions. With this package, you can seamlessly navigate the diverse landscape of Bitcoin transactions, ensuring your interactions with the Bitcoin network are secure, efficient, and tailored to your specific needs. - P2PKH (Pay-to-Public-Key-Hash): The most common transaction type, it sends funds to a recipient's public key hash. Provides security and anonymity. @@ -29,12 +31,15 @@ This comprehensive package provides robust support for a wide array of Bitcoin t - Coinbase Transactions: The first transaction in each block, generating new Bitcoins as a block reward for miners. It includes the miner's payout address. ### Create Transaction + Using this package, you can create a Bitcoin transaction in two ways: either through the `BtcTransaction` struct or the `BitcoinTransactionBuilder` struct + - BtcTransaction: To use the `BtcTransaction` struct, you should have a general understanding of how Bitcoin transactions work, including knowledge of UTXOs, scripts, various types of scripts, Bitcoin addresses, signatures, and more. We created examples and tests to enhance your understanding. An example of this transaction type is explained below, and you can also find numerous examples in the [`test`](https://github.com/mrtnetwork/bitcoin/tree/main/test) folder. - BitcoinTransactionBuilder: Even with limited prior knowledge, you can utilize this class to send various types of transactions. Below, I've provided an example in which a transaction features 8 distinct input addresses with different types and private keys, as well as 10 different output addresses. Furthermore, additional examples have been prepared, which you can find in the [`example`](https://github.com/mrtnetwork/bitcoin/tree/main/example) folder. ### Addresses + - P2PKH A P2PKH (Pay-to-Public-Key-Hash) address in Bitcoin represents ownership of a cryptocurrency wallet by encoding a hashed public key - P2WPKH: A P2WPKH (Pay-to-Witness-Public-Key-Hash) address in Bitcoin is a Segregated Witness (SegWit) address that enables more efficient and secure transactions by segregating witness data, enhancing network scalability and security. @@ -48,28 +53,32 @@ Using this package, you can create a Bitcoin transaction in two ways: either thr - P2SH(SEGWIT): A P2SH (Pay-to-Script-Hash) Segregated Witness (SegWit) address in Bitcoin combines the benefits of P2SH and SegWit technologies, allowing for enhanced transaction security, reduced fees, and improved scalability. ### Sign + - Sign message: ECDSA Signature Algorithm - Sign Segwit(v0) and legacy transaction: ECDSA Signature Algorithm - Sign Taproot transaction - - Script Path and TapTweak: Taproot allows for multiple script paths (smart contract conditions) to be included in a single transaction. The "taptweak" ensures that the correct + - Script Path and TapTweak: Taproot allows for multiple script paths (smart contract conditions) to be included in a single transaction. The "taptweak" ensures that the correct script path is used when spending. This enhances privacy by making it difficult to determine the spending conditions from the transaction. - - - Schnorr Signatures: While ECDSA is still used for Taproot, it also provides support for Schnorr signatures. Schnorr signatures offer benefits such as smaller signature sizes and + + - Schnorr Signatures: While ECDSA is still used for Taproot, it also provides support for Schnorr signatures. Schnorr signatures offer benefits such as smaller signature sizes and signature aggregation, contributing to improved scalability and privacy. - - - Schnorr-Musig: Taproot can leverage Schnorr-Musig, a technique for securely aggregating multiple signatures into a single signature. This feature enables collaborative spending and + + - Schnorr-Musig: Taproot can leverage Schnorr-Musig, a technique for securely aggregating multiple signatures into a single signature. This feature enables collaborative spending and enhances privacy. ### BIP-39 + - Generate BIP39 mnemonics, providing a secure and standardized way to manage keys and seed phrases ### HD Wallet + - Implement hierarchical deterministic (HD) wallet derivation ### Web3 Secret Storage Definition + - JSON Format: Private keys are stored in a JSON (JavaScript Object Notation) format, making it easy to work with in various programming languages. - Encryption: The private key is encrypted using the user's chosen password. This ensures that even if the JSON file is compromised, an attacker cannot access the private key without the password. @@ -87,13 +96,16 @@ Using this package, you can create a Bitcoin transaction in two ways: either thr - Metadata: Additional metadata, such as the address associated with the private key, may be included in the JSON file. ### Node Provider + We have added two APIs (Mempool and BlockCypher) to the plugin for network access. You can easily use these two APIs to obtain information such as unspent transactions (UTXO), network fees, sending transactions, receiving transaction information, and retrieving account transactions. ## EXAMPLES ### Key and addresses - - Private key - ``` + +- Private key + + ```go // Create an EC private key instance from a WIF (Wallet Import Format) encoded string. privateKey, _ := keypair.NewECPrivateFromWIF("cT33CWKwcV8afBs5NYzeSzeSoGETtAB8izjDjMEuGqyqPoF7fbQR") @@ -113,59 +125,65 @@ We have added two APIs (Mempool and BlockCypher) to the plugin for network acces // Convert the private key to its hexadecimal representation. toHex := privateKey.ToHex() ``` + - Public key - ``` - // Create an instance of an EC public key from a hexadecimal representation. - publicKey, _ := keypair.NewECPPublicFromHex() - // Generate a Pay-to-Public-Key-Hash (P2PKH) address from the public key. - p2pkh := publicKey.ToAddress() +```go + + // Create an instance of an EC public key from a hexadecimal representation. + publicKey, _ := keypair.NewECPPublicFromHex() + + // Generate a Pay-to-Public-Key-Hash (P2PKH) address from the public key. + p2pkh := publicKey.ToAddress() + + // Generate a Pay-to-Witness-Public-Key-Hash (P2WPKH) Segregated Witness (SegWit) address from the public key. + p2wpkh := publicKey.ToSegwitAddress() - // Generate a Pay-to-Witness-Public-Key-Hash (P2WPKH) Segregated Witness (SegWit) address from the public key. - p2wpkh := publicKey.ToSegwitAddress() + // Generate a Pay-to-Witness-Script-Hash (P2WSH) Segregated Witness (SegWit) address from the public key. + p2wsh := publicKey.ToP2WSHAddress() - // Generate a Pay-to-Witness-Script-Hash (P2WSH) Segregated Witness (SegWit) address from the public key. - p2wsh := publicKey.ToP2WSHAddress() + // Generate a Taproot address from the public key. + p2tr := publicKey.ToTaprootAddress() - // Generate a Taproot address from the public key. - p2tr := publicKey.ToTaprootAddress() + // Generate a Pay-to-Public-Key-Hash (P2PKH) inside Pay-to-Script-Hash (P2SH) address from the public key. + p2pkhInP2sh := publicKey.ToP2PKHInP2SH() - // Generate a Pay-to-Public-Key-Hash (P2PKH) inside Pay-to-Script-Hash (P2SH) address from the public key. - p2pkhInP2sh := publicKey.ToP2PKHInP2SH() + // Generate a Pay-to-Witness-Public-Key-Hash (P2WPKH) inside Pay-to-Script-Hash (P2SH) address from the public key. + p2wpkhInP2sh := publicKey.ToP2WPKHInP2SH() - // Generate a Pay-to-Witness-Public-Key-Hash (P2WPKH) inside Pay-to-Script-Hash (P2SH) address from the public key. - p2wpkhInP2sh := publicKey.ToP2WPKHInP2SH() + // Generate a Pay-to-Witness-Script-Hash (P2WSH) inside Pay-to-Script-Hash (P2SH) address from the public key. + p2wshInP2sh := publicKey.ToP2WSHInP2SH() - // Generate a Pay-to-Witness-Script-Hash (P2WSH) inside Pay-to-Script-Hash (P2SH) address from the public key. - p2wshInP2sh := publicKey.ToP2WSHInP2SH() + // Generate a Pay-to-Public-Key (P2PK) inside Pay-to-Script-Hash (P2SH) address from the public key. + p2pkInP2sh := publicKey.ToP2PKInP2SH() - // Generate a Pay-to-Public-Key (P2PK) inside Pay-to-Script-Hash (P2SH) address from the public key. - p2pkInP2sh := publicKey.ToP2PKInP2SH() + // Get the compressed bytes representation of the public key. + compressedBytes := publicKey.ToCompressedBytes() - // Get the compressed bytes representation of the public key. - compressedBytes := publicKey.ToCompressedBytes() + // Get the uncompressed bytes representation of the public key. + unCompressedBytes := publicKey.ToUnCompressedBytes(true) - // Get the uncompressed bytes representation of the public key. - unCompressedBytes := publicKey.ToUnCompressedBytes(true) + // extracts and returns the x-coordinate (first 32 bytes) of the ECPublic key + // as a hexadecimal string. + onlyX := publicKey.ToXOnlyHex() - // extracts and returns the x-coordinate (first 32 bytes) of the ECPublic key - // as a hexadecimal string. - onlyX := publicKey.ToXOnlyHex() + // CalculateTweak computes and returns the TapTweak value based on the ECPublic key + // and an optional script. It uses the key's x-coordinate and the Merkle root of the script + // (if provided) to calculate the tweak. + tweak, _ := publicKey.CalculateTweak(scripts.Script{}) + + // computes and returns the Taproot commitment point's x-coordinate + // derived from the ECPublic key and an optional script, represented as a hexadecimal string. + taproot, _ := publicKey.ToTapRotHex([]interface{}{}) - // CalculateTweak computes and returns the TapTweak value based on the ECPublic key - // and an optional script. It uses the key's x-coordinate and the Merkle root of the script - // (if provided) to calculate the tweak. - tweak, _ := publicKey.CalculateTweak(scripts.Script{}) - - // computes and returns the Taproot commitment point's x-coordinate - // derived from the ECPublic key and an optional script, represented as a hexadecimal string. - taproot, _ := publicKey.ToTapRotHex([]interface{}{}) + // Verify verifies a signature against a message + verify := publicKey.Verify() - // Verify verifies a signature against a message - verify := publicKey.Verify() ``` + - Addresses - ``` + + ```go // The `...FromAddress` methods only check the address and return an error if it does not belong to Bitcoin. // If you also want to verify that the address belongs to a specific network, // please select the desired network using the parameters. @@ -216,8 +234,10 @@ We have added two APIs (Mempool and BlockCypher) to the plugin for network acces ``` ### Transaction + - With TransactionBuilder - ``` + + ```go // spending from 3 private key with 8 different address to 10 address // network network := address.TestnetNetwork @@ -283,43 +303,44 @@ We have added two APIs (Mempool and BlockCypher) to the plugin for network acces // now we chose some address for spending from multiple address // i use some different address type for this spenders := []provider.UtxoOwnerDetails{ - {PublicKey: public1.ToHex(), Address: exampleAddr1}, // p2pkh address from public1 - {PublicKey: public2.ToHex(), Address: exampleAddr2}, // P2TRAddress address from public2 - {PublicKey: public3.ToHex(), Address: exampleAddr7}, // P2SH(P2WPKH) address from public3 - {PublicKey: public3.ToHex(), Address: exampleAddr9}, - {PublicKey: public3.ToHex(), Address: exampleAddr10}, - {PublicKey: public2.ToHex(), Address: exampleAddr3}, // P2SH(P2PKH) address public2 - {PublicKey: public4.ToHex(), Address: exampleAddr8}, // P2SH(P2PKH) address public2 - {PublicKey: public3.ToHex(), Address: exampleAddr4}, // p2pkh address from public1 - } + {PublicKey: public1.ToHex(), Address: exampleAddr1}, // p2pkh address from public1 + {PublicKey: public2.ToHex(), Address: exampleAddr2}, // P2TRAddress address from public2 + {PublicKey: public3.ToHex(), Address: exampleAddr7}, // P2SH(P2WPKH) address from public3 + {PublicKey: public3.ToHex(), Address: exampleAddr9}, + {PublicKey: public3.ToHex(), Address: exampleAddr10}, + {PublicKey: public2.ToHex(), Address: exampleAddr3}, // P2SH(P2PKH) address public2 + {PublicKey: public4.ToHex(), Address: exampleAddr8}, // P2SH(P2PKH) address public2 + {PublicKey: public3.ToHex(), Address: exampleAddr4}, // p2pkh address from public1 + + utxos := provider.UtxoWithOwnerList{} // i add some method for provider to read utxos from mempol or blockCypher // looping address to read Utxos for _, spender := range spenders { - // read ech address utxo from mempol - spenderUtxos, err := api.GetAccountUtxo(spender) - - // oh this address does not have any satoshi for spending - if !spenderUtxos.CanSpending() { - continue - } - // oh something bad happen when reading Utxos - if err != nil { - return - } - // we append address utxos to utxos list - utxos = append(utxos, spenderUtxos...) - } + // read ech address utxo from mempol + spenderUtxos, err := api.GetAccountUtxo(spender) + + // oh this address does not have any satoshi for spending + if !spenderUtxos.CanSpending() { + continue + } + // oh something bad happen when reading Utxos + if err != nil { + return + } + // we append address utxos to utxos list + utxos = append(utxos, spenderUtxos...) + // Well, now we calculate how much we can spend sumOfUtxo := utxos.SumOfUtxosValue() hasSatoshi := sumOfUtxo.Cmp(big.NewInt(0)) != 0 if !hasSatoshi { - // Are you kidding? We don't have btc to spend - return + // Are you kidding? We don't have btc to spend + return } // 1817320 sum of all utxos @@ -332,74 +353,74 @@ We have added two APIs (Mempool and BlockCypher) to the plugin for network acces // we create 8 different output with different address type like (pt2r,p2sh(p2wpkh),p2sh(p2wsh),p2sh(p2pkh),p2sh(p2pk),p2pkh,p2wph,p2wsh and etc..) // We consider the spendable amount for 10 outputs and divide by 10, each output 176,732 output1 := provider.BitcoinOutputDetails{ - Address: exampleAddr4, - Value: big.NewInt(176732), + Address: exampleAddr4, + Value: big.NewInt(176732), } output2 := provider.BitcoinOutputDetails{ - Address: exampleAddr9, - Value: big.NewInt(176732), + Address: exampleAddr9, + Value: big.NewInt(176732), } output3 := provider.BitcoinOutputDetails{ - Address: exampleAddr10, - Value: big.NewInt(176732), + Address: exampleAddr10, + Value: big.NewInt(176732), } output4 := provider.BitcoinOutputDetails{ - Address: exampleAddr1, - Value: big.NewInt(176732), + Address: exampleAddr1, + Value: big.NewInt(176732), } output5 := provider.BitcoinOutputDetails{ - Address: exampleAddr3, - Value: big.NewInt(176732), + Address: exampleAddr3, + Value: big.NewInt(176732), } output6 := provider.BitcoinOutputDetails{ - Address: exampleAddr2, - Value: big.NewInt(176732), + Address: exampleAddr2, + Value: big.NewInt(176732), } output7 := provider.BitcoinOutputDetails{ - Address: exampleAddr7, - Value: big.NewInt(176732), + Address: exampleAddr7, + Value: big.NewInt(176732), } output8 := provider.BitcoinOutputDetails{ - Address: exampleAddr8, - Value: big.NewInt(176732), + Address: exampleAddr8, + Value: big.NewInt(176732), } output9 := provider.BitcoinOutputDetails{ - Address: exampleAddr5, - Value: big.NewInt(176732), + Address: exampleAddr5, + Value: big.NewInt(176732), } output10 := provider.BitcoinOutputDetails{ - Address: exampleAddr6, - Value: big.NewInt(176732), + Address: exampleAddr6, + Value: big.NewInt(176732), } // Well, now it is clear to whom we are going to pay the amount // Now let's create the transaction transactionBuilder := provider.NewBitcoinTransactionBuilder( - // Now, we provide the UTXOs we want to spend. - utxos, + // Now, we provide the UTXOs we want to spend. + utxos, - // We select transaction outputs - []provider.BitcoinOutputDetails{output1, output2, output3, output4, output5, output6, output7, output8, output9, output10}, + // We select transaction outputs + []provider.BitcoinOutputDetails{output1, output2, output3, output4, output5, output6, output7, output8, output9, output10}, - // Transaction fee - // Ensure that you have accurately calculated the amounts. - // If the sum of the outputs, including the transaction fee, - // does not match the total amount of UTXOs, - // it will result in an error. Please double-check your calculations. + // Transaction fee + // Ensure that you have accurately calculated the amounts. + // If the sum of the outputs, including the transaction fee, + // does not match the total amount of UTXOs, + // it will result in an error. Please double-check your calculations. - FEE, - // network (address.BitcoinNetwork ,ddress.TestnetNetwork) - &network, + FEE, + // network (address.BitcoinNetwork ,ddress.TestnetNetwork) + &network, - // If you like the note write something else and leave it blank - // I will put my GitHub address here - "https://github.com/mrtnetwork", + // If you like the note write something else and leave it blank + // I will put my GitHub address here + "https://github.com/mrtnetwork", - // RBF, or Replace-By-Fee, is a feature in Bitcoin that allows you to increase the fee of an unconfirmed - // transaction that you've broadcasted to the network. - // This feature is useful when you want to speed up a - // transaction that is taking longer than expected to get confirmed due to low transaction fees. - true, + // RBF, or Replace-By-Fee, is a feature in Bitcoin that allows you to increase the fee of an unconfirmed + // transaction that you've broadcasted to the network. + // This feature is useful when you want to speed up a + // transaction that is taking longer than expected to get confirmed due to low transaction fees. + true, ) // now we use BuildTransaction to complete them @@ -419,51 +440,51 @@ We have added two APIs (Mempool and BlockCypher) to the plugin for network acces // This TransactionBuilder only works with SIGHASH_ALL and TAPROOT_SIGHASH_ALL for taproot input // If you want to use another sighash, you should create another TransactionBuilder transaction, err := transactionBuilder.BuildTransaction(func(trDigest []byte, utxo provider.UtxoWithOwner, multiSigPublicKey string) (string, error) { - var key keypair.ECPrivate - - currentPublicKey := utxo.OwnerDetails.PublicKey - if utxo.IsMultiSig() { - currentPublicKey = multiSigPublicKey - } - // ok we have the public key of the current UTXO and we use some conditions to find private key and sign transaction - switch currentPublicKey { - case public3.ToHex(): - { - key = *private3 - } - case public2.ToHex(): - { - key = *private2 - } - - case public1.ToHex(): - { - key = *private1 - } - case public4.ToHex(): - { - key = *private4 - } - default: - { - return "", fmt.Errorf("cannot find private key") - } - } - // Ok, now we have the private key, we need to check which method to use for signing - // We check whether the UTX corresponds to the P2TR address or not. - if utxo.Utxo.IsP2tr() { - // yes is p2tr utxo and now we use SignTaprootTransaction(Schnorr sign) - // for now this transaction builder support only tweak transaction - return key.SignTaprootTransaction( - trDigest, constant.TAPROOT_SIGHASH_ALL, []interface{}{}, true, - ), nil - } - // is seqwit(v0) or lagacy address we use SingInput (ECDSA) - return key.SingInput(trDigest, constant.SIGHASH_ALL), nil - }) + var key keypair.ECPrivate + + currentPublicKey := utxo.OwnerDetails.PublicKey + if utxo.IsMultiSig() { + currentPublicKey = multiSigPublicKey + } + // ok we have the public key of the current UTXO and we use some conditions to find private key and sign transaction + switch currentPublicKey { + case public3.ToHex(): + { + key = *private3 + } + case public2.ToHex(): + { + key = *private2 + } + + case public1.ToHex(): + { + key = *private1 + } + case public4.ToHex(): + { + key = *private4 + } + default: + { + return "", fmt.Errorf("cannot find private key") + } + } + // Ok, now we have the private key, we need to check which method to use for signing + // We check whether the UTX corresponds to the P2TR address or not. + if utxo.Utxo.IsP2tr() { + // yes is p2tr utxo and now we use SignTaprootTransaction(Schnorr sign) + // for now this transaction builder support only tweak transaction + return key.SignTaprootTransaction( + trDigest, constant.TAPROOT_SIGHASH_ALL, []interface{}{}, true, + ), nil + } + // is seqwit(v0) or lagacy address we use SingInput (ECDSA) + return key.SingInput(trDigest, constant.SIGHASH_ALL), nil + }) if err != nil { - return + return } // ok everything is fine and we need a transaction output for broadcasting // We use the Serialize method to receive the transaction output @@ -480,24 +501,26 @@ We have added two APIs (Mempool and BlockCypher) to the plugin for network acces var transactionSize int if isSegwitTr { - transactionSize = transaction.GetVSize() + transactionSize = transaction.GetVSize() } else { - transactionSize = transaction.GetSize() + transactionSize = transaction.GetSize() } // now we send transaction to network trId, err := api.SendRawTransaction(digest) if err != nil { - return + return } // Yes, we did :) 5015a7748d8d6df47358902b6cdc6d77ef839945c479924f4592fd89315ac0e0 // Now we check Mempol for what happened https://mempool.space/testnet/tx/5015a7748d8d6df47358902b6cdc6d77ef839945c479924f4592fd89315ac0e0 ``` + - With BtcTransaction - Spend P2TR UTXO - ``` + + ```go // Private key of the UTXO owner privateKey, _ := keypair.NewECPrivateFromWIF("cRvyLwCPLU88jsyj94L7iJjQX5C2f8koG4G2gevN4BeSGcEvfKe9") @@ -513,16 +536,16 @@ We have added two APIs (Mempool and BlockCypher) to the plugin for network acces // Create an output: Send 3000 to `n4bkvTyU1dVdzsrhWBqBw8fEMbHjJvtmJR` txout := scripts.NewTxOutput( - big.NewInt(3000), - addr.ToScriptPubKey(), - ) + big.NewInt(3000), + addr.ToScriptPubKey(), + ) // Create a transaction tx := scripts.NewBtcTransaction( - []*scripts.TxInput{sigTxin1}, - []*scripts.TxOutput{txout}, - true, // The transaction contains one or more segwit UTXOs - ) + []*scripts.TxInput{sigTxin1}, + []*scripts.TxOutput{txout}, + true, // The transaction contains one or more segwit UTXOs + ) // Get the transaction digest for signing input at index 0 // Arguments: @@ -553,10 +576,13 @@ We have added two APIs (Mempool and BlockCypher) to the plugin for network acces // Transaction digest ready for broadcast tx.Serialize() - + ``` + - Spend P2PKH UTXO - ``` + + ```go + // private key of UTXO owner privateKey, _ := keypair.NewECPrivateFromWIF("...") // address we want to spend @@ -571,27 +597,27 @@ We have added two APIs (Mempool and BlockCypher) to the plugin for network acces // outputs 1 send 10000000 to `n4bkvTyU1dVdzsrhWBqBw8fEMbHjJvtmJR` txout := scripts.NewTxOutput( - big.NewInt(3000), - addr.ToScriptPubKey()) + big.NewInt(3000), + addr.ToScriptPubKey()) // We will return the spent transaction balance to the account holder changeTxout := scripts.NewTxOutput( - big.NewInt(29000000), - fromAddr.ToScriptPubKey(), - ) + big.NewInt(29000000), + fromAddr.ToScriptPubKey(), + ) // create transaction tx := scripts.NewBtcTransaction( - []*scripts.TxInput{sigTxin1}, - []*scripts.TxOutput{txout, changeTxout}, - false) + []*scripts.TxInput{sigTxin1}, + []*scripts.TxOutput{txout, changeTxout}, + false) // get transaction digest for signing input at index 0 digest := tx.GetTransactionDigest(0, fromAddr.Program().ToScriptPubKey()) // sign the transaction sig := privateKey.SingInput(digest) - + // set unlock script to transaction tx.SetScriptSig(0, scripts.NewScript(sig, privateKey.GetPublic().ToHex())) // transaction id @@ -602,14 +628,15 @@ We have added two APIs (Mempool and BlockCypher) to the plugin for network acces tx.Serialize() ``` - ### BIP39 -``` + +```go + // Create a new Bip39 instance with the desired language. // The default language is English. You can choose from various supported languages. // Language options include: English, Spanish, Portuguese, Korean, Japanese, Italian, French, Czech, ChineseTraditional, and ChineseSimplified. bip := bip39.Bip39{ - Language: bip39.Japanese, // Set the language to Japanese + Language: bip39.Japanese, // Set the language to Japanese } // Generate a mnemonic phrase with the specified number of words. @@ -631,14 +658,16 @@ toMnemonicFromEntropy, err := bip.EntropyToMnemonic(toEntropy) bip.ChangeLanguage(bip39.Italian) ``` -### HD Wallet -``` + +### HD-Wallet + +```go // Create a pointer to the Mainnet network network := &address.MainnetNetwork // Initialize a Bip39 instance with the Japanese language bip39Instance := bip39.Bip39{ - Language: bip39.Japanese, + Language: bip39.Japanese, } // Generate a 24-word mnemonic using the Bip39 instance @@ -678,14 +707,16 @@ publicKey := drivePrivateWallet.GetPublic() privateKey, _ := drivePrivateWallet.GetPrivate() ``` -### Web3 Secret Storage Definition -``` + +### Web3 Secret Storage Definition(KeyStore) + +```go // Create a pointer to the Mainnet network network := &address.MainnetNetwork // Initialize a Bip39 instance with the Japanese language bip39Instance := bip39.Bip39{ - Language: bip39.Japanese, + Language: bip39.Japanese, } // Generate a 24-word mnemonic using the Bip39 instance @@ -717,8 +748,10 @@ decodedWallet, _ := secretwallet.DecodeSecretWallet(encrypted, myPassword) newWallet, _ := hdwallet.FromXPrivateKey(decodedWallet.Credentials, true, network) ``` + ### Node provider -``` + +```go // select network testnet or mainnet network := address.TestnetNetwork @@ -733,8 +766,8 @@ tr, e := api.GetTransaction("d4bad8e07d30ca4389ec8a203318aa523cc3e36c9730d0a6852 // Read accounts UTXOS addr, _ := address.P2WPKHAddresssFromAddress("tb1q92nmnvhj04sqd4x7wjaewlt5jn8n3ngmplcymy") utxos, e := api.GetAccountUtxo(provider.UtxoOwnerDetails{ - PublicKey: "", - Address: addr, + PublicKey: "", + Address: addr, }) // Network fee @@ -745,7 +778,7 @@ _, _ = api.SendRawTransaction("TRANSACTION DIGEST") // Read account transactions transaction, _ := api.GetAccountTransactions(addr.Show(network), func(url string) string { - return url + return url }) ``` @@ -753,13 +786,11 @@ transaction, _ := api.GetAccountTransactions(addr.Show(network), func(url string ## Contributing Contributions are welcome! Please follow these guidelines: - - Fork the repository and create a new branch. - - Make your changes and ensure tests pass. - - Submit a pull request with a detailed description of your changes. - -## Feature requests and bugs # - -Please file feature requests and bugs in the issue tracker. +- Fork the repository and create a new branch. +- Make your changes and ensure tests pass. +- Submit a pull request with a detailed description of your changes. +## Feature requests and bugs +Please file feature requests and bugs in the issue tracker. diff --git a/go.mod b/go.mod index 73456c2..9aac47f 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/mrtnetwork/bitcoin go 1.21 -require golang.org/x/crypto v0.18.0 +require golang.org/x/crypto v0.22.0 diff --git a/go.sum b/go.sum index d1494eb..bf1bf12 100644 --- a/go.sum +++ b/go.sum @@ -2,3 +2,5 @@ golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= diff --git a/provider/transction_builder.go b/provider/transction_builder.go index fb31505..04b5fe3 100644 --- a/provider/transction_builder.go +++ b/provider/transction_builder.go @@ -84,7 +84,7 @@ func (builder *BitcoinTransactionBuilder) HasTaproot() bool { // It is used to make the appropriate scriptSig func buildInputScriptPubKeys(utxo UtxoWithOwner, isTaproot bool) (*scripts.Script, error) { if utxo.IsMultiSig() { - script, e := scripts.ScriptFromRaw(utxo.OwnerDetails.MultiSigAddress.ScriptDetails, true) + script, e := scripts.ScriptFromRaw(formating.HexToBytes(utxo.OwnerDetails.MultiSigAddress.ScriptDetails), true) if e != nil { return nil, e } @@ -205,7 +205,7 @@ func buildP2shSegwitRedeemScriptSig(utx UtxoWithOwner) ([]string, error) { if utx.IsMultiSig() { switch utx.OwnerDetails.MultiSigAddress.Address.GetType() { case address.P2WSHInP2SH: - script, e := scripts.ScriptFromRaw(utx.OwnerDetails.MultiSigAddress.ScriptDetails, true) + script, e := scripts.ScriptFromRaw(formating.HexToBytes(utx.OwnerDetails.MultiSigAddress.ScriptDetails), true) if e != nil { return nil, e } diff --git a/scripts/script.go b/scripts/script.go index 40711b9..ea2c2f5 100644 --- a/scripts/script.go +++ b/scripts/script.go @@ -52,46 +52,41 @@ func (s *Script) ToP2shScriptPubKey() *Script { } // Imports a Script commands list from raw hexadecimal data -func ScriptFromRaw(hexData string, hasSegwit bool) (*Script, error) { +func ScriptFromRaw(scriptBytes []byte, hasSegwit bool) (*Script, error) { var commands []interface{} index := 0 - scriptraw, err := formating.HexToBytesCatch(hexData) - if err != nil { - return nil, fmt.Errorf("invalid script bytes") - } - - for index < len(scriptraw) { - b := int(scriptraw[index]) + for index < len(scriptBytes) { + b := int(scriptBytes[index]) if constant.CODE_OPS[b] != "" { commands = append(commands, constant.CODE_OPS[b]) index++ } else if !hasSegwit && b == 0x4c { - bytesToRead := int(scriptraw[index+1]) + bytesToRead := int(scriptBytes[index+1]) index++ - data := scriptraw[index : index+bytesToRead] + data := scriptBytes[index : index+bytesToRead] commands = append(commands, hex.EncodeToString(data)) index += bytesToRead } else if !hasSegwit && b == 0x4d { - bytesToRead := int(binary.LittleEndian.Uint16(scriptraw[index+1 : index+3])) + bytesToRead := int(binary.LittleEndian.Uint16(scriptBytes[index+1 : index+3])) index += 3 - data := scriptraw[index : index+bytesToRead] + data := scriptBytes[index : index+bytesToRead] commands = append(commands, hex.EncodeToString(data)) index += bytesToRead } else if !hasSegwit && b == 0x4e { - bytesToRead := int(binary.LittleEndian.Uint32(scriptraw[index+1 : index+5])) + bytesToRead := int(binary.LittleEndian.Uint32(scriptBytes[index+1 : index+5])) index += 5 - data := scriptraw[index : index+bytesToRead] + data := scriptBytes[index : index+bytesToRead] commands = append(commands, hex.EncodeToString(data)) index += bytesToRead } else { - vi, size := formating.ViToInt(scriptraw[index:]) + vi, size := formating.ViToInt(scriptBytes[index:]) dataSize := vi // size := size lastIndex := index + size + dataSize - if lastIndex > len(scriptraw) { - lastIndex = len(scriptraw) + if lastIndex > len(scriptBytes) { + lastIndex = len(scriptBytes) } - commands = append(commands, hex.EncodeToString(scriptraw[index+size:lastIndex])) + commands = append(commands, hex.EncodeToString(scriptBytes[index+size:lastIndex])) index += dataSize + size } } @@ -100,8 +95,9 @@ func ScriptFromRaw(hexData string, hasSegwit bool) (*Script, error) { // GetScriptType determines the script type based on the provided hash and whether it has // SegWit data. It returns the identified ScriptType. -func GetScriptType(hash string, hasSegwit bool) (ScriptType, error) { - s, err := ScriptFromRaw(hash, hasSegwit) +func GetScriptType(scriptBytes []byte, hasSegwit bool) (ScriptType, error) { + + s, err := ScriptFromRaw(scriptBytes, hasSegwit) if err != nil { return -1, err } diff --git a/scripts/transaction.go b/scripts/transaction.go index 3bcee3f..e0295f1 100644 --- a/scripts/transaction.go +++ b/scripts/transaction.go @@ -1,10 +1,11 @@ package scripts import ( + "math/big" + "github.com/mrtnetwork/bitcoin/constant" "github.com/mrtnetwork/bitcoin/digest" "github.com/mrtnetwork/bitcoin/formating" - "math/big" ) // Define BtcTransaction struct @@ -95,37 +96,37 @@ func (tx *BtcTransaction) Copy() *BtcTransaction { } } func BtcTransactionFromRaw(raw string) (*BtcTransaction, error) { - rawtx := formating.HexToBytes(raw) + txBytes := formating.HexToBytes(raw) cursor := 4 var flag []byte hasSegwit := false - if rawtx[4] == 0 { - flag = rawtx[5:6] + if txBytes[4] == 0 { + flag = txBytes[5:6] if flag[0] == 1 { hasSegwit = true } cursor += 2 } - vi, viCursor := formating.ViToInt(rawtx[cursor:]) + vi, viCursor := formating.ViToInt(txBytes[cursor:]) cursor += viCursor inputs := make([]*TxInput, vi) for index := 0; index < len(inputs); index++ { - inp, inpCursor, err := TxInputFromRaw(raw, cursor, hasSegwit) + inp, inpCursor, err := TxInputFromRaw(txBytes, cursor, hasSegwit) if err != nil { return nil, err } inputs[index] = inp cursor = inpCursor } - viOut, viOutCursor := formating.ViToInt(rawtx[cursor:]) + viOut, viOutCursor := formating.ViToInt(txBytes[cursor:]) cursor += viOutCursor outputs := make([]*TxOutput, viOut) for index := 0; index < len(outputs); index++ { - out, outCursor, err := TxOutputFromRaw(raw, cursor, hasSegwit) + out, outCursor, err := TxOutputFromRaw(txBytes, cursor, hasSegwit) if err != nil { return nil, err } @@ -135,14 +136,14 @@ func BtcTransactionFromRaw(raw string) (*BtcTransaction, error) { witnesses := make([]TxWitnessInput, len(inputs)) if hasSegwit { for n := 0; n < len(inputs); n++ { - wVi, wViCursor := formating.ViToInt(rawtx[cursor:]) + wVi, wViCursor := formating.ViToInt(txBytes[cursor:]) cursor += wViCursor witnessesTmp := make([]string, wVi) for m := 0; m < len(witnessesTmp); m++ { var witness []byte - wtVi, wtViCursor := formating.ViToInt(rawtx[cursor:]) + wtVi, wtViCursor := formating.ViToInt(txBytes[cursor:]) if wtVi != 0 { - witness = rawtx[cursor+wtViCursor : cursor+wtViCursor+wtVi] + witness = txBytes[cursor+wtViCursor : cursor+wtViCursor+wtVi] } cursor += wtViCursor + wtVi witnessesTmp[m] = formating.BytesToHex(witness) diff --git a/scripts/tx_input.go b/scripts/tx_input.go index 0f6a313..2720b38 100644 --- a/scripts/tx_input.go +++ b/scripts/tx_input.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "encoding/hex" "fmt" + "github.com/mrtnetwork/bitcoin/constant" "github.com/mrtnetwork/bitcoin/formating" ) @@ -81,45 +82,41 @@ func (ti *TxInput) ToBytes() []byte { } // FromRaw parses a raw transaction input string into a TxInput -func TxInputFromRaw(raw string, cursor int, hasSegwit bool) (*TxInput, int, error) { - txInputRaw, err := formating.HexToBytesCatch(raw) - if err != nil { - return nil, cursor, err - } +func TxInputFromRaw(inputBytes []byte, cursor int, hasSegwit bool) (*TxInput, int, error) { - if cursor+32 >= len(txInputRaw) { + if cursor+32 >= len(inputBytes) { return nil, cursor, fmt.Errorf("input transaction hash not found. Probably malformed raw transaction") } inpHash := make([]byte, 32) - copy(inpHash, formating.ReverseBytes(txInputRaw[cursor:cursor+32])) + copy(inpHash, formating.ReverseBytes(inputBytes[cursor:cursor+32])) cursor += 32 - if cursor+4 >= len(txInputRaw) { + if cursor+4 >= len(inputBytes) { return nil, cursor, fmt.Errorf("output number not found. Probably malformed raw transaction") } - outputN := binary.LittleEndian.Uint32(formating.ReverseBytes(txInputRaw[cursor : cursor+4])) + outputN := binary.LittleEndian.Uint32(inputBytes[cursor : cursor+4]) cursor += 4 - vi, viSize := formating.ViToInt(txInputRaw[cursor:]) + vi, viSize := formating.ViToInt(inputBytes[cursor:]) cursor += viSize - if cursor+vi > len(txInputRaw) { + if cursor+vi > len(inputBytes) { return nil, cursor, fmt.Errorf("unlocking script length exceeds available data. Probably malformed raw transaction") } - unlockingScript := txInputRaw[cursor : cursor+vi] + unlockingScript := inputBytes[cursor : cursor+vi] cursor += vi - if cursor+4 > len(txInputRaw) { + if cursor+4 > len(inputBytes) { return nil, cursor, fmt.Errorf("Sequence number not found. Probably malformed raw transaction") } - sequenceNumberData := txInputRaw[cursor : cursor+4] + sequenceNumberData := inputBytes[cursor : cursor+4] cursor += 4 - script, err := ScriptFromRaw(formating.BytesToHex(unlockingScript), hasSegwit) + script, err := ScriptFromRaw(unlockingScript, hasSegwit) if err != nil { return nil, cursor, err } diff --git a/scripts/tx_output.go b/scripts/tx_output.go index d6feed1..76f392b 100644 --- a/scripts/tx_output.go +++ b/scripts/tx_output.go @@ -2,8 +2,9 @@ package scripts import ( "encoding/binary" - "github.com/mrtnetwork/bitcoin/formating" "math/big" + + "github.com/mrtnetwork/bitcoin/formating" ) type TxOutput struct { @@ -42,19 +43,19 @@ func (txOutput *TxOutput) ToBytes() []byte { // raw The hexadecimal raw string of the Transaction // The cursor of which the algorithm will start to read the data // hasSegwit Is the Tx Output segwit or not -func TxOutputFromRaw(raw string, cursor int, hasSegwit bool) (*TxOutput, int, error) { - rawBytes := formating.HexToBytes(raw) +func TxOutputFromRaw(outputBytes []byte, cursor int, hasSegwit bool) (*TxOutput, int, error) { + // Parse TxOutput from raw bytes - value := int64(binary.LittleEndian.Uint64(rawBytes[cursor : cursor+8])) + value := int64(binary.LittleEndian.Uint64(outputBytes[cursor : cursor+8])) cursor += 8 - vi, viSize := formating.ViToInt(rawBytes[cursor:]) + vi, viSize := formating.ViToInt(outputBytes[cursor:]) cursor += viSize - lockScript := rawBytes[cursor : cursor+vi] + lockScript := outputBytes[cursor : cursor+vi] cursor += vi - scriptPubKey, err := ScriptFromRaw(formating.BytesToHex(lockScript), hasSegwit) + scriptPubKey, err := ScriptFromRaw(lockScript, hasSegwit) if err != nil { return nil, cursor, err } diff --git a/test/bip_39_test.go b/test/bip_39_test.go index 151acff..a1fa1bf 100644 --- a/test/bip_39_test.go +++ b/test/bip_39_test.go @@ -1,10 +1,9 @@ package test import ( - "fmt" - "github.com/mrtnetwork/bitcoin/bip39" - "github.com/mrtnetwork/bitcoin/formating" "testing" + + "github.com/mrtnetwork/bitcoin/bip39" ) func TestBip39(t *testing.T) { @@ -26,13 +25,11 @@ func TestBip39(t *testing.T) { // Select the desired number of words. 12(Words12), 15(Words15), 18(Words18), 21(Words21) or 24(Words24) words mnemonic, err := bip.GenerateMnemonic(bip39.Words24) - fmt.Println("mnemonic: ", mnemonic) if err != nil { t.Errorf(err.Error()) } // passphrase: An optional passphrase used for seed derivation. Can be an empty string. - toSeed := bip39.ToSeed(mnemonic, "PASSPHRASE") - fmt.Println("seed: ", formating.BytesToHex(toSeed)) + _ = bip39.ToSeed(mnemonic, "PASSPHRASE") toEntropy, err := bip.MnemonicToEntropy(mnemonic) if err != nil { diff --git a/test/hd_wallet_test.go b/test/hd_wallet_test.go index 809bc4d..5fc9d3f 100644 --- a/test/hd_wallet_test.go +++ b/test/hd_wallet_test.go @@ -1,10 +1,10 @@ package test import ( - "fmt" + "testing" + "github.com/mrtnetwork/bitcoin/address" hdwallet "github.com/mrtnetwork/bitcoin/hd_wallet" - "testing" ) func TestHDWallet(t *testing.T) { @@ -72,7 +72,6 @@ func TestHDWallet(t *testing.T) { } p2wsh := publicKey.ToP2WSHAddress().Show(network) - fmt.Println("p2wsh: ", p2wsh) if p2wsh != p2wshPublicWallet { t.Errorf("Expected %v, but got %v", p2wshPublicWallet, p2wsh) } diff --git a/test/keys_test.go b/test/keys_test.go index 709bcda..4f0db42 100644 --- a/test/keys_test.go +++ b/test/keys_test.go @@ -102,7 +102,7 @@ func TestP2pkhAddresses(t *testing.T) { if err == nil { fmt.Println(tx.Program().Hash160 == hash160) } else { - print(err.Error()) + t.Errorf(err.Error()) } }) @@ -234,7 +234,6 @@ func TestP2trAddresses(t *testing.T) { t.Run("t7", func(t *testing.T) { pub := privOdd.GetPublic() program := pub.ToTaprootAddress().Program().Program - fmt.Println("program: ", program) if !strings.EqualFold(program, correctOddTweakedPk) { t.Errorf("Expected %v, but got %v", correctOddTweakedPk, program) } diff --git a/test/p2pkh_test.go b/test/p2pkh_test.go index 1ef4932..2fd37bd 100644 --- a/test/p2pkh_test.go +++ b/test/p2pkh_test.go @@ -1,13 +1,14 @@ package test import ( + "math/big" + "strings" + "testing" + "github.com/mrtnetwork/bitcoin/address" "github.com/mrtnetwork/bitcoin/constant" "github.com/mrtnetwork/bitcoin/keypair" "github.com/mrtnetwork/bitcoin/scripts" - "math/big" - "strings" - "testing" ) func TestP2PKH(t *testing.T) { @@ -82,6 +83,10 @@ func TestP2PKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), coreTxResult) { t.Errorf("Expected %v, but got %v", coreTxResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test2", func(t *testing.T) { @@ -95,6 +100,10 @@ func TestP2PKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), coreTxSignedResult) { t.Errorf("Expected %v, but got %v", coreTxSignedResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test3", func(t *testing.T) { @@ -107,6 +116,10 @@ func TestP2PKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), coreTxSignedLowSSigallResult) { t.Errorf("Expected %v, but got %v", coreTxSignedLowSSigallResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test4", func(t *testing.T) { @@ -122,6 +135,10 @@ func TestP2PKH(t *testing.T) { if !strings.EqualFold(tx.TxId(), coreTxSignedLowSSignoneTxid) { t.Errorf("Expected %v, but got %v", coreTxSignedLowSSignoneTxid, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test5", func(t *testing.T) { @@ -134,6 +151,10 @@ func TestP2PKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), sigSighashSingleResult) { t.Errorf("Expected %v, but got %v", sigSighashSingleResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test6", func(t *testing.T) { @@ -149,6 +170,10 @@ func TestP2PKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), signSighashAll2in2outResult) { t.Errorf("Expected %v, but got %v", signSighashAll2in2outResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test7", func(t *testing.T) { @@ -164,6 +189,10 @@ func TestP2PKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), signSighashNone2in2outResult) { t.Errorf("Expected %v, but got %v", signSighashNone2in2outResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test8", func(t *testing.T) { @@ -179,6 +208,10 @@ func TestP2PKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), signSighashAllSingleAnyone2in2outResult) { t.Errorf("Expected %v, but got %v", signSighashAllSingleAnyone2in2outResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) } diff --git a/test/p2sh_test.go b/test/p2sh_test.go index 4b19297..4b210ae 100644 --- a/test/p2sh_test.go +++ b/test/p2sh_test.go @@ -1,13 +1,14 @@ package test import ( + "math/big" + "strings" + "testing" + "github.com/mrtnetwork/bitcoin/address" "github.com/mrtnetwork/bitcoin/constant" "github.com/mrtnetwork/bitcoin/keypair" "github.com/mrtnetwork/bitcoin/scripts" - "math/big" - "strings" - "testing" ) func TestP2SH(t *testing.T) { @@ -52,6 +53,10 @@ func TestP2SH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), createP2shAndSendResult) { t.Errorf("Expected %v, but got %v", createP2shAndSendResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test2", func(t *testing.T) { @@ -64,6 +69,10 @@ func TestP2SH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), spendP2shResult) { t.Errorf("Expected %v, but got %v", spendP2shResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test3", func(t *testing.T) { @@ -85,6 +94,10 @@ func TestP2SH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), spendP2shCsvP2pkhResult) { t.Errorf("Expected %v, but got %v", spendP2shCsvP2pkhResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) } diff --git a/test/p2tr_test.go b/test/p2tr_test.go index b88cfad..e8903fb 100644 --- a/test/p2tr_test.go +++ b/test/p2tr_test.go @@ -1,13 +1,14 @@ package test import ( + "math/big" + "strings" + "testing" + "github.com/mrtnetwork/bitcoin/address" "github.com/mrtnetwork/bitcoin/constant" "github.com/mrtnetwork/bitcoin/keypair" "github.com/mrtnetwork/bitcoin/scripts" - "math/big" - "strings" - "testing" ) func TestCreateP2trWithSingleTapScript(t *testing.T) { @@ -58,6 +59,10 @@ func TestCreateP2trWithSingleTapScript(t *testing.T) { if !strings.EqualFold(digestHex, signedTx2) { t.Errorf("Expected %v, but got %v", signedTx2, digestHex) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) // 3-spend taproot from script path (has single tapleaf script for spending) @@ -74,6 +79,10 @@ func TestCreateP2trWithSingleTapScript(t *testing.T) { if !strings.EqualFold(digestHex, signedTx3) { t.Errorf("Expected %v, but got %v", signedTx2, digestHex) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) } @@ -114,6 +123,10 @@ func TestCreateP2trWithTwoTapScripts(t *testing.T) { if !strings.EqualFold(tx.Serialize(), signedTx3) { t.Errorf("Expected %v, but got %v", signedTx3, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) } @@ -158,6 +171,10 @@ func TestCreateP2trWithThreeTapScripts(t *testing.T) { if !strings.EqualFold(tx.Serialize(), signedTx3) { t.Errorf("Expected %v, but got %v", signedTx3, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) } diff --git a/test/p2wpkh_test.go b/test/p2wpkh_test.go index 5cfe933..a9917bc 100644 --- a/test/p2wpkh_test.go +++ b/test/p2wpkh_test.go @@ -1,12 +1,13 @@ package test import ( - "github.com/mrtnetwork/bitcoin/constant" - "github.com/mrtnetwork/bitcoin/keypair" - "github.com/mrtnetwork/bitcoin/scripts" "math/big" "strings" "testing" + + "github.com/mrtnetwork/bitcoin/constant" + "github.com/mrtnetwork/bitcoin/keypair" + "github.com/mrtnetwork/bitcoin/scripts" ) func TestP2WPKH(t *testing.T) { @@ -100,6 +101,10 @@ func TestP2WPKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), createSendToP2wpkhResult) { t.Errorf("Expected %v, but got %v", createSendToP2wpkhResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test_spend_p2wpkh", func(t *testing.T) { @@ -116,6 +121,10 @@ func TestP2WPKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), spendP2pkhResult) { t.Errorf("Expected %v, but got %v", spendP2pkhResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test_p2pkh_and_p2wpkh_to_p2pkh", func(t *testing.T) { @@ -136,6 +145,10 @@ func TestP2WPKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), p2pkhAndP2wpkhToP2pkhResult) { t.Errorf("Expected %v, but got %v", p2pkhAndP2wpkhToP2pkhResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) @@ -153,6 +166,10 @@ func TestP2WPKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), testSignoneSendResult) { t.Errorf("Expected %v, but got %v", testSignoneSendResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test_sigsingle_send", func(t *testing.T) { @@ -169,6 +186,10 @@ func TestP2WPKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), testSigsingleSendResult) { t.Errorf("Expected %v, but got %v", testSigsingleSendResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) @@ -190,6 +211,10 @@ func TestP2WPKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), testSiganyonecanpayAllSendResult) { t.Errorf("Expected %v, but got %v", testSiganyonecanpayAllSendResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test_siganyonecanpay_none_send", func(t *testing.T) { @@ -213,6 +238,10 @@ func TestP2WPKH(t *testing.T) { t.Errorf("Expected %v, but got %v", testSiganyonecanpayNoneSendResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("test_siganyonecanpay_single_send", func(t *testing.T) { @@ -229,6 +258,10 @@ func TestP2WPKH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), testSiganyonecanpaySingleSendResult) { t.Errorf("Expected %v, but got %v", testSiganyonecanpaySingleSendResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) } diff --git a/test/p2wsh_test.go b/test/p2wsh_test.go index 3b317fe..199a084 100644 --- a/test/p2wsh_test.go +++ b/test/p2wsh_test.go @@ -1,13 +1,14 @@ package test_test import ( + "math/big" + "strings" + "testing" + "github.com/mrtnetwork/bitcoin/address" "github.com/mrtnetwork/bitcoin/constant" "github.com/mrtnetwork/bitcoin/keypair" "github.com/mrtnetwork/bitcoin/scripts" - "math/big" - "strings" - "testing" ) func TestP2WSH(t *testing.T) { @@ -32,8 +33,7 @@ func TestP2WSH(t *testing.T) { txin1Multiple := scripts.NewDefaultTxInput("24d949f8c77d7fc0cd09c8d5fccf7a0249178c16170c738da19f6c4b176c9f4b", 0) txin2Multiple := scripts.NewDefaultTxInput("65f4d69c91a8de54dc11096eaa315e84ef91a389d1d1c17a691b72095100a3a4", 0) txin3Multiple := scripts.NewDefaultTxInput("6c8fc6453a2a3039c2b5b55dcc59587e8b0afa52f92607385b5f4c7e84f38aa2", 0) - // txin2MultipleAmount := big.NewInt(690000) - // txin3MultipleAmount := big.NewInt(790000) + output1Multiple := scripts.NewTxOutput(big.NewInt(100000), p2wshAddr.Program().ToScriptPubKey()) output2Multiple := scripts.NewTxOutput(big.NewInt(100000), sk1.GetPublic().ToSegwitAddress(true).Program().ToScriptPubKey()) output3Multiple := scripts.NewTxOutput(big.NewInt(1770000), p2pkhAddr.Program().ToScriptPubKey()) @@ -52,6 +52,10 @@ func TestP2WSH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), createSendToP2pkhResult) { t.Errorf("Expected %v, but got %v", createSendToP2pkhResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) t.Run("spend_from_p2wsh_multisig_to_p2pkh", func(t *testing.T) { @@ -67,6 +71,10 @@ func TestP2WSH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), spendP2pkhResult) { t.Errorf("Expected %v, but got %v", spendP2pkhResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) @@ -99,6 +107,10 @@ func TestP2WSH(t *testing.T) { if !strings.EqualFold(tx.Serialize(), multipleInputMultipleOuputResult) { t.Errorf("Expected %v, but got %v", multipleInputMultipleOuputResult, tx.Serialize()) } + fromRaw, _ := scripts.BtcTransactionFromRaw(tx.Serialize()) + if !strings.EqualFold(fromRaw.TxId(), tx.TxId()) { + t.Errorf("Expected %v, but got %v", tx.TxId(), fromRaw.Serialize()) + } }) }