Skip to content

Commit

Permalink
feat: add StakeAddress support for Address
Browse files Browse the repository at this point in the history
  • Loading branch information
kevink1103 committed Nov 23, 2022
1 parent 5d486b0 commit 0ece58a
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 26 deletions.
65 changes: 50 additions & 15 deletions address.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import (

type AddressType byte

// refer to pg.120 in https://hydra.iohk.io/build/7918420/download/1/ledger-spec.pdf
const (
Base AddressType = 0x00
Ptr AddressType = 0x04
Enterprise AddressType = 0x06
Stake AddressType = 0x0E
)

// Address represents a Cardano address.
Expand Down Expand Up @@ -160,6 +162,22 @@ func NewAddressFromBytes(bytes []byte) (Address, error) {
Type: ScriptCredential,
ScriptHash: bytes[1:29],
}
case Stake:
if len(bytes) != 29 {
return addr, errors.New("enterprise address length should be 29")
}
addr.Stake = StakeCredential{
Type: KeyCredential,
KeyHash: bytes[1:29],
}
case Stake + 1:
if len(bytes) != 29 {
return addr, errors.New("enterprise address length should be 29")
}
addr.Stake = StakeCredential{
Type: KeyCredential,
KeyHash: bytes[1:29],
}
}

return addr, nil
Expand Down Expand Up @@ -206,21 +224,24 @@ func (addr *Address) Bytes() []byte {
case Base, Base + 1, Base + 2, Base + 3:
addrBytes = append(addrBytes, addr.Payment.Hash()...)
addrBytes = append(addrBytes, addr.Stake.Hash()...)
case Enterprise, Enterprise + 1:
addrBytes = append(addrBytes, addr.Payment.Hash()...)
case Ptr, Ptr + 1:
addrBytes = append(addrBytes, addr.Payment.Hash()...)
addrBytes = append(addrBytes, encodeToNat(addr.Pointer.Slot)...)
addrBytes = append(addrBytes, encodeToNat(addr.Pointer.TxIndex)...)
addrBytes = append(addrBytes, encodeToNat(addr.Pointer.CertIndex)...)
case Enterprise, Enterprise + 1:
addrBytes = append(addrBytes, addr.Payment.Hash()...)
case Stake, Stake + 1:
addrBytes = append(addrBytes, addr.Stake.Hash()...)
}

return addrBytes
}

// Bech32 returns the Address encoded as bech32.
func (addr *Address) Bech32() string {
addrStr, err := bech32.EncodeFromBase256(getHrp(addr.Network), addr.Bytes())
isStake := addr.Type == Stake || addr.Type == Stake+1
addrStr, err := bech32.EncodeFromBase256(getHrp(addr.Network, isStake), addr.Bytes())
if err != nil {
panic(err)
}
Expand All @@ -245,15 +266,6 @@ func NewBaseAddress(network Network, payment StakeCredential, stake StakeCredent
return Address{Type: addrType, Network: network, Payment: payment, Stake: stake}, nil
}

// NewEnterpriseAddress returns a new Enterprise Address.
func NewEnterpriseAddress(network Network, payment StakeCredential) (Address, error) {
addrType := Enterprise
if payment.Type == ScriptCredential {
addrType = Enterprise + 1
}
return Address{Type: addrType, Network: network, Payment: payment}, nil
}

// Pointer is the location of the Stake Registration Certificate in the blockchain.
type Pointer struct {
Slot uint64
Expand All @@ -270,6 +282,24 @@ func NewPointerAddress(network Network, payment StakeCredential, ptr Pointer) (A
return Address{Type: addrType, Network: network, Payment: payment, Pointer: ptr}, nil
}

// NewEnterpriseAddress returns a new Enterprise Address.
func NewEnterpriseAddress(network Network, payment StakeCredential) (Address, error) {
addrType := Enterprise
if payment.Type == ScriptCredential {
addrType = Enterprise + 1
}
return Address{Type: addrType, Network: network, Payment: payment}, nil
}

// NewStakeAddress returns a new Stake Address.
func NewStakeAddress(network Network, stake StakeCredential) (Address, error) {
addrType := Stake
if stake.Type == ScriptCredential {
addrType = Stake + 1
}
return Address{Type: addrType, Network: network, Stake: stake}, nil
}

func decodeFromNat(data []byte) (uint64, uint, error) {
out := big.NewInt(0)
n := uint(0)
Expand Down Expand Up @@ -316,11 +346,16 @@ func Blake224Hash(b []byte) ([]byte, error) {
return hash.Sum(nil), err
}

func getHrp(network Network) string {
func getHrp(network Network, isStake bool) string {
hrp := "addr"
if isStake {
hrp = "stake"
}

switch network {
case Testnet, Preprod:
return "addr_test"
return hrp + "_test"
default:
return "addr"
return hrp
}
}
42 changes: 31 additions & 11 deletions address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ import (
)

const (
paymentKey = "addr_vk1w0l2sr2zgfm26ztc6nl9xy8ghsk5sh6ldwemlpmp9xylzy4dtf7st80zhd"
stakeKey = "stake_vk1px4j0r2fk7ux5p23shz8f3y5y2qam7s954rgf3lg5merqcj6aetsft99wu"
scriptHash = "script1cda3khwqv60360rp5m7akt50m6ttapacs8rqhn5w342z7r35m37"
addrType0 = "addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x"
addrType1 = "addr1z8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gten0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs9yc0hh"
addrType2 = "addr1yx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzerkr0vd4msrxnuwnccdxlhdjar77j6lg0wypcc9uar5d2shs2z78ve"
addrType3 = "addr1x8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gt7r0vd4msrxnuwnccdxlhdjar77j6lg0wypcc9uar5d2shskhj42g"
addrType4 = "addr1gx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer5pnz75xxcrzqf96k"
addrType5 = "addr128phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtupnz75xxcrtw79hu"
addrType6 = "addr1vx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzers66hrl8"
addrType7 = "addr1w8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtcyjy7wx"
paymentKey = "addr_vk1w0l2sr2zgfm26ztc6nl9xy8ghsk5sh6ldwemlpmp9xylzy4dtf7st80zhd"
stakeKey = "stake_vk1px4j0r2fk7ux5p23shz8f3y5y2qam7s954rgf3lg5merqcj6aetsft99wu"
scriptHash = "script1cda3khwqv60360rp5m7akt50m6ttapacs8rqhn5w342z7r35m37"
addrType0 = "addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x"
addrType1 = "addr1z8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gten0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs9yc0hh"
addrType2 = "addr1yx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzerkr0vd4msrxnuwnccdxlhdjar77j6lg0wypcc9uar5d2shs2z78ve"
addrType3 = "addr1x8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gt7r0vd4msrxnuwnccdxlhdjar77j6lg0wypcc9uar5d2shskhj42g"
addrType4 = "addr1gx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer5pnz75xxcrzqf96k"
addrType5 = "addr128phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtupnz75xxcrtw79hu"
addrType6 = "addr1vx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzers66hrl8"
addrType7 = "addr1w8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtcyjy7wx"
stakeAddrType1 = "stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw"
stakeAddrType2 = "stake178phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtcccycj5"
)

var (
Expand All @@ -31,6 +33,8 @@ var (
"addr128phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtupnz75xxcrtw79hu",
"addr1vx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzers66hrl8",
"addr1w8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtcyjy7wx",
"stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw",
"stake178phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtcccycj5",
}
)

Expand Down Expand Up @@ -140,6 +144,22 @@ func TestNewAddress(t *testing.T) {
if got, want := enterprise1.Bech32(), addrType7; got != want {
t.Errorf("invalid enterprise address\ngot: %s\nwant: %s", got, want)
}

stake0, err := NewStakeAddress(Mainnet, stakeAddrCred)
if err != nil {
t.Fatal(err)
}
if got, want := stake0.Bech32(), stakeAddrType1; got != want {
t.Errorf("invalid stake address\ngot: %s\nwant: %s", got, want)
}

stake1, err := NewStakeAddress(Mainnet, scriptCred)
if err != nil {
t.Fatal(err)
}
if got, want := stake1.Bech32(), stakeAddrType2; got != want {
t.Errorf("invalid stake address\ngot: %s\nwant: %s", got, want)
}
}

func TestNat(t *testing.T) {
Expand Down

0 comments on commit 0ece58a

Please sign in to comment.