Skip to content

Commit

Permalink
feat: CBOR for block transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
agaffney committed Oct 15, 2023
1 parent 55f3575 commit 99a9a5b
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 14 deletions.
9 changes: 7 additions & 2 deletions cbor/cbor.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,18 @@ type StructAsArray struct {

type DecodeStoreCborInterface interface {
Cbor() []byte
SetCbor([]byte)
}

type DecodeStoreCbor struct {
cborData []byte
}

func (d *DecodeStoreCbor) SetCbor(cborData []byte) {
d.cborData = make([]byte, len(cborData))
copy(d.cborData, cborData)
}

// Cbor returns the original CBOR for the object
func (d *DecodeStoreCbor) Cbor() []byte {
return d.cborData
Expand All @@ -72,7 +78,6 @@ func (d *DecodeStoreCbor) UnmarshalCbor(cborData []byte, dest DecodeStoreCborInt
// Store a copy of the original CBOR data
// This must be done after we copy from the temp object above, or it gets wiped out
// when using struct embedding and the DecodeStoreCbor struct is embedded at a deeper level
d.cborData = make([]byte, len(cborData))
copy(d.cborData, cborData)
d.SetCbor(cborData)
return nil
}
26 changes: 26 additions & 0 deletions ledger/allegra.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,32 @@ func (t AllegraTransaction) Metadata() *cbor.Value {
return t.TxMetadata
}

func (t *AllegraTransaction) Cbor() []byte {
// Return stored CBOR if we have any
cborData := t.DecodeStoreCbor.Cbor()
if cborData != nil {
return cborData[:]
}
// Return immediately if the body CBOR is also empty, which implies an empty TX object
if t.Body.Cbor() == nil {
return nil
}
// Generate our own CBOR
// This is necessary when a transaction is put together from pieces stored separately in a block
tmpObj := []any{
cbor.RawMessage(t.Body.Cbor()),
cbor.RawMessage(t.WitnessSet.Cbor()),
}
if t.TxMetadata != nil {
tmpObj = append(tmpObj, cbor.RawMessage(t.TxMetadata.Cbor()))
} else {
tmpObj = append(tmpObj, nil)
}
// This should never fail, since we're only encoding a list and a bool value
cborData, _ = cbor.Encode(&tmpObj)
return cborData
}

func NewAllegraBlockFromCbor(data []byte) (*AllegraBlock, error) {
var allegraBlock AllegraBlock
if _, err := cbor.Decode(data, &allegraBlock); err != nil {
Expand Down
39 changes: 33 additions & 6 deletions ledger/alonzo.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,15 @@ func (b *AlonzoTransactionBody) Outputs() []TransactionOutput {

type AlonzoTransactionOutput struct {
cbor.StructAsArray
cborData []byte
cbor.DecodeStoreCbor
OutputAddress Address
OutputAmount MaryTransactionOutputValue
TxOutputDatumHash *Blake2b256
}

func (o *AlonzoTransactionOutput) UnmarshalCBOR(cborData []byte) error {
// Save original CBOR
o.cborData = cborData[:]
o.SetCbor(cborData)
// Try to parse as Mary output first
var tmpOutput MaryTransactionOutput
if _, err := cbor.Decode(cborData, &tmpOutput); err == nil {
Expand Down Expand Up @@ -152,10 +152,6 @@ func (o AlonzoTransactionOutput) MarshalJSON() ([]byte, error) {
return json.Marshal(&tmpObj)
}

func (o *AlonzoTransactionOutput) Cbor() []byte {
return o.cborData
}

func (o AlonzoTransactionOutput) Address() Address {
return o.OutputAddress
}
Expand Down Expand Up @@ -183,6 +179,10 @@ type AlonzoTransactionWitnessSet struct {
Redeemers []cbor.RawMessage `cbor:"5,keyasint,omitempty"`
}

func (t *AlonzoTransactionWitnessSet) UnmarshalCBOR(cborData []byte) error {
return t.UnmarshalCbor(cborData, t)
}

type AlonzoTransaction struct {
cbor.StructAsArray
cbor.DecodeStoreCbor
Expand All @@ -208,6 +208,33 @@ func (t AlonzoTransaction) Metadata() *cbor.Value {
return t.TxMetadata
}

func (t *AlonzoTransaction) Cbor() []byte {
// Return stored CBOR if we have any
cborData := t.DecodeStoreCbor.Cbor()
if cborData != nil {
return cborData[:]
}
// Return immediately if the body CBOR is also empty, which implies an empty TX object
if t.Body.Cbor() == nil {
return nil
}
// Generate our own CBOR
// This is necessary when a transaction is put together from pieces stored separately in a block
tmpObj := []any{
cbor.RawMessage(t.Body.Cbor()),
cbor.RawMessage(t.WitnessSet.Cbor()),
t.IsValid,
}
if t.TxMetadata != nil {
tmpObj = append(tmpObj, cbor.RawMessage(t.TxMetadata.Cbor()))
} else {
tmpObj = append(tmpObj, nil)
}
// This should never fail, since we're only encoding a list and a bool value
cborData, _ = cbor.Encode(&tmpObj)
return cborData
}

func NewAlonzoBlockFromCbor(data []byte) (*AlonzoBlock, error) {
var alonzoBlock AlonzoBlock
if _, err := cbor.Decode(data, &alonzoBlock); err != nil {
Expand Down
39 changes: 33 additions & 6 deletions ledger/babbage.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func (d *BabbageTransactionOutputDatumOption) MarshalCBOR() ([]byte, error) {
}

type BabbageTransactionOutput struct {
cborData []byte
cbor.DecodeStoreCbor
OutputAddress Address `cbor:"0,keyasint,omitempty"`
OutputAmount MaryTransactionOutputValue `cbor:"1,keyasint,omitempty"`
DatumOption *BabbageTransactionOutputDatumOption `cbor:"2,keyasint,omitempty"`
Expand All @@ -225,7 +225,7 @@ type BabbageTransactionOutput struct {

func (o *BabbageTransactionOutput) UnmarshalCBOR(cborData []byte) error {
// Save original CBOR
o.cborData = cborData[:]
o.SetCbor(cborData)
// Try to parse as legacy output first
var tmpOutput AlonzoTransactionOutput
if _, err := cbor.Decode(cborData, &tmpOutput); err == nil {
Expand Down Expand Up @@ -262,10 +262,6 @@ func (o BabbageTransactionOutput) MarshalJSON() ([]byte, error) {
return json.Marshal(&tmpObj)
}

func (o *BabbageTransactionOutput) Cbor() []byte {
return o.cborData
}

func (o BabbageTransactionOutput) Address() Address {
return o.OutputAddress
}
Expand Down Expand Up @@ -297,6 +293,10 @@ type BabbageTransactionWitnessSet struct {
PlutusV2Scripts []cbor.RawMessage `cbor:"6,keyasint,omitempty"`
}

func (t *BabbageTransactionWitnessSet) UnmarshalCBOR(cborData []byte) error {
return t.UnmarshalCbor(cborData, t)
}

type BabbageTransaction struct {
cbor.StructAsArray
cbor.DecodeStoreCbor
Expand All @@ -322,6 +322,33 @@ func (t BabbageTransaction) Metadata() *cbor.Value {
return t.TxMetadata
}

func (t *BabbageTransaction) Cbor() []byte {
// Return stored CBOR if we have any
cborData := t.DecodeStoreCbor.Cbor()
if cborData != nil {
return cborData[:]
}
// Return immediately if the body CBOR is also empty, which implies an empty TX object
if t.Body.Cbor() == nil {
return nil
}
// Generate our own CBOR
// This is necessary when a transaction is put together from pieces stored separately in a block
tmpObj := []any{
cbor.RawMessage(t.Body.Cbor()),
cbor.RawMessage(t.WitnessSet.Cbor()),
t.IsValid,
}
if t.TxMetadata != nil {
tmpObj = append(tmpObj, cbor.RawMessage(t.TxMetadata.Cbor()))
} else {
tmpObj = append(tmpObj, nil)
}
// This should never fail, since we're only encoding a list and a bool value
cborData, _ = cbor.Encode(&tmpObj)
return cborData
}

func NewBabbageBlockFromCbor(data []byte) (*BabbageBlock, error) {
var babbageBlock BabbageBlock
if _, err := cbor.Decode(data, &babbageBlock); err != nil {
Expand Down
26 changes: 26 additions & 0 deletions ledger/mary.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,32 @@ func (t MaryTransaction) Metadata() *cbor.Value {
return t.TxMetadata
}

func (t *MaryTransaction) Cbor() []byte {
// Return stored CBOR if we have any
cborData := t.DecodeStoreCbor.Cbor()
if cborData != nil {
return cborData[:]
}
// Return immediately if the body CBOR is also empty, which implies an empty TX object
if t.Body.Cbor() == nil {
return nil
}
// Generate our own CBOR
// This is necessary when a transaction is put together from pieces stored separately in a block
tmpObj := []any{
cbor.RawMessage(t.Body.Cbor()),
cbor.RawMessage(t.WitnessSet.Cbor()),
}
if t.TxMetadata != nil {
tmpObj = append(tmpObj, cbor.RawMessage(t.TxMetadata.Cbor()))
} else {
tmpObj = append(tmpObj, nil)
}
// This should never fail, since we're only encoding a list and a bool value
cborData, _ = cbor.Encode(&tmpObj)
return cborData
}

type MaryTransactionOutput struct {
cbor.StructAsArray
cbor.DecodeStoreCbor
Expand Down
31 changes: 31 additions & 0 deletions ledger/shelley.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,16 @@ func (o ShelleyTransactionOutput) Datum() *cbor.LazyValue {
}

type ShelleyTransactionWitnessSet struct {
cbor.DecodeStoreCbor
VkeyWitnesses []interface{} `cbor:"0,keyasint,omitempty"`
MultisigScripts []interface{} `cbor:"1,keyasint,omitempty"`
BootstrapWitnesses []interface{} `cbor:"2,keyasint,omitempty"`
}

func (t *ShelleyTransactionWitnessSet) UnmarshalCBOR(cborData []byte) error {
return t.UnmarshalCbor(cborData, t)
}

type ShelleyTransaction struct {
cbor.StructAsArray
cbor.DecodeStoreCbor
Expand All @@ -248,6 +253,32 @@ func (t ShelleyTransaction) Metadata() *cbor.Value {
return t.TxMetadata
}

func (t *ShelleyTransaction) Cbor() []byte {
// Return stored CBOR if we have any
cborData := t.DecodeStoreCbor.Cbor()
if cborData != nil {
return cborData[:]
}
// Return immediately if the body CBOR is also empty, which implies an empty TX object
if t.Body.Cbor() == nil {
return nil
}
// Generate our own CBOR
// This is necessary when a transaction is put together from pieces stored separately in a block
tmpObj := []any{
cbor.RawMessage(t.Body.Cbor()),
cbor.RawMessage(t.WitnessSet.Cbor()),
}
if t.TxMetadata != nil {
tmpObj = append(tmpObj, cbor.RawMessage(t.TxMetadata.Cbor()))
} else {
tmpObj = append(tmpObj, nil)
}
// This should never fail, since we're only encoding a list and a bool value
cborData, _ = cbor.Encode(&tmpObj)
return cborData
}

func NewShelleyBlockFromCbor(data []byte) (*ShelleyBlock, error) {
var shelleyBlock ShelleyBlock
if _, err := cbor.Decode(data, &shelleyBlock); err != nil {
Expand Down

0 comments on commit 99a9a5b

Please sign in to comment.