Skip to content

Commit

Permalink
add bitcoin address validator
Browse files Browse the repository at this point in the history
  • Loading branch information
rohenaz committed Sep 25, 2020
1 parent b42f8ca commit 1db326e
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 0 deletions.
81 changes: 81 additions & 0 deletions address.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,76 @@
package bitcoin

import (
"bytes"
"errors"

"crypto/sha256"

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

// A25 is a type for a 25 byte (not base58 encoded) bitcoin address.
type A25 [25]byte

// DoubleSHA256 computes a double sha256 hash of the first 21 bytes of the
// address. This is the one function shared with the other bitcoin RC task.
// Returned is the full 32 byte sha256 hash. (The bitcoin checksum will be
// the first four bytes of the slice.)
func (a *A25) doubleSHA256() []byte {
h := sha256.New()
h.Write(a[:21])
d := h.Sum([]byte{})
h = sha256.New()
h.Write(d)
return h.Sum(d[:0])
}

// Version returns the version byte of a A25 address
func (a *A25) Version() byte {
return a[0]
}

// EmbeddedChecksum returns the 4 checksum bytes of a A25 address
func (a *A25) EmbeddedChecksum() (c [4]byte) {
copy(c[:], a[21:])
return
}

// Tmpl and Set58 are adapted from the C solution.
// Go has big integers but this techinique seems better.
var tmpl = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")

// Set58 takes a base58 encoded address and decodes it into the receiver.
// Errors are returned if the argument is not valid base58 or if the decoded
// value does not fit in the 25 byte address. The address is not otherwise
// checked for validity.
func (a *A25) Set58(s []byte) error {
for _, s1 := range s {
c := bytes.IndexByte(tmpl, s1)
if c < 0 {
return errors.New("bad char")
}
for j := 24; j >= 0; j-- {
c += 58 * int(a[j])
a[j] = byte(c % 256)
c /= 256
}
if c > 0 {
return errors.New("too long")
}
}
return nil
}

// ComputeChecksum returns a four byte checksum computed from the first 21
// bytes of the address. The embedded checksum is not updated.
func (a *A25) ComputeChecksum() (c [4]byte) {
copy(c[:], a.doubleSHA256())
return
} /* {{header|Go}} */

// AddressFromPrivKey takes a private key string and returns a Bitcoin address
func AddressFromPrivKey(privKey string) string {
pubKey := PrivateKey(privKey).PubKey()
Expand All @@ -18,3 +83,19 @@ func Address(publicKey *bsvec.PublicKey) (address *bsvutil.LegacyAddressPubKeyHa
address, _ = bsvutil.NewLegacyAddressPubKeyHash(publicKeyHash, &chaincfg.MainNetParams)
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) (ok bool, err 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
}
17 changes: 17 additions & 0 deletions address_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package bitcoin

import (
"testing"
)

// Test address
const address = "1KCEAmVS6FFggtc7W9as7sEENvjt7DqMi2"

func TestValidA58(t *testing.T) {

valid, err := ValidA58([]byte(address))

if !valid {
t.Error("Failed to validate address", err)
}
}

0 comments on commit 1db326e

Please sign in to comment.