Skip to content

Commit

Permalink
Added AddressFromScript method
Browse files Browse the repository at this point in the history
  • Loading branch information
mrz1836 committed Oct 1, 2020
1 parent bf1cd57 commit a3a83d6
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 16 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ View the generated [documentation](https://pkg.go.dev/github.com/bitcoinschema/g

### Features
- [Sign](sign.go) / [Verify](verify.go) Bitcoin Message
- [Private key (string) to Address (string)](address.go)
- [Address from Private key (bsvec.PrivateKey)](address.go)
- [Create Private Key](private_key.go)
- [Create PubKey From Private Key](pubkey.go)
- [PrivateKey (string) to Address (string)](address.go)
- [Address from PrivateKey (bsvec.PrivateKey)](address.go)
- [Address from Script](address.go)
- [Create PrivateKey](private_key.go)
- [Create PubKey from PrivateKey](pubkey.go)


<details>
Expand Down
64 changes: 52 additions & 12 deletions address.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package bitcoin

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

"crypto/sha256"

"github.com/bitcoinsv/bsvd/bsvec"
"github.com/bitcoinsv/bsvd/chaincfg"
"github.com/bitcoinsv/bsvd/txscript"
"github.com/bitcoinsv/bsvutil"
)

Expand Down Expand Up @@ -72,6 +74,22 @@ func (a *A25) ComputeChecksum() (c [4]byte) {
return
}

// ValidA58 validates a base58 encoded bitcoin address. An address is valid
// if it can be decoded into a 25 byte address, the version number is 0,
// and the checksum validates. Return value ok will be true for valid
// addresses. If ok is false, the address is invalid and the error value
// may indicate why.
func ValidA58(a58 []byte) (bool, error) {
var a A25
if err := a.Set58(a58); err != nil {
return false, err
}
if a.Version() != 0 {
return false, errors.New("not version 0")
}
return a.EmbeddedChecksum() == a.ComputeChecksum(), nil
}

// AddressFromPrivateKey takes a private key string and returns a Bitcoin address
func AddressFromPrivateKey(privateKey string) (string, error) {
rawKey, err := PrivateKeyFromString(privateKey)
Expand All @@ -95,18 +113,40 @@ func AddressFromPubKey(publicKey *bsvec.PublicKey) (*bsvutil.LegacyAddressPubKey
return bsvutil.NewLegacyAddressPubKeyHash(bsvutil.Hash160(publicKey.SerializeCompressed()), &chaincfg.MainNetParams)
}

// ValidA58 validates a base58 encoded bitcoin address. An address is valid
// if it can be decoded into a 25 byte address, the version number is 0,
// and the checksum validates. Return value ok will be true for valid
// addresses. If ok is false, the address is invalid and the error value
// may indicate why.
func ValidA58(a58 []byte) (bool, error) {
var a A25
if err := a.Set58(a58); err != nil {
return false, err
// AddressFromScript will take an output script and extract a standard bitcoin address
func AddressFromScript(script string) (string, error) {

// No script?
if len(script) == 0 {
return "", errors.New("missing script")
}
if a.Version() != 0 {
return false, errors.New("not version 0")

// Decode the hex string into bytes
scriptBytes, err := hex.DecodeString(script)
if err != nil {
return "", err
}
return a.EmbeddedChecksum() == a.ComputeChecksum(), nil

// Extract the components from the script
var addresses []bsvutil.Address
_, addresses, _, err = txscript.ExtractPkScriptAddrs(scriptBytes, &chaincfg.MainNetParams)
if err != nil {
return "", err
}

// Missing an address?
if len(addresses) == 0 {
// This error case should not occur since the error above will occur when no address is found,
// however we ensure that we have an address for the NewLegacyAddressPubKeyHash() below
return "", fmt.Errorf("invalid output script, missing an address")
}

// Extract the address from the pubkey hash
var address *bsvutil.LegacyAddressPubKeyHash
if address, err = bsvutil.NewLegacyAddressPubKeyHash(addresses[0].ScriptAddress(), &chaincfg.MainNetParams); err != nil {
return "", err
}

// Use the encoded version of the address
return address.EncodeAddress(), nil
}
52 changes: 52 additions & 0 deletions address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,55 @@ func BenchmarkAddressFromPubKey(b *testing.B) {
_, _ = AddressFromPubKey(pubKey)
}
}

// TestAddressFromScript will test the method AddressFromScript()
func TestAddressFromScript(t *testing.T) {
t.Parallel()

// Create the list of tests
var tests = []struct {
inputScript string
expectedAddress string
expectedError bool
}{
{"", "", true},
{"0", "", true},
{"76a914b424110292f4ea2ac92beb9e83cf5e6f0fa2996388ac", "1HRVqUGDzpZSMVuNSZxJVaB9xjneEShfA7", false},
{"76a914b424110292f4ea2ac92beb9e83cf5e6f0fa2", "", true},
{"76a914b424110292f4ea2ac92beb9e83", "", true},
{"76a914b424110292f", "", true},
{"1HRVqUGDzpZSMVuNSZxJVaB9xjneEShfA7", "", true},
{"514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae", "", true},
{"410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3", "", true},
{"47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901", "", true},
}

// Run tests
for _, test := range tests {
if address, err := AddressFromScript(test.inputScript); err != nil && !test.expectedError {
t.Errorf("%s Failed: [%v] inputted and error not expected but got: %s", t.Name(), test.inputScript, err.Error())
} else if err == nil && test.expectedError {
t.Errorf("%s Failed: [%v] inputted and error was expected", t.Name(), test.inputScript)
} else if address != test.expectedAddress {
t.Errorf("%s Failed: [%v] inputted [%s] expected but failed comparison of addresses, got: %s", t.Name(), test.inputScript, test.expectedAddress, address)
}
}
}

// ExampleAddressFromScript example using AddressFromScript()
func ExampleAddressFromScript() {
address, err := AddressFromScript("76a914b424110292f4ea2ac92beb9e83cf5e6f0fa2996388ac")
if err != nil {
fmt.Printf("error occurred: %s", err.Error())
return
}
fmt.Printf("address found: %s", address)
// Output:address found: 1HRVqUGDzpZSMVuNSZxJVaB9xjneEShfA7
}

// BenchmarkAddressFromScript benchmarks the method AddressFromScript()
func BenchmarkAddressFromScript(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = AddressFromScript("76a914b424110292f4ea2ac92beb9e83cf5e6f0fa2996388ac")
}
}

0 comments on commit a3a83d6

Please sign in to comment.