介绍
- Base58 是 Bitcoin 中使用的一种独特的编码方式,主要用于产生 Bitcoin 的钱包地址。
- Base58 采用数字、大写字母、小写字母,去除歧义字符 0(零)、O(大写字母 O)、I(大写字母i)、l(小写字母L),总计58个字符作为编码的字母表。
- Base58Check 是一种 Base58 编码格式,可在前几个字符中明确编码的数据类型(版本号 Version),并在最后几个字符中包含一个错误检测代码(校验和 Checksum)。
引用:https://www.jianshu.com/p/d8af38e091be
Base58
const (
// base58 编码基数表
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
)
func Encode(b []byte) []byte {
var (
x *big.Int // b对应的大整数
radix *big.Int // 除数
zero *big.Int // 大整数0
mod *big.Int // 模
dst []byte // 编码结果
)
x = big.NewInt(0).SetBytes(b)
radix = big.NewInt(58) // 58
zero = big.NewInt(0)
mod = &big.Int{}
for x.Cmp(zero) != 0 {
x.DivMod(x, radix, mod) // 除余法
dst = append(dst, alphabet[mod.Int64()])
}
reverse(dst)
return dst
}
func Decode(b []byte) []byte {
r := big.NewInt(0)
for _, c := range b {
i := bytes.IndexByte([]byte(alphabet), c)
r.Mul(r, big.NewInt(58))
r.Add(r, big.NewInt(int64(i)))
}
return r.Bytes()
}
func reverse(b []byte) {
i, j := 0, len(b)-1
for i < j {
b[i], b[j] = b[j], b[i]
i++
j--
}
}
Base58check
const (
version = byte(0x00)
)
func Encode(hash160 []byte) (base58checkEncoded []byte) {
encoded := make([]byte, len(hash160)+1)
encoded[0] = version
copy(encoded[1:], hash160)
// 执行两次 SHA-256
hash := sha256.Sum256(encoded)
hash2 := sha256.Sum256(hash[:])
checksum := hash2[0:4]
encodedChecksum := append(encoded, checksum...)
base58EncodedChecksum := base58.Encode(encodedChecksum)
// 由于base58会将0删除,比特币要求在0的位置补上1,即比特币地址由1开始的原因
var buffer bytes.Buffer
for _, v := range encodedChecksum {
if v != byte(0x00) {
break
}
buffer.WriteByte('1')
}
buffer.Write(base58EncodedChecksum)
return buffer.Bytes()
}
func Decode(b []byte) (hash160 []byte, err error) {
encodedChecksum := base58.Decode(b[1:])
hash160 = encodedChecksum[:len(encodedChecksum)-4]
checksum := encodedChecksum[len(encodedChecksum)-4:]
var buffer bytes.Buffer
for _, v := range b {
if v != '1' {
break
}
buffer.WriteByte(0)
}
buffer.Write(hash160)
encoded := buffer.Bytes()
// 执行两次 SHA-256,验证校验码是否正确
hash := sha256.Sum256(encoded)
hash2 := sha256.Sum256(hash[:])
if !bytes.Equal(hash2[:4], checksum) {
return nil, fmt.Errorf("checksum error")
}
return hash160, nil
}
Test
func TestBase58(t *testing.T) {
src := []byte("this is the example")
e := Encode(src)
t.Log(string(e))
d := Decode(e)
require.Equal(t, src, d)
}
func TestBase58check(t *testing.T) {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
pub := elliptic.Marshal(elliptic.P256(), key.PublicKey.X, key.PublicKey.Y)
hash := sha256.Sum256(pub)
r := ripemd160.New()
r.Write(hash[:])
hash160 := r.Sum(nil)
address := Encode(hash160)
// t.Log(string(address))
dexHash160, err := Decode(address)
require.NoError(t, err)
require.Equal(t, hash160, dexHash160)
}
完整代码:https://github.com/treeforest/easyblc/tree/main/pkg