Skip to content

Commit

Permalink
Merge pull request #368 from blinklabs-io/feat/ledger-address-from-parts
Browse files Browse the repository at this point in the history
feat: build Address from parts
  • Loading branch information
agaffney authored Aug 12, 2023
2 parents feea96c + 8793624 commit 199fc0e
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 25 deletions.
60 changes: 35 additions & 25 deletions ledger/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,21 +180,21 @@ func (a AssetFingerprint) String() string {
}

const (
addressHeaderTypeMask = 0xF0
addressHeaderNetworkMask = 0x0F
addressHashSize = 28

addressTypeKeyKey = 0b0000
addressTypeScriptKey = 0b0001
addressTypeKeyScript = 0b0010
addressTypeScriptScript = 0b0011
addressTypeKeyPointer = 0b0100
addressTypeScriptPointer = 0b0101
addressTypeKeyNone = 0b0110
addressTypeScriptNone = 0b0111
addressTypeByron = 0b1000
addressTypeNoneKey = 0b1110
addressTypeNoneScript = 0b1111
AddressHeaderTypeMask = 0xF0
AddressHeaderNetworkMask = 0x0F
AddressHashSize = 28

AddressTypeKeyKey = 0b0000
AddressTypeScriptKey = 0b0001
AddressTypeKeyScript = 0b0010
AddressTypeScriptScript = 0b0011
AddressTypeKeyPointer = 0b0100
AddressTypeScriptPointer = 0b0101
AddressTypeKeyNone = 0b0110
AddressTypeScriptNone = 0b0111
AddressTypeByron = 0b1000
AddressTypeNoneKey = 0b1110
AddressTypeNoneScript = 0b1111
)

type Address struct {
Expand All @@ -219,18 +219,28 @@ func NewAddress(addr string) (Address, error) {
return a, nil
}

// NewAddressFromParts returns an Address based on the individual parts of the address that are provided
func NewAddressFromParts(addrType uint8, networkId uint8, paymentAddr []byte, stakingAddr []byte) Address {
return Address{
addressType: addrType,
networkId: networkId,
paymentAddress: paymentAddr,
stakingAddress: stakingAddr,
}
}

func (a *Address) populateFromBytes(data []byte) {
// Extract header info
header := data[0]
a.addressType = (header & addressHeaderTypeMask) >> 4
a.networkId = header & addressHeaderNetworkMask
a.addressType = (header & AddressHeaderTypeMask) >> 4
a.networkId = header & AddressHeaderNetworkMask
// Extract payload
// NOTE: this is probably incorrect for Byron
payload := data[1:]
a.paymentAddress = payload[:addressHashSize]
a.stakingAddress = payload[addressHashSize:]
a.paymentAddress = payload[:AddressHashSize]
a.stakingAddress = payload[AddressHashSize:]
// Adjust stake addresses
if a.addressType == addressTypeNoneKey || a.addressType == addressTypeNoneScript {
if a.addressType == AddressTypeNoneKey || a.addressType == AddressTypeNoneScript {
a.stakingAddress = a.paymentAddress[:]
a.paymentAddress = make([]byte, 0)
}
Expand All @@ -252,11 +262,11 @@ func (a *Address) MarshalCBOR() ([]byte, error) {

// StakeAddress returns a new Address with only the stake key portion. This will return nil if the address is not a payment/staking key pair
func (a Address) StakeAddress() *Address {
if a.addressType != addressTypeKeyKey && a.addressType != addressTypeScriptKey {
if a.addressType != AddressTypeKeyKey && a.addressType != AddressTypeScriptKey {
return nil
}
newAddr := &Address{
addressType: addressTypeNoneKey,
addressType: AddressTypeNoneKey,
networkId: a.networkId,
stakingAddress: a.stakingAddress[:],
}
Expand All @@ -265,7 +275,7 @@ func (a Address) StakeAddress() *Address {

func (a Address) generateHRP() string {
var ret string
if a.addressType == addressTypeNoneKey || a.addressType == addressTypeNoneScript {
if a.addressType == AddressTypeNoneKey || a.addressType == AddressTypeNoneScript {
ret = "stake"
} else {
ret = "addr"
Expand All @@ -280,7 +290,7 @@ func (a Address) generateHRP() string {
// Bytes returns the underlying bytes for the address
func (a Address) Bytes() []byte {
ret := []byte{}
ret = append(ret, (byte(a.addressType)<<4)|(byte(a.networkId)&addressHeaderNetworkMask))
ret = append(ret, (byte(a.addressType)<<4)|(byte(a.networkId)&AddressHeaderNetworkMask))
ret = append(ret, a.paymentAddress...)
ret = append(ret, a.stakingAddress...)
return ret
Expand All @@ -289,7 +299,7 @@ func (a Address) Bytes() []byte {
// String returns the bech32-encoded version of the address
func (a Address) String() string {
data := a.Bytes()
if a.addressType == addressTypeByron {
if a.addressType == AddressTypeByron {
// Encode data to base58
encoded := base58.Encode(data)
return encoded
Expand Down
45 changes: 45 additions & 0 deletions ledger/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,51 @@ func TestAddressFromBytes(t *testing.T) {
}
}

func TestAddressFromParts(t *testing.T) {
// We can't use our network helpers due to an import cycle
var networkMainnetId uint8 = 1
testDefs := []struct {
addressType uint8
networkId uint8
paymentAddr []byte
stakingAddr []byte
expectedAddress string
}{
{
addressType: AddressTypeScriptKey,
networkId: networkMainnetId,
paymentAddr: test.DecodeHexString("e1317b152faac13426e6a83e06ff88a4d62cce3c1634ab0a5ec13309"),
stakingAddr: test.DecodeHexString("52563c5410bff6a0d43ccebb7c37e1f69f5eb260552521adff33b9c2"),
expectedAddress: "addr1z8snz7c4974vzdpxu65ruphl3zjdvtxw8strf2c2tmqnxz2j2c79gy9l76sdg0xwhd7r0c0kna0tycz4y5s6mlenh8pq0xmsha",
},
{
addressType: AddressTypeKeyKey,
networkId: networkMainnetId,
paymentAddr: test.DecodeHexString("3f35615835258addded1c2e169f3a2ab4ae94d606bde030e7947f518"),
stakingAddr: test.DecodeHexString("4ff5f8e3d43ce6b19ec4197e331e86d0f5e58b02d7a75b5e74cff95d"),
expectedAddress: "addr1qyln2c2cx5jc4hw768pwz60n5245462dvp4auqcw09rl2xz07huw84puu6cea3qe0ce3apks7hjckqkh5ad4uax0l9ws0q9xty",
},
{
addressType: AddressTypeScriptNone,
networkId: networkMainnetId,
paymentAddr: test.DecodeHexString("21bd8c2e0df2fbe92137f78dbaba48f62308e52303049f0d628b6c4c"),
expectedAddress: "addr1wysmmrpwphe0h6fpxlmcmw46frmzxz89yvpsf8cdv29kcnqsw3vw6",
},
{
addressType: AddressTypeKeyNone,
networkId: networkMainnetId,
paymentAddr: test.DecodeHexString("cfe224295a282d69edda5fa8de4f131e2b9cd21a6c9235597fa4ff6b"),
expectedAddress: "addr1v887yfpftg5z660dmf063hj0zv0zh8xjrfkfyd2e07j076cecha5k",
},
}
for _, testDef := range testDefs {
addr := NewAddressFromParts(testDef.addressType, testDef.networkId, testDef.paymentAddr, testDef.stakingAddr)
if addr.String() != testDef.expectedAddress {
t.Fatalf("address did not match expected value, got: %s, wanted: %s", addr.String(), testDef.expectedAddress)
}
}
}

func TestAddressStakeAddress(t *testing.T) {
testDefs := []struct {
address string
Expand Down

0 comments on commit 199fc0e

Please sign in to comment.