Created
June 9, 2017 00:59
-
-
Save etscrivner/e8e11b4f8d9c21f19232f67de9a996b7 to your computer and use it in GitHub Desktop.
Base58Check Encoding In Go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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