diff --git a/go.mod b/go.mod index 090f3c11..67aa334e 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.24.1 require ( filippo.io/edwards25519 v1.1.0 github.com/blinklabs-io/ouroboros-mock v0.3.8 - github.com/blinklabs-io/plutigo v0.0.7 + github.com/blinklabs-io/plutigo v0.0.8 github.com/btcsuite/btcd/btcutil v1.1.6 github.com/fxamacker/cbor/v2 v2.9.0 github.com/jinzhu/copier v0.4.0 @@ -21,7 +21,7 @@ require ( github.com/bits-and-blooms/bitset v1.20.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.5 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect - github.com/consensys/gnark-crypto v0.18.0 // indirect + github.com/consensys/gnark-crypto v0.19.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect diff --git a/go.sum b/go.sum index 9a556178..e4155627 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3M github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blinklabs-io/ouroboros-mock v0.3.8 h1:+DAt2rx0ouZUxee5DBMgZq3I1+ZdxFSHG9g3tYl/FKU= github.com/blinklabs-io/ouroboros-mock v0.3.8/go.mod h1:UwQIf4KqZwO13P9d90fbi3UL/X7JaJfeEbqk+bEeFQA= -github.com/blinklabs-io/plutigo v0.0.7 h1:wgb7v47FggrZEfikolV12WhPsWEXzFyzLtak5IrAOEk= -github.com/blinklabs-io/plutigo v0.0.7/go.mod h1:gxTWAu9n7+4SgQ+zAoO91LYU+5WanUNdRkl9mLdm8f8= +github.com/blinklabs-io/plutigo v0.0.8 h1:p0agbMDZ00skO1yx3Mg0S55phz/mCo8nYuixOaAAGJs= +github.com/blinklabs-io/plutigo v0.0.8/go.mod h1:L639Q8i2cSRuBhjgCHttPR0nnYwwsYVT4Btz7KpQjSw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= @@ -33,8 +33,8 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0= -github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= +github.com/consensys/gnark-crypto v0.19.0 h1:zXCqeY2txSaMl6G5wFpZzMWJU9HPNh8qxPnYJ1BL9vA= +github.com/consensys/gnark-crypto v0.19.0/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/ledger/common/address.go b/ledger/common/address.go index 512cd7c1..a6a5dd16 100644 --- a/ledger/common/address.go +++ b/ledger/common/address.go @@ -311,6 +311,21 @@ func (a *Address) ToPlutusData() data.PlutusData { // There is no PlutusData representation for Byron addresses return nil } + // Stake-only address + if a.paymentPayload == nil && a.stakingPayload != nil { + switch p := a.stakingPayload.(type) { + case AddressPayloadKeyHash: + return data.NewConstr( + 0, + data.NewByteString(p.Hash.Bytes()), + ) + case AddressPayloadScriptHash: + return data.NewConstr( + 1, + data.NewByteString(p.Hash.Bytes()), + ) + } + } // Build payment part var paymentPd data.PlutusData switch p := a.paymentPayload.(type) { @@ -324,8 +339,6 @@ func (a *Address) ToPlutusData() data.PlutusData { 1, data.NewByteString(p.Hash[:]), ) - default: - return nil } // Build stake part var stakePd data.PlutusData @@ -359,15 +372,18 @@ func (a *Address) ToPlutusData() data.PlutusData { ) case AddressPayloadPointer: stakePd = data.NewConstr( - 1, - data.NewInteger( - new(big.Int).SetUint64(p.Slot), - ), - data.NewInteger( - new(big.Int).SetUint64(p.TxIndex), - ), - data.NewInteger( - new(big.Int).SetUint64(p.CertIndex), + 0, + data.NewConstr( + 1, + data.NewInteger( + new(big.Int).SetUint64(p.Slot), + ), + data.NewInteger( + new(big.Int).SetUint64(p.TxIndex), + ), + data.NewInteger( + new(big.Int).SetUint64(p.CertIndex), + ), ), ) default: diff --git a/ledger/common/data_test.go b/ledger/common/data_test.go index 3db51c11..84bbb2f1 100644 --- a/ledger/common/data_test.go +++ b/ledger/common/data_test.go @@ -50,11 +50,14 @@ func TestDatumDecode(t *testing.T) { "d8799fd8799fd8799f581cb255e2283f9b495dd663b841090c42bc5a5103283fc2aef5c6cd2f5cffd8799fd8799fd8799f581c07d8b4b15e9609e76a38b25637900d60cdf13a6abce984757bbc1349ffffffffd8799f581cf5808c2c990d86da54bfc97d89cee6efa20cd8461616359478d96b4c582073e1518e92f367fd5820ac2da1d40ab24fbca1d6cb2c28121ad92f57aff8abceff1b0000000148f3f3579fd8799fd8799f4040ff1a094f78d8ffd8799fd8799f581cf13ac4d66b3ee19a6aa0f2a22298737bd907cc95121662fc971b527546535452494b45ff1af7c5c601ffffff", ) expectedDatum := common.Datum{ - Data: data.NewConstr( + Data: data.NewConstrDefIndef( + true, 0, - data.NewConstr( + data.NewConstrDefIndef( + true, 0, - data.NewConstr( + data.NewConstrDefIndef( + true, 0, data.NewByteString( test.DecodeHexString( @@ -62,11 +65,14 @@ func TestDatumDecode(t *testing.T) { ), ), ), - data.NewConstr( + data.NewConstrDefIndef( + true, 0, - data.NewConstr( + data.NewConstrDefIndef( + true, 0, - data.NewConstr( + data.NewConstrDefIndef( + true, 0, data.NewByteString( test.DecodeHexString( @@ -77,7 +83,8 @@ func TestDatumDecode(t *testing.T) { ), ), ), - data.NewConstr( + data.NewConstrDefIndef( + true, 0, data.NewByteString( test.DecodeHexString( @@ -91,19 +98,24 @@ func TestDatumDecode(t *testing.T) { ), ), data.NewInteger(big.NewInt(5518914391)), - data.NewList( - data.NewConstr( + data.NewListDefIndef( + true, + data.NewConstrDefIndef( + true, 0, - data.NewConstr( + data.NewConstrDefIndef( + true, 0, data.NewByteString(nil), data.NewByteString(nil), ), data.NewInteger(big.NewInt(156203224)), ), - data.NewConstr( + data.NewConstrDefIndef( + true, 0, - data.NewConstr( + data.NewConstrDefIndef( + true, 0, data.NewByteString( test.DecodeHexString( diff --git a/ledger/common/script/context.go b/ledger/common/script/context.go index 50602166..665252e1 100644 --- a/ledger/common/script/context.go +++ b/ledger/common/script/context.go @@ -124,7 +124,7 @@ type TxInfoV3 struct { Fee uint64 Mint lcommon.MultiAsset[lcommon.MultiAssetTypeMint] Certificates []lcommon.Certificate - Withdrawals map[*lcommon.Address]uint64 + Withdrawals KeyValuePairs[*lcommon.Address, uint64] ValidRange TimeRange Signatories []lcommon.Blake2b224 Redeemers KeyValuePairs[ScriptInfo, Redeemer] @@ -170,14 +170,24 @@ func (t TxInfoV3) ToPlutusData() data.PlutusData { } func NewTxInfoV3FromTransaction( + slotState lcommon.SlotState, tx lcommon.Transaction, resolvedInputs []lcommon.Utxo, -) TxInfoV3 { +) (TxInfoV3, error) { + validityRange, err := validityRangeInfo( + slotState, + tx.ValidityIntervalStart(), + tx.TTL(), + ) + if err != nil { + return TxInfoV3{}, err + } assetMint := tx.AssetMint() if assetMint == nil { assetMint = &lcommon.MultiAsset[lcommon.MultiAssetTypeMint]{} } inputs := sortInputs(tx.Inputs()) + withdrawals := withdrawalsInfo(tx.Withdrawals()) redeemers := redeemersInfo( tx.Witnesses(), scriptPurposeBuilder( @@ -185,7 +195,7 @@ func NewTxInfoV3FromTransaction( inputs, *assetMint, tx.Certificates(), - tx.Withdrawals(), + withdrawals, // TODO: proposal procedures // TODO: votes ), @@ -197,15 +207,12 @@ func NewTxInfoV3FromTransaction( sortInputs(tx.ReferenceInputs()), resolvedInputs, ), - Outputs: collapseOutputs(tx.Produced()), - Fee: tx.Fee(), - Mint: *assetMint, - ValidRange: TimeRange{ - tx.TTL(), - tx.ValidityIntervalStart(), - }, + Outputs: collapseOutputs(tx.Produced()), + Fee: tx.Fee(), + Mint: *assetMint, + ValidRange: validityRange, Certificates: tx.Certificates(), - Withdrawals: tx.Withdrawals(), + Withdrawals: withdrawals, Signatories: signatoriesInfo(tx.RequiredSigners()), Redeemers: redeemers, Data: tmpData, @@ -219,7 +226,7 @@ func NewTxInfoV3FromTransaction( if amt := tx.Donation(); amt > 0 { ret.TreasuryDonation.Value = amt } - return ret + return ret, nil } type TimeRange struct { @@ -336,6 +343,54 @@ func sortedRedeemerKeys( return ret } +func validityRangeInfo( + slotState lcommon.SlotState, + startSlot uint64, + endSlot uint64, +) (TimeRange, error) { + var ret TimeRange + if startSlot > 0 { + startTime, err := slotState.SlotToTime(startSlot) + if err != nil { + return ret, err + } + ret.lowerBound = uint64(startTime.UnixMilli()) // nolint:gosec + } + if endSlot > 0 { + endTime, err := slotState.SlotToTime(endSlot) + if err != nil { + return ret, err + } + ret.upperBound = uint64(endTime.UnixMilli()) // nolint:gosec + } + return ret, nil +} + +func withdrawalsInfo( + withdrawals map[*lcommon.Address]uint64, +) KeyValuePairs[*lcommon.Address, uint64] { + var ret KeyValuePairs[*lcommon.Address, uint64] + for addr, amt := range withdrawals { + ret = append( + ret, + KeyValuePair[*lcommon.Address, uint64]{ + Key: addr, + Value: amt, + }, + ) + } + // Sort by address bytes + slices.SortFunc( + ret, + func(a, b KeyValuePair[*lcommon.Address, uint64]) int { + aBytes, _ := a.Key.Bytes() + bBytes, _ := b.Key.Bytes() + return bytes.Compare(aBytes, bBytes) + }, + ) + return ret +} + func dataInfo( witnessSet lcommon.TransactionWitnessSet, ) KeyValuePairs[lcommon.DatumHash, data.PlutusData] { diff --git a/ledger/common/script/context_test.go b/ledger/common/script/context_test.go index b588d143..8379fd20 100644 --- a/ledger/common/script/context_test.go +++ b/ledger/common/script/context_test.go @@ -21,6 +21,7 @@ import ( "regexp" "strings" "testing" + "time" "github.com/blinklabs-io/gouroboros/cbor" "github.com/blinklabs-io/gouroboros/ledger/babbage" @@ -70,6 +71,7 @@ func formatPlutusData(pd data.PlutusData) string { } func buildTxInfoV3( + slotState lcommon.SlotState, txHex string, inputsHex string, inputOutputsHex string, @@ -116,10 +118,37 @@ func buildTxInfoV3( ) } // Build TxInfo - txInfo := NewTxInfoV3FromTransaction(tx, resolvedInputs) + txInfo, err := NewTxInfoV3FromTransaction(slotState, tx, resolvedInputs) + if err != nil { + return nil, err + } return txInfo, nil } +type mockSlotState struct { + ZeroTime time.Time + ZeroSlot uint64 + SlotLength time.Duration +} + +func (m mockSlotState) SlotToTime(slot uint64) (time.Time, error) { + slot -= m.ZeroSlot + ret := m.ZeroTime.Add(time.Duration(slot) * m.SlotLength) + return ret, nil +} + +func (m mockSlotState) TimeToSlot(t time.Time) (uint64, error) { + // TODO + return 0, nil +} + +var preprodSlotState = mockSlotState{ + SlotLength: 1 * time.Second, + // Shelley start + ZeroTime: time.UnixMilli(1596059091000), + ZeroSlot: 4492800, +} + var scriptContextV3TestDefs = []struct { name string txHex string @@ -127,6 +156,7 @@ var scriptContextV3TestDefs = []struct { outputsHex string redeemerTag common.RedeemerTag redeemerIndex uint32 + slotState common.SlotState expectedCbor string }{ { @@ -195,6 +225,16 @@ var scriptContextV3TestDefs = []struct { redeemerIndex: 20, expectedCbor: "d8799fd8799f9fd8799fd8799f5820000000000000000000000000000000000000000000000000000000000000000000ffd8799fd8799fd8799f581c00000000000000000000000000000000000000000000000000000000ffd87a80ffa140a1401a000f4240d87980d87a80ffffff8080182aa09fd8799fd87a9f581c22222222222222222222222222222222222222222222222222222222ffd87a80ffd8799fd8799f581c00000000000000000000000000000000000000000000000000000000ffd87a80ffd87a9fd8799f581c00000000000000000000000000000000000000000000000000000000ffd87a80ffd905009f581c1111111111111111111111111111111111111111111111111111111158209999999999999999999999999999999999999999999999999999999999999999ffd905019f581c11111111111111111111111111111111111111111111111111111111190539ffd8799fd8799f581c00000000000000000000000000000000000000000000000000000000ffd87a80ffd87a9fd8799f581c00000000000000000000000000000000000000000000000000000000ffd87a80ffd87b9fd8799f581c00000000000000000000000000000000000000000000000000000000ffd87a9fd8799fd8799f581c00000000000000000000000000000000000000000000000000000000ffffffffd87b9fd8799f581c00000000000000000000000000000000000000000000000000000000ffd87a9fd8799fd87a9f581c00000000000000000000000000000000000000000000000000000000ffffffffd87b9fd8799f581c00000000000000000000000000000000000000000000000000000000ffd87a9fd87a80ffffd87b9fd8799f581c00000000000000000000000000000000000000000000000000000000ffd87a9fd87b80ffffd87b9fd8799f581c00000000000000000000000000000000000000000000000000000000ffd87b9f581c11111111111111111111111111111111111111111111111111111111d87b80ffffd87c9fd8799f581c00000000000000000000000000000000000000000000000000000000ffd8799f581c11111111111111111111111111111111111111111111111111111111ff1a002dc6c0ffd87c9fd8799f581c00000000000000000000000000000000000000000000000000000000ffd87a9fd87b80ff1a002dc6c0ffd87c9fd8799f581c00000000000000000000000000000000000000000000000000000000ffd87b9f581c11111111111111111111111111111111111111111111111111111111d87b80ff1a002dc6c0ffd905029fd8799f581c00000000000000000000000000000000000000000000000000000000ffd8799f581c22222222222222222222222222222222222222222222222222222222ffffd905039fd8799f581c00000000000000000000000000000000000000000000000000000000ffffd87d9fd8799f581c00000000000000000000000000000000000000000000000000000000ff1a002dc6c0ffd87f9fd8799f581c00000000000000000000000000000000000000000000000000000000ff1a002dc6c0ffd87e9fd8799f581c00000000000000000000000000000000000000000000000000000000ffffd87b9fd87a9f581c9b24324046544393443e1fb35c8b72c3c39e18a516a95df5f6654101ffd8799f581c11111111111111111111111111111111111111111111111111111111ffffffa0d8799fd8799fd87980d87a80ffd8799fd87b80d87a80ffff80a1d87c9f14d87b9fd87a9f581c9b24324046544393443e1fb35c8b72c3c39e18a516a95df5f6654101ffd8799f581c11111111111111111111111111111111111111111111111111111111ffffffd87980a0582080bf68ced2c50b8734fc8cebd0a049e16e53d2fe5ec7e42822f07c263ced98aba080d8799f1a00989680ffd8799f0effffd87980d87c9f14d87b9fd87a9f581c9b24324046544393443e1fb35c8b72c3c39e18a516a95df5f6654101ffd8799f581c11111111111111111111111111111111111111111111111111111111ffffffff", }, + { + name: "Withdraw", + txHex: "84a700818258200000000000000000000000000000000000000000000000000000000000000000000183a2005839200000000000000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111011a000f4240a200582340000000000000000000000000000000000000000000000000000000008198bd431b03011a000f4240a200582350111111111111111111111111111111111111111111111111111111118198bd431b03011a000f424002182a031a00448e0105a1581df004036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2000e81581c000000000000000000000000000000000000000000000000000000001601a10582840000d87a81d87980821a000f42401a05f5e100840300d87980821a000f42401a05f5e100f5f6", + inputsHex: "81825820000000000000000000000000000000000000000000000000000000000000000000", + outputsHex: "81a40058393004036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a204036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2011a000f4240028201d81843d8798003d818590221820359021c5902190101003232323232323232322232533333300c00215323330073001300937540062a660109211c52756e6e696e672032206172672076616c696461746f72206d696e740013533333300d004153330073001300937540082a66601660146ea8010494ccc021288a4c2a660129211856616c696461746f722072657475726e65642066616c7365001365600600600600600600600315330084911d52756e6e696e672033206172672076616c696461746f72207370656e640013533333300d004153330073001300937540082a66601660146ea8010494cccccc03800454ccc020c008c028dd50008a99980618059baa0011253330094a22930a998052491856616c696461746f722072657475726e65642066616c73650013656006006006006006006006006006006006006300c300a37540066e1d2000153300700116153300700116153300700116153300700116490191496e636f72726563742072656465656d6572207479706520666f722076616c696461746f72207370656e642e0a2020202020202020202020202020202020202020446f75626c6520636865636b20796f7520686176652077726170706564207468652072656465656d657220747970652061732073706563696669656420696e20796f757220706c757475732e6a736f6e0015330034910b5f746d70313a20566f6964001615330024910b5f746d70303a20566f696400165734ae7155ceaab9e5573eae855d21", + redeemerTag: common.RedeemerTagReward, + redeemerIndex: 0, + slotState: preprodSlotState, + expectedCbor: "d8799fd8799f9fd8799fd8799f5820000000000000000000000000000000000000000000000000000000000000000000ffd8799fd8799fd87a9f581c04036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2ffd8799fd8799fd87a9f581c04036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2ffffffffa140a1401a000f4240d87b9fd87980ffd8799f581c04036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2ffffffff809fd8799fd8799fd8799f581c00000000000000000000000000000000000000000000000000000000ffd8799fd8799fd87a9f581c11111111111111111111111111111111111111111111111111111111ffffffffa140a1401a000f4240d87980d87a80ffd8799fd8799fd8799f581c00000000000000000000000000000000000000000000000000000000ffd8799fd87a9f1a00261ec3181b03ffffffa140a1401a000f4240d87980d87a80ffd8799fd8799fd87a9f581c11111111111111111111111111111111111111111111111111111111ffd8799fd87a9f1a00261ec3181b03ffffffa140a1401a000f4240d87980d87a80ffff182aa080a1d87a9f581c04036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2ff00d8799fd8799fd87980d87a80ffd8799fd87a9f1b000001739c890420ffd87980ffff9f581c00000000000000000000000000000000000000000000000000000000ffa2d87a9fd8799f5820000000000000000000000000000000000000000000000000000000000000000000ffffd87a81d87980d87b9fd87a9f581c04036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2ffffd87980a0582040bee3b25a585a854fe73f3448a6f6b417fe669c813a1881e665971f34a9e984a080d87a80d8799f01ffffd87980d87b9fd87a9f581c04036eecadc2f19e95f831b4bc08919cde1d1088d74602bd3dcd78a2ffffff", + }, } func TestScriptContextV3(t *testing.T) { @@ -203,6 +243,7 @@ func TestScriptContextV3(t *testing.T) { testDef.name, func(t *testing.T) { txInfo, err := buildTxInfoV3( + testDef.slotState, testDef.txHex, testDef.inputsHex, testDef.outputsHex, diff --git a/ledger/common/script/purpose.go b/ledger/common/script/purpose.go index 907ba33c..5bc70b8a 100644 --- a/ledger/common/script/purpose.go +++ b/ledger/common/script/purpose.go @@ -82,13 +82,14 @@ type ScriptInfoRewarding struct { func (ScriptInfoRewarding) isScriptInfo() {} func (s ScriptInfoRewarding) ScriptHash() lcommon.ScriptHash { - // TODO - return lcommon.ScriptHash{} + return lcommon.ScriptHash(s.StakeCredential.Credential) } func (s ScriptInfoRewarding) ToPlutusData() data.PlutusData { - // TODO - return nil + return data.NewConstr( + 2, + s.StakeCredential.ToPlutusData(), + ) } type ScriptInfoCertifying struct { @@ -187,7 +188,7 @@ func scriptPurposeBuilder( inputs []lcommon.TransactionInput, mint lcommon.MultiAsset[lcommon.MultiAssetTypeMint], certificates []lcommon.Certificate, - withdrawals map[*lcommon.Address]uint64, + withdrawals KeyValuePairs[*lcommon.Address, uint64], // TODO: proposal procedures // TODO: votes ) toScriptPurposeFunc { @@ -228,7 +229,12 @@ func scriptPurposeBuilder( Certificate: certificates[redeemerKey.Index], } case lcommon.RedeemerTagReward: - return nil + return ScriptInfoRewarding{ + StakeCredential: lcommon.Credential{ + CredType: lcommon.CredentialTypeScriptHash, + Credential: withdrawals[redeemerKey.Index].Key.StakeKeyHash(), + }, + } case lcommon.RedeemerTagVoting: return nil case lcommon.RedeemerTagProposing: diff --git a/ledger/common/state.go b/ledger/common/state.go index d75f8354..d5a9aafd 100644 --- a/ledger/common/state.go +++ b/ledger/common/state.go @@ -15,6 +15,8 @@ package common import ( + "time" + pcommon "github.com/blinklabs-io/gouroboros/protocol/common" ) @@ -40,3 +42,9 @@ type LedgerState interface { type TipState interface { Tip() (pcommon.Tip, error) } + +// SlotState defines the interface for querying slots +type SlotState interface { + SlotToTime(uint64) (time.Time, error) + TimeToSlot(time.Time) (uint64, error) +}