110 lines
3.3 KiB
Go
110 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"errors"
|
|
"os"
|
|
)
|
|
|
|
// With at least one other bitcoin RC task, this source is styled more like
|
|
// a package to show how functions of the two tasks might be combined into
|
|
// a single package. It turns out there's not really that much shared code,
|
|
// just the A25 type and doubleSHA256 method, but it's enough to suggest how
|
|
// the code might be organized. Types, methods, and functions are capitalized
|
|
// where they might be exported from a package.
|
|
|
|
// A25 is a type for a 25 byte (not base58 encoded) bitcoin address.
|
|
type A25 [25]byte
|
|
|
|
func (a *A25) Version() byte {
|
|
return a[0]
|
|
}
|
|
|
|
func (a *A25) EmbeddedChecksum() (c [4]byte) {
|
|
copy(c[:], a[21:])
|
|
return
|
|
}
|
|
|
|
// 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])
|
|
}
|
|
|
|
// 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}} */
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// Program returns exit code 0 with valid address and produces no output.
|
|
// Otherwise exit code is 1 and a message is written to stderr.
|
|
func main() {
|
|
if len(os.Args) != 2 {
|
|
errorExit("Usage: valid <base58 address>")
|
|
}
|
|
switch ok, err := ValidA58([]byte(os.Args[1])); {
|
|
case ok:
|
|
case err == nil:
|
|
errorExit("Invalid")
|
|
default:
|
|
errorExit(err.Error())
|
|
}
|
|
}
|
|
|
|
func errorExit(m string) {
|
|
os.Stderr.WriteString(m + "\n")
|
|
os.Exit(1)
|
|
}
|