Skip to content

Commit

Permalink
Merge pull request #113 from SiaFoundation/add-contract-transaction
Browse files Browse the repository at this point in the history
Transaction ID on file contracts
  • Loading branch information
n8maninger authored Oct 10, 2024
2 parents 9ccf6eb + 0a0ec7a commit e55bde7
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 46 deletions.
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

0 comments on commit e55bde7

Please sign in to comment.