Skip to content

Commit

Permalink
main
Browse files Browse the repository at this point in the history
  • Loading branch information
mrtnetwork committed Sep 27, 2023
1 parent 662143a commit 1601366
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 45 deletions.
88 changes: 88 additions & 0 deletions address/core.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,91 @@
// Package address provides utilities for working with Bitcoin address types.
//
// Bitcoin uses different address types, such as Pay-to-Public-Key-Hash (P2PKH),
// Pay-to-Script-Hash (P2SH), and Segregated Witness (SegWit) addresses. This package
// offers functions for creating, parsing, and validating addresses of these types.
//
// Usage:
// - Use the functions in this package to generate Bitcoin addresses.
// - Parse Bitcoin addresses to extract information.
// - Validate Bitcoin addresses to ensure they adhere to the correct format.
// - Create address from scripts p2wsh, p2tr, tapleaf, ....
//
// Example:
// package main
//
// import (
// "fmt"
// "github.com/mrtnetwork/bitcoin/address"
// )
//
// func main() {
// P2PKH ADDRESS
// address in testnet: myVMJgRi6arv4hLbeUcJYKUJWmFnpjtVme
// address in mainnet: 1JyQ1dLjHZRfHaryvudviQFyemf5vjbmUf
// exampleAddr1, _ := address.P2PKHAddressFromAddress("myVMJgRi6arv4hLbeUcJYKUJWmFnpjtVme")
// fmt.Println("address in testnet: ", exampleAddr1.Show(address.TestnetNetwork))
// fmt.Println("address in mainnet: ", exampleAddr1.Show(address.MainnetNetwork))

// P2TR ADDRESS
// address in testnet: tb1pyhmqwlcrws4dxcgalt4mrffgnys879vs59xf6sve4hazyvmhecxq3e6sc0
// address in mainnet: bc1pyhmqwlcrws4dxcgalt4mrffgnys879vs59xf6sve4hazyvmhecxqx3vlzq
// exampleAddr2, _ := address.P2TRAddressFromAddress("tb1pyhmqwlcrws4dxcgalt4mrffgnys879vs59xf6sve4hazyvmhecxq3e6sc0")
// fmt.Println("address in testnet: ", exampleAddr2.Show(address.TestnetNetwork))
// fmt.Println("address in mainnet: ", exampleAddr2.Show(address.MainnetNetwork))

// P2SH(P2PKH) ADDRESS
// address in testnet: 2N2yqygBJRvDzLzvPe91qKfSYnK5utGckJX
// address in mainnet: 3BRduwFGpTie9DHqy1PxhiTHZxsk7jpr9x
// exampleAddr3, _ := address.P2SHAddressFromAddress("2N2yqygBJRvDzLzvPe91qKfSYnK5utGckJX", address.P2PKHInP2SH)
// fmt.Println("address in testnet: ", exampleAddr3.Show(address.TestnetNetwork))
// fmt.Println("address in mainnet: ", exampleAddr3.Show(address.MainnetNetwork))

// P2PKH ADDRESS
// address in testnet: mzUzciYUGsNxLCaaHwou27F4RbnDTzKomV
// address in mainnet: 1Ky3KfTVTqwhZ66xaNqXCC2jZcBWZD6ppQ
// exampleAddr4, _ := address.P2PKHAddressFromAddress("mzUzciYUGsNxLCaaHwou27F4RbnDTzKomV")
// fmt.Println("address in testnet: ", exampleAddr4.Show(address.TestnetNetwork))
// fmt.Println("address in mainnet: ", exampleAddr4.Show(address.MainnetNetwork))

// P2SH(P2PKH) ADDRESS
// address in testnet: 2MzibgEeJYCN8mjJsZTg79AH7au4PCkHXHo
// address in mainnet: 39APcViGvjrnZwgKtL4EXDHrNYrDT9YuCQ
// exampleAddr5, _ := address.P2SHAddressFromAddress("2MzibgEeJYCN8mjJsZTg79AH7au4PCkHXHo", address.P2PKHInP2SH)
// fmt.Println("address in testnet: ", exampleAddr5.Show(address.TestnetNetwork))
// fmt.Println("address in mainnet: ", exampleAddr5.Show(address.MainnetNetwork))

// P2SH(P2WSH) ADDRESS
// address in testnet: 2N7bNV1WPwCVHfoqqRhvtmbAfktazfjHEW2
// address in mainnet: 3G3ARGaNKjywU2DHkaK29eBQYYNppD2xDE
// exampleAddr6, _ := address.P2SHAddressFromAddress("2N7bNV1WPwCVHfoqqRhvtmbAfktazfjHEW2", address.P2WSHInP2SH)
// fmt.Println("address in testnet: ", exampleAddr6.Show(address.TestnetNetwork))
// fmt.Println("address in mainnet: ", exampleAddr6.Show(address.MainnetNetwork))

// P2SH(P2WPKH) ADDRESS
// address in testnet: 2N38S8G9q6qyEjPWmicqxrVwjq4QiTbcyf4
// address in mainnet: 3BaE4XDoVPTtXbtE3VE6EYxUciCYd5rspb
// exampleAddr7, _ := address.P2SHAddressFromAddress("2N38S8G9q6qyEjPWmicqxrVwjq4QiTbcyf4", address.P2WPKHInP2SH)
// fmt.Println("address in testnet: ", exampleAddr7.Show(address.TestnetNetwork))
// fmt.Println("address in mainnet: ", exampleAddr7.Show(address.MainnetNetwork))

// P2SH(P2PK) ADDRESS
// address in testnet: 2MugsNcgzLJ1HosnZyC2CfZVmgbMPK1XubR
// address in mainnet: 348fJskxiqVwc6A2J4QL3cWWUF9DbWjTni
// exampleAddr8, _ := address.P2SHAddressFromAddress("2MugsNcgzLJ1HosnZyC2CfZVmgbMPK1XubR", address.P2PKInP2SH)
// fmt.Println("address in testnet: ", exampleAddr8.Show(address.TestnetNetwork))
// fmt.Println("address in mainnet: ", exampleAddr8.Show(address.MainnetNetwork))

// P2WPKH ADDRESS
// address in testnet: tb1q6q9halaazasd42gzsc2cvv5xls295w7kawkhxy
// address in mainnet: bc1q6q9halaazasd42gzsc2cvv5xls295w7khgdyah
// exampleAddr9, _ := address.P2WPKHAddresssFromAddress("tb1q6q9halaazasd42gzsc2cvv5xls295w7kawkhxy")
// fmt.Println("address in testnet: ", exampleAddr9.Show(address.TestnetNetwork))
// fmt.Println("address in mainnet: ", exampleAddr9.Show(address.MainnetNetwork))
// }
//
// This package aims to simplify Bitcoin address handling and make it easier to work
// with different Bitcoin address formats.

package address

import "github.com/mrtnetwork/bitcoin/scripts"
Expand Down
7 changes: 4 additions & 3 deletions address/lagacy_address.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package address
import (
"bytes"
"fmt"
"math/big"

"github.com/mrtnetwork/bitcoin/base58"
"github.com/mrtnetwork/bitcoin/digest"
"github.com/mrtnetwork/bitcoin/ecc"
"github.com/mrtnetwork/bitcoin/formating"
"github.com/mrtnetwork/bitcoin/scripts"
"math/big"
)

// IsValidAddress checks the validity of a Bitcoin address. It verifies whether the input address is
Expand Down Expand Up @@ -164,11 +165,11 @@ func fromScript(script *scripts.Script) string {
// - string: A hexadecimal string representing the hash160 of the Bitcoin address.
// - error: An error if the input address is invalid or cannot be decoded.
func addressToHash160(address string) (string, error) {
dec, err := base58.Decode(address)
dec, err := base58.DecodeCheck(address)
if err != nil {
return "", fmt.Errorf("invalid addresss")
}
hash160 := dec[1 : len(dec)-4]
hash160 := dec[1:]
return formating.BytesToHex(hash160), nil
}

Expand Down
3 changes: 2 additions & 1 deletion example/example_multi_sig_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import (

"errors"
"fmt"
"math/big"

"github.com/mrtnetwork/bitcoin/constant"
hdwallet "github.com/mrtnetwork/bitcoin/hd_wallet"
"github.com/mrtnetwork/bitcoin/keypair"
"math/big"
)

func ExampleMultiSigTransactionSpending() {
Expand Down
23 changes: 12 additions & 11 deletions example/example_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ package example

import (
"fmt"
"math/big"
"testing"

"github.com/mrtnetwork/bitcoin/address"
"github.com/mrtnetwork/bitcoin/constant"
hdwallet "github.com/mrtnetwork/bitcoin/hd_wallet"
"github.com/mrtnetwork/bitcoin/keypair"
"github.com/mrtnetwork/bitcoin/provider"
"math/big"
)

// spend from 8 different address type to 10 different output
func TestExampleSpendingFrom8InputTo10Output() {
func TestExampleSpendingFrom8InputTo10Output(t *testing.T) {
network := address.TestnetNetwork
api := provider.SelectApi(provider.MempoolApi, &network)
// i generate random mnemonic for test
Expand All @@ -33,6 +35,7 @@ func TestExampleSpendingFrom8InputTo10Output() {
private2, _ := sp2.GetPrivate()
private3, _ := sp3.GetPrivate()
private4, _ := sp4.GetPrivate()

// access to public key `ECPublic`
public1 := sp1.GetPublic()
public2 := sp2.GetPublic()
Expand All @@ -46,7 +49,7 @@ func TestExampleSpendingFrom8InputTo10Output() {
// myVMJgRi6arv4hLbeUcJYKUJWmFnpjtVme
// equals to exampleAddr1 := address.P2PKHAddressFromAddress("myVMJgRi6arv4hLbeUcJYKUJWmFnpjtVme")
exampleAddr1 := public1.ToAddress()

fmt.Println("example 1: ", exampleAddr1.Show(network))
// P2TR ADDRESS
// tb1pyhmqwlcrws4dxcgalt4mrffgnys879vs59xf6sve4hazyvmhecxq3e6sc0
// equals to exampleAddr2 := address.P2TRAddressFromAddress("tb1pyhmqwlcrws4dxcgalt4mrffgnys879vs59xf6sve4hazyvmhecxq3e6sc0")
Expand Down Expand Up @@ -117,7 +120,7 @@ func TestExampleSpendingFrom8InputTo10Output() {
// 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
// read each address utxo from mempol
spenderUtxos, err := api.GetAccountUtxo(spender)

// oh this address does not have any satoshi for spending
Expand Down Expand Up @@ -215,7 +218,7 @@ func TestExampleSpendingFrom8InputTo10Output() {

// If you like the note write something else and leave it blank
// I will put my GitHub address here
"https://github.com/MohsenHaydari",
"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.
Expand Down Expand Up @@ -299,18 +302,16 @@ func TestExampleSpendingFrom8InputTo10Output() {
isSegwitTr := transactionBuilder.HasSegwit()

// transaction id
transactionId := transaction.TxId()
fmt.Println("transaction ID: ", transactionId)
_ = transaction.TxId()

// transaction size
var transactionSize int
var _ int

if isSegwitTr {
transactionSize = transaction.GetVSize()
_ = transaction.GetVSize()
} else {
transactionSize = transaction.GetSize()
_ = transaction.GetSize()
}
fmt.Println("transaction size: ", transactionSize)

// now we send transaction to network
trId, err := api.SendRawTransaction(digest)
Expand Down
25 changes: 4 additions & 21 deletions keypair/private_key.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package keypair

import (
"bytes"
"encoding/hex"
"fmt"
"strings"

"github.com/mrtnetwork/bitcoin/address"
"github.com/mrtnetwork/bitcoin/base58"
"github.com/mrtnetwork/bitcoin/constant"
"github.com/mrtnetwork/bitcoin/digest"
"github.com/mrtnetwork/bitcoin/ecc"
"github.com/mrtnetwork/bitcoin/formating"
"strings"
)

type ECPrivate struct {
Expand Down Expand Up @@ -75,28 +75,14 @@ func NewECPrivateFromBytes(privBytes []byte) (*ECPrivate, error) {
// NewECPrivateFromWIF creates an ECPrivate instance from a WIF string
// and returns a pointer to the initialized object.
func NewECPrivateFromWIF(wif string) (*ECPrivate, error) {
b64, err := base58.Decode(wif)
keyBytes, err := base58.DecodeCheck(wif)
if err != nil {
return nil, fmt.Errorf("invalid WIF length")
}
lengthWithoutChecksum := len(b64) - 4

// Extract keyBytes
keyBytes := b64[:lengthWithoutChecksum]

// Extract checksum
checksum := b64[lengthWithoutChecksum:]

h := digest.DoubleHash(keyBytes)

if !bytes.Equal(h[:4], checksum) {
panic("invalid checksum")
}
keyBytes = keyBytes[1:]
if len(keyBytes) > 32 {
keyBytes = keyBytes[:len(keyBytes)-1]
}

return NewECPrivateFromBytes(keyBytes)
}

Expand All @@ -109,10 +95,7 @@ func (ecPriv *ECPrivate) ToWIF(compressed bool, networkType address.NetworkInfo)
} else {
bytes = append([]byte{networkType.WIF()}, ecPriv.privateKey...)
}

checksum := digest.DoubleHash(bytes)
bytes = append(bytes, checksum[:4]...)
return base58.Encode(bytes)
return base58.EncodeCheck(bytes)
}

func (ecPriv *ECPrivate) GetPublic() *ECPublic {
Expand Down
5 changes: 3 additions & 2 deletions keypair/public_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package keypair

import (
"fmt"
"strings"

"github.com/mrtnetwork/bitcoin/address"
"github.com/mrtnetwork/bitcoin/constant"
"github.com/mrtnetwork/bitcoin/digest"
"github.com/mrtnetwork/bitcoin/ecc"
"github.com/mrtnetwork/bitcoin/formating"
"github.com/mrtnetwork/bitcoin/scripts"
"strings"
)

type ECPublic struct {
Expand Down Expand Up @@ -91,7 +92,7 @@ func (ecPublic *ECPublic) ToAddress(compressed ...interface{}) *address.P2PKHAdd
return addr
}

// ToAddress generates a P2PKH (Pay-to-Public-Key-Hash) address from the ECPublic key.
// ToP2PKHInP2SH generates a P2PKH (Pay-to-Public-Key-Hash) address from the ECPublic key.
// If 'compressed' is true, the key is in compressed format.
func (ecPublic *ECPublic) ToP2PKHInP2SH(compressed ...interface{}) *address.P2SHAdress {
addr := ecPublic.ToAddress(compressed...)
Expand Down
5 changes: 3 additions & 2 deletions provider/transction_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package provider
import (
"encoding/hex"
"fmt"
"math/big"
"strings"

"github.com/mrtnetwork/bitcoin/address"
"github.com/mrtnetwork/bitcoin/constant"
"github.com/mrtnetwork/bitcoin/formating"
"github.com/mrtnetwork/bitcoin/scripts"
"math/big"
"strings"
)

type BitcoinSignerCallBack func(trDigest []byte, utxo UtxoWithOwner, multiSigPublicKey string) (string, error)
Expand Down
5 changes: 2 additions & 3 deletions provider/utxo_details.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package provider

import (
"fmt"
"math/big"

"github.com/mrtnetwork/bitcoin/address"
"github.com/mrtnetwork/bitcoin/keypair"
"math/big"
)

// UtxoOwnerDetails represents ownership details associated with a Bitcoin unspent transaction output (UTXO).
Expand All @@ -20,8 +21,6 @@ type UtxoOwnerDetails struct {
// MultiSigAddress is a pointer to a MultiSignaturAddress instance representing a multi-signature address
// associated with the UTXO owner. It may be nil if the UTXO owner is not using a multi-signature scheme.
MultiSigAddress *MultiSignaturAddress
// Utxo is a BitcoinUtxo instance representing the unspent transaction output.
Utxo BitcoinUtxo
}

// UtxoWithOwner represents an unspent transaction output (UTXO) along with its associated owner details.
Expand Down
16 changes: 14 additions & 2 deletions test/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package test

import (
"bytes"
"fmt"
"strings"
"testing"

"github.com/mrtnetwork/bitcoin/address"
"github.com/mrtnetwork/bitcoin/formating"
"github.com/mrtnetwork/bitcoin/keypair"
"github.com/mrtnetwork/bitcoin/scripts"
"strings"
"testing"
)

func TestPrivateKeys(t *testing.T) {
Expand Down Expand Up @@ -95,6 +97,15 @@ func TestP2pkhAddresses(t *testing.T) {
if !strings.EqualFold(p2.Program().Hash160, hash160c) {
t.Errorf("Expected %v, but got %v", hash160c, p2.Program().Hash160)
}
t.Run("testx", func(t *testing.T) {
tx, err := address.P2PKHAddressFromAddress(address1)
if err == nil {
fmt.Println(tx.Program().Hash160 == hash160)
} else {
print(err.Error())
}

})
}
func TestP2SHhAddresses(t *testing.T) {
prive, _ := keypair.NewECPrivateFromWIF("cTALNpTpRbbxTCJ2A5Vq88UxT44w1PE2cYqiB3n4hRvzyCev1Wwo")
Expand Down Expand Up @@ -223,6 +234,7 @@ 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)
}
Expand Down

0 comments on commit 1601366

Please sign in to comment.