Skip to content

Instantly share code, notes, and snippets.

@etscrivner
Created June 9, 2017 00:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save etscrivner/e8e11b4f8d9c21f19232f67de9a996b7 to your computer and use it in GitHub Desktop.
Save etscrivner/e8e11b4f8d9c21f19232f67de9a996b7 to your computer and use it in GitHub Desktop.
Base58Check Encoding In Go
package main
import (
"bytes"
"crypto/sha256"
"golang.org/x/crypto/ripemd160"
"fmt"
"math/big"
)
const Base58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
// Fixtures for testing our code
const PublicKeyFixture = "0202a406624211f2abbdc68da3df929f938c3399dd79fac1b51b0e4ad1d26a47aa"
const P2PKHAddressFixture = "1PRTTaJesdNovgne6Ehcdu1fpEdX7913CK"
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
func CountLeadingZeros(payload []byte) int {
num_zeros := 0
for _, value := range payload {
if value != 0 {
break
}
num_zeros += 1
}
return num_zeros
}
func Base58PackValue(result []byte, carry int) {
for idx, v := range result {
carry += 256 * int(v)
result[idx] = byte(carry % 58)
carry /= 58
}
}
func Base58EncodeBytes(payload []byte) string {
leading_zeros := CountLeadingZeros(payload)
num_nonzero := len(payload) - leading_zeros
indexes_size := num_nonzero * 138 / 100 + 1
indexes := make([]byte, indexes_size)
for _, v := range payload[leading_zeros:len(payload)] {
Base58PackValue(indexes, int(v))
}
var buffer bytes.Buffer
for _, v := range indexes {
buffer.WriteString(string(Base58Alphabet[int(v)]))
}
return Reverse(buffer.String())
}
func BitcoinHash(payload []byte) []byte {
hasher1 := sha256.New()
hasher1.Write(payload)
hasher2 := sha256.New()
hasher2.Write(hasher1.Sum(nil))
return hasher2.Sum(nil)
}
func BitcoinShortHash(payload []byte) []byte {
sha_hasher := sha256.New()
sha_hasher.Write(payload)
ripemd_hasher := ripemd160.New()
ripemd_hasher.Write(sha_hasher.Sum(nil))
return ripemd_hasher.Sum(nil)
}
func Base58CheckEncodeBytes(payload []byte, version byte) string {
// 1: Prepend version to payload
var prefix_payload []byte
prefix_payload = append(prefix_payload, version)
prefix_payload = append(prefix_payload, payload...)
// 2: Hash version prefixed payload
checksum := BitcoinHash(prefix_payload)
// 3: Append first 4 bytes of hash to payload
prefix_payload = append(prefix_payload, checksum[0:4]...)
// 4: Base58 encode the final payload
return Base58EncodeBytes(prefix_payload)
}
func main() {
public_key := big.NewInt(0)
public_key, result := public_key.SetString(PublicKeyFixture, 16)
if !result {
panic("Unable to decode hex string")
}
public_key_hash := BitcoinShortHash(public_key.Bytes())
encoded := Base58CheckEncodeBytes(public_key_hash, 0)
fmt.Println("Expected: ", P2PKHAddressFixture)
fmt.Println("Actual: ", encoded)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment