diff --git a/address.go b/address.go index 1942c2d..4d5c827 100644 --- a/address.go +++ b/address.go @@ -100,16 +100,16 @@ func DecodeAddress(addr string, defaultNet *chaincfg.Params) (Address, error) { switch len(decoded) { case ripemd160.Size: // P2PKH or P2SH switch typ { - case AddrTypePayToPubKeyHash: + case AddrTypePayToPubKeyHash, AddrTypeTokenPayToPubKeyHash: return newAddressPubKeyHash(decoded, defaultNet) - case AddrTypePayToScriptHash: + case AddrTypePayToScriptHash, AddrTypeTokenPayToScriptHash: return newAddressScriptHashFromHash(decoded, defaultNet) default: return nil, ErrUnknownAddressType } case sha256.Size: // P2SH32 switch typ { - case AddrTypePayToScriptHash32: + case AddrTypePayToScriptHash, AddrTypeTokenPayToScriptHash: return newAddressScriptHash32FromHash(decoded, defaultNet) default: return nil, ErrUnknownAddressType @@ -139,7 +139,7 @@ func DecodeAddress(addr string, defaultNet *chaincfg.Params) (Address, error) { } case sha256.Size: // P2SH32 switch typ { - case AddrTypePayToScriptHash32: + case AddrTypeTokenPayToScriptHash: return NewSlpAddressScriptHash32FromHash(decoded, defaultNet) default: return nil, ErrUnknownAddressType @@ -759,16 +759,21 @@ func checkDecodeCashAddress(input string) (result []byte, prefix string, t Addre if err != nil { return data, prefix, AddrTypePayToPubKeyHash, err } - if len(data) != 21 { + if len(data) != 21 && len(data) != 33 { // version byte + ripemd160.Size or sha256.Size return data, prefix, AddrTypePayToPubKeyHash, errors.New("incorrect data length") } - switch data[0] { - case 0x00: + + switch data[0] >> 3 { // bits 1 to 4 are the address type. + case 0: t = AddrTypePayToPubKeyHash - case 0x08: + case 1: t = AddrTypePayToScriptHash + case 2: + t = AddrTypeTokenPayToPubKeyHash + case 3: + t = AddrTypeTokenPayToScriptHash } - return data[1:21], prefix, t, nil + return data[1:], prefix, t, nil } // AddressType represents the type of address and is used @@ -784,9 +789,13 @@ const ( // a cashaddr PayToPubkeyHash address AddrTypePayToScriptHash AddressType = 1 - // AddrTypePayToScriptHash32 is the numeric identifier for - // a cashaddr PayToPubkeyHash address - AddrTypePayToScriptHash32 AddressType = 2 + // AddrTypePayToScriptHash is the numeric identifier for + // a token aware cashaddr PayToPubkeyHash address + AddrTypeTokenPayToPubKeyHash AddressType = 2 + + // AddrTypeTokenPayToScriptHash is the numeric identifier for + // a token aware cashaddr PayToPubkeyHash address + AddrTypeTokenPayToScriptHash AddressType = 3 ) // Charset is the base32 character set for the cashaddr. diff --git a/address_test.go b/address_test.go index d96daff..278873a 100644 --- a/address_test.go +++ b/address_test.go @@ -1017,3 +1017,85 @@ func TestConvertSlpToCashAddressP2sh(t *testing.T) { t.Fatal("incorrect conversion") } } + +// Source: https://github.com/cashtokens/cashtokens/blob/master/test-vectors/cashaddr.json +var p2SH32CashAddreTestVectors = []string{ + "bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2", + "bitcoincash:zr6m7j9njldwwzlg9v7v53unlr4jkmx6eycnjehshe", + "bchtest:pr6m7j9njldwwzlg9v7v53unlr4jkmx6eyvwc0uz5t", + "pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtks5", + "bitcoincash:qr7fzmep8g7h7ymfxy74lgc0v950j3r2959lhtxxsl", + "bitcoincash:zr7fzmep8g7h7ymfxy74lgc0v950j3r295z4y4gq0v", + "bchtest:qr7fzmep8g7h7ymfxy74lgc0v950j3r295pdnvy3hr", + "bchtest:zr7fzmep8g7h7ymfxy74lgc0v950j3r295x8qj2hgs", + "bchreg:qr7fzmep8g7h7ymfxy74lgc0v950j3r295m39d8z59", + "bchreg:zr7fzmep8g7h7ymfxy74lgc0v950j3r295umknfytk", + "prefix:qr7fzmep8g7h7ymfxy74lgc0v950j3r295fu6e430r", + "prefix:zr7fzmep8g7h7ymfxy74lgc0v950j3r295wkf8mhss", + "bitcoincash:qpagr634w55t4wp56ftxx53xukhqgl24yse53qxdge", + "bitcoincash:zpagr634w55t4wp56ftxx53xukhqgl24ys77z7gth2", + "bitcoincash:qq9l9e2dgkx0hp43qm3c3h252e9euugrfc6vlt3r9e", + "bitcoincash:zq9l9e2dgkx0hp43qm3c3h252e9euugrfcaxv4l962", + "bitcoincash:qre24q38ghy6k3pegpyvtxahu8q8hqmxmqqn28z85p", + "bitcoincash:zre24q38ghy6k3pegpyvtxahu8q8hqmxmq8eeevptj", + "bitcoincash:qz7xc0vl85nck65ffrsx5wvewjznp9lflgktxc5878", + "bitcoincash:zz7xc0vl85nck65ffrsx5wvewjznp9lflg3p4x6pp5", + "bitcoincash:ppawqn2h74a4t50phuza84kdp3794pq3ccvm92p8sh", + "bitcoincash:rpawqn2h74a4t50phuza84kdp3794pq3cct3k50p0y", + "bitcoincash:pqv53dwyatxse2xh7nnlqhyr6ryjgfdtagkd4vc388", + "bitcoincash:rqv53dwyatxse2xh7nnlqhyr6ryjgfdtag38xjkhc5", + "bitcoincash:prseh0a4aejjcewhc665wjqhppgwrz2lw5txgn666a", + "bitcoincash:rrseh0a4aejjcewhc665wjqhppgwrz2lw5vvmd5u9w", + "bitcoincash:pzltaslh7xnrsxeqm7qtvh0v53n3gfk0v5wwf6d7j4", + "bitcoincash:rzltaslh7xnrsxeqm7qtvh0v53n3gfk0v5fy6yrcdx", + "bitcoincash:pvqqqqqqqqqqqqqqqqqqqqqqzg69v7ysqqqqqqqqqqqqqqqqqqqqqpkp7fqn0", + "bitcoincash:rvqqqqqqqqqqqqqqqqqqqqqqzg69v7ysqqqqqqqqqqqqqqqqqqqqqn9alsp2y", + "bitcoincash:pdzyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3jh2p5nn", + "bitcoincash:rdzyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygrpttc42c", + "bitcoincash:pwyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsh3sujgcr", + "bitcoincash:rwyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9zvatfpg", + "bitcoincash:p0xvenxvenxvenxvenxvenxvenxvenxvenxvenxvenxvenxvenxvcm6gz4t77", + "bitcoincash:r0xvenxvenxvenxvenxvenxvenxvenxvenxvenxvenxvenxvenxvcff5rv284", + "bitcoincash:p0llllllllllllllllllllllllllllllllllllllllllllllllll7x3vthu35", + "bitcoincash:r0llllllllllllllllllllllllllllllllllllllllllllllllll75zs2wagl", + "bchtest:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq7fqng6m6", + "pref:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq4k9m7qf9", +} + +func TestP2SH32CashAddreTestVectors(t *testing.T) { + for _, s := range p2SH32CashAddreTestVectors { + result := strings.Split(s, ":") + params := &chaincfg.MainNetParams + switch result[0] { + case "bchtest": + params = &chaincfg.TestNet4Params + case "bchreg": + params = &chaincfg.RegressionNetParams + case "prefix", "pref": + continue + + } + _, err := bchutil.DecodeAddress(s, params) + if err != nil { + t.Error(err) + } + } +} + +var invalidAddreTestVectors = []string{ + "bitcoincash:prseh0a4aejjcewhc665wjqhppgwrz2lw5txgn676a", + "bitcoincash:rrseh0a4aejjcewhc665wjqhppgwrz2lw5vVmd5u9w", + "bitcoincash:izltaslh7xnrsxeqm7qtvh0v53n3gfk0v5wwf6d7j4", + "bitcoincash:pvqqqqqqqqqqqqqqqqqqqqqqqqqqqqzg69v7ysqqqqqqqqqqqqqqqqqqqqqpkp7fqn0", + "bitcoincash:rv0qqqqqqqqqqqqqqqqqqqqqqzg69v7ysqqqqqqqqqqqqqqqqqqqqqn9alsp2y", +} + +func TestInvalidCashAddressTestVectors(t *testing.T) { + for _, s := range invalidAddreTestVectors { + params := &chaincfg.MainNetParams + _, err := bchutil.DecodeAddress(s, params) + if err == nil { + t.Fatalf("Failed to error on invalid address string: %s", s) + } + } +}