From 134f5ba03b659678d5be78573de4e5c6fd4a8c37 Mon Sep 17 00:00:00 2001 From: Christopher Tarry Date: Thu, 12 Dec 2024 15:24:37 -0500 Subject: [PATCH] use explorer types for events --- explorer/events.go | 174 ++++++++++++++++++++--------- internal/testutil/check.go | 2 +- persist/sqlite/consensus.go | 19 ++-- persist/sqlite/consensus_test.go | 17 ++- persist/sqlite/events.go | 153 +++---------------------- persist/sqlite/v2consensus_test.go | 63 ++++++++--- 6 files changed, 202 insertions(+), 226 deletions(-) diff --git a/explorer/events.go b/explorer/events.go index ad3321e..5f2144a 100644 --- a/explorer/events.go +++ b/explorer/events.go @@ -1,48 +1,74 @@ package explorer import ( + "time" + "go.sia.tech/core/consensus" "go.sia.tech/core/types" "go.sia.tech/coreutils/wallet" ) -// event types indicate the source of an event. Events can -// either be created by sending Siacoins between addresses or they can be -// created by consensus (e.g. a miner payout, a siafund claim, or a contract). -const ( - EventTypeMinerPayout = wallet.EventTypeMinerPayout - EventTypeFoundationSubsidy = wallet.EventTypeFoundationSubsidy - EventTypeSiafundClaim = wallet.EventTypeSiafundClaim - - EventTypeV1Transaction = wallet.EventTypeV1Transaction - EventTypeV1ContractResolution = wallet.EventTypeV1ContractResolution - - EventTypeV2Transaction = wallet.EventTypeV2Transaction - EventTypeV2ContractResolution = wallet.EventTypeV2ContractResolution -) - type ( // An EventPayout represents a miner payout, siafund claim, or foundation // subsidy. - EventPayout = wallet.EventPayout + EventPayout struct { + SiacoinElement SiacoinOutput `json:"siacoinElement"` + } + // An EventV1Transaction pairs a v1 transaction with its spent siacoin and // siafund elements. - EventV1Transaction = wallet.EventV1Transaction + EventV1Transaction struct { + Transaction Transaction `json:"transaction"` + // v1 siacoin inputs do not describe the value of the spent utxo + SpentSiacoinElements []SiacoinOutput `json:"spentSiacoinElements,omitempty"` + // v1 siafund inputs do not describe the value of the spent utxo + SpentSiafundElements []SiacoinOutput `json:"spentSiafundElements,omitempty"` + } + // An EventV1ContractResolution represents a file contract payout from a v1 // contract. - EventV1ContractResolution = wallet.EventV1ContractResolution - // EventV2Transaction is a transaction event that includes the transaction - EventV2Transaction = wallet.EventV2Transaction + EventV1ContractResolution struct { + Parent ExtendedFileContract `json:"parent"` + SiacoinElement SiacoinOutput `json:"siacoinElement"` + Missed bool `json:"missed"` + } + // An EventV2ContractResolution represents a file contract payout from a v2 // contract. - EventV2ContractResolution = wallet.EventV2ContractResolution + EventV2ContractResolution struct { + Resolution V2FileContractResolution `json:"resolution"` + SiacoinElement SiacoinOutput `json:"siacoinElement"` + Missed bool `json:"missed"` + } - // EventData is the data associated with an event. - EventData = wallet.EventData - // An Event is a record of a consensus event that affects the wallet. - Event = wallet.Event + // EventV2Transaction is a transaction event that includes the transaction + EventV2Transaction V2Transaction + + // EventData contains the data associated with an event. + EventData interface { + isEvent() bool + } + + // An Event is a transaction or other event that affects the wallet including + // miner payouts, siafund claims, and file contract payouts. + Event struct { + ID types.Hash256 `json:"id"` + Index types.ChainIndex `json:"index"` + Confirmations uint64 `json:"confirmations"` + Type string `json:"type"` + Data EventData `json:"data"` + MaturityHeight uint64 `json:"maturityHeight"` + Timestamp time.Time `json:"timestamp"` + Relevant []types.Address `json:"relevant,omitempty"` + } ) +func (EventPayout) isEvent() bool { return true } +func (EventV1Transaction) isEvent() bool { return true } +func (EventV1ContractResolution) isEvent() bool { return true } +func (EventV2Transaction) isEvent() bool { return true } +func (EventV2ContractResolution) isEvent() bool { return true } + // A ChainUpdate is a set of changes to the consensus state. type ChainUpdate interface { ForEachSiacoinElement(func(sce types.SiacoinElement, created, spent bool)) @@ -91,9 +117,9 @@ func AppliedEvents(cs consensus.State, b types.Block, cu ChainUpdate) (events [] for _, txn := range b.Transactions { addresses := make(map[types.Address]struct{}) e := EventV1Transaction{ - Transaction: txn, - SpentSiacoinElements: make([]types.SiacoinElement, 0, len(txn.SiacoinInputs)), - SpentSiafundElements: make([]types.SiafundElement, 0, len(txn.SiafundInputs)), + // Transaction: txn, + // SpentSiacoinElements: make([]types.SiacoinElement, 0, len(txn.SiacoinInputs)), + // SpentSiafundElements: make([]types.SiafundElement, 0, len(txn.SiafundInputs)), } for _, sci := range txn.SiacoinInputs { @@ -102,7 +128,7 @@ func AppliedEvents(cs consensus.State, b types.Block, cu ChainUpdate) (events [] continue } - e.SpentSiacoinElements = append(e.SpentSiacoinElements, sce) + // e.SpentSiacoinElements = append(e.SpentSiacoinElements, sce) addresses[sce.SiacoinOutput.Address] = struct{}{} } for _, sco := range txn.SiacoinOutputs { @@ -115,13 +141,13 @@ func AppliedEvents(cs consensus.State, b types.Block, cu ChainUpdate) (events [] continue } - e.SpentSiafundElements = append(e.SpentSiafundElements, sfe) + // e.SpentSiafundElements = append(e.SpentSiafundElements, sfe) addresses[sfe.SiafundOutput.Address] = struct{}{} sce, ok := sces[sfi.ParentID.ClaimOutputID()] if ok { - addEvent(types.Hash256(sce.ID), sce.MaturityHeight, EventTypeSiafundClaim, EventPayout{ - SiacoinElement: sce, + addEvent(types.Hash256(sce.ID), sce.MaturityHeight, wallet.EventTypeSiafundClaim, EventPayout{ + SiacoinElement: SiacoinOutput{SiacoinElement: sce}, }, []types.Address{sfi.ClaimAddress}) } } @@ -148,7 +174,7 @@ func AppliedEvents(cs consensus.State, b types.Block, cu ChainUpdate) (events [] relevant = append(relevant, addr) } - addEvent(types.Hash256(txn.ID()), cs.Index.Height, EventTypeV1Transaction, e, relevant) // transaction maturity height is the current block height + addEvent(types.Hash256(txn.ID()), cs.Index.Height, wallet.EventTypeV1Transaction, e, relevant) // transaction maturity height is the current block height } // handle v2 transactions @@ -165,8 +191,8 @@ func AppliedEvents(cs consensus.State, b types.Block, cu ChainUpdate) (events [] sce, ok := sces[types.SiafundOutputID(sfi.Parent.ID).V2ClaimOutputID()] if ok { - addEvent(types.Hash256(sce.ID), sce.MaturityHeight, EventTypeSiafundClaim, EventPayout{ - SiacoinElement: sce, + addEvent(types.Hash256(sce.ID), sce.MaturityHeight, wallet.EventTypeSiafundClaim, EventPayout{ + SiacoinElement: SiacoinOutput{SiacoinElement: sce}, }, []types.Address{sfi.ClaimAddress}) } } @@ -174,12 +200,13 @@ func AppliedEvents(cs consensus.State, b types.Block, cu ChainUpdate) (events [] addresses[sco.Address] = struct{}{} } - ev := EventV2Transaction(txn) + // ev := EventV2Transaction(txn) + var ev EventV2Transaction relevant := make([]types.Address, 0, len(addresses)) for addr := range addresses { relevant = append(relevant, addr) } - addEvent(types.Hash256(txn.ID()), cs.Index.Height, EventTypeV2Transaction, ev, relevant) // transaction maturity height is the current block height + addEvent(types.Hash256(txn.ID()), cs.Index.Height, wallet.EventTypeV2Transaction, ev, relevant) // transaction maturity height is the current block height } // handle contracts @@ -190,13 +217,34 @@ func AppliedEvents(cs consensus.State, b types.Block, cu ChainUpdate) (events [] fce.StateElement.MerkleProof = nil + var mpos, vpos []ContractSiacoinOutput + for _, mpo := range fce.FileContract.MissedProofOutputs { + mpos = append(mpos, ContractSiacoinOutput{SiacoinOutput: mpo}) + } + for _, vpo := range fce.FileContract.ValidProofOutputs { + vpos = append(vpos, ContractSiacoinOutput{SiacoinOutput: vpo}) + } + efc := ExtendedFileContract{ + ID: fce.ID, + Filesize: fce.FileContract.Filesize, + FileMerkleRoot: fce.FileContract.FileMerkleRoot, + WindowStart: fce.FileContract.WindowStart, + WindowEnd: fce.FileContract.WindowEnd, + Payout: fce.FileContract.Payout, + ValidProofOutputs: vpos, + MissedProofOutputs: mpos, + UnlockHash: fce.FileContract.UnlockHash, + RevisionNumber: fce.FileContract.RevisionNumber, + } + if valid { for i := range fce.FileContract.ValidProofOutputs { address := fce.FileContract.ValidProofOutputs[i].Address element := sces[types.FileContractID(fce.ID).ValidOutputID(i)] - addEvent(types.Hash256(element.ID), element.MaturityHeight, EventTypeV1ContractResolution, EventV1ContractResolution{ - Parent: fce, - SiacoinElement: element, + + addEvent(types.Hash256(element.ID), element.MaturityHeight, wallet.EventTypeV1ContractResolution, EventV1ContractResolution{ + Parent: efc, + SiacoinElement: SiacoinOutput{SiacoinElement: element}, Missed: false, }, []types.Address{address}) } @@ -204,9 +252,10 @@ func AppliedEvents(cs consensus.State, b types.Block, cu ChainUpdate) (events [] for i := range fce.FileContract.MissedProofOutputs { address := fce.FileContract.MissedProofOutputs[i].Address element := sces[types.FileContractID(fce.ID).MissedOutputID(i)] - addEvent(types.Hash256(element.ID), element.MaturityHeight, EventTypeV1ContractResolution, EventV1ContractResolution{ - Parent: fce, - SiacoinElement: element, + + addEvent(types.Hash256(element.ID), element.MaturityHeight, wallet.EventTypeV1ContractResolution, EventV1ContractResolution{ + Parent: efc, + SiacoinElement: SiacoinOutput{SiacoinElement: element}, Missed: true, }, []types.Address{address}) } @@ -225,26 +274,41 @@ func AppliedEvents(cs consensus.State, b types.Block, cu ChainUpdate) (events [] missed = true } + var typ string + switch res.(type) { + case *types.V2FileContractRenewal: + typ = "renewal" + case *types.V2StorageProof: + typ = "storageProof" + case *types.V2FileContractExpiration: + typ = "expiration" + default: + panic("unknown resolution type") + } + + efc := V2FileContract{V2FileContractElement: fce} { element := sces[types.FileContractID(fce.ID).V2HostOutputID()] - addEvent(types.Hash256(element.ID), element.MaturityHeight, EventTypeV2ContractResolution, EventV2ContractResolution{ - Resolution: types.V2FileContractResolution{ - Parent: fce, + addEvent(types.Hash256(element.ID), element.MaturityHeight, wallet.EventTypeV2ContractResolution, EventV2ContractResolution{ + Resolution: V2FileContractResolution{ + Parent: efc, + Type: typ, Resolution: res, }, - SiacoinElement: element, + SiacoinElement: SiacoinOutput{SiacoinElement: element}, Missed: missed, }, []types.Address{fce.V2FileContract.HostOutput.Address}) } { element := sces[types.FileContractID(fce.ID).V2RenterOutputID()] - addEvent(types.Hash256(element.ID), element.MaturityHeight, EventTypeV2ContractResolution, EventV2ContractResolution{ - Resolution: types.V2FileContractResolution{ - Parent: fce, + addEvent(types.Hash256(element.ID), element.MaturityHeight, wallet.EventTypeV2ContractResolution, EventV2ContractResolution{ + Resolution: V2FileContractResolution{ + Parent: efc, + Type: typ, Resolution: res, }, - SiacoinElement: element, + SiacoinElement: SiacoinOutput{SiacoinElement: element}, Missed: missed, }, []types.Address{fce.V2FileContract.RenterOutput.Address}) } @@ -253,16 +317,16 @@ func AppliedEvents(cs consensus.State, b types.Block, cu ChainUpdate) (events [] // handle block rewards for i := range b.MinerPayouts { element := sces[cs.Index.ID.MinerOutputID(i)] - addEvent(types.Hash256(element.ID), element.MaturityHeight, EventTypeMinerPayout, EventPayout{ - SiacoinElement: element, + addEvent(types.Hash256(element.ID), element.MaturityHeight, wallet.EventTypeMinerPayout, EventPayout{ + SiacoinElement: SiacoinOutput{SiacoinElement: element}, }, []types.Address{b.MinerPayouts[i].Address}) } // handle foundation subsidy element, ok := sces[cs.Index.ID.FoundationOutputID()] if ok { - addEvent(types.Hash256(element.ID), element.MaturityHeight, EventTypeFoundationSubsidy, EventPayout{ - SiacoinElement: element, + addEvent(types.Hash256(element.ID), element.MaturityHeight, wallet.EventTypeFoundationSubsidy, EventPayout{ + SiacoinElement: SiacoinOutput{SiacoinElement: element}, }, []types.Address{element.SiacoinOutput.Address}) } diff --git a/internal/testutil/check.go b/internal/testutil/check.go index eba7a32..545c2b3 100644 --- a/internal/testutil/check.go +++ b/internal/testutil/check.go @@ -16,7 +16,7 @@ import ( func Equal[T any](t *testing.T, desc string, expect, got T) { t.Helper() - if !cmp.Equal(expect, got, cmpopts.EquateEmpty(), cmpopts.IgnoreUnexported(consensus.Work{}), cmpopts.IgnoreTypes(types.StateElement{}, "MerkleProof")) { + if !cmp.Equal(expect, got, cmpopts.EquateEmpty(), cmpopts.IgnoreUnexported(consensus.Work{}), cmpopts.IgnoreFields(types.StateElement{}, "MerkleProof")) { t.Fatalf("%s expected != got, diff: %s", desc, cmp.Diff(expect, got)) } } diff --git a/persist/sqlite/consensus.go b/persist/sqlite/consensus.go index 20ad1d2..63408e4 100644 --- a/persist/sqlite/consensus.go +++ b/persist/sqlite/consensus.go @@ -4,12 +4,12 @@ import ( "database/sql" "errors" "fmt" + "log" "reflect" "time" "go.sia.tech/core/types" "go.sia.tech/coreutils/chain" - "go.sia.tech/coreutils/wallet" "go.sia.tech/explored/explorer" ) @@ -620,7 +620,7 @@ func addSiafundElements(tx *txn, index types.ChainIndex, spentElements, newEleme return sfDBIds, nil } -func addEvents(tx *txn, bid types.BlockID, scDBIds map[types.SiacoinOutputID]int64, sfDBIds map[types.SiafundOutputID]int64, fcDBIds map[explorer.DBFileContract]int64, v2FcDBIds map[explorer.DBFileContract]int64, txnDBIds map[types.TransactionID]txnDBId, v2TxnDBIds map[types.TransactionID]txnDBId, events []wallet.Event) error { +func addEvents(tx *txn, bid types.BlockID, scDBIds map[types.SiacoinOutputID]int64, sfDBIds map[types.SiafundOutputID]int64, fcDBIds map[explorer.DBFileContract]int64, v2FcDBIds map[explorer.DBFileContract]int64, txnDBIds map[types.TransactionID]txnDBId, v2TxnDBIds map[types.TransactionID]txnDBId, events []explorer.Event) error { if len(events) == 0 { return nil } @@ -703,21 +703,24 @@ func addEvents(tx *txn, bid types.BlockID, scDBIds map[types.SiacoinOutputID]int } switch v := event.Data.(type) { - case wallet.EventV1Transaction: + case explorer.EventV1Transaction: dbID := txnDBIds[types.TransactionID(event.ID)].id if _, err = v1TransactionEventStmt.Exec(eventID, dbID); err != nil { return fmt.Errorf("failed to insert transaction event: %w", err) } - case wallet.EventV2Transaction: + case explorer.EventV2Transaction: dbID := v2TxnDBIds[types.TransactionID(event.ID)].id if _, err = v2TransactionEventStmt.Exec(eventID, dbID); err != nil { return fmt.Errorf("failed to insert transaction event: %w", err) } - case wallet.EventPayout: + case explorer.EventPayout: _, err = payoutEventStmt.Exec(eventID, scDBIds[types.SiacoinOutputID(event.ID)]) - case wallet.EventV1ContractResolution: - _, err = v1ContractResolutionEventStmt.Exec(eventID, scDBIds[v.SiacoinElement.ID], fcDBIds[explorer.DBFileContract{ID: v.Parent.ID, RevisionNumber: v.Parent.FileContract.RevisionNumber}], v.Missed) - case wallet.EventV2ContractResolution: + case explorer.EventV1ContractResolution: + ddd := explorer.DBFileContract{ID: v.Parent.ID, RevisionNumber: v.Parent.RevisionNumber} + log.Printf("scDBIDs[%+v] = %v, fcDBIds[%+v] = %d", v.SiacoinElement.ID, scDBIds[v.SiacoinElement.ID], ddd, fcDBIds[ddd]) + + _, err = v1ContractResolutionEventStmt.Exec(eventID, scDBIds[v.SiacoinElement.ID], fcDBIds[explorer.DBFileContract{ID: v.Parent.ID, RevisionNumber: v.Parent.RevisionNumber}], v.Missed) + case explorer.EventV2ContractResolution: _, err = v2ContractResolutionEventStmt.Exec(eventID, scDBIds[v.SiacoinElement.ID], v2FcDBIds[explorer.DBFileContract{ID: v.Resolution.Parent.ID, RevisionNumber: v.Resolution.Parent.V2FileContract.RevisionNumber}], v.Missed) default: return fmt.Errorf("unknown event type: %T", reflect.TypeOf(event.Data)) diff --git a/persist/sqlite/consensus_test.go b/persist/sqlite/consensus_test.go index 9c6c30e..f2fed79 100644 --- a/persist/sqlite/consensus_test.go +++ b/persist/sqlite/consensus_test.go @@ -12,7 +12,6 @@ import ( "go.sia.tech/coreutils" "go.sia.tech/coreutils/chain" ctestutil "go.sia.tech/coreutils/testutil" - "go.sia.tech/coreutils/wallet" "go.sia.tech/explored/explorer" "go.sia.tech/explored/internal/testutil" "go.sia.tech/explored/persist/sqlite" @@ -690,13 +689,13 @@ func TestFileContract(t *testing.T) { } testutil.Equal(t, "events", 2, len(events)) - ev0 := events[0].Data.(wallet.EventV1ContractResolution) + ev0 := events[0].Data.(explorer.EventV1ContractResolution) testutil.Equal(t, "event 0 parent ID", fcID, ev0.Parent.ID) testutil.Equal(t, "event 0 output ID", fcID.MissedOutputID(0), ev0.SiacoinElement.ID) testutil.Equal(t, "event 0 missed", true, ev0.Missed) - ev1 := events[1].Data.(wallet.EventV1Transaction) - testutil.Equal(t, "event 1", txn, ev1.Transaction) + ev1 := events[1].Data.(explorer.EventV1Transaction) + testutil.CheckTransaction(t, txn, ev1.Transaction) } { @@ -1640,8 +1639,8 @@ func TestHostAnnouncement(t *testing.T) { t.Fatal(err) } testutil.Equal(t, "events", 2, len(events)) - testutil.Equal(t, "event 0", txn1, events[0].Data.(wallet.EventV1Transaction).Transaction) - testutil.Equal(t, "event 1", genesisBlock.Transactions[0], events[1].Data.(wallet.EventV1Transaction).Transaction) + testutil.CheckTransaction(t, txn1, events[0].Data.(explorer.EventV1Transaction).Transaction) + testutil.CheckTransaction(t, genesisBlock.Transactions[0], events[1].Data.(explorer.EventV1Transaction).Transaction) } { @@ -2131,8 +2130,8 @@ func TestMultipleReorgFileContract(t *testing.T) { t.Fatal(err) } testutil.Equal(t, "events", 2, len(events)) - testutil.Equal(t, "event 0", txn, events[0].Data.(wallet.EventV1Transaction).Transaction) - testutil.Equal(t, "event 1", genesisBlock.Transactions[0], events[1].Data.(wallet.EventV1Transaction).Transaction) + testutil.CheckTransaction(t, txn, events[0].Data.(explorer.EventV1Transaction).Transaction) + testutil.CheckTransaction(t, genesisBlock.Transactions[0], events[1].Data.(explorer.EventV1Transaction).Transaction) } uc := types.UnlockConditions{ @@ -2376,7 +2375,7 @@ func TestMultipleReorgFileContract(t *testing.T) { t.Fatal(err) } testutil.Equal(t, "events", 1, len(events)) - testutil.Equal(t, "event 0", genesisBlock.Transactions[0], events[0].Data.(wallet.EventV1Transaction).Transaction) + testutil.CheckTransaction(t, genesisBlock.Transactions[0], events[0].Data.(explorer.EventV1Transaction).Transaction) } } diff --git a/persist/sqlite/events.go b/persist/sqlite/events.go index 9a7a967..6971287 100644 --- a/persist/sqlite/events.go +++ b/persist/sqlite/events.go @@ -4,7 +4,6 @@ import ( "database/sql" "errors" "fmt" - "reflect" "go.sia.tech/core/types" "go.sia.tech/coreutils/wallet" @@ -62,126 +61,6 @@ WHERE ev.event_id = $1` return } -func explorerToTypesV2Resolution(e explorer.V2FileContractResolution) (fcr types.V2FileContractResolution) { - fcr.Parent = e.Parent.V2FileContractElement - - switch v := e.Resolution.(type) { - case *explorer.V2FileContractRenewal: - fcr.Resolution = &types.V2FileContractRenewal{ - FinalRenterOutput: v.FinalRenterOutput, - FinalHostOutput: v.FinalHostOutput, - RenterRollover: v.RenterRollover, - HostRollover: v.HostRollover, - NewContract: v.NewContract.V2FileContractElement.V2FileContract, - RenterSignature: v.RenterSignature, - HostSignature: v.HostSignature, - } - case *types.V2StorageProof: - fcr.Resolution = v - case *types.V2FileContractExpiration: - fcr.Resolution = v - default: - panic(fmt.Errorf("unexpected revision type: %v", reflect.TypeOf(v))) - } - return -} - -func explorerToEventV1Transaction(e explorer.Transaction) (ev wallet.EventV1Transaction) { - extendedFCToTypes := func(fc explorer.ExtendedFileContract) types.FileContract { - result := types.FileContract{ - Filesize: fc.Filesize, - FileMerkleRoot: fc.FileMerkleRoot, - WindowStart: fc.WindowStart, - WindowEnd: fc.WindowEnd, - Payout: fc.Payout, - UnlockHash: fc.UnlockHash, - RevisionNumber: fc.RevisionNumber, - } - for _, vpo := range fc.ValidProofOutputs { - result.ValidProofOutputs = append(result.ValidProofOutputs, vpo.SiacoinOutput) - } - for _, mpo := range fc.MissedProofOutputs { - result.MissedProofOutputs = append(result.MissedProofOutputs, mpo.SiacoinOutput) - } - return result - } - - txn := &ev.Transaction - for _, sci := range e.SiacoinInputs { - txn.SiacoinInputs = append(txn.SiacoinInputs, sci.SiacoinInput) - } - for _, sco := range e.SiacoinOutputs { - txn.SiacoinOutputs = append(txn.SiacoinOutputs, sco.SiacoinOutput) - } - for _, sfi := range e.SiafundInputs { - txn.SiafundInputs = append(txn.SiafundInputs, sfi.SiafundInput) - } - for _, sfo := range e.SiafundOutputs { - txn.SiafundOutputs = append(txn.SiafundOutputs, sfo.SiafundOutput) - } - for _, fc := range e.FileContracts { - txn.FileContracts = append(txn.FileContracts, extendedFCToTypes(fc)) - } - for _, fcr := range e.FileContractRevisions { - txn.FileContractRevisions = append(txn.FileContractRevisions, types.FileContractRevision{ - ParentID: fcr.ParentID, - UnlockConditions: fcr.UnlockConditions, - FileContract: extendedFCToTypes(fcr.ExtendedFileContract), - }) - } - for _, sp := range e.StorageProofs { - txn.StorageProofs = append(txn.StorageProofs, sp) - } - for _, fee := range e.MinerFees { - txn.MinerFees = append(txn.MinerFees, fee) - } - for _, arb := range e.ArbitraryData { - txn.ArbitraryData = append(txn.ArbitraryData, arb) - } - for _, sig := range e.Signatures { - txn.Signatures = append(txn.Signatures, sig) - } - - return -} - -func explorerToEventV2Transaction(e explorer.V2Transaction) (txn wallet.EventV2Transaction) { - for _, sci := range e.SiacoinInputs { - txn.SiacoinInputs = append(txn.SiacoinInputs, sci) - } - for _, sco := range e.SiacoinOutputs { - txn.SiacoinOutputs = append(txn.SiacoinOutputs, sco.SiacoinOutput) - } - for _, sfi := range e.SiafundInputs { - txn.SiafundInputs = append(txn.SiafundInputs, sfi) - } - for _, sfo := range e.SiafundOutputs { - txn.SiafundOutputs = append(txn.SiafundOutputs, sfo.SiafundOutput) - } - for _, fc := range e.FileContracts { - txn.FileContracts = append(txn.FileContracts, fc.V2FileContractElement.V2FileContract) - } - for _, fcr := range e.FileContractRevisions { - txn.FileContractRevisions = append(txn.FileContractRevisions, types.V2FileContractRevision{ - Parent: fcr.Parent.V2FileContractElement, - Revision: fcr.Revision.V2FileContractElement.V2FileContract, - }) - } - for _, fcr := range e.FileContractResolutions { - txn.FileContractResolutions = append(txn.FileContractResolutions, explorerToTypesV2Resolution(fcr)) - } - for _, a := range e.Attestations { - txn.Attestations = append(txn.Attestations, a) - } - for _, arb := range e.ArbitraryData { - txn.ArbitraryData = append(txn.ArbitraryData, arb) - } - txn.NewFoundationAddress = e.NewFoundationAddress - txn.MinerFee = e.MinerFee - - return -} - func scanEvent(tx *txn, s scanner) (ev explorer.Event, eventID int64, err error) { err = s.Scan(&eventID, decode(&ev.ID), &ev.MaturityHeight, decode(&ev.Timestamp), &ev.Index.Height, decode(&ev.Index.ID), &ev.Confirmations, &ev.Type) if err != nil { @@ -199,27 +78,29 @@ func scanEvent(tx *txn, s scanner) (ev explorer.Event, eventID int64, err error) if err != nil || len(txns) == 0 { return explorer.Event{}, 0, fmt.Errorf("failed to fetch v1 transaction: %w", err) } - ev.Data = explorerToEventV1Transaction(txns[0]) + ev.Data = explorer.EventV1Transaction{ + Transaction: txns[0], + } case wallet.EventTypeV2Transaction: txns, err := getV2Transactions(tx, []types.TransactionID{types.TransactionID(ev.ID)}) if err != nil || len(txns) == 0 { return explorer.Event{}, 0, fmt.Errorf("failed to fetch v2 transaction: %w", err) } - ev.Data = explorerToEventV2Transaction(txns[0]) + ev.Data = explorer.EventV2Transaction(txns[0]) case wallet.EventTypeV1ContractResolution: - var resolution wallet.EventV1ContractResolution + var resolution explorer.EventV1ContractResolution fce, sce := &resolution.Parent, &resolution.SiacoinElement - err := tx.QueryRow(`SELECT sce.output_id, sce.leaf_index, sce.maturity_height, sce.address, sce.value, fce.contract_id, fce.leaf_index, fce.filesize, fce.file_merkle_root, fce.window_start, fce.window_end, fce.payout, fce.unlock_hash, fce.revision_number, ev.missed + err := tx.QueryRow(`SELECT sce.output_id, sce.leaf_index, sce.maturity_height, sce.address, sce.value, fce.contract_id, fce.filesize, fce.file_merkle_root, fce.window_start, fce.window_end, fce.payout, fce.unlock_hash, fce.revision_number, ev.missed FROM v1_contract_resolution_events ev JOIN siacoin_elements sce ON ev.output_id = sce.id JOIN file_contract_elements fce ON ev.parent_id = fce.id - WHERE ev.event_id = ?`, eventID).Scan(decode(&sce.ID), decode(&sce.StateElement.LeafIndex), decode(&sce.MaturityHeight), decode(&sce.SiacoinOutput.Address), decode(&sce.SiacoinOutput.Value), decode(&fce.ID), decode(&fce.StateElement.LeafIndex), decode(&fce.FileContract.Filesize), decode(&fce.FileContract.FileMerkleRoot), decode(&fce.FileContract.WindowStart), decode(&fce.FileContract.WindowEnd), decode(&fce.FileContract.Payout), decode(&fce.FileContract.UnlockHash), decode(&fce.FileContract.RevisionNumber), &resolution.Missed) + WHERE ev.event_id = ?`, eventID).Scan(decode(&sce.ID), decode(&sce.StateElement.LeafIndex), decode(&sce.MaturityHeight), decode(&sce.SiacoinOutput.Address), decode(&sce.SiacoinOutput.Value), decode(&fce.ID), decode(&fce.Filesize), decode(&fce.FileMerkleRoot), decode(&fce.WindowStart), decode(&fce.WindowEnd), decode(&fce.Payout), decode(&fce.UnlockHash), decode(&fce.RevisionNumber), &resolution.Missed) if err != nil { - return wallet.Event{}, 0, fmt.Errorf("failed to retrieve v1 resolution event: %w", err) + return explorer.Event{}, 0, fmt.Errorf("failed to retrieve v1 resolution event: %w", err) } ev.Data = resolution case wallet.EventTypeV2ContractResolution: - var resolution wallet.EventV2ContractResolution + var resolution explorer.EventV2ContractResolution var parentContractID types.FileContractID var resolutionTransactionID types.TransactionID sce := &resolution.SiacoinElement @@ -230,14 +111,14 @@ func scanEvent(tx *txn, s scanner) (ev explorer.Event, eventID int64, err error) JOIN v2_last_contract_revision rev ON fce.contract_id = rev.contract_id WHERE ev.event_id = ?`, eventID).Scan(decode(&sce.ID), decode(&sce.StateElement.LeafIndex), decode(&sce.MaturityHeight), decode(&sce.SiacoinOutput.Address), decode(&sce.SiacoinOutput.Value), decode(&parentContractID), decode(&resolutionTransactionID), &resolution.Missed) if err != nil { - return wallet.Event{}, 0, fmt.Errorf("failed to retrieve v2 resolution event: %w", err) + return explorer.Event{}, 0, fmt.Errorf("failed to retrieve v2 resolution event: %w", err) } resolutionTxns, err := getV2Transactions(tx, []types.TransactionID{resolutionTransactionID}) if err != nil { - return wallet.Event{}, 0, fmt.Errorf("failed to get transaction with v2 resolution: %w", err) + return explorer.Event{}, 0, fmt.Errorf("failed to get transaction with v2 resolution: %w", err) } else if len(resolutionTxns) == 0 { - return wallet.Event{}, 0, fmt.Errorf("v2 resolution transaction not found") + return explorer.Event{}, 0, fmt.Errorf("v2 resolution transaction not found") } txn := resolutionTxns[0] @@ -245,28 +126,28 @@ func scanEvent(tx *txn, s scanner) (ev explorer.Event, eventID int64, err error) for _, fcr := range txn.FileContractResolutions { if fcr.Parent.ID == parentContractID { found = true - resolution.Resolution = explorerToTypesV2Resolution(fcr) + resolution.Resolution = fcr break } } if !found { - return wallet.Event{}, 0, fmt.Errorf("failed to find resolution in v2 resolution transaction") + return explorer.Event{}, 0, fmt.Errorf("failed to find resolution in v2 resolution transaction") } ev.Data = resolution case wallet.EventTypeSiafundClaim, wallet.EventTypeMinerPayout, wallet.EventTypeFoundationSubsidy: - var payout wallet.EventPayout + var payout explorer.EventPayout sce := &payout.SiacoinElement err := tx.QueryRow(`SELECT sce.output_id, sce.leaf_index, sce.maturity_height, sce.address, sce.value FROM payout_events ev JOIN siacoin_elements sce ON ev.output_id = sce.id WHERE ev.event_id = ?`, eventID).Scan(decode(&sce.ID), decode(&sce.StateElement.LeafIndex), decode(&sce.MaturityHeight), decode(&sce.SiacoinOutput.Address), decode(&sce.SiacoinOutput.Value)) if err != nil { - return wallet.Event{}, 0, fmt.Errorf("failed to retrieve payout event: %w", err) + return explorer.Event{}, 0, fmt.Errorf("failed to retrieve payout event: %w", err) } ev.Data = payout default: - return wallet.Event{}, 0, fmt.Errorf("unknown event type: %q", ev.Type) + return explorer.Event{}, 0, fmt.Errorf("unknown event type: %q", ev.Type) } return diff --git a/persist/sqlite/v2consensus_test.go b/persist/sqlite/v2consensus_test.go index 01f0d3f..e1839a0 100644 --- a/persist/sqlite/v2consensus_test.go +++ b/persist/sqlite/v2consensus_test.go @@ -10,7 +10,6 @@ import ( rhp2 "go.sia.tech/core/rhp/v2" "go.sia.tech/core/types" "go.sia.tech/coreutils/chain" - "go.sia.tech/coreutils/wallet" "go.sia.tech/explored/explorer" "go.sia.tech/explored/internal/testutil" ) @@ -243,6 +242,18 @@ func TestV2FoundationAddress(t *testing.T) { } testutil.CheckV2Transaction(t, txn1, dbTxns[0]) } + + { + events, err := db.AddressEvents(addr1, 0, math.MaxInt64) + if err != nil { + t.Fatal(err) + } + testutil.Equal(t, "events", 3, len(events)) + + testutil.Equal(t, "event 0 type", "foundation", events[0].Type) + testutil.Equal(t, "event 1 type", "v2Transaction", events[1].Type) + testutil.Equal(t, "event 2 type", "v1Transaction", events[2].Type) + } } func TestV2Attestations(t *testing.T) { @@ -298,8 +309,8 @@ func TestV2Attestations(t *testing.T) { } testutil.Equal(t, "events", 2, len(events)) - testutil.Equal(t, "event 0", txn1, types.V2Transaction(events[0].Data.(wallet.EventV2Transaction))) - testutil.Equal(t, "event 1", genesisBlock.Transactions[0], events[1].Data.(wallet.EventV1Transaction).Transaction) + testutil.CheckV2Transaction(t, txn1, explorer.V2Transaction(events[0].Data.(explorer.EventV2Transaction))) + testutil.CheckTransaction(t, genesisBlock.Transactions[0], events[1].Data.(explorer.EventV1Transaction).Transaction) } { @@ -1174,23 +1185,41 @@ func TestV2FileContractResolution(t *testing.T) { } testutil.Equal(t, "events", 3, len(events)) - ev0 := events[0].Data.(wallet.EventV2ContractResolution) + ev0 := events[0].Data.(explorer.EventV2ContractResolution) testutil.Equal(t, "event 0 parent ID", v2FC3ID, ev0.Resolution.Parent.ID) testutil.Equal(t, "event 0 output ID", v2FC3ID.V2RenterOutputID(), ev0.SiacoinElement.ID) testutil.Equal(t, "event 0 missed", true, ev0.Missed) - testutil.Equal(t, "event 0 resolution", txn4.FileContractResolutions[0], ev0.Resolution) - - ev1 := events[1].Data.(wallet.EventV2ContractResolution) - testutil.Equal(t, "event 0 parent ID", v2FC2ID, ev1.Resolution.Parent.ID) - testutil.Equal(t, "event 0 output ID", v2FC2ID.V2RenterOutputID(), ev1.SiacoinElement.ID) - testutil.Equal(t, "event 0 missed", false, ev1.Missed) - testutil.Equal(t, "event 0 resolution", txn3.FileContractResolutions[0], ev1.Resolution) - - ev2 := events[2].Data.(wallet.EventV2ContractResolution) - testutil.Equal(t, "event 0 parent ID", v2FC1ID, ev2.Resolution.Parent.ID) - testutil.Equal(t, "event 0 output ID", v2FC1ID.V2RenterOutputID(), ev2.SiacoinElement.ID) - testutil.Equal(t, "event 0 missed", false, ev2.Missed) - testutil.Equal(t, "event 0 resolution", txn2.FileContractResolutions[0], ev2.Resolution) + { + dbTxns, err := db.V2Transactions([]types.TransactionID{txn4.ID()}) + if err != nil { + t.Fatal(err) + } + testutil.Equal(t, "event 0 resolution", dbTxns[0].FileContractResolutions[0], ev0.Resolution) + } + + ev1 := events[1].Data.(explorer.EventV2ContractResolution) + testutil.Equal(t, "event 1 parent ID", v2FC2ID, ev1.Resolution.Parent.ID) + testutil.Equal(t, "event 1 output ID", v2FC2ID.V2RenterOutputID(), ev1.SiacoinElement.ID) + testutil.Equal(t, "event 1 missed", false, ev1.Missed) + { + dbTxns, err := db.V2Transactions([]types.TransactionID{txn3.ID()}) + if err != nil { + t.Fatal(err) + } + testutil.Equal(t, "event 1 resolution", dbTxns[0].FileContractResolutions[0], ev1.Resolution) + } + + ev2 := events[2].Data.(explorer.EventV2ContractResolution) + testutil.Equal(t, "event 2 parent ID", v2FC1ID, ev2.Resolution.Parent.ID) + testutil.Equal(t, "event 2 output ID", v2FC1ID.V2RenterOutputID(), ev2.SiacoinElement.ID) + testutil.Equal(t, "event 2 missed", false, ev2.Missed) + { + dbTxns, err := db.V2Transactions([]types.TransactionID{txn2.ID()}) + if err != nil { + t.Fatal(err) + } + testutil.Equal(t, "event 2 resolution", dbTxns[0].FileContractResolutions[0], ev2.Resolution) + } } // revert the block