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  }