diff --git a/explorer/types.go b/explorer/types.go index 58352b5..a78969f 100644 --- a/explorer/types.go +++ b/explorer/types.go @@ -139,8 +139,8 @@ type ExtendedFileContract struct { TransactionID types.TransactionID `json:"transactionID"` - ConfirmationIndex *types.ChainIndex `json:"confirmationIndex"` - ConfirmationTransactionID *types.TransactionID `json:"confirmationTransactionID"` + ConfirmationIndex types.ChainIndex `json:"confirmationIndex"` + ConfirmationTransactionID types.TransactionID `json:"confirmationTransactionID"` ProofIndex *types.ChainIndex `json:"proofIndex"` ProofTransactionID *types.TransactionID `json:"proofTransactionID"` @@ -188,8 +188,8 @@ type Transaction struct { type V2FileContract struct { TransactionID types.TransactionID `json:"transactionID"` - ConfirmationIndex *types.ChainIndex `json:"confirmationIndex"` - ConfirmationTransactionID *types.TransactionID `json:"confirmationTransactionID"` + ConfirmationIndex types.ChainIndex `json:"confirmationIndex"` + ConfirmationTransactionID types.TransactionID `json:"confirmationTransactionID"` ResolutionIndex *types.ChainIndex `json:"resolutionIndex"` ResolutionTransactionID *types.TransactionID `json:"resolutionTransactionID"` diff --git a/explorer/update.go b/explorer/update.go index cd5e6a7..a5800c8 100644 --- a/explorer/update.go +++ b/explorer/update.go @@ -180,7 +180,7 @@ func applyChainUpdate(tx UpdateTx, cau chain.ApplyUpdate) error { for _, txn := range cau.Block.V2Transactions() { txnID := txn.ID() for i := range txn.FileContracts { - fcID := txn.V2FileContractID(txn.ID(), i) + fcID := txn.V2FileContractID(txnID, i) v := v2FceMap[fcID] v.ConfirmationTransactionID = &txnID @@ -192,6 +192,13 @@ func applyChainUpdate(tx UpdateTx, cau chain.ApplyUpdate) error { v := v2FceMap[fcID] v.ResolutionTransactionID = &txnID v2FceMap[fcID] = v + + if _, ok := fcr.Resolution.(*types.V2FileContractRenewal); ok { + renewalID := fcID.V2RenewalID() + v := v2FceMap[renewalID] + v.ConfirmationTransactionID = &txnID + v2FceMap[renewalID] = v + } } } diff --git a/persist/sqlite/consensus.go b/persist/sqlite/consensus.go index b4f3987..ffadc30 100644 --- a/persist/sqlite/consensus.go +++ b/persist/sqlite/consensus.go @@ -747,7 +747,7 @@ func deleteBlock(tx *txn, bid types.BlockID) error { return err } -func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []explorer.FileContractUpdate) (map[explorer.DBFileContract]int64, error) { +func updateFileContractElements(tx *txn, revert bool, index types.ChainIndex, b types.Block, fces []explorer.FileContractUpdate) (map[explorer.DBFileContract]int64, error) { stmt, err := tx.Prepare(`INSERT INTO file_contract_elements(contract_id, block_id, transaction_id, leaf_index, resolved, valid, filesize, file_merkle_root, window_start, window_end, payout, unlock_hash, revision_number) VALUES (?, ?, ?, ?, FALSE, FALSE, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT (contract_id, revision_number) @@ -758,10 +758,10 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl } defer stmt.Close() - revisionStmt, err := tx.Prepare(`INSERT INTO last_contract_revision(contract_id, contract_element_id, ed25519_renter_key, ed25519_host_key) - VALUES (?, ?, ?, ?) + revisionStmt, err := tx.Prepare(`INSERT INTO last_contract_revision(contract_id, contract_element_id, ed25519_renter_key, ed25519_host_key, confirmation_index, confirmation_transaction_id) + VALUES (?, ?, ?, ?, COALESCE(?, X'aa'), COALESCE(?, X'aa')) ON CONFLICT (contract_id) - DO UPDATE SET contract_element_id = ?, ed25519_renter_key = COALESCE(?, ed25519_renter_key), ed25519_host_key = COALESCE(?, ed25519_host_key)`) + DO UPDATE SET contract_element_id = ?, ed25519_renter_key = COALESCE(?, ed25519_renter_key), ed25519_host_key = COALESCE(?, ed25519_host_key), confirmation_index = COALESCE(?, confirmation_index), confirmation_transaction_id = COALESCE(?, confirmation_transaction_id)`) if err != nil { return nil, fmt.Errorf("updateFileContractElements: failed to prepare last_contract_revision statement: %w", err) } @@ -829,10 +829,10 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl } fcDBIds := make(map[explorer.DBFileContract]int64) - addFC := func(fcID types.FileContractID, leafIndex uint64, fc types.FileContract, resolved, valid, lastRevision bool) error { + addFC := func(fcID types.FileContractID, leafIndex uint64, fc types.FileContract, confirmationTransactionID *types.TransactionID, resolved, valid, lastRevision bool) error { var dbID int64 dbFC := explorer.DBFileContract{ID: fcID, RevisionNumber: fc.RevisionNumber} - err := stmt.QueryRow(encode(fcID), encode(b.ID()), encode(fcTxns[dbFC]), encode(leafIndex), encode(fc.Filesize), encode(fc.FileMerkleRoot), encode(fc.WindowStart), encode(fc.WindowEnd), encode(fc.Payout), encode(fc.UnlockHash), encode(fc.RevisionNumber), resolved, valid, encode(leafIndex)).Scan(&dbID) + err := stmt.QueryRow(encode(fcID), encode(index.ID), encode(fcTxns[dbFC]), encode(leafIndex), encode(fc.Filesize), encode(fc.FileMerkleRoot), encode(fc.WindowStart), encode(fc.WindowEnd), encode(fc.Payout), encode(fc.UnlockHash), encode(fc.RevisionNumber), resolved, valid, encode(leafIndex)).Scan(&dbID) if err != nil { return fmt.Errorf("failed to execute file_contract_elements statement: %w", err) } @@ -851,13 +851,20 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl // only update if it's the most recent revision which will come from // running ForEachFileContractElement on the update if lastRevision { - var renterKey, hostKey []byte + var encodedRenterKey, encodedHostKey []byte if keys, ok := fcKeys[dbFC]; ok { - renterKey = encode(keys[0]).([]byte) - hostKey = encode(keys[1]).([]byte) + encodedRenterKey = encode(keys[0]).([]byte) + encodedHostKey = encode(keys[1]).([]byte) + } + + var encodedChainIndex []byte + var encodedConfirmationTransactionID []byte + if confirmationTransactionID != nil { + encodedChainIndex = encode(index).([]byte) + encodedConfirmationTransactionID = encode(*confirmationTransactionID).([]byte) } - if _, err := revisionStmt.Exec(encode(fcID), dbID, renterKey, hostKey, dbID, renterKey, hostKey); err != nil { + if _, err := revisionStmt.Exec(encode(fcID), dbID, encodedRenterKey, encodedHostKey, encodedChainIndex, encodedConfirmationTransactionID, dbID, encodedRenterKey, encodedHostKey, encodedChainIndex, encodedConfirmationTransactionID); err != nil { return fmt.Errorf("failed to update last revision number: %w", err) } } @@ -897,6 +904,7 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl fce.ID, fce.StateElement.LeafIndex, fce.FileContract, + update.ConfirmationTransactionID, update.Resolved, update.Valid, true, @@ -919,7 +927,7 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl continue } - if err := addFC(fcID, 0, fc, false, false, false); err != nil { + if err := addFC(fcID, 0, fc, nil, false, false, false); err != nil { return nil, fmt.Errorf("updateFileContractElements: %w", err) } } @@ -932,7 +940,7 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl continue } - if err := addFC(fcr.ParentID, 0, fc, false, false, false); err != nil { + if err := addFC(fcr.ParentID, 0, fc, nil, false, false, false); err != nil { return nil, fmt.Errorf("updateFileContractElements: %w", err) } } @@ -942,12 +950,6 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl } func updateFileContractIndices(tx *txn, revert bool, index types.ChainIndex, fces []explorer.FileContractUpdate) error { - confirmationIndexStmt, err := tx.Prepare(`UPDATE last_contract_revision SET confirmation_index = ?, confirmation_transaction_id = ? WHERE contract_id = ?`) - if err != nil { - return fmt.Errorf("updateFileContractIndices: failed to prepare confirmation index statement: %w", err) - } - defer confirmationIndexStmt.Close() - proofIndexStmt, err := tx.Prepare(`UPDATE last_contract_revision SET proof_index = ?, proof_transaction_id = ? WHERE contract_id = ?`) if err != nil { return fmt.Errorf("updateFileContractIndices: failed to prepare proof index statement: %w", err) @@ -959,22 +961,12 @@ func updateFileContractIndices(tx *txn, revert bool, index types.ChainIndex, fce fcID := update.FileContractElement.ID if revert { - if update.ConfirmationTransactionID != nil { - if _, err := confirmationIndexStmt.Exec(nil, nil, encode(fcID)); err != nil { - return fmt.Errorf("updateFileContractIndices: failed to update confirmation index: %w", err) - } - } if update.ProofTransactionID != nil { if _, err := proofIndexStmt.Exec(nil, nil, encode(fcID)); err != nil { return fmt.Errorf("updateFileContractIndices: failed to update proof index: %w", err) } } } else { - if update.ConfirmationTransactionID != nil { - if _, err := confirmationIndexStmt.Exec(encode(index), encode(update.ConfirmationTransactionID), encode(fcID)); err != nil { - return fmt.Errorf("updateFileContractIndices: failed to update confirmation index: %w", err) - } - } if update.ProofTransactionID != nil { if _, err := proofIndexStmt.Exec(encode(index), encode(update.ProofTransactionID), encode(fcID)); err != nil { return fmt.Errorf("updateFileContractIndices: failed to update proof index: %w", err) @@ -1052,12 +1044,12 @@ func (ut *updateTx) ApplyIndex(state explorer.UpdateState) error { if err != nil { return fmt.Errorf("ApplyIndex: failed to add siafund outputs: %w", err) } - fcDBIds, err := updateFileContractElements(ut.tx, false, state.Block, state.FileContractElements) + fcDBIds, err := updateFileContractElements(ut.tx, false, state.Metrics.Index, state.Block, state.FileContractElements) if err != nil { return fmt.Errorf("ApplyIndex: failed to add file contracts: %w", err) } - v2FcDBIds, err := updateV2FileContractElements(ut.tx, false, state.Block, state.V2FileContractElements) + v2FcDBIds, err := updateV2FileContractElements(ut.tx, false, state.Metrics.Index, state.Block, state.V2FileContractElements) if err != nil { return fmt.Errorf("ApplyIndex: failed to add v2 file contracts: %w", err) } @@ -1104,9 +1096,9 @@ func (ut *updateTx) RevertIndex(state explorer.UpdateState) error { return fmt.Errorf("RevertIndex: failed to update siafund output state: %w", err) } else if err := updateBalances(ut.tx, state.Metrics.Index.Height, state.SpentSiacoinElements, state.NewSiacoinElements, state.SpentSiafundElements, state.NewSiafundElements); err != nil { return fmt.Errorf("RevertIndex: failed to update balances: %w", err) - } else if _, err := updateFileContractElements(ut.tx, true, state.Block, state.FileContractElements); err != nil { + } else if _, err := updateFileContractElements(ut.tx, true, state.Metrics.Index, state.Block, state.FileContractElements); err != nil { return fmt.Errorf("RevertIndex: failed to update file contract state: %w", err) - } else if _, err := updateV2FileContractElements(ut.tx, true, state.Block, state.V2FileContractElements); err != nil { + } else if _, err := updateV2FileContractElements(ut.tx, true, state.Metrics.Index, state.Block, state.V2FileContractElements); err != nil { return fmt.Errorf("ApplyIndex: failed to add v2 file contracts: %w", err) } else if err := deleteBlock(ut.tx, state.Block.ID()); err != nil { return fmt.Errorf("RevertIndex: failed to delete block: %w", err) diff --git a/persist/sqlite/consensus_test.go b/persist/sqlite/consensus_test.go index 6d56046..f5dffb7 100644 --- a/persist/sqlite/consensus_test.go +++ b/persist/sqlite/consensus_test.go @@ -19,6 +19,8 @@ import ( ) func syncDB(t *testing.T, db explorer.Store, cm *chain.Manager) { + t.Helper() + index, err := db.Tip() if err != nil && !errors.Is(err, explorer.ErrNoTip) { t.Fatal(err) @@ -45,6 +47,7 @@ func syncDB(t *testing.T, db explorer.Store, cm *chain.Manager) { func newStore(t *testing.T, v2 bool, f func(*consensus.Network, types.Block)) (*consensus.Network, types.Block, *chain.Manager, explorer.Store) { log := zaptest.NewLogger(t) dir := t.TempDir() + db, err := sqlite.OpenDatabase(filepath.Join(dir, "explored.sqlite3"), log.Named("sqlite3")) if err != nil { t.Fatal(err) @@ -128,8 +131,8 @@ func CheckFCRevisions(t *testing.T, confirmationIndex types.ChainIndex, confirma testutil.Equal(t, "number of revisions", len(revisionNumbers), len(fcs)) for i := range revisionNumbers { testutil.Equal(t, "revision number", revisionNumbers[i], fcs[i].RevisionNumber) - testutil.Equal(t, "confirmation index", confirmationIndex, *fcs[i].ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", confirmationTransactionID, *fcs[i].ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", confirmationIndex, fcs[i].ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", confirmationTransactionID, fcs[i].ConfirmationTransactionID) testutil.Equal(t, "valid proof outputs", len(valid), len(fcs[i].ValidProofOutputs)) for j := range valid { @@ -542,8 +545,8 @@ func TestFileContract(t *testing.T) { testutil.Equal(t, "fcs", 1, len(dbFCs)) testutil.CheckFC(t, false, false, false, fc, dbFCs[0]) testutil.Equal(t, "transaction ID", txn.ID(), dbFCs[0].TransactionID) - testutil.Equal(t, "confirmation index", cm.Tip(), *dbFCs[0].ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", txn.ID(), *dbFCs[0].ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", cm.Tip(), dbFCs[0].ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn.ID(), dbFCs[0].ConfirmationTransactionID) } { @@ -564,8 +567,8 @@ func TestFileContract(t *testing.T) { testutil.CheckFC(t, false, false, false, fc, txns[0].FileContracts[0]) testutil.Equal(t, "transaction ID", txn.ID(), txns[0].FileContracts[0].TransactionID) - testutil.Equal(t, "confirmation index", cm.Tip(), *txns[0].FileContracts[0].ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", txn.ID(), *txns[0].FileContracts[0].ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", cm.Tip(), txns[0].FileContracts[0].ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn.ID(), txns[0].FileContracts[0].ConfirmationTransactionID) } uc := types.UnlockConditions{ @@ -607,10 +610,10 @@ func TestFileContract(t *testing.T) { testutil.Equal(t, "transaction ID", reviseTxn.ID(), renterContracts[0].TransactionID) testutil.Equal(t, "transaction ID", reviseTxn.ID(), hostContracts[0].TransactionID) - testutil.Equal(t, "confirmation index", prevTip, *renterContracts[0].ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", txn.ID(), *renterContracts[0].ConfirmationTransactionID) - testutil.Equal(t, "confirmation index", prevTip, *hostContracts[0].ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", txn.ID(), *hostContracts[0].ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", prevTip, renterContracts[0].ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn.ID(), renterContracts[0].ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", prevTip, hostContracts[0].ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn.ID(), hostContracts[0].ConfirmationTransactionID) } CheckMetrics(t, db, cm, explorer.Metrics{ @@ -649,8 +652,8 @@ func TestFileContract(t *testing.T) { testutil.Equal(t, "parent id", txn.FileContractID(0), fcr.ParentID) testutil.Equal(t, "unlock conditions", uc, fcr.UnlockConditions) - testutil.Equal(t, "confirmation index", prevTip, *fcr.ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", txn.ID(), *fcr.ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", prevTip, fcr.ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn.ID(), fcr.ConfirmationTransactionID) testutil.CheckFC(t, false, false, false, fc, fcr.ExtendedFileContract) } @@ -684,8 +687,8 @@ func TestFileContract(t *testing.T) { testutil.Equal(t, "fcs", 1, len(dbFCs)) testutil.CheckFC(t, false, true, false, fc, dbFCs[0]) - testutil.Equal(t, "confirmation index", prevTip, *dbFCs[0].ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", txn.ID(), *dbFCs[0].ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", prevTip, dbFCs[0].ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn.ID(), dbFCs[0].ConfirmationTransactionID) } for i := 0; i < 100; i++ { @@ -711,10 +714,10 @@ func TestFileContract(t *testing.T) { testutil.Equal(t, "transaction ID", reviseTxn.ID(), renterContracts[0].TransactionID) testutil.Equal(t, "transaction ID", reviseTxn.ID(), hostContracts[0].TransactionID) - testutil.Equal(t, "confirmation index", prevTip, *renterContracts[0].ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", txn.ID(), *renterContracts[0].ConfirmationTransactionID) - testutil.Equal(t, "confirmation index", prevTip, *hostContracts[0].ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", txn.ID(), *hostContracts[0].ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", prevTip, renterContracts[0].ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn.ID(), renterContracts[0].ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", prevTip, hostContracts[0].ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn.ID(), hostContracts[0].ConfirmationTransactionID) } CheckMetrics(t, db, cm, explorer.Metrics{ @@ -838,8 +841,8 @@ func TestEphemeralFileContract(t *testing.T) { testutil.CheckFC(t, true, false, false, fc, txns[0].FileContracts[0]) testutil.Equal(t, "transaction ID", txn.ID(), txns[0].FileContracts[0].TransactionID) - testutil.Equal(t, "confirmation index", cm.Tip(), *txns[0].FileContracts[0].ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", txn.ID(), *txns[0].FileContracts[0].ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", cm.Tip(), txns[0].FileContracts[0].ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn.ID(), txns[0].FileContracts[0].ConfirmationTransactionID) } { @@ -2082,8 +2085,8 @@ func TestMultipleReorgFileContract(t *testing.T) { testutil.Equal(t, "fcs", 1, len(dbFCs)) testutil.CheckFC(t, false, false, false, fc, dbFCs[0]) - testutil.Equal(t, "confirmation index", cm.Tip(), *dbFCs[0].ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", txn.ID(), *dbFCs[0].ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", cm.Tip(), dbFCs[0].ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn.ID(), dbFCs[0].ConfirmationTransactionID) } { @@ -2148,8 +2151,8 @@ func TestMultipleReorgFileContract(t *testing.T) { testutil.CheckFC(t, false, false, false, revFC, dbFCs[0]) testutil.Equal(t, "transaction ID", reviseTxn.ID(), dbFCs[0].TransactionID) - testutil.Equal(t, "confirmation index", prevState1.Index, *dbFCs[0].ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", txn.ID(), *dbFCs[0].ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", prevState1.Index, dbFCs[0].ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn.ID(), dbFCs[0].ConfirmationTransactionID) } { @@ -2222,8 +2225,8 @@ func TestMultipleReorgFileContract(t *testing.T) { testutil.CheckFC(t, false, false, false, fc, dbFCs[0]) testutil.Equal(t, "transaction ID", txn.ID(), dbFCs[0].TransactionID) - testutil.Equal(t, "confirmation index", prevState1.Index, *dbFCs[0].ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", txn.ID(), *dbFCs[0].ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", prevState1.Index, dbFCs[0].ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn.ID(), dbFCs[0].ConfirmationTransactionID) } { @@ -2273,8 +2276,8 @@ func TestMultipleReorgFileContract(t *testing.T) { testutil.CheckFC(t, false, false, false, revFC, dbFCs[0]) testutil.Equal(t, "transaction ID", reviseTxn.ID(), dbFCs[0].TransactionID) - testutil.Equal(t, "confirmation index", prevState1.Index, *dbFCs[0].ConfirmationIndex) - testutil.Equal(t, "confirmation transaction ID", txn.ID(), *dbFCs[0].ConfirmationTransactionID) + testutil.Equal(t, "confirmation index", prevState1.Index, dbFCs[0].ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn.ID(), dbFCs[0].ConfirmationTransactionID) } // should have revision filesize diff --git a/persist/sqlite/contracts.go b/persist/sqlite/contracts.go index 7b06b65..036c034 100644 --- a/persist/sqlite/contracts.go +++ b/persist/sqlite/contracts.go @@ -16,16 +16,10 @@ func encodedIDs(ids []types.FileContractID) []any { } func scanFileContract(s scanner) (contractID int64, fc explorer.ExtendedFileContract, err error) { - var confirmationIndex, proofIndex types.ChainIndex - var confirmationTransactionID, proofTransactionID types.TransactionID - err = s.Scan(&contractID, decode(&fc.ID), &fc.Resolved, &fc.Valid, decode(&fc.TransactionID), decodeNull(&confirmationIndex), decodeNull(&confirmationTransactionID), decodeNull(&proofIndex), decodeNull(&proofTransactionID), decode(&fc.Filesize), decode(&fc.FileMerkleRoot), decode(&fc.WindowStart), decode(&fc.WindowEnd), decode(&fc.Payout), decode(&fc.UnlockHash), decode(&fc.RevisionNumber)) + var proofIndex types.ChainIndex + var proofTransactionID types.TransactionID + err = s.Scan(&contractID, decode(&fc.ID), &fc.Resolved, &fc.Valid, decode(&fc.TransactionID), decode(&fc.ConfirmationIndex), decode(&fc.ConfirmationTransactionID), decodeNull(&proofIndex), decodeNull(&proofTransactionID), decode(&fc.Filesize), decode(&fc.FileMerkleRoot), decode(&fc.WindowStart), decode(&fc.WindowEnd), decode(&fc.Payout), decode(&fc.UnlockHash), decode(&fc.RevisionNumber)) - if confirmationIndex != (types.ChainIndex{}) { - fc.ConfirmationIndex = &confirmationIndex - } - if confirmationTransactionID != (types.TransactionID{}) { - fc.ConfirmationTransactionID = &confirmationTransactionID - } if proofIndex != (types.ChainIndex{}) { fc.ProofIndex = &proofIndex } diff --git a/persist/sqlite/init.sql b/persist/sqlite/init.sql index a3fbfb4..4637294 100644 --- a/persist/sqlite/init.sql +++ b/persist/sqlite/init.sql @@ -106,8 +106,8 @@ CREATE TABLE last_contract_revision ( ed25519_renter_key BLOB, ed25519_host_key BLOB, - confirmation_index BLOB, - confirmation_transaction_id BLOB REFERENCES transactions(transaction_id), + confirmation_index BLOB NOT NULL, + confirmation_transaction_id BLOB NOT NULL REFERENCES transactions(transaction_id), proof_index BLOB, proof_transaction_id BLOB REFERENCES transactions(transaction_id), @@ -452,8 +452,8 @@ CREATE INDEX v2_file_contract_elements_contract_id_revision_number_index ON v2_f CREATE TABLE v2_last_contract_revision ( contract_id BLOB PRIMARY KEY NOT NULL, - confirmation_index BLOB, - confirmation_transaction_id BLOB REFERENCES v2_transactions(transaction_id), + confirmation_index BLOB NOT NULL, + confirmation_transaction_id BLOB NOT NULL REFERENCES v2_transactions(transaction_id), resolution_index BLOB, resolution_transaction_id BLOB REFERENCES v2_transactions(transaction_id), diff --git a/persist/sqlite/transactions.go b/persist/sqlite/transactions.go index 4a05b3a..a0499a7 100644 --- a/persist/sqlite/transactions.go +++ b/persist/sqlite/transactions.go @@ -305,19 +305,12 @@ ORDER BY ts.transaction_order ASC` var txnID, contractID int64 var fc explorer.ExtendedFileContract - var confirmationIndex, proofIndex types.ChainIndex - var confirmationTransactionID, proofTransactionID types.TransactionID - if err := rows.Scan(&txnID, &contractID, decodeNull(&confirmationIndex), decodeNull(&confirmationTransactionID), decodeNull(&proofIndex), decodeNull(&proofTransactionID), decode(&fc.ID), &fc.Resolved, &fc.Valid, decode(&fc.TransactionID), decode(&fc.Filesize), decode(&fc.FileMerkleRoot), decode(&fc.WindowStart), decode(&fc.WindowEnd), decode(&fc.Payout), decode(&fc.UnlockHash), decode(&fc.RevisionNumber)); err != nil { + var proofIndex types.ChainIndex + var proofTransactionID types.TransactionID + if err := rows.Scan(&txnID, &contractID, decode(&fc.ConfirmationIndex), decode(&fc.ConfirmationTransactionID), decodeNull(&proofIndex), decodeNull(&proofTransactionID), decode(&fc.ID), &fc.Resolved, &fc.Valid, decode(&fc.TransactionID), decode(&fc.Filesize), decode(&fc.FileMerkleRoot), decode(&fc.WindowStart), decode(&fc.WindowEnd), decode(&fc.Payout), decode(&fc.UnlockHash), decode(&fc.RevisionNumber)); err != nil { return nil, fmt.Errorf("failed to scan file contract: %w", err) } - if confirmationIndex != (types.ChainIndex{}) { - fc.ConfirmationIndex = &confirmationIndex - } - if confirmationTransactionID != (types.TransactionID{}) { - fc.ConfirmationTransactionID = &confirmationTransactionID - } - if proofIndex != (types.ChainIndex{}) { fc.ProofIndex = &proofIndex } @@ -366,19 +359,12 @@ ORDER BY ts.transaction_order ASC` var txnID, contractID int64 var fc explorer.FileContractRevision - var confirmationIndex, proofIndex types.ChainIndex - var confirmationTransactionID, proofTransactionID types.TransactionID - if err := rows.Scan(&txnID, &contractID, decodeNull(&confirmationIndex), decodeNull(&confirmationTransactionID), decodeNull(&proofIndex), decodeNull(&proofTransactionID), decode(&fc.ParentID), decode(&fc.UnlockConditions), decode(&fc.ID), &fc.Resolved, &fc.Valid, decode(&fc.TransactionID), decode(&fc.ExtendedFileContract.Filesize), decode(&fc.ExtendedFileContract.FileMerkleRoot), decode(&fc.ExtendedFileContract.WindowStart), decode(&fc.ExtendedFileContract.WindowEnd), decode(&fc.ExtendedFileContract.Payout), decode(&fc.ExtendedFileContract.UnlockHash), decode(&fc.ExtendedFileContract.RevisionNumber)); err != nil { + var proofIndex types.ChainIndex + var proofTransactionID types.TransactionID + if err := rows.Scan(&txnID, &contractID, decode(&fc.ConfirmationIndex), decode(&fc.ConfirmationTransactionID), decodeNull(&proofIndex), decodeNull(&proofTransactionID), decode(&fc.ParentID), decode(&fc.UnlockConditions), decode(&fc.ID), &fc.Resolved, &fc.Valid, decode(&fc.TransactionID), decode(&fc.ExtendedFileContract.Filesize), decode(&fc.ExtendedFileContract.FileMerkleRoot), decode(&fc.ExtendedFileContract.WindowStart), decode(&fc.ExtendedFileContract.WindowEnd), decode(&fc.ExtendedFileContract.Payout), decode(&fc.ExtendedFileContract.UnlockHash), decode(&fc.ExtendedFileContract.RevisionNumber)); err != nil { return nil, fmt.Errorf("failed to scan file contract: %w", err) } - if confirmationIndex != (types.ChainIndex{}) { - fc.ConfirmationIndex = &confirmationIndex - } - if confirmationTransactionID != (types.TransactionID{}) { - fc.ConfirmationTransactionID = &confirmationTransactionID - } - if proofIndex != (types.ChainIndex{}) { fc.ProofIndex = &proofIndex } diff --git a/persist/sqlite/v2consensus.go b/persist/sqlite/v2consensus.go index 31de318..7006877 100644 --- a/persist/sqlite/v2consensus.go +++ b/persist/sqlite/v2consensus.go @@ -62,7 +62,7 @@ func addV2Transactions(tx *txn, bid types.BlockID, txns []types.V2Transaction) ( return txnDBIds, nil } -func updateV2FileContractElements(tx *txn, revert bool, b types.Block, fces []explorer.V2FileContractUpdate) (map[explorer.DBFileContract]int64, error) { +func updateV2FileContractElements(tx *txn, revert bool, index types.ChainIndex, b types.Block, fces []explorer.V2FileContractUpdate) (map[explorer.DBFileContract]int64, error) { stmt, err := tx.Prepare(`INSERT INTO v2_file_contract_elements(contract_id, block_id, transaction_id, leaf_index, capacity, filesize, file_merkle_root, proof_height, expiration_height, renter_output_address, renter_output_value, host_output_address, host_output_value, missed_host_value, total_collateral, renter_public_key, host_public_key, revision_number, renter_signature, host_signature) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT (contract_id, revision_number) @@ -73,10 +73,10 @@ func updateV2FileContractElements(tx *txn, revert bool, b types.Block, fces []ex } defer stmt.Close() - revisionStmt, err := tx.Prepare(`INSERT INTO v2_last_contract_revision(contract_id, contract_element_id) - VALUES (?, ?) + revisionStmt, err := tx.Prepare(`INSERT INTO v2_last_contract_revision(contract_id, contract_element_id, confirmation_index, confirmation_transaction_id) + VALUES (?, ?, COALESCE(?, X'aa'), COALESCE(?, X'aa')) ON CONFLICT (contract_id) - DO UPDATE SET contract_element_id = ?`) + DO UPDATE SET contract_element_id = ?, confirmation_index = COALESCE(?, confirmation_index), confirmation_transaction_id = COALESCE(?, confirmation_transaction_id)`) if err != nil { return nil, fmt.Errorf("updateV2FileContractElements: failed to prepare last_contract_revision statement: %w", err) } @@ -120,7 +120,7 @@ func updateV2FileContractElements(tx *txn, revert bool, b types.Block, fces []ex } fcDBIds := make(map[explorer.DBFileContract]int64) - addFC := func(fcID types.FileContractID, leafIndex uint64, fc types.V2FileContract, lastRevision bool) error { + addFC := func(fcID types.FileContractID, leafIndex uint64, fc types.V2FileContract, confirmationTransactionID *types.TransactionID, lastRevision bool) error { var dbID int64 dbFC := explorer.DBFileContract{ID: fcID, RevisionNumber: fc.RevisionNumber} err := stmt.QueryRow(encode(fcID), encode(b.ID()), encode(fcTxns[dbFC]), encode(leafIndex), encode(fc.Capacity), encode(fc.Filesize), encode(fc.FileMerkleRoot), encode(fc.ProofHeight), encode(fc.ExpirationHeight), encode(fc.RenterOutput.Address), encode(fc.RenterOutput.Value), encode(fc.HostOutput.Address), encode(fc.HostOutput.Value), encode(fc.MissedHostValue), encode(fc.TotalCollateral), encode(fc.RenterPublicKey), encode(fc.HostPublicKey), encode(fc.RevisionNumber), encode(fc.RenterSignature), encode(fc.HostSignature), encode(leafIndex)).Scan(&dbID) @@ -131,7 +131,14 @@ func updateV2FileContractElements(tx *txn, revert bool, b types.Block, fces []ex // only update if it's the most recent revision which will come from // running ForEachFileContractElement on the update if lastRevision { - if _, err := revisionStmt.Exec(encode(fcID), dbID, dbID); err != nil { + var encodedChainIndex []byte + var encodedConfirmationTransactionID []byte + if confirmationTransactionID != nil { + encodedChainIndex = encode(index).([]byte) + encodedConfirmationTransactionID = encode(*confirmationTransactionID).([]byte) + } + + if _, err := revisionStmt.Exec(encode(fcID), dbID, encodedChainIndex, encodedConfirmationTransactionID, dbID, encodedChainIndex, encodedConfirmationTransactionID); err != nil { return fmt.Errorf("failed to update last revision number: %w", err) } } @@ -173,6 +180,7 @@ func updateV2FileContractElements(tx *txn, revert bool, b types.Block, fces []ex types.FileContractID(fce.ID), fce.StateElement.LeafIndex, fce.V2FileContract, + update.ConfirmationTransactionID, true, ); err != nil { return nil, fmt.Errorf("updateV2FileContractElements: %w", err) @@ -193,7 +201,7 @@ func updateV2FileContractElements(tx *txn, revert bool, b types.Block, fces []ex continue } - if err := addFC(fcID, 0, fc, false); err != nil { + if err := addFC(fcID, 0, fc, nil, false); err != nil { return nil, fmt.Errorf("updateV2FileContractElements: %w", err) } } @@ -207,7 +215,7 @@ func updateV2FileContractElements(tx *txn, revert bool, b types.Block, fces []ex continue } - if err := addFC(fcID, 0, fc, false); err != nil { + if err := addFC(fcID, 0, fc, nil, false); err != nil { return nil, fmt.Errorf("updateV2FileContractElements: %w", err) } } @@ -221,7 +229,7 @@ func updateV2FileContractElements(tx *txn, revert bool, b types.Block, fces []ex // Add final revision, because the ApplyUpdate doesn't tell // us about this case. We set lastRevision to true here // because this is the final revision. - if err := addFC(fcID, fcr.Parent.StateElement.LeafIndex, fc, true); err != nil { + if err := addFC(fcID, fcr.Parent.StateElement.LeafIndex, fc, nil, true); err != nil { return nil, fmt.Errorf("updateV2FileContractElements: failed to add final revision: %w", err) } } @@ -237,7 +245,7 @@ func updateV2FileContractElements(tx *txn, revert bool, b types.Block, fces []ex continue } - if err := addFC(fcID, 0, fc, false); err != nil { + if err := addFC(fcID, 0, fc, nil, false); err != nil { return nil, fmt.Errorf("updateV2FileContractElements: failed to add new contract: %w", err) } } @@ -271,12 +279,6 @@ func updateV2FileContractElements(tx *txn, revert bool, b types.Block, fces []ex } func updateV2FileContractIndices(tx *txn, revert bool, index types.ChainIndex, fces []explorer.V2FileContractUpdate) error { - confirmationIndexStmt, err := tx.Prepare(`UPDATE v2_last_contract_revision SET confirmation_index = ?, confirmation_transaction_id = ? WHERE contract_id = ?`) - if err != nil { - return fmt.Errorf("updateV2FileContractIndices: failed to prepare confirmation index statement: %w", err) - } - defer confirmationIndexStmt.Close() - resolutionIndexStmt, err := tx.Prepare(`UPDATE v2_last_contract_revision SET resolution_index = ?, resolution_transaction_id = ? WHERE contract_id = ?`) if err != nil { return fmt.Errorf("updateV2FileContractIndices: failed to prepare resolution index statement: %w", err) @@ -288,22 +290,12 @@ func updateV2FileContractIndices(tx *txn, revert bool, index types.ChainIndex, f fcID := update.FileContractElement.ID if revert { - if update.ConfirmationTransactionID != nil { - if _, err := confirmationIndexStmt.Exec(nil, nil, encode(fcID)); err != nil { - return fmt.Errorf("updateV2FileContractIndices: failed to update confirmation index: %w", err) - } - } if update.ResolutionTransactionID != nil { if _, err := resolutionIndexStmt.Exec(nil, nil, encode(fcID)); err != nil { return fmt.Errorf("updateV2FileContractIndices: failed to update resolution index: %w", err) } } } else { - if update.ConfirmationTransactionID != nil { - if _, err := confirmationIndexStmt.Exec(encode(index), encode(update.ConfirmationTransactionID), encode(fcID)); err != nil { - return fmt.Errorf("updateV2FileContractIndices: failed to update confirmation index: %w", err) - } - } if update.ResolutionTransactionID != nil { if _, err := resolutionIndexStmt.Exec(encode(index), encode(update.ResolutionTransactionID), encode(fcID)); err != nil { return fmt.Errorf("updateV2FileContractIndices: failed to update resolution index: %w", err) diff --git a/persist/sqlite/v2consensus_test.go b/persist/sqlite/v2consensus_test.go index f44995b..c7a973b 100644 --- a/persist/sqlite/v2consensus_test.go +++ b/persist/sqlite/v2consensus_test.go @@ -1019,6 +1019,8 @@ func TestV2FileContractResolution(t *testing.T) { } syncDB(t, db, cm) + tip1 := cm.Tip() + v2FC0ID := txn1.V2FileContractID(txn1.ID(), 0) v2FC1ID := txn1.V2FileContractID(txn1.ID(), 1) v2FC2ID := txn1.V2FileContractID(txn1.ID(), 2) @@ -1091,8 +1093,14 @@ func TestV2FileContractResolution(t *testing.T) { t.Fatal(err) } testutil.CheckV2Transaction(t, txn2, dbTxns[0]) + + testutil.Equal(t, "confirmation index", tip1, dbTxns[0].FileContractResolutions[0].Parent.ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn1.ID(), dbTxns[0].FileContractResolutions[0].Parent.ConfirmationTransactionID) testutil.Equal(t, "resolution index", cm.Tip(), *dbTxns[0].FileContractResolutions[0].Parent.ResolutionIndex) testutil.Equal(t, "resolution transaction ID", txn2.ID(), *dbTxns[0].FileContractResolutions[0].Parent.ResolutionTransactionID) + + testutil.Equal(t, "confirmation index", tip1, dbTxns[0].FileContractResolutions[1].Parent.ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn1.ID(), dbTxns[0].FileContractResolutions[1].Parent.ConfirmationTransactionID) testutil.Equal(t, "resolution index", cm.Tip(), *dbTxns[0].FileContractResolutions[1].Parent.ResolutionIndex) testutil.Equal(t, "resolution transaction ID", txn2.ID(), *dbTxns[0].FileContractResolutions[1].Parent.ResolutionTransactionID) } @@ -1130,6 +1138,8 @@ func TestV2FileContractResolution(t *testing.T) { t.Fatal(err) } testutil.CheckV2Transaction(t, txn3, dbTxns[0]) + testutil.Equal(t, "confirmation index", tip1, dbTxns[0].FileContractResolutions[0].Parent.ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn1.ID(), dbTxns[0].FileContractResolutions[0].Parent.ConfirmationTransactionID) testutil.Equal(t, "resolution index", cm.Tip(), *dbTxns[0].FileContractResolutions[0].Parent.ResolutionIndex) testutil.Equal(t, "resolution transaction ID", txn3.ID(), *dbTxns[0].FileContractResolutions[0].Parent.ResolutionTransactionID) } @@ -1155,6 +1165,8 @@ func TestV2FileContractResolution(t *testing.T) { t.Fatal(err) } testutil.CheckV2Transaction(t, txn4, dbTxns[0]) + testutil.Equal(t, "confirmation index", tip1, dbTxns[0].FileContractResolutions[0].Parent.ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn1.ID(), dbTxns[0].FileContractResolutions[0].Parent.ConfirmationTransactionID) testutil.Equal(t, "resolution index", cm.Tip(), *dbTxns[0].FileContractResolutions[0].Parent.ResolutionIndex) testutil.Equal(t, "resolution transaction ID", txn4.ID(), *dbTxns[0].FileContractResolutions[0].Parent.ResolutionTransactionID) } @@ -1213,6 +1225,8 @@ func TestV2FileContractResolution(t *testing.T) { t.Fatal(err) } testutil.CheckV2Transaction(t, txn2, dbTxns[0]) + testutil.Equal(t, "confirmation index", tip1, dbTxns[0].FileContractResolutions[0].Parent.ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn1.ID(), dbTxns[0].FileContractResolutions[0].Parent.ConfirmationTransactionID) testutil.Equal(t, "resolution index", cm.Tip(), *dbTxns[0].FileContractResolutions[0].Parent.ResolutionIndex) testutil.Equal(t, "resolution transaction ID", txn2.ID(), *dbTxns[0].FileContractResolutions[0].Parent.ResolutionTransactionID) } @@ -1238,6 +1252,8 @@ func TestV2FileContractResolution(t *testing.T) { t.Fatal(err) } testutil.CheckV2Transaction(t, txn3, dbTxns[0]) + testutil.Equal(t, "confirmation index", tip1, dbTxns[0].FileContractResolutions[0].Parent.ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn1.ID(), dbTxns[0].FileContractResolutions[0].Parent.ConfirmationTransactionID) testutil.Equal(t, "resolution index", cm.Tip(), *dbTxns[0].FileContractResolutions[0].Parent.ResolutionIndex) testutil.Equal(t, "resolution transaction ID", txn3.ID(), *dbTxns[0].FileContractResolutions[0].Parent.ResolutionTransactionID) } @@ -1256,6 +1272,8 @@ func TestV2FileContractResolution(t *testing.T) { t.Fatal(err) } testutil.CheckV2Transaction(t, txn4, dbTxns[0]) + testutil.Equal(t, "confirmation index", tip1, dbTxns[0].FileContractResolutions[0].Parent.ConfirmationIndex) + testutil.Equal(t, "confirmation transaction ID", txn1.ID(), dbTxns[0].FileContractResolutions[0].Parent.ConfirmationTransactionID) testutil.Equal(t, "resolution index", cm.Tip(), *dbTxns[0].FileContractResolutions[0].Parent.ResolutionIndex) testutil.Equal(t, "resolution transaction ID", txn4.ID(), *dbTxns[0].FileContractResolutions[0].Parent.ResolutionTransactionID) } diff --git a/persist/sqlite/v2contracts.go b/persist/sqlite/v2contracts.go index a5e5226..81b2c68 100644 --- a/persist/sqlite/v2contracts.go +++ b/persist/sqlite/v2contracts.go @@ -8,23 +8,17 @@ import ( ) func scanV2FileContract(s scanner) (fce explorer.V2FileContract, err error) { - var confirmationIndex, resolutionIndex types.ChainIndex - var confirmationTransactionID, resolutionTransactionID types.TransactionID + var resolutionIndex types.ChainIndex + var resolutionTransactionID types.TransactionID fc := &fce.V2FileContractElement.V2FileContract - if err = s.Scan(decode(&fce.TransactionID), decodeNull(&confirmationIndex), decodeNull(&confirmationTransactionID), decodeNull(&resolutionIndex), decodeNull(&resolutionTransactionID), decode(&fce.V2FileContractElement.ID), decode(&fce.V2FileContractElement.StateElement.LeafIndex), decode(&fc.Capacity), decode(&fc.Filesize), decode(&fc.FileMerkleRoot), decode(&fc.ProofHeight), decode(&fc.ExpirationHeight), decode(&fc.RenterOutput.Address), decode(&fc.RenterOutput.Value), decode(&fc.HostOutput.Address), decode(&fc.HostOutput.Value), decode(&fc.MissedHostValue), decode(&fc.TotalCollateral), decode(&fc.RenterPublicKey), decode(&fc.HostPublicKey), decode(&fc.RevisionNumber), decode(&fc.RenterSignature), decode(&fc.HostSignature)); err != nil { + if err = s.Scan(decode(&fce.TransactionID), decode(&fce.ConfirmationIndex), decode(&fce.ConfirmationTransactionID), decodeNull(&resolutionIndex), decodeNull(&resolutionTransactionID), decode(&fce.V2FileContractElement.ID), decode(&fce.V2FileContractElement.StateElement.LeafIndex), decode(&fc.Capacity), decode(&fc.Filesize), decode(&fc.FileMerkleRoot), decode(&fc.ProofHeight), decode(&fc.ExpirationHeight), decode(&fc.RenterOutput.Address), decode(&fc.RenterOutput.Value), decode(&fc.HostOutput.Address), decode(&fc.HostOutput.Value), decode(&fc.MissedHostValue), decode(&fc.TotalCollateral), decode(&fc.RenterPublicKey), decode(&fc.HostPublicKey), decode(&fc.RevisionNumber), decode(&fc.RenterSignature), decode(&fc.HostSignature)); err != nil { return } - if confirmationIndex != (types.ChainIndex{}) { - fce.ConfirmationIndex = &confirmationIndex - } if resolutionIndex != (types.ChainIndex{}) { fce.ResolutionIndex = &resolutionIndex } - if confirmationTransactionID != (types.TransactionID{}) { - fce.ConfirmationTransactionID = &confirmationTransactionID - } if resolutionTransactionID != (types.TransactionID{}) { fce.ResolutionTransactionID = &resolutionTransactionID }