Skip to content

Commit 1db326e

Browse files
committed
add bitcoin address validator
1 parent b42f8ca commit 1db326e

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

address.go

+81
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,76 @@
11
package bitcoin
22

33
import (
4+
"bytes"
5+
"errors"
6+
7+
"crypto/sha256"
8+
49
"github.com/bitcoinsv/bsvd/bsvec"
510
"github.com/bitcoinsv/bsvd/chaincfg"
611
"github.com/bitcoinsv/bsvutil"
712
)
813

14+
// A25 is a type for a 25 byte (not base58 encoded) bitcoin address.
15+
type A25 [25]byte
16+
17+
// DoubleSHA256 computes a double sha256 hash of the first 21 bytes of the
18+
// address. This is the one function shared with the other bitcoin RC task.
19+
// Returned is the full 32 byte sha256 hash. (The bitcoin checksum will be
20+
// the first four bytes of the slice.)
21+
func (a *A25) doubleSHA256() []byte {
22+
h := sha256.New()
23+
h.Write(a[:21])
24+
d := h.Sum([]byte{})
25+
h = sha256.New()
26+
h.Write(d)
27+
return h.Sum(d[:0])
28+
}
29+
30+
// Version returns the version byte of a A25 address
31+
func (a *A25) Version() byte {
32+
return a[0]
33+
}
34+
35+
// EmbeddedChecksum returns the 4 checksum bytes of a A25 address
36+
func (a *A25) EmbeddedChecksum() (c [4]byte) {
37+
copy(c[:], a[21:])
38+
return
39+
}
40+
41+
// Tmpl and Set58 are adapted from the C solution.
42+
// Go has big integers but this techinique seems better.
43+
var tmpl = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
44+
45+
// Set58 takes a base58 encoded address and decodes it into the receiver.
46+
// Errors are returned if the argument is not valid base58 or if the decoded
47+
// value does not fit in the 25 byte address. The address is not otherwise
48+
// checked for validity.
49+
func (a *A25) Set58(s []byte) error {
50+
for _, s1 := range s {
51+
c := bytes.IndexByte(tmpl, s1)
52+
if c < 0 {
53+
return errors.New("bad char")
54+
}
55+
for j := 24; j >= 0; j-- {
56+
c += 58 * int(a[j])
57+
a[j] = byte(c % 256)
58+
c /= 256
59+
}
60+
if c > 0 {
61+
return errors.New("too long")
62+
}
63+
}
64+
return nil
65+
}
66+
67+
// ComputeChecksum returns a four byte checksum computed from the first 21
68+
// bytes of the address. The embedded checksum is not updated.
69+
func (a *A25) ComputeChecksum() (c [4]byte) {
70+
copy(c[:], a.doubleSHA256())
71+
return
72+
} /* {{header|Go}} */
73+
974
// AddressFromPrivKey takes a private key string and returns a Bitcoin address
1075
func AddressFromPrivKey(privKey string) string {
1176
pubKey := PrivateKey(privKey).PubKey()
@@ -18,3 +83,19 @@ func Address(publicKey *bsvec.PublicKey) (address *bsvutil.LegacyAddressPubKeyHa
1883
address, _ = bsvutil.NewLegacyAddressPubKeyHash(publicKeyHash, &chaincfg.MainNetParams)
1984
return
2085
}
86+
87+
// ValidA58 validates a base58 encoded bitcoin address. An address is valid
88+
// if it can be decoded into a 25 byte address, the version number is 0,
89+
// and the checksum validates. Return value ok will be true for valid
90+
// addresses. If ok is false, the address is invalid and the error value
91+
// may indicate why.
92+
func ValidA58(a58 []byte) (ok bool, err error) {
93+
var a A25
94+
if err := a.Set58(a58); err != nil {
95+
return false, err
96+
}
97+
if a.Version() != 0 {
98+
return false, errors.New("not version 0")
99+
}
100+
return a.EmbeddedChecksum() == a.ComputeChecksum(), nil
101+
}

address_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package bitcoin
2+
3+
import (
4+
"testing"
5+
)
6+
7+
// Test address
8+
const address = "1KCEAmVS6FFggtc7W9as7sEENvjt7DqMi2"
9+
10+
func TestValidA58(t *testing.T) {
11+
12+
valid, err := ValidA58([]byte(address))
13+
14+
if !valid {
15+
t.Error("Failed to validate address", err)
16+
}
17+
}

0 commit comments

Comments
 (0)