Skip to content

Commit

Permalink
Merge pull request #16 from catenocrypt/polkadot
Browse files Browse the repository at this point in the history
Add Polkadot support
  • Loading branch information
vikmeup authored Oct 27, 2020
2 parents c7b8cfc + 0936885 commit 5626844
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 21 deletions.
6 changes: 3 additions & 3 deletions base58_checksum.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ func Base58ChecksumDecode(b string, alphabet string) ([]byte, error) {
}

// Check checksum
checksum := doubleSha256(val[: len(val) - 4])
expected := val[len(val) - 4:]
checksum := doubleSha256(val[:len(val)-4])
expected := val[len(val)-4:]
if !bytes.Equal(checksum[0:4], expected) {
return nil, fmt.Errorf("Bad Base58 checksum: %v expected %v", checksum, expected)
}

// strip checksum
return val[:len(val) - 4], nil
return val[:len(val)-4], nil
}

// Base58Encode encodes a byte slice to a modified base58 string.
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/btcsuite/btcd v0.20.1-beta // indirect
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
github.com/cpacia/bchutil v0.0.0-20181003130114-b126f6a35b6c
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1
github.com/pkg/errors v0.8.1
github.com/wealdtech/go-slip44 v1.0.0
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
Expand All @@ -32,6 +34,7 @@ golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down
24 changes: 24 additions & 0 deletions polkadot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package coincodec

import (
"github.com/wealdtech/go-slip44"
)

const (
PolkadotNetwork = 0
)

func init() {
toBytesMap[slip44.POLKADOT] = PolkadotDecodeToBytes
toStringMap[slip44.POLKADOT] = PolkadotEncodeToString
}

// PolkadotDecodeToBytes converts the input string to a byte array
func PolkadotDecodeToBytes(input string) ([]byte, error) {
return SS58AddressDecodeToBytes(input, PolkadotNetwork)
}

// PolkadotEncodeToString converts the input byte array to a string representation of the Polkadot address.
func PolkadotEncodeToString(data []byte) (string, error) {
return SS58AddressEncodeToString(data, PolkadotNetwork)
}
107 changes: 107 additions & 0 deletions polkadot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package coincodec

import (
"testing"

"github.com/pkg/errors"
"github.com/wealdtech/go-slip44"
)

func TestPolkadotEncodeToBytes(t *testing.T) {
tests := []TestcaseEncode{
{
name: "Normal1",
input: "15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu",
output: "beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908",
},
{
name: "Normal2",
input: "15AeCjMpcSt3Fwa47jJBd7JzQ395Kr2cuyF5Zp4UBf1g9ony",
output: "b84b605a51045e43740bc74db62d0077ff9d971e49d588f9b307915721bb3251",
},
{
name: "Normal3 from ensdomains/address-encoder",
input: "1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg",
output: "0aff6865635ae11013a83835c019d44ec3f865145943f487ae82a8e7bed3a66b",
},
{
name: "Too short",
input: "15KR",
err: errors.New("Base58 string too short"),
},
{
name: "Invalid characters",
input: "1a+-/=",
err: errors.New("Bad Base58 string"),
},
{
name: "Bad checksum",
input: "15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTwoF",
err: errors.New("Invalid checksum"),
},
{
name: "Substrate ed25519",
input: "5FqqU2rytGPhcwQosKRtW1E3ha6BJKAjHgtcodh71dSyXhoZ",
err: errors.New("Invalid network"),
},
{
name: "Bitcoin",
input: "1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA",
err: errors.New("Invalid length"),
},
{
name: "Kusama ed25519",
input: "FHKAe66mnbk8ke8zVWE9hFVFrJN1mprFPVmD5rrevotkcDZ",
err: errors.New("Invalid network"),
},
{
name: "Kusama secp256k1",
input: "FxQFyTorsjVsjjMyjdgq8w5vGx8LiA1qhWbRYcFijxKKchx",
err: errors.New("Invalid network"),
},
{
name: "Kusama sr25519",
input: "EJ5UJ12GShfh7EWrcNZFLiYU79oogdtXFUuDDZzk7Wb2vCe",
err: errors.New("Invalid network"),
},
}

RunTestsEncode(t, slip44.POLKADOT, tests)
}

func TestPolkadotDecodeToString(t *testing.T) {
tests := []TestcaseDecode{
{
name: "Good1",
input: "beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908",
output: "15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu",
},
{
name: "Good2",
input: "b84b605a51045e43740bc74db62d0077ff9d971e49d588f9b307915721bb3251",
output: "15AeCjMpcSt3Fwa47jJBd7JzQ395Kr2cuyF5Zp4UBf1g9ony",
},
{
name: "Good3 from ensdomains/address-encoder",
input: "0aff6865635ae11013a83835c019d44ec3f865145943f487ae82a8e7bed3a66b",
output: "1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg",
},
{
name: "Empty",
input: "",
err: errors.New("Invalid decoded address length"),
},
{
name: "Too short",
input: "06a1a1a7f2ff4762",
err: errors.New("Invalid decoded address length"),
},
{
name: "Too long",
input: "06a1a1a7f2ff476205a51045e43740bc74db62d0077ff9d971e49d588f9b307915721bb3",
err: errors.New("Invalid decoded address length"),
},
}

RunTestsDecode(t, slip44.POLKADOT, tests)
}
62 changes: 62 additions & 0 deletions ss58address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package coincodec

import (
"bytes"
"errors"

"golang.org/x/crypto/blake2b"
)

const (
ss58AddressLength = 32
ss58AddressChecksumSize = 2
ss58AddressTotalLength = ss58AddressLength + 1 + ss58AddressChecksumSize // incl. network byte and checksum
)

var (
ss58Prefix = []byte("SS58PRE")
)

func computeChecksum(data []byte) []byte {
prefixed := append(ss58Prefix, data...)
hashres := blake2b.Sum512(prefixed)
checksum := hashres[:ss58AddressChecksumSize]
return checksum
}

// SS58AddressDecodeToBytes converts the input string to a byte array
func SS58AddressDecodeToBytes(input string, network byte) ([]byte, error) {
decoded, err := Base58Decode(input, Base58DefaultAlphabet)
if err != nil {
return nil, err
}
if len(decoded) != ss58AddressTotalLength {
return nil, errors.New("Invalid length")
}
// check network
networkActual := decoded[0]
if networkActual != network {
return nil, errors.New("Invalid network")
}
checksum := computeChecksum(decoded[:len(decoded)-ss58AddressChecksumSize])
// compare checksum
if !bytes.Equal(checksum, decoded[len(decoded)-ss58AddressChecksumSize:]) {
return nil, errors.New("Invalid checksum")
}
// strip network, checksum
return decoded[1 : len(decoded)-2], nil
}

// SS58AddressEncodeToString converts the input byte array to a string representation of the address.
func SS58AddressEncodeToString(data []byte, network byte) (string, error) {
if len(data) != ss58AddressLength {
return "", errors.New("Invalid decoded address length")
}
networked := []byte{}
networked = append(networked, network)
networked = append(networked, data...)
checksum := computeChecksum(networked)
checksummed := append(networked, checksum...)
encoded := Base58Encode(checksummed, Base58DefaultAlphabet)
return encoded, nil
}
39 changes: 22 additions & 17 deletions test_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,31 @@ package coincodec

import (
"encoding/hex"
"fmt"
"reflect"
"testing"
"strings"
"fmt"
"testing"
)

type TestcaseEncode struct {
name string
input string // string
output string // encoded as hex string
err error
name string
input string // string
output string // encoded as hex string
err error
}

type TestcaseDecode struct {
name string
input string // encoded as hex string
output string // string
err error
name string
input string // encoded as hex string
output string // string
err error
}

func errorString(err error) string {
if err != nil {
return err.Error()
}
return "(no error)"
}

func RunTestsEncode(t *testing.T, coinType uint32, tests []TestcaseEncode) {
Expand All @@ -33,21 +40,20 @@ func RunTestsEncode(t *testing.T, coinType uint32, tests []TestcaseEncode) {
}
}

func RunTestEncode(coinType uint32, tt TestcaseEncode) (error) {
func RunTestEncode(coinType uint32, tt TestcaseEncode) error {
testfun, _ := toBytesMap[coinType]

got, err := testfun(tt.input)

var goterror string = "(no error)"
if err != nil { goterror = err.Error() }
goterror := errorString(err)
if tt.err != nil {
if !strings.HasPrefix(goterror, tt.err.Error()) {
return fmt.Errorf("%v %v: ToBytes() error = %v, wantErr %v", coinType, tt.name, goterror, tt.err)
}
} else {
gothex := hex.EncodeToString(got)
if !reflect.DeepEqual(gothex, tt.output) {
return fmt.Errorf("%v %v: ToBytes() = %v, want %v, err: %v", coinType, tt.name, gothex, tt.output, tt.err)
return fmt.Errorf("%v %v: ToBytes() = %v, err: %v, want %v, err: %v", coinType, tt.name, gothex, goterror, tt.output, tt.err)
}
}
return nil
Expand All @@ -64,7 +70,7 @@ func RunTestsDecode(t *testing.T, coinType uint32, tests []TestcaseDecode) {
}
}

func RunTestDecode(coinType uint32, tt TestcaseDecode) (error) {
func RunTestDecode(coinType uint32, tt TestcaseDecode) error {
decoded, err := hex.DecodeString(tt.input)
if err != nil {
return fmt.Errorf("%v %v: Preparation error, input is not valid hex string err %v input %v", coinType, tt.name, err, tt.input)
Expand All @@ -73,8 +79,7 @@ func RunTestDecode(coinType uint32, tt TestcaseDecode) (error) {

got, err := testfun(decoded)

var goterror string = "(no error)"
if err != nil { goterror = err.Error() }
goterror := errorString(err)
if tt.err != nil {
if goterror != tt.err.Error() {
return fmt.Errorf("%v %v: ToString() error = %v, wantErr %v", coinType, tt.name, goterror, tt.err)
Expand Down
2 changes: 1 addition & 1 deletion tezos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func TestTezosDecodeToString(t *testing.T) {
err: errors.New("Invalid decoded address length"),
},
{
name: "Empty",
name: "Short",
input: keyhash3,
err: errors.New("Invalid decoded address length"),
},
Expand Down

0 comments on commit 5626844

Please sign in to comment.