Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transaction ID on file contracts #113

Merged
merged 5 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions explorer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ type FileContract struct {
Resolved bool `json:"resolved"`
Valid bool `json:"valid"`

TransactionID types.TransactionID `json:"transactionID"`

ConfirmationIndex *types.ChainIndex `json:"confirmationIndex"`
ConfirmationTransactionID *types.TransactionID `json:"confirmationTransactionID"`

Expand Down
112 changes: 75 additions & 37 deletions persist/sqlite/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,12 @@ func addStorageProofs(tx *txn, id int64, txn types.Transaction) error {
return nil
}

func addTransactions(tx *txn, bid types.BlockID, txns []types.Transaction, scDBIds map[types.SiacoinOutputID]int64, sfDBIds map[types.SiafundOutputID]int64, fcDBIds map[explorer.DBFileContract]int64) (map[types.TransactionID]int64, error) {
type txnDBId struct {
id int64
exist bool
}

func addTransactions(tx *txn, bid types.BlockID, txns []types.Transaction) (map[types.TransactionID]txnDBId, error) {
checkTransactionStmt, err := tx.Prepare(`SELECT id FROM transactions WHERE transaction_id = ?`)
if err != nil {
return nil, fmt.Errorf("failed to prepare check transaction statement: %v", err)
Expand All @@ -234,7 +239,7 @@ func addTransactions(tx *txn, bid types.BlockID, txns []types.Transaction, scDBI
}
defer blockTransactionsStmt.Close()

txnDBIds := make(map[types.TransactionID]int64)
txnDBIds := make(map[types.TransactionID]txnDBId)
for i, txn := range txns {
var exist bool
var txnID int64
Expand All @@ -254,40 +259,52 @@ func addTransactions(tx *txn, bid types.BlockID, txns []types.Transaction, scDBI
return nil, fmt.Errorf("failed to get transaction ID: %w", err)
}
}
txnDBIds[txn.ID()] = txnID
txnDBIds[txn.ID()] = txnDBId{id: txnID, exist: exist}

if _, err := blockTransactionsStmt.Exec(encode(bid), txnID, i); err != nil {
return nil, fmt.Errorf("failed to insert into block_transactions: %w", err)
}
}

return txnDBIds, nil
}

func addTransactionFields(tx *txn, txns []types.Transaction, scDBIds map[types.SiacoinOutputID]int64, sfDBIds map[types.SiafundOutputID]int64, fcDBIds map[explorer.DBFileContract]int64, txnDBIds map[types.TransactionID]txnDBId) error {
for _, txn := range txns {
dbID, ok := txnDBIds[txn.ID()]
if !ok {
panic(fmt.Errorf("txn %v should be in txnDBIds", txn.ID()))
}

// transaction already exists, don't reinsert its fields
if exist {
if dbID.exist {
continue
}

if err := addMinerFees(tx, txnID, txn); err != nil {
return nil, fmt.Errorf("failed to add miner fees: %w", err)
} else if err := addArbitraryData(tx, txnID, txn); err != nil {
return nil, fmt.Errorf("failed to add arbitrary data: %w", err)
} else if err := addSignatures(tx, txnID, txn); err != nil {
return nil, fmt.Errorf("failed to add signatures: %w", err)
} else if err := addSiacoinInputs(tx, txnID, txn); err != nil {
return nil, fmt.Errorf("failed to add siacoin inputs: %w", err)
} else if err := addSiacoinOutputs(tx, txnID, txn, scDBIds); err != nil {
return nil, fmt.Errorf("failed to add siacoin outputs: %w", err)
} else if err := addSiafundInputs(tx, txnID, txn); err != nil {
return nil, fmt.Errorf("failed to add siafund inputs: %w", err)
} else if err := addSiafundOutputs(tx, txnID, txn, sfDBIds); err != nil {
return nil, fmt.Errorf("failed to add siafund outputs: %w", err)
} else if err := addFileContracts(tx, txnID, txn, fcDBIds); err != nil {
return nil, fmt.Errorf("failed to add file contract: %w", err)
} else if err := addFileContractRevisions(tx, txnID, txn, fcDBIds); err != nil {
return nil, fmt.Errorf("failed to add file contract revisions: %w", err)
} else if err := addStorageProofs(tx, txnID, txn); err != nil {
return nil, fmt.Errorf("failed to add storage proofs: %w", err)
if err := addMinerFees(tx, dbID.id, txn); err != nil {
return fmt.Errorf("failed to add miner fees: %w", err)
} else if err := addArbitraryData(tx, dbID.id, txn); err != nil {
return fmt.Errorf("failed to add arbitrary data: %w", err)
} else if err := addSignatures(tx, dbID.id, txn); err != nil {
return fmt.Errorf("failed to add signatures: %w", err)
} else if err := addSiacoinInputs(tx, dbID.id, txn); err != nil {
return fmt.Errorf("failed to add siacoin inputs: %w", err)
} else if err := addSiacoinOutputs(tx, dbID.id, txn, scDBIds); err != nil {
return fmt.Errorf("failed to add siacoin outputs: %w", err)
} else if err := addSiafundInputs(tx, dbID.id, txn); err != nil {
return fmt.Errorf("failed to add siafund inputs: %w", err)
} else if err := addSiafundOutputs(tx, dbID.id, txn, sfDBIds); err != nil {
return fmt.Errorf("failed to add siafund outputs: %w", err)
} else if err := addFileContracts(tx, dbID.id, txn, fcDBIds); err != nil {
return fmt.Errorf("failed to add file contract: %w", err)
} else if err := addFileContractRevisions(tx, dbID.id, txn, fcDBIds); err != nil {
return fmt.Errorf("failed to add file contract revisions: %w", err)
} else if err := addStorageProofs(tx, dbID.id, txn); err != nil {
return fmt.Errorf("failed to add storage proofs: %w", err)
}
}
return txnDBIds, nil

return nil
}

type balance struct {
Expand Down Expand Up @@ -578,7 +595,7 @@ func addSiafundElements(tx *txn, index types.ChainIndex, spentElements, newEleme
return sfDBIds, nil
}

func addEvents(tx *txn, scDBIds map[types.SiacoinOutputID]int64, fcDBIds map[explorer.DBFileContract]int64, txnDBIds map[types.TransactionID]int64, events []explorer.Event) error {
func addEvents(tx *txn, scDBIds map[types.SiacoinOutputID]int64, fcDBIds map[explorer.DBFileContract]int64, txnDBIds map[types.TransactionID]txnDBId, events []explorer.Event) error {
if len(events) == 0 {
return nil
}
Expand Down Expand Up @@ -649,7 +666,7 @@ func addEvents(tx *txn, scDBIds map[types.SiacoinOutputID]int64, fcDBIds map[exp

switch v := event.Data.(type) {
case *explorer.EventTransaction:
dbID := txnDBIds[types.TransactionID(event.ID)]
dbID := txnDBIds[types.TransactionID(event.ID)].id
if _, err = transactionEventStmt.Exec(eventID, dbID, encode(v.Fee)); err != nil {
return fmt.Errorf("failed to insert transaction event: %w", err)
}
Expand Down Expand Up @@ -713,8 +730,8 @@ func deleteBlock(tx *txn, bid types.BlockID) error {
}

func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []explorer.FileContractUpdate) (map[explorer.DBFileContract]int64, error) {
stmt, err := tx.Prepare(`INSERT INTO file_contract_elements(contract_id, block_id, leaf_index, resolved, valid, filesize, file_merkle_root, window_start, window_end, payout, unlock_hash, revision_number)
VALUES (?, ?, ?, FALSE, FALSE, ?, ?, ?, ?, ?, ?, ?)
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)
DO UPDATE SET resolved = ?, valid = ?, leaf_index = ?
RETURNING id;`)
Expand Down Expand Up @@ -775,11 +792,29 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl
}
}

fcTxns := make(map[explorer.DBFileContract]types.TransactionID)
for _, txn := range b.Transactions {
id := txn.ID()

for i, fc := range txn.FileContracts {
fcTxns[explorer.DBFileContract{
ID: txn.FileContractID(i),
RevisionNumber: fc.RevisionNumber,
}] = id
}
for _, fcr := range txn.FileContractRevisions {
fcTxns[explorer.DBFileContract{
ID: fcr.ParentID,
RevisionNumber: fcr.FileContract.RevisionNumber,
}] = id
}
}

fcDBIds := make(map[explorer.DBFileContract]int64)
addFC := func(fcID types.FileContractID, leafIndex uint64, fc types.FileContract, 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(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(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)
if err != nil {
return fmt.Errorf("failed to execute file_contract_elements statement: %w", err)
}
Expand Down Expand Up @@ -966,6 +1001,11 @@ func (ut *updateTx) ApplyIndex(state explorer.UpdateState) error {
return fmt.Errorf("ApplyIndex: failed to update matured balances: %w", err)
}

txnDBIds, err := addTransactions(ut.tx, state.Block.ID(), state.Block.Transactions)
if err != nil {
return fmt.Errorf("ApplyIndex: failed to add transactions: %w", err)
}

scDBIds, err := addSiacoinElements(
ut.tx,
state.Metrics.Index,
Expand All @@ -984,19 +1024,17 @@ func (ut *updateTx) ApplyIndex(state explorer.UpdateState) error {
if err != nil {
return fmt.Errorf("ApplyIndex: failed to add siafund outputs: %w", err)
}
if err := updateBalances(ut.tx, state.Metrics.Index.Height, state.SpentSiacoinElements, state.NewSiacoinElements, state.SpentSiafundElements, state.NewSiafundElements); err != nil {
return fmt.Errorf("ApplyIndex: failed to update balances: %w", err)
}

fcDBIds, err := updateFileContractElements(ut.tx, false, state.Block, state.FileContractElements)
if err != nil {
return fmt.Errorf("ApplyIndex: failed to add file contracts: %w", err)
}

if err := addMinerPayouts(ut.tx, state.Block.ID(), state.Block.MinerPayouts, scDBIds); err != nil {
if err := addTransactionFields(ut.tx, state.Block.Transactions, scDBIds, sfDBIds, fcDBIds, txnDBIds); err != nil {
return fmt.Errorf("ApplyIndex: failed to add transaction fields: %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("ApplyIndex: failed to update balances: %w", err)
} else if err := addMinerPayouts(ut.tx, state.Block.ID(), state.Block.MinerPayouts, scDBIds); err != nil {
return fmt.Errorf("ApplyIndex: failed to add miner payouts: %w", err)
} else if txnDBIds, err := addTransactions(ut.tx, state.Block.ID(), state.Block.Transactions, scDBIds, sfDBIds, fcDBIds); err != nil {
return fmt.Errorf("ApplyIndex: failed to add transactions: addTransactions: %w", err)
} else if err := updateStateTree(ut.tx, state.TreeUpdates); err != nil {
return fmt.Errorf("ApplyIndex: failed to update state tree: %w", err)
} else if err := addMetrics(ut.tx, state); err != nil {
Expand Down
14 changes: 14 additions & 0 deletions persist/sqlite/consensus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ 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)
}
Expand All @@ -605,6 +606,7 @@ func TestFileContract(t *testing.T) {
testutil.Equal(t, "file contracts", 1, len(txns[0].FileContracts))
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)
}
Expand Down Expand Up @@ -646,6 +648,8 @@ func TestFileContract(t *testing.T) {
testutil.CheckFC(t, false, false, false, fc, renterContracts[0])
testutil.CheckFC(t, false, false, false, fc, hostContracts[0])

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)
Expand Down Expand Up @@ -748,6 +752,8 @@ func TestFileContract(t *testing.T) {
testutil.CheckFC(t, false, true, false, fc, renterContracts[0])
testutil.CheckFC(t, false, true, false, fc, hostContracts[0])

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)
Expand Down Expand Up @@ -875,6 +881,7 @@ func TestEphemeralFileContract(t *testing.T) {
}
testutil.Equal(t, "fcs", 1, len(dbFCs))
testutil.CheckFC(t, true, false, false, revisedFC1, dbFCs[0])
testutil.Equal(t, "transaction ID", reviseTxn1.ID(), dbFCs[0].TransactionID)
}

{
Expand All @@ -894,6 +901,7 @@ func TestEphemeralFileContract(t *testing.T) {
testutil.Equal(t, "file contracts", 1, len(txns[0].FileContracts))
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)
}
Expand Down Expand Up @@ -955,6 +963,7 @@ func TestEphemeralFileContract(t *testing.T) {
}
testutil.Equal(t, "fcs", 1, len(dbFCs))
testutil.CheckFC(t, true, false, false, revisedFC3, dbFCs[0])
testutil.Equal(t, "transaction ID", reviseTxn3.ID(), dbFCs[0].TransactionID)
}

{
Expand Down Expand Up @@ -2302,6 +2311,7 @@ func TestMultipleReorgFileContract(t *testing.T) {
testutil.Equal(t, "fcs", 1, len(dbFCs))
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)
}
Expand Down Expand Up @@ -2340,6 +2350,8 @@ func TestMultipleReorgFileContract(t *testing.T) {
}
testutil.Equal(t, "renter contracts and host contracts", len(renterContracts), len(hostContracts))
testutil.Equal(t, "len(contracts)", 1, len(renterContracts))
testutil.Equal(t, "transaction ID", reviseTxn.ID(), renterContracts[0].TransactionID)
testutil.Equal(t, "transaction ID", reviseTxn.ID(), hostContracts[0].TransactionID)
testutil.CheckFC(t, false, false, false, revFC, renterContracts[0])
testutil.CheckFC(t, false, false, false, revFC, hostContracts[0])
}
Expand Down Expand Up @@ -2373,6 +2385,7 @@ func TestMultipleReorgFileContract(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", prevState1.Index, *dbFCs[0].ConfirmationIndex)
testutil.Equal(t, "confirmation transaction ID", txn.ID(), *dbFCs[0].ConfirmationTransactionID)
}
Expand Down Expand Up @@ -2423,6 +2436,7 @@ func TestMultipleReorgFileContract(t *testing.T) {
testutil.Equal(t, "fcs", 1, len(dbFCs))
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)
}
Expand Down
Loading
Loading