From ea934b570abb4f8a97cf4231e727d03897dee11a Mon Sep 17 00:00:00 2001 From: Aurora Gaffney Date: Thu, 28 Mar 2024 07:08:08 -0500 Subject: [PATCH] feat: ledger support for Byron TX inputs/outputs (#567) Fixes #285 --- ledger/byron.go | 137 +++++++++++++++++++++++++++++++++++++++--- ledger/common.go | 18 +++--- ledger/common_test.go | 5 ++ 3 files changed, 144 insertions(+), 16 deletions(-) diff --git a/ledger/byron.go b/ledger/byron.go index 8c3b1b4e..11320889 100644 --- a/ledger/byron.go +++ b/ledger/byron.go @@ -107,7 +107,7 @@ func (h *ByronMainBlockHeader) SlotNumber() uint64 { func (h *ByronMainBlockHeader) IssuerVkey() IssuerVkey { // Byron blocks don't have an issuer - return IssuerVkey([]byte{}) + return IssuerVkey{} } func (h *ByronMainBlockHeader) BlockBodySize() uint64 { @@ -123,8 +123,8 @@ type ByronTransaction struct { cbor.StructAsArray cbor.DecodeStoreCbor // TODO: flesh these out - TxInputs []any - TxOutputs []any + TxInputs []ByronTransactionInput + TxOutputs []ByronTransactionOutput Attributes *cbor.Value } @@ -134,13 +134,20 @@ func (t *ByronTransaction) Hash() string { } func (t *ByronTransaction) Inputs() []TransactionInput { - // TODO - return nil + ret := []TransactionInput{} + for _, input := range t.TxInputs { + ret = append(ret, input) + } + return ret } func (t *ByronTransaction) Outputs() []TransactionOutput { - // TODO - return nil + ret := []TransactionOutput{} + for _, output := range t.TxOutputs { + output := output + ret = append(ret, &output) + } + return ret } func (t *ByronTransaction) Fee() uint64 { @@ -170,6 +177,114 @@ func (t *ByronTransaction) Utxorpc() *utxorpc.Tx { return &utxorpc.Tx{} } +type ByronTransactionInput struct { + cbor.StructAsArray + TxId Blake2b256 + OutputIndex uint32 +} + +func (i *ByronTransactionInput) UnmarshalCBOR(data []byte) error { + id, err := cbor.DecodeIdFromList(data) + if err != nil { + return err + } + switch id { + case 0: + var tmpData struct { + cbor.StructAsArray + Id int + Cbor []byte + } + if _, err := cbor.Decode(data, &tmpData); err != nil { + return err + } + if err := cbor.DecodeGeneric(tmpData.Cbor, i); err != nil { + return err + } + default: + // [u8 .ne 0, encoded-cbor] + return fmt.Errorf("can't parse yet") + } + return nil +} + +func (i ByronTransactionInput) Id() Blake2b256 { + return i.TxId +} + +func (i ByronTransactionInput) Index() uint32 { + return i.OutputIndex +} + +func (i ByronTransactionInput) Utxorpc() *utxorpc.TxInput { + return &utxorpc.TxInput{ + TxHash: i.TxId.Bytes(), + OutputIndex: i.OutputIndex, + // AsOutput: i.AsOutput, + // Redeemer: i.Redeemer, + } +} + +func (i ByronTransactionInput) String() string { + return fmt.Sprintf("%s#%d", i.TxId, i.OutputIndex) +} + +func (i ByronTransactionInput) MarshalJSON() ([]byte, error) { + return []byte("\"" + i.String() + "\""), nil +} + +type ByronTransactionOutput struct { + cbor.StructAsArray + cbor.DecodeStoreCbor + OutputAddress Address `json:"address"` + OutputAmount uint64 `json:"amount"` +} + +func (o *ByronTransactionOutput) UnmarshalCBOR(data []byte) error { + // Save original CBOR + o.SetCbor(data) + var tmpData struct { + cbor.StructAsArray + WrappedAddress cbor.RawMessage + Amount uint64 + } + if _, err := cbor.Decode(data, &tmpData); err != nil { + return err + } + o.OutputAmount = tmpData.Amount + if _, err := cbor.Decode(tmpData.WrappedAddress, &o.OutputAddress); err != nil { + return err + } + return nil +} + +func (o ByronTransactionOutput) Address() Address { + return o.OutputAddress +} + +func (o ByronTransactionOutput) Amount() uint64 { + return o.OutputAmount +} + +func (o ByronTransactionOutput) Assets() *MultiAsset[MultiAssetTypeOutput] { + return nil +} + +func (o ByronTransactionOutput) DatumHash() *Blake2b256 { + return nil +} + +func (o ByronTransactionOutput) Datum() *cbor.LazyValue { + return nil +} + +func (o ByronTransactionOutput) Utxorpc() *utxorpc.TxOutput { + return &utxorpc.TxOutput{ + Address: o.OutputAddress.Bytes(), + Coin: o.Amount(), + } +} + type ByronMainBlockBody struct { cbor.StructAsArray // TODO: split this to its own type @@ -279,8 +394,12 @@ func (b *ByronMainBlock) Era() Era { } func (b *ByronMainBlock) Transactions() []Transaction { - // TODO - return nil + ret := make([]Transaction, len(b.Body.TxPayload)) + for idx, payload := range b.Body.TxPayload { + payload := payload + ret[idx] = &payload.Transaction + } + return ret } func (b *ByronMainBlock) Utxorpc() *utxorpc.Block { diff --git a/ledger/common.go b/ledger/common.go index 3c6db37a..e520ecac 100644 --- a/ledger/common.go +++ b/ledger/common.go @@ -336,14 +336,18 @@ func (a *Address) populateFromBytes(data []byte) error { } func (a *Address) UnmarshalCBOR(data []byte) error { - // Decode bytes from CBOR + // Try to unwrap as bytestring (Shelley and forward) tmpData := []byte{} - if _, err := cbor.Decode(data, &tmpData); err != nil { - return err - } - err := a.populateFromBytes(tmpData) - if err != nil { - return err + if _, err := cbor.Decode(data, &tmpData); err == nil { + err := a.populateFromBytes(tmpData) + if err != nil { + return err + } + } else { + // Probably a Byron address + if err := a.populateFromBytes(data); err != nil { + return err + } } return nil } diff --git a/ledger/common_test.go b/ledger/common_test.go index 1adee454..3ec338eb 100644 --- a/ledger/common_test.go +++ b/ledger/common_test.go @@ -139,6 +139,11 @@ func TestAddressFromBytes(t *testing.T) { addressBytesHex: "015bad085057ac10ecc7060f7ac41edd6f63068d8963ef7d86ca58669e5ecf2d283418a60be5a848a2380eb721000da1e0bbf39733134beca4cb57afb0b35fc89c63061c9914e055001a518c7516", expectedAddress: "addr1q9d66zzs27kppmx8qc8h43q7m4hkxp5d39377lvxefvxd8j7eukjsdqc5c97t2zg5guqadepqqx6rc9m7wtnxy6tajjvk4a0kze4ljyuvvrpexg5up2sqxj33363v35gtew", }, + // Byron address + { + addressBytesHex: "82d818584283581caf56de241bcca83d72c51e74d18487aa5bc68b45e2caa170fa329d3aa101581e581cea1425ccdd649b25af5deb7e6335da2eb8167353a55e77925122e95f001a3a858621", + expectedAddress: "DdzFFzCqrht2ii4Vc7KRchSkVvQtCqdGkQt4nF4Yxg1NpsubFBity2Tpt2eSEGrxBH1eva8qCFKM2Y5QkwM1SFBizRwZgz1N452WYvgG", + }, } for _, testDef := range testDefs { addr := Address{}