Skip to content

Commit

Permalink
Merge pull request #133 from SiaFoundation/add-v2-file-contract-revis…
Browse files Browse the repository at this point in the history
…ions

Add v2 file contract revisions
  • Loading branch information
chris124567 authored Nov 5, 2024
2 parents fa0f334 + dce889b commit f3fe466
Show file tree
Hide file tree
Showing 11 changed files with 314 additions and 58 deletions.
12 changes: 10 additions & 2 deletions explorer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,14 @@ type V2FileContract struct {
types.V2FileContractElement
}

// A V2Transaction is a v2 transaction that uses the wrapped types above.
// A V2FileContractRevision is a V2 file contract revision with the
// explorer V2FileContract type.
type V2FileContractRevision struct {
Parent V2FileContract `json:"parent"`
Revision V2FileContract `json:"revision"`
}

// A V2Transaction is a V2 transaction that uses the wrapped types above.
type V2Transaction struct {
ID types.TransactionID `json:"id"`

Expand All @@ -190,7 +197,8 @@ type V2Transaction struct {
SiafundInputs []types.V2SiafundInput `json:"siafundInputs,omitempty"`
SiafundOutputs []SiafundOutput `json:"siafundOutputs,omitempty"`

FileContracts []V2FileContract `json:"fileContracts,omitempty"`
FileContracts []V2FileContract `json:"fileContracts,omitempty"`
FileContractRevisions []V2FileContractRevision `json:"fileContractRevisions,omitempty"`

Attestations []types.Attestation `json:"attestations,omitempty"`
ArbitraryData []byte `json:"arbitraryData,omitempty"`
Expand Down
11 changes: 11 additions & 0 deletions internal/testutil/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,17 @@ func CheckV2Transaction(t *testing.T, expectTxn types.V2Transaction, gotTxn expl
CheckV2FC(t, expected, got)
}

Equal(t, "file contract revision", len(expectTxn.FileContractRevisions), len(gotTxn.FileContractRevisions))
for i := range expectTxn.FileContractRevisions {
expected := expectTxn.FileContractRevisions[i]
got := gotTxn.FileContractRevisions[i]

Equal(t, "parent ID", expected.Parent.ID, got.Parent.ID)
Equal(t, "revision ID", expected.Parent.ID, got.Revision.ID)
CheckV2FC(t, expected.Parent.V2FileContract, got.Parent)
CheckV2FC(t, expected.Revision, got.Revision)
}

Equal(t, "attestations", len(expectTxn.Attestations), len(gotTxn.Attestations))
for i := range expectTxn.Attestations {
expected := expectTxn.Attestations[i]
Expand Down
8 changes: 4 additions & 4 deletions persist/sqlite/addresses.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ WHERE ev.event_id = ?`, eventID).Scan(decode(&m.SiacoinOutput.StateElement.ID),
var m explorer.EventMinerPayout
err = tx.QueryRow(`SELECT sc.output_id, sc.leaf_index, sc.maturity_height, sc.address, sc.value
FROM siacoin_elements sc
INNER JOIN miner_payout_events ev ON (ev.output_id = sc.id)
INNER JOIN miner_payout_events ev ON ev.output_id = sc.id
WHERE ev.event_id = ?`, eventID).Scan(decode(&m.SiacoinOutput.StateElement.ID), decode(&m.SiacoinOutput.StateElement.LeafIndex), decode(&m.SiacoinOutput.MaturityHeight), decode(&m.SiacoinOutput.SiacoinOutput.Address), decode(&m.SiacoinOutput.SiacoinOutput.Value))
if err != nil {
return explorer.Event{}, 0, fmt.Errorf("failed to fetch miner payout event data: %w", err)
Expand All @@ -85,7 +85,7 @@ WHERE ev.event_id = ?`, eventID).Scan(decode(&m.SiacoinOutput.StateElement.ID),
var m explorer.EventFoundationSubsidy
err = tx.QueryRow(`SELECT sc.output_id, sc.leaf_index, sc.maturity_height, sc.address, sc.value
FROM siacoin_elements sc
INNER JOIN foundation_subsidy_events ev ON (ev.output_id = sc.id)
INNER JOIN foundation_subsidy_events ev ON ev.output_id = sc.id
WHERE ev.event_id = ?`, eventID).Scan(decode(&m.SiacoinOutput.StateElement.ID), decode(&m.SiacoinOutput.StateElement.LeafIndex), decode(&m.SiacoinOutput.MaturityHeight), decode(&m.SiacoinOutput.SiacoinOutput.Address), decode(&m.SiacoinOutput.SiacoinOutput.Value))
ev.Data = &m
default:
Expand Down Expand Up @@ -153,8 +153,8 @@ func (s *Store) AddressEvents(address types.Address, offset, limit uint64) (even
err = s.transaction(func(tx *txn) error {
const query = `SELECT ev.id, ev.event_id, ev.maturity_height, ev.date_created, ev.height, ev.block_id, ev.event_type
FROM events ev
INNER JOIN event_addresses ea ON (ev.id = ea.event_id)
INNER JOIN address_balance sa ON (ea.address_id = sa.id)
INNER JOIN event_addresses ea ON ev.id = ea.event_id
INNER JOIN address_balance sa ON ea.address_id = sa.id
WHERE sa.address = $1
ORDER BY ev.maturity_height DESC, ev.id DESC
LIMIT $2 OFFSET $3`
Expand Down
4 changes: 4 additions & 0 deletions persist/sqlite/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,8 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl
}

for _, txn := range b.Transactions {
// add in any contracts that are not the latest, i.e. contracts that
// were created and revised in the same block
for j, fc := range txn.FileContracts {
fcID := txn.FileContractID(j)
dbFC := explorer.DBFileContract{ID: txn.FileContractID(j), RevisionNumber: fc.RevisionNumber}
Expand All @@ -939,6 +941,8 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl
return nil, fmt.Errorf("updateFileContractElements: %w", err)
}
}
// add in any revisions that are not the latest, i.e. contracts that
// were revised multiple times in one block
for _, fcr := range txn.FileContractRevisions {
fc := fcr.FileContract
dbFC := explorer.DBFileContract{ID: fcr.ParentID, RevisionNumber: fc.RevisionNumber}
Expand Down
6 changes: 3 additions & 3 deletions persist/sqlite/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (s *Store) Contracts(ids []types.FileContractID) (result []explorer.FileCon
err = s.transaction(func(tx *txn) error {
query := `SELECT fc1.id, fc1.contract_id, fc1.leaf_index, fc1.resolved, fc1.valid, fc1.transaction_id, rev.confirmation_index, rev.confirmation_transaction_id, rev.proof_index, rev.proof_transaction_id, fc1.filesize, fc1.file_merkle_root, fc1.window_start, fc1.window_end, fc1.payout, fc1.unlock_hash, fc1.revision_number
FROM file_contract_elements fc1
INNER JOIN last_contract_revision rev ON (rev.contract_element_id = fc1.id)
INNER JOIN last_contract_revision rev ON rev.contract_element_id = fc1.id
WHERE rev.contract_id IN (` + queryPlaceHolders(len(ids)) + `)`
rows, err := tx.Query(query, encodedIDs(ids)...)
if err != nil {
Expand Down Expand Up @@ -86,7 +86,7 @@ func (s *Store) ContractRevisions(id types.FileContractID) (revisions []explorer
err = s.transaction(func(tx *txn) error {
query := `SELECT fc.id, fc.contract_id, fc.leaf_index, fc.resolved, fc.valid, fc.transaction_id, rev.confirmation_index, rev.confirmation_transaction_id, rev.proof_index, rev.proof_transaction_id, fc.filesize, fc.file_merkle_root, fc.window_start, fc.window_end, fc.payout, fc.unlock_hash, fc.revision_number
FROM file_contract_elements fc
JOIN last_contract_revision rev ON (rev.contract_id = fc.contract_id)
JOIN last_contract_revision rev ON rev.contract_id = fc.contract_id
WHERE fc.contract_id = ?
ORDER BY fc.revision_number ASC`
rows, err := tx.Query(query, encode(id))
Expand Down Expand Up @@ -144,7 +144,7 @@ func (s *Store) ContractsKey(key types.PublicKey) (result []explorer.FileContrac
err = s.transaction(func(tx *txn) error {
query := `SELECT fc1.id, fc1.contract_id, fc1.leaf_index, fc1.resolved, fc1.valid, fc1.transaction_id, rev.confirmation_index, rev.confirmation_transaction_id, rev.proof_index, rev.proof_transaction_id, fc1.filesize, fc1.file_merkle_root, fc1.window_start, fc1.window_end, fc1.payout, fc1.unlock_hash, fc1.revision_number
FROM file_contract_elements fc1
INNER JOIN last_contract_revision rev ON (rev.contract_element_id = fc1.id)
INNER JOIN last_contract_revision rev ON rev.contract_element_id = fc1.id
WHERE rev.ed25519_renter_key = ? OR rev.ed25519_host_key = ?`
rows, err := tx.Query(query, encode(key), encode(key))
if err != nil {
Expand Down
11 changes: 10 additions & 1 deletion persist/sqlite/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,16 @@ CREATE TABLE v2_transaction_file_contracts (
contract_id INTEGER REFERENCES v2_file_contract_elements(id) ON DELETE CASCADE NOT NULL, -- add an index to all foreign keys
UNIQUE(transaction_id, transaction_order)
);
CREATE INDEX v2_transaction_file_contracts_transaction_id_index ON v2_transaction_file_contracts(transaction_id);
CREATE INDEX v2_transaction_file_contracts_transaction_id_index ON v2_transaction_file_contracts(transaction_id);

CREATE TABLE v2_transaction_file_contract_revisions (
transaction_id INTEGER REFERENCES v2_transactions(id) ON DELETE CASCADE NOT NULL,
transaction_order INTEGER NOT NULL,
parent_contract_id INTEGER REFERENCES v2_file_contract_elements(id) ON DELETE CASCADE NOT NULL, -- add an index to all foreign keys
revision_contract_id INTEGER REFERENCES v2_file_contract_elements(id) ON DELETE CASCADE NOT NULL, -- add an index to all foreign keys
UNIQUE(transaction_id, transaction_order)
);
CREATE INDEX v2_transaction_file_contract_revisions_transaction_id_index ON v2_transaction_file_contract_revisions(transaction_id);

CREATE TABLE v2_transaction_attestations (
transaction_id INTEGER REFERENCES v2_transactions(id) ON DELETE CASCADE NOT NULL,
Expand Down
24 changes: 12 additions & 12 deletions persist/sqlite/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (
func (s *Store) TransactionChainIndices(txnID types.TransactionID, offset, limit uint64) (indices []types.ChainIndex, err error) {
err = s.transaction(func(tx *txn) error {
rows, err := tx.Query(`SELECT DISTINCT b.id, b.height FROM blocks b
INNER JOIN block_transactions bt ON (bt.block_id = b.id)
INNER JOIN transactions t ON (t.id = bt.transaction_id)
INNER JOIN block_transactions bt ON bt.block_id = b.id
INNER JOIN transactions t ON t.id = bt.transaction_id
WHERE t.transaction_id = ?
ORDER BY b.height DESC
LIMIT ? OFFSET ?`, encode(txnID), limit, offset)
Expand Down Expand Up @@ -112,7 +112,7 @@ ORDER BY transaction_order ASC`
func transactionSiacoinOutputs(tx *txn, txnIDs []int64) (map[int64][]explorer.SiacoinOutput, error) {
query := `SELECT ts.transaction_id, sc.output_id, sc.leaf_index, sc.spent_index, sc.source, sc.maturity_height, sc.address, sc.value
FROM siacoin_elements sc
INNER JOIN transaction_siacoin_outputs ts ON (ts.output_id = sc.id)
INNER JOIN transaction_siacoin_outputs ts ON ts.output_id = sc.id
WHERE ts.transaction_id IN (` + queryPlaceHolders(len(txnIDs)) + `)
ORDER BY ts.transaction_order ASC`
rows, err := tx.Query(query, queryArgs(txnIDs)...)
Expand Down Expand Up @@ -142,7 +142,7 @@ ORDER BY ts.transaction_order ASC`
func transactionSiacoinInputs(tx *txn, txnIDs []int64) (map[int64][]explorer.SiacoinInput, error) {
query := `SELECT sc.id, ts.transaction_id, sc.output_id, ts.unlock_conditions, sc.value
FROM siacoin_elements sc
INNER JOIN transaction_siacoin_inputs ts ON (ts.parent_id = sc.id)
INNER JOIN transaction_siacoin_inputs ts ON ts.parent_id = sc.id
WHERE ts.transaction_id IN (` + queryPlaceHolders(len(txnIDs)) + `)
ORDER BY ts.transaction_order ASC`
rows, err := tx.Query(query, queryArgs(txnIDs)...)
Expand All @@ -168,7 +168,7 @@ ORDER BY ts.transaction_order ASC`
func transactionSiafundInputs(tx *txn, txnIDs []int64) (map[int64][]explorer.SiafundInput, error) {
query := `SELECT ts.transaction_id, sf.output_id, ts.unlock_conditions, ts.claim_address, sf.value
FROM siafund_elements sf
INNER JOIN transaction_siafund_inputs ts ON (ts.parent_id = sf.id)
INNER JOIN transaction_siafund_inputs ts ON ts.parent_id = sf.id
WHERE ts.transaction_id IN (` + queryPlaceHolders(len(txnIDs)) + `)
ORDER BY ts.transaction_order ASC`
rows, err := tx.Query(query, queryArgs(txnIDs)...)
Expand All @@ -195,7 +195,7 @@ ORDER BY ts.transaction_order ASC`
func transactionSiafundOutputs(tx *txn, txnIDs []int64) (map[int64][]explorer.SiafundOutput, error) {
query := `SELECT ts.transaction_id, sf.output_id, sf.leaf_index, sf.spent_index, sf.claim_start, sf.address, sf.value
FROM siafund_elements sf
INNER JOIN transaction_siafund_outputs ts ON (ts.output_id = sf.id)
INNER JOIN transaction_siafund_outputs ts ON ts.output_id = sf.id
WHERE ts.transaction_id IN (` + queryPlaceHolders(len(txnIDs)) + `)
ORDER BY ts.transaction_order ASC`
rows, err := tx.Query(query, queryArgs(txnIDs)...)
Expand Down Expand Up @@ -286,8 +286,8 @@ type contractOrder struct {
func transactionFileContracts(tx *txn, txnIDs []int64) (map[int64][]explorer.FileContract, error) {
query := `SELECT ts.transaction_id, fc.id, rev.confirmation_index, rev.confirmation_transaction_id, rev.proof_index, rev.proof_transaction_id, fc.contract_id, fc.leaf_index, fc.resolved, fc.valid, fc.transaction_id, fc.filesize, fc.file_merkle_root, fc.window_start, fc.window_end, fc.payout, fc.unlock_hash, fc.revision_number
FROM file_contract_elements fc
INNER JOIN transaction_file_contracts ts ON (ts.contract_id = fc.id)
INNER JOIN last_contract_revision rev ON (rev.contract_id = fc.contract_id)
INNER JOIN transaction_file_contracts ts ON ts.contract_id = fc.id
INNER JOIN last_contract_revision rev ON rev.contract_id = fc.contract_id
WHERE ts.transaction_id IN (` + queryPlaceHolders(len(txnIDs)) + `)
ORDER BY ts.transaction_order ASC`
rows, err := tx.Query(query, queryArgs(txnIDs)...)
Expand Down Expand Up @@ -347,8 +347,8 @@ ORDER BY ts.transaction_order ASC`
func transactionFileContractRevisions(tx *txn, txnIDs []int64) (map[int64][]explorer.FileContractRevision, error) {
query := `SELECT ts.transaction_id, fc.id, rev.confirmation_index, rev.confirmation_transaction_id, rev.proof_index, rev.proof_transaction_id, ts.parent_id, ts.unlock_conditions, fc.contract_id, fc.leaf_index, fc.resolved, fc.valid, fc.transaction_id, fc.filesize, fc.file_merkle_root, fc.window_start, fc.window_end, fc.payout, fc.unlock_hash, fc.revision_number
FROM file_contract_elements fc
INNER JOIN transaction_file_contract_revisions ts ON (ts.contract_id = fc.id)
INNER JOIN last_contract_revision rev ON (rev.contract_id = fc.contract_id)
INNER JOIN transaction_file_contract_revisions ts ON ts.contract_id = fc.id
INNER JOIN last_contract_revision rev ON rev.contract_id = fc.contract_id
WHERE ts.transaction_id IN (` + queryPlaceHolders(len(txnIDs)) + `)
ORDER BY ts.transaction_order ASC`
rows, err := tx.Query(query, queryArgs(txnIDs)...)
Expand Down Expand Up @@ -442,7 +442,7 @@ type transactionID struct {
func blockTransactionIDs(tx *txn, blockID types.BlockID) (idMap map[int64]transactionID, err error) {
rows, err := tx.Query(`SELECT bt.transaction_id, block_order, t.transaction_id
FROM block_transactions bt
INNER JOIN transactions t ON (t.id = bt.transaction_id)
INNER JOIN transactions t ON t.id = bt.transaction_id
WHERE block_id = ? ORDER BY block_order ASC`, encode(blockID))
if err != nil {
return nil, err
Expand All @@ -466,7 +466,7 @@ WHERE block_id = ? ORDER BY block_order ASC`, encode(blockID))
func blockMinerPayouts(tx *txn, blockID types.BlockID) ([]explorer.SiacoinOutput, error) {
query := `SELECT sc.output_id, sc.leaf_index, sc.spent_index, sc.source, sc.maturity_height, sc.address, sc.value
FROM siacoin_elements sc
INNER JOIN miner_payouts mp ON (mp.output_id = sc.id)
INNER JOIN miner_payouts mp ON mp.output_id = sc.id
WHERE mp.block_id = ?
ORDER BY mp.block_order ASC`
rows, err := tx.Query(query, encode(blockID))
Expand Down
61 changes: 58 additions & 3 deletions persist/sqlite/v2consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ func updateV2FileContractElements(tx *txn, revert bool, b types.Block, fces []ex
}
defer revisionStmt.Close()

// so we can get the ids of revision parents to add to the DB
parentStmt, err := tx.Prepare(`SELECT id FROM v2_file_contract_elements WHERE contract_id = ? AND revision_number = ?`)
if err != nil {
return nil, fmt.Errorf("updateV2FileContractElements: failed to prepare parent statement: %w", err)
}
defer parentStmt.Close()

fcTxns := make(map[explorer.DBFileContract]types.TransactionID)
for _, txn := range b.V2Transactions() {
id := txn.ID()
Expand Down Expand Up @@ -164,6 +171,8 @@ func updateV2FileContractElements(tx *txn, revert bool, b types.Block, fces []ex
}

for _, txn := range b.V2Transactions() {
// add in any contracts that are not the latest, i.e. contracts that
// were created and revised in the same block
for j, fc := range txn.FileContracts {
fcID := txn.V2FileContractID(txn.ID(), j)
dbFC := explorer.DBFileContract{ID: fcID, RevisionNumber: fc.RevisionNumber}
Expand All @@ -175,18 +184,31 @@ func updateV2FileContractElements(tx *txn, revert bool, b types.Block, fces []ex
return nil, fmt.Errorf("updateFileContractElements: %w", err)
}
}
// add in any revisions that are not the latest, i.e. contracts that
// were revised multiple times in one block
for _, fcr := range txn.FileContractRevisions {
fc := fcr.Revision
fcid := types.FileContractID(fcr.Parent.ID)
dbFC := explorer.DBFileContract{ID: fcid, RevisionNumber: fc.RevisionNumber}
fcID := types.FileContractID(fcr.Parent.ID)
dbFC := explorer.DBFileContract{ID: fcID, RevisionNumber: fc.RevisionNumber}
if _, exists := fcDBIds[dbFC]; exists {
continue
}

if err := addFC(fcid, 0, fc, nil, false); err != nil {
if err := addFC(fcID, 0, fc, nil, false); err != nil {
return nil, fmt.Errorf("updateFileContractElements: %w", err)
}
}
// don't add anything, just set parent db IDs in fcDBIds map
for _, fcr := range txn.FileContractRevisions {
fcID := types.FileContractID(fcr.Parent.ID)
parentDBFC := explorer.DBFileContract{ID: fcID, RevisionNumber: fcr.Parent.V2FileContract.RevisionNumber}

var dbID int64
if err := parentStmt.QueryRow(encode(fcID), encode(parentDBFC.RevisionNumber)).Scan(&dbID); err != nil {
return nil, fmt.Errorf("updateFileContractElements: failed to get parent contract ID: %w", err)
}
fcDBIds[parentDBFC] = dbID
}
}

return fcDBIds, nil
Expand Down Expand Up @@ -342,6 +364,37 @@ func addV2FileContracts(tx *txn, txnID int64, txn types.V2Transaction, dbIDs map
return nil
}

func addV2FileContractRevisions(tx *txn, txnID int64, txn types.V2Transaction, dbIDs map[explorer.DBFileContract]int64) error {
stmt, err := tx.Prepare(`INSERT INTO v2_transaction_file_contract_revisions(transaction_id, transaction_order, parent_contract_id, revision_contract_id) VALUES (?, ?, ?, ?)`)
if err != nil {
return fmt.Errorf("addV2FileContractRevisions: failed to prepare statement: %w", err)
}
defer stmt.Close()

for i, fcr := range txn.FileContractRevisions {
parentDBID, ok := dbIDs[explorer.DBFileContract{
ID: types.FileContractID(fcr.Parent.ID),
RevisionNumber: fcr.Parent.V2FileContract.RevisionNumber,
}]
if !ok {
return errors.New("addV2FileContractRevisions: parent dbID not in map")
}

dbID, ok := dbIDs[explorer.DBFileContract{
ID: types.FileContractID(fcr.Parent.ID),
RevisionNumber: fcr.Revision.RevisionNumber,
}]
if !ok {
return errors.New("addV2FileContractRevisions: dbID not in map")
}

if _, err := stmt.Exec(txnID, i, parentDBID, dbID); err != nil {
return fmt.Errorf("addV2FileContractRevisions: failed to execute statement: %w", err)
}
}
return nil
}

func addV2Attestations(tx *txn, txnID int64, txn types.V2Transaction) error {
stmt, err := tx.Prepare(`INSERT INTO v2_transaction_attestations(transaction_id, transaction_order, public_key, key, value, signature) VALUES (?, ?, ?, ?, ?, ?)`)
if err != nil {
Expand Down Expand Up @@ -381,6 +434,8 @@ func addV2TransactionFields(tx *txn, txns []types.V2Transaction, scDBIds map[typ
return fmt.Errorf("failed to add siafund outputs: %w", err)
} else if err := addV2FileContracts(tx, dbID.id, txn, v2FcDBIds); err != nil {
return fmt.Errorf("failed to add file contracts: %w", err)
} else if err := addV2FileContractRevisions(tx, dbID.id, txn, v2FcDBIds); err != nil {
return fmt.Errorf("failed to add file contract revisions: %w", err)
}
}

Expand Down
Loading

0 comments on commit f3fe466

Please sign in to comment.