github.com/stafiprotocol/go-substrate-rpc-client@v1.4.7/subkey/ss58_address.go (about) 1 package subkey 2 3 import ( 4 "bytes" 5 "fmt" 6 7 "github.com/decred/base58" 8 "golang.org/x/crypto/blake2b" 9 ) 10 11 // SS58Decode decodes an SS58 checksumed value into its data and format. 12 func SS58Decode(address string) (uint16, []byte, error) { 13 // Adapted from https://github.com/paritytech/substrate/blob/e6def65920d30029e42d498cb07cec5dd433b927/primitives/core/src/crypto.rs#L264 14 15 data := base58.Decode(address) 16 if len(data) < 2 { 17 return 0, nil, fmt.Errorf("expected at least 2 bytes in base58 decoded address") 18 } 19 20 prefixLen := int8(0) 21 ident := uint16(0) 22 if data[0] <= 63 { 23 prefixLen = 1 24 ident = uint16(data[0]) 25 } else if data[0] < 127 { 26 lower := (data[0] << 2) | (data[1] >> 6) 27 upper := data[1] & 0b00111111 28 prefixLen = 2 29 ident = uint16(lower) | (uint16(upper) << 8) 30 } else { 31 return 0, nil, fmt.Errorf("invalid address") 32 } 33 34 checkSumLength := 2 35 hash := ss58hash(data[:len(data)-checkSumLength]) 36 checksum := hash[:checkSumLength] 37 38 givenChecksum := data[len(data)-checkSumLength:] 39 if !bytes.Equal(givenChecksum, checksum) { 40 return 0, nil, fmt.Errorf("checksum mismatch: expected %v but got %v", checksum, givenChecksum) 41 } 42 43 return ident, data[prefixLen : len(data)-checkSumLength], nil 44 } 45 46 // SS58Encode encodes data and format identifier to an SS58 checksumed string. 47 func SS58Encode(pubkey []byte, format uint16) string { 48 // Adapted from https://github.com/paritytech/substrate/blob/e6def65920d30029e42d498cb07cec5dd433b927/primitives/core/src/crypto.rs#L319 49 ident := format & 0b0011_1111_1111_1111 50 var prefix []byte 51 if ident <= 63 { 52 prefix = []byte{uint8(ident)} 53 } else if ident <= 16_383 { 54 // upper six bits of the lower byte(!) 55 first := uint8(ident&0b0000_0000_1111_1100) >> 2 56 // lower two bits of the lower byte in the high pos, 57 // lower bits of the upper byte in the low pos 58 second := uint8(ident>>8) | uint8(ident&0b0000_0000_0000_0011)<<6 59 prefix = []byte{first | 0b01000000, second} 60 } else { 61 panic("unreachable: masked out the upper two bits; qed") 62 } 63 body := append(prefix, pubkey...) 64 hash := ss58hash(body) 65 return base58.Encode(append(body, hash[:2]...)) 66 } 67 68 func ss58hash(data []byte) [64]byte { 69 // Adapted from https://github.com/paritytech/substrate/blob/e6def65920d30029e42d498cb07cec5dd433b927/primitives/core/src/crypto.rs#L369 70 prefix := []byte("SS58PRE") 71 return blake2b.Sum512(append(prefix, data...)) 72 }