From df27fdcf99cb0b012751ab5c0d47c23ae0863bfb Mon Sep 17 00:00:00 2001 From: Aurora Gaffney Date: Sat, 9 Mar 2024 09:07:56 -0600 Subject: [PATCH] fix: handling for long addresses Fixes #519 --- ledger/common.go | 39 +++++++++++++++++++++++++++++++++------ ledger/common_test.go | 11 ++++++++++- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/ledger/common.go b/ledger/common.go index 5d98d568..9dab8d4f 100644 --- a/ledger/common.go +++ b/ledger/common.go @@ -213,6 +213,7 @@ type Address struct { networkId uint8 paymentAddress []byte stakingAddress []byte + extraData []byte } // NewAddress returns an Address based on the provided bech32 address string @@ -236,16 +237,32 @@ func NewAddressFromParts( networkId uint8, paymentAddr []byte, stakingAddr []byte, -) Address { +) (Address, error) { + if len(paymentAddr) != AddressHashSize { + return Address{}, fmt.Errorf("invalid payment address hash length: %d", len(paymentAddr)) + } + if len(stakingAddr) > 0 && len(stakingAddr) != AddressHashSize { + return Address{}, fmt.Errorf("invalid staking address hash length: %d", len(stakingAddr)) + } return Address{ addressType: addrType, networkId: networkId, - paymentAddress: paymentAddr, - stakingAddress: stakingAddr, - } + paymentAddress: paymentAddr[:], + stakingAddress: stakingAddr[:], + }, nil } -func (a *Address) populateFromBytes(data []byte) { +func (a *Address) populateFromBytes(data []byte) error { + // Check length + dataLen := len(data) + if dataLen < (AddressHashSize + 1) { + return fmt.Errorf("invalid address length: %d", dataLen) + } + if dataLen > (AddressHashSize + 1) { + if dataLen < (AddressHashSize + AddressHashSize + 1) { + return fmt.Errorf("invalid address length: %d", dataLen) + } + } // Extract header info header := data[0] a.addressType = (header & AddressHeaderTypeMask) >> 4 @@ -254,13 +271,22 @@ func (a *Address) populateFromBytes(data []byte) { // NOTE: this is probably incorrect for Byron payload := data[1:] a.paymentAddress = payload[:AddressHashSize] - a.stakingAddress = payload[AddressHashSize:] + if len(payload) > AddressHashSize { + a.stakingAddress = payload[AddressHashSize : AddressHashSize+AddressHashSize] + } + // Store any extra address data + // This is needed to handle the case describe in: + // https://github.com/IntersectMBO/cardano-ledger/issues/2729 + if len(payload) > (AddressHashSize + AddressHashSize) { + a.extraData = payload[AddressHashSize+AddressHashSize:] + } // Adjust stake addresses if a.addressType == AddressTypeNoneKey || a.addressType == AddressTypeNoneScript { a.stakingAddress = a.paymentAddress[:] a.paymentAddress = make([]byte, 0) } + return nil } func (a *Address) UnmarshalCBOR(data []byte) error { @@ -344,6 +370,7 @@ func (a Address) Bytes() []byte { ) ret = append(ret, a.paymentAddress...) ret = append(ret, a.stakingAddress...) + ret = append(ret, a.extraData...) return ret } diff --git a/ledger/common_test.go b/ledger/common_test.go index 70e1b842..826c4db9 100644 --- a/ledger/common_test.go +++ b/ledger/common_test.go @@ -133,6 +133,12 @@ func TestAddressFromBytes(t *testing.T) { addressBytesHex: "61cfe224295a282d69edda5fa8de4f131e2b9cd21a6c9235597fa4ff6b", expectedAddress: "addr1v887yfpftg5z660dmf063hj0zv0zh8xjrfkfyd2e07j076cecha5k", }, + // Long (but apparently valid) address from: + // https://github.com/IntersectMBO/cardano-ledger/issues/2729 + { + addressBytesHex: "015bad085057ac10ecc7060f7ac41edd6f63068d8963ef7d86ca58669e5ecf2d283418a60be5a848a2380eb721000da1e0bbf39733134beca4cb57afb0b35fc89c63061c9914e055001a518c7516", + expectedAddress: "addr1q9d66zzs27kppmx8qc8h43q7m4hkxp5d39377lvxefvxd8j7eukjsdqc5c97t2zg5guqadepqqx6rc9m7wtnxy6tajjvk4a0kze4ljyuvvrpexg5up2sqxj33363v35gtew", + }, } for _, testDef := range testDefs { addr := Address{} @@ -197,12 +203,15 @@ func TestAddressFromParts(t *testing.T) { }, } for _, testDef := range testDefs { - addr := NewAddressFromParts( + addr, err := NewAddressFromParts( testDef.addressType, testDef.networkId, testDef.paymentAddr, testDef.stakingAddr, ) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } if addr.String() != testDef.expectedAddress { t.Fatalf( "address did not match expected value, got: %s, wanted: %s",