Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ require (
github.com/btcsuite/btcd/btcutil v1.1.6
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0
github.com/dchest/blake2b v1.0.0
github.com/stretchr/testify v1.8.4
golang.org/x/crypto v0.43.0
)

require (
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.37.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
Expand Down
1 change: 1 addition & 0 deletions sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ var upgradeParams = []upgradeParam{
{1046400, []byte{0xA6, 0x75, 0xFF, 0xE9}}, // Canopy 0xe9ff75a6
{1687104, []byte{0xB4, 0xD0, 0xD6, 0xC2}}, // NU5 0xc2d6d0b4
{2726400, []byte{0x55, 0x10, 0xE7, 0xC8}}, // NU6 0xc8e71055
{3146400, []byte{0xF0, 0x4D, 0xEC, 0x4D}}, // NU6.1 0x4dec4df0
}

// RawTxInSignature returns the serialized ECDSA signature for the input idx of
Expand Down
77 changes: 77 additions & 0 deletions zecaddr.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package zecutil
import (
"crypto/sha256"
"errors"
"strings"


"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/btcutil/base58"
bech32 "github.com/btcsuite/btcd/btcutil/bech32" // tex address
"github.com/btcsuite/btcd/chaincfg"
"golang.org/x/crypto/ripemd160"
)
Expand Down Expand Up @@ -92,6 +95,11 @@ func DecodeAddress(address string, netName string) (btcutil.Address, error) {
return nil, errors.New("unknown net")
}

// add tex address support
if strings.HasPrefix(address, "tex1") || strings.HasPrefix(address, "textest1") {
return decodeTexAddress(address, netName)
}

var decoded = base58.Decode(address)
if len(decoded) != 26 {
return nil, base58.ErrInvalidFormat
Expand Down Expand Up @@ -122,6 +130,46 @@ func DecodeAddress(address string, netName string) (btcutil.Address, error) {
return nil, errors.New("unknown address")
}

// decode tex/textest address
func decodeTexAddress(address string, netName string) (btcutil.Address, error) {
// 1. bech32 decode
hrp, data, ver, err := bech32.DecodeGeneric(address)
if err != nil {
return nil, err
}

// 2. must be bech32m
if ver != bech32.VersionM {
return nil, errors.New("tex address must use bech32m")
}

// 3. verify hrp: mainnet = tex testnet = textest
expectedHRP := "tex"
if strings.Contains(netName, "test") {
expectedHRP = "textest"
}
if hrp != expectedHRP {
return nil, errors.New("invalid tex hrp")
}

// 4. 5bit words -> 8bit bytes(20-byte pkHash)
pkh, err := bech32.ConvertBits(data, 5, 8, false)
if err != nil {
return nil, err
}
if len(pkh) != ripemd160.Size {
return nil, errors.New("invalid tex payload length")
}

// 5. construct ZecAddressPubKeyHash
addr := &ZecAddressPubKeyHash{prefix: netName}
copy(addr.hash[:], pkh)

return addr, nil
}



// EncodeAddress returns the string encoding of a pay-to-pubkey-hash
// address. Part of the Address interface.
func (a *ZecAddressPubKeyHash) EncodeAddress() (addr string) {
Expand Down Expand Up @@ -192,3 +240,32 @@ func addrChecksum(input []byte) (cksum [4]byte) {

return
}

// PkHashFromAddress parses a zcash address (t1/t3/tex/textest) and returns
// the 20-byte hash160 payload (pkHash/scriptHash), reusing DecodeAddress.
func PkHashFromAddress(address string, net *chaincfg.Params) ([]byte, error) {
if net == nil {
return nil, errors.New("nil net params")
}

addr, err := DecodeAddress(address, net.Name)
if err != nil {
return nil, err
}

switch a := addr.(type) {
case *ZecAddressPubKeyHash:
return a.ScriptAddress(), nil
case *ZecAddressScriptHash:
return a.ScriptAddress(), nil
default:

type scriptAddresser interface {
ScriptAddress() []byte
}
if sa, ok := addr.(scriptAddresser); ok {
return sa.ScriptAddress(), nil
}
return nil, errors.New("unsupported address type for pkHash")
}
}
70 changes: 69 additions & 1 deletion zecaddr_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package zecutil

import (
"github.com/btcsuite/btcd/btcutil"
"reflect"
"testing"

"github.com/btcsuite/btcd/btcutil"
"github.com/stretchr/testify/require"

"github.com/btcsuite/btcd/chaincfg"
)

Expand Down Expand Up @@ -61,3 +64,68 @@ func TestDecode(t *testing.T) {
}
}
}

func TestDecodeTexAndT1SameHash(t *testing.T) {
t1 := "t1XtsHnj4Ev6CWC3HfJ7Xu3GkEP7SCy8hxV"
tex := "tex1n88w7cmg9uzdluuct3krjqlkcxyz8tku8sq40s"

addr1, err := DecodeAddress(t1, "mainnet")
require.NoError(t, err)

addr2, err := DecodeAddress(tex, "mainnet")
require.NoError(t, err)

a1 := addr1.(*ZecAddressPubKeyHash)
a2 := addr2.(*ZecAddressPubKeyHash)

require.Equal(t, a1.hash, a2.hash)
}


func TestPkHashFromAddress_T1AndTexSame(t *testing.T) {
//(mainnet)
t1 := "t1XtsHnj4Ev6CWC3HfJ7Xu3GkEP7SCy8hxV"
tex := "tex1n88w7cmg9uzdluuct3krjqlkcxyz8tku8sq40s"

netParam := &chaincfg.Params{
Name: "mainnet",
}

// 1. use PkHashFromAddress for pkHash
pkhT1, err := PkHashFromAddress(t1, netParam)
if err != nil {
t.Fatalf("PkHashFromAddress(t1) error: %v", err)
}

pkhTex, err := PkHashFromAddress(tex, netParam)
if err != nil {
t.Fatalf("PkHashFromAddress(tex) error: %v", err)
}

if !reflect.DeepEqual(pkhT1, pkhTex) {
t.Fatalf("pkHash not equal: t1=%x tex=%x", pkhT1, pkhTex)
}

// 2. use DecodeAddress to check ScriptAddress
addrT1, err := DecodeAddress(t1, netParam.Name)
if err != nil {
t.Fatalf("DecodeAddress(t1) error: %v", err)
}
addrTex, err := DecodeAddress(tex, netParam.Name)
if err != nil {
t.Fatalf("DecodeAddress(tex) error: %v", err)
}

z1, ok1 := addrT1.(*ZecAddressPubKeyHash)
if !ok1 {
t.Fatalf("DecodeAddress(t1) type = %T, want *ZecAddressPubKeyHash", addrT1)
}
z2, ok2 := addrTex.(*ZecAddressPubKeyHash)
if !ok2 {
t.Fatalf("DecodeAddress(tex) type = %T, want *ZecAddressPubKeyHash", addrTex)
}

if !reflect.DeepEqual(z1.ScriptAddress(), z2.ScriptAddress()) {
t.Fatalf("ScriptAddress not equal: t1=%x tex=%x", z1.ScriptAddress(), z2.ScriptAddress())
}
}