Skip to content

Commit

Permalink
add proof output IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
chris124567 committed Nov 6, 2024
1 parent 408dd43 commit 1b9460e
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 55 deletions.
8 changes: 4 additions & 4 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,26 +186,26 @@ func (c *Client) AddressBalance(address types.Address) (resp AddressBalanceRespo
}

// Contract returns the file contract with the specified ID.
func (c *Client) Contract(id types.FileContractID) (resp explorer.FileContract, err error) {
func (c *Client) Contract(id types.FileContractID) (resp explorer.EnhancedFileContract, err error) {
err = c.c.GET(fmt.Sprintf("/contracts/%s", id), &resp)
return
}

// Contracts returns the transactions with the specified IDs.
func (c *Client) Contracts(ids []types.FileContractID) (resp []explorer.FileContract, err error) {
func (c *Client) Contracts(ids []types.FileContractID) (resp []explorer.EnhancedFileContract, err error) {
err = c.c.POST("/contracts", ids, &resp)
return
}

// ContractsKey returns the contracts for a particular ed25519 key.
func (c *Client) ContractsKey(key types.PublicKey) (resp []explorer.FileContract, err error) {
func (c *Client) ContractsKey(key types.PublicKey) (resp []explorer.EnhancedFileContract, err error) {
err = c.c.GET(fmt.Sprintf("/pubkey/%s/contracts", key), &resp)
return
}

// ContractRevisions returns all the revisions of the contract with the
// specified ID.
func (c *Client) ContractRevisions(id types.FileContractID) (resp []explorer.FileContract, err error) {
func (c *Client) ContractRevisions(id types.FileContractID) (resp []explorer.EnhancedFileContract, err error) {
err = c.c.GET(fmt.Sprintf("/contracts/%s/revisions", id), &resp)
return
}
Expand Down
6 changes: 3 additions & 3 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ type (
UnspentSiacoinOutputs(address types.Address, offset, limit uint64) ([]explorer.SiacoinOutput, error)
UnspentSiafundOutputs(address types.Address, offset, limit uint64) ([]explorer.SiafundOutput, error)
AddressEvents(address types.Address, offset, limit uint64) (events []explorer.Event, err error)
Contracts(ids []types.FileContractID) (result []explorer.FileContract, err error)
ContractsKey(key types.PublicKey) (result []explorer.FileContract, err error)
ContractRevisions(id types.FileContractID) (result []explorer.FileContract, err error)
Contracts(ids []types.FileContractID) (result []explorer.EnhancedFileContract, err error)
ContractsKey(key types.PublicKey) (result []explorer.EnhancedFileContract, err error)
ContractRevisions(id types.FileContractID) (result []explorer.EnhancedFileContract, err error)
Search(id types.Hash256) (explorer.SearchType, error)

Hosts(pks []types.PublicKey) ([]explorer.Host, error)
Expand Down
12 changes: 6 additions & 6 deletions explorer/explorer.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ type Store interface {
UnspentSiafundOutputs(address types.Address, offset, limit uint64) ([]SiafundOutput, error)
AddressEvents(address types.Address, offset, limit uint64) (events []Event, err error)
Balance(address types.Address) (sc types.Currency, immatureSC types.Currency, sf uint64, err error)
Contracts(ids []types.FileContractID) (result []FileContract, err error)
ContractsKey(key types.PublicKey) (result []FileContract, err error)
ContractRevisions(id types.FileContractID) (result []FileContract, err error)
Contracts(ids []types.FileContractID) (result []EnhancedFileContract, err error)
ContractsKey(key types.PublicKey) (result []EnhancedFileContract, err error)
ContractRevisions(id types.FileContractID) (result []EnhancedFileContract, err error)
SiacoinElements(ids []types.SiacoinOutputID) (result []SiacoinOutput, err error)
SiafundElements(ids []types.SiafundOutputID) (result []SiafundOutput, err error)

Expand Down Expand Up @@ -247,18 +247,18 @@ func (e *Explorer) Balance(address types.Address) (sc types.Currency, immatureSC
}

// Contracts returns the contracts with the specified IDs.
func (e *Explorer) Contracts(ids []types.FileContractID) (result []FileContract, err error) {
func (e *Explorer) Contracts(ids []types.FileContractID) (result []EnhancedFileContract, err error) {
return e.s.Contracts(ids)
}

// ContractsKey returns the contracts for a particular ed25519 key.
func (e *Explorer) ContractsKey(key types.PublicKey) (result []FileContract, err error) {
func (e *Explorer) ContractsKey(key types.PublicKey) (result []EnhancedFileContract, err error) {
return e.s.ContractsKey(key)
}

// ContractRevisions returns all the revisions of the contract with the
// specified ID.
func (e *Explorer) ContractRevisions(id types.FileContractID) (result []FileContract, err error) {
func (e *Explorer) ContractRevisions(id types.FileContractID) (result []EnhancedFileContract, err error) {
return e.s.ContractRevisions(id)
}

Expand Down
39 changes: 34 additions & 5 deletions explorer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,38 @@ type SiafundOutput struct {
types.SiafundElement
}

// A FileContract is a types.FileContractElement with added fields for
// resolved/valid state.
type ContractSiacoinOutput struct {

Check failure on line 126 in explorer/types.go

View workflow job for this annotation

GitHub Actions / test / test (1.22, ubuntu-latest)

exported: exported type ContractSiacoinOutput should have comment or be unexported (revive)

Check failure on line 126 in explorer/types.go

View workflow job for this annotation

GitHub Actions / test / test (1.23, ubuntu-latest)

exported: exported type ContractSiacoinOutput should have comment or be unexported (revive)
ID types.SiacoinOutputID `json:"id"`
types.SiacoinOutput
}

// A FileContract is a storage agreement between a renter and a host. It
// contains a bidirectional payment channel that resolves as either "valid" or
// "missed" depending on whether a valid StorageProof is submitted for the
// contract.
type FileContract struct {
Filesize uint64 `json:"filesize"`
FileMerkleRoot types.Hash256 `json:"fileMerkleRoot"`
WindowStart uint64 `json:"windowStart"`
WindowEnd uint64 `json:"windowEnd"`
Payout types.Currency `json:"payout"`
ValidProofOutputs []ContractSiacoinOutput `json:"validProofOutputs"`
MissedProofOutputs []ContractSiacoinOutput `json:"missedProofOutputs"`
UnlockHash types.Hash256 `json:"unlockHash"`
RevisionNumber uint64 `json:"revisionNumber"`
}

// A FileContractElement is a record of a FileContract within the state
// accumulator.
type FileContractElement struct {
ID types.FileContractID `json:"id"`
StateElement types.StateElement `json:"stateElement"`
FileContract FileContract `json:"fileContract"`
}

// A EnhancedFileContract is a FileContractElement with added fields for
// resolved/valid state, and when the transaction was confirmed and proved.
type EnhancedFileContract struct {
Resolved bool `json:"resolved"`
Valid bool `json:"valid"`

Expand All @@ -137,7 +166,7 @@ type FileContract struct {
ProofIndex *types.ChainIndex `json:"proofIndex"`
ProofTransactionID *types.TransactionID `json:"proofTransactionID"`

types.FileContractElement
FileContractElement
}

// A FileContractRevision is a FileContract with extra fields for revision
Expand All @@ -146,7 +175,7 @@ type FileContractRevision struct {
ParentID types.FileContractID `json:"parentID"`
UnlockConditions types.UnlockConditions `json:"unlockConditions"`

FileContract
EnhancedFileContract
}

// A Transaction is a transaction that uses the wrapped types above.
Expand All @@ -156,7 +185,7 @@ type Transaction struct {
SiacoinOutputs []SiacoinOutput `json:"siacoinOutputs,omitempty"`
SiafundInputs []SiafundInput `json:"siafundInputs,omitempty"`
SiafundOutputs []SiafundOutput `json:"siafundOutputs,omitempty"`
FileContracts []FileContract `json:"fileContracts,omitempty"`
FileContracts []EnhancedFileContract `json:"fileContracts,omitempty"`
FileContractRevisions []FileContractRevision `json:"fileContractRevisions,omitempty"`
StorageProofs []types.StorageProof `json:"storageProofs,omitempty"`
MinerFees []types.Currency `json:"minerFees,omitempty"`
Expand Down
2 changes: 1 addition & 1 deletion internal/testutil/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func CheckV2ChainIndices(t *testing.T, db explorer.Store, txnID types.Transactio

// CheckFC checks the retrieved file contract with the source file contract in
// addition to checking the resolved and valid fields.
func CheckFC(t *testing.T, revision, resolved, valid bool, expected types.FileContract, got explorer.FileContract) {
func CheckFC(t *testing.T, revision, resolved, valid bool, expected types.FileContract, got explorer.EnhancedFileContract) {
t.Helper()

Equal(t, "resolved state", resolved, got.Resolved)
Expand Down
8 changes: 4 additions & 4 deletions persist/sqlite/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -790,13 +790,13 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl
}
defer revisionStmt.Close()

validOutputsStmt, err := tx.Prepare(`INSERT INTO file_contract_valid_proof_outputs(contract_id, contract_order, address, value) VALUES (?, ?, ?, ?) ON CONFLICT DO NOTHING`)
validOutputsStmt, err := tx.Prepare(`INSERT INTO file_contract_valid_proof_outputs(contract_id, contract_order, id, address, value) VALUES (?, ?, ?, ?, ?) ON CONFLICT DO NOTHING`)
if err != nil {
return nil, fmt.Errorf("addFileContracts: failed to prepare valid proof outputs statement: %w", err)
}
defer validOutputsStmt.Close()

missedOutputsStmt, err := tx.Prepare(`INSERT INTO file_contract_missed_proof_outputs(contract_id, contract_order, address, value) VALUES (?, ?, ?, ?) ON CONFLICT DO NOTHING`)
missedOutputsStmt, err := tx.Prepare(`INSERT INTO file_contract_missed_proof_outputs(contract_id, contract_order, id, address, value) VALUES (?, ?, ?, ?, ?) ON CONFLICT DO NOTHING`)
if err != nil {
return nil, fmt.Errorf("addFileContracts: failed to prepare missed proof outputs statement: %w", err)
}
Expand Down Expand Up @@ -861,12 +861,12 @@ func updateFileContractElements(tx *txn, revert bool, b types.Block, fces []expl
}

for i, sco := range fc.ValidProofOutputs {
if _, err := validOutputsStmt.Exec(dbID, i, encode(sco.Address), encode(sco.Value)); err != nil {
if _, err := validOutputsStmt.Exec(dbID, i, encode(fcID.ValidOutputID(i)), encode(sco.Address), encode(sco.Value)); err != nil {
return fmt.Errorf("updateFileContractElements: failed to execute valid proof outputs statement: %w", err)
}
}
for i, sco := range fc.MissedProofOutputs {
if _, err := missedOutputsStmt.Exec(dbID, i, encode(sco.Address), encode(sco.Value)); err != nil {
if _, err := missedOutputsStmt.Exec(dbID, i, encode(fcID.MissedOutputID(i)), encode(sco.Address), encode(sco.Value)); err != nil {
return fmt.Errorf("updateFileContractElements: failed to execute missed proof outputs statement: %w", err)
}
}
Expand Down
36 changes: 27 additions & 9 deletions persist/sqlite/consensus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,34 @@ func CheckChainIndices(t *testing.T, db explorer.Store, txnID types.TransactionI
}

// CheckFCRevisions checks that the revision numbers for the file contracts match.
func CheckFCRevisions(t *testing.T, confirmationIndex types.ChainIndex, confirmationTransactionID types.TransactionID, valid, missed []types.SiacoinOutput, revisionNumbers []uint64, fcs []explorer.FileContract) {
func CheckFCRevisions(t *testing.T, confirmationIndex types.ChainIndex, confirmationTransactionID types.TransactionID, valid, missed []types.SiacoinOutput, revisionNumbers []uint64, fcs []explorer.EnhancedFileContract) {
t.Helper()

testutil.Equal(t, "number of revisions", len(revisionNumbers), len(fcs))
for i := range revisionNumbers {
testutil.Equal(t, "revision number", revisionNumbers[i], fcs[i].FileContract.RevisionNumber)
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", valid, fcs[i].FileContract.ValidProofOutputs)
testutil.Equal(t, "missed proof outputs", missed, fcs[i].FileContract.MissedProofOutputs)
testutil.Equal(t, "revision number", revisionNumbers[i], fcs[i].FileContract.RevisionNumber)

testutil.Equal(t, "valid proof outputs", len(valid), len(fcs[i].FileContract.ValidProofOutputs))
for j := range valid {
expected := valid[j]
got := fcs[i].FileContract.ValidProofOutputs[j]

testutil.Equal(t, "id", fcs[i].ID.ValidOutputID(j), got.ID)
testutil.Equal(t, "value", expected.Value, got.Value)
testutil.Equal(t, "address", expected.Address, got.Address)
}

testutil.Equal(t, "missed proof outputs", len(missed), len(fcs[i].FileContract.MissedProofOutputs))
for j := range missed {
expected := missed[j]
got := fcs[i].FileContract.MissedProofOutputs[j]

testutil.Equal(t, "id", fcs[i].ID.MissedOutputID(j), got.ID)
testutil.Equal(t, "value", expected.Value, got.Value)
testutil.Equal(t, "address", expected.Address, got.Address)
}
}
}

Expand Down Expand Up @@ -697,7 +715,7 @@ func TestFileContract(t *testing.T) {
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.FileContract)
testutil.CheckFC(t, false, false, false, fc, fcr.EnhancedFileContract)
}

for i := cm.Tip().Height; i < windowEnd; i++ {
Expand Down Expand Up @@ -920,7 +938,7 @@ func TestEphemeralFileContract(t *testing.T) {
testutil.Equal(t, "parent id", txn.FileContractID(0), fcr.ParentID)
testutil.Equal(t, "unlock conditions", uc, fcr.UnlockConditions)

testutil.CheckFC(t, true, false, false, revisedFC1, fcr.FileContract)
testutil.CheckFC(t, true, false, false, revisedFC1, fcr.EnhancedFileContract)
}

revisedFC2 := revisedFC1
Expand Down Expand Up @@ -1002,7 +1020,7 @@ func TestEphemeralFileContract(t *testing.T) {
fcr := txns[0].FileContractRevisions[0]
testutil.Equal(t, "parent id", txn.FileContractID(0), fcr.ParentID)
testutil.Equal(t, "unlock conditions", uc, fcr.UnlockConditions)
testutil.CheckFC(t, true, false, false, revisedFC2, fcr.FileContract)
testutil.CheckFC(t, true, false, false, revisedFC2, fcr.EnhancedFileContract)
}

{
Expand All @@ -1016,7 +1034,7 @@ func TestEphemeralFileContract(t *testing.T) {
fcr := txns[0].FileContractRevisions[0]
testutil.Equal(t, "parent id", txn.FileContractID(0), fcr.ParentID)
testutil.Equal(t, "unlock conditions", uc, fcr.UnlockConditions)
testutil.CheckFC(t, true, false, false, revisedFC3, fcr.FileContract)
testutil.CheckFC(t, true, false, false, revisedFC3, fcr.EnhancedFileContract)
}
}

Expand Down Expand Up @@ -2330,7 +2348,7 @@ func TestMultipleReorgFileContract(t *testing.T) {
testutil.Equal(t, "parent id", txn.FileContractID(0), fcr.ParentID)
testutil.Equal(t, "unlock conditions", uc, fcr.UnlockConditions)

testutil.CheckFC(t, false, false, false, revFC, fcr.FileContract)
testutil.CheckFC(t, false, false, false, revFC, fcr.EnhancedFileContract)
}

{
Expand Down
18 changes: 9 additions & 9 deletions persist/sqlite/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func encodedIDs(ids []types.FileContractID) []any {
return result
}

func scanFileContract(s scanner) (contractID int64, fc explorer.FileContract, err error) {
func scanFileContract(s scanner) (contractID int64, fc explorer.EnhancedFileContract, err error) {
var confirmationIndex, proofIndex types.ChainIndex
var confirmationTransactionID, proofTransactionID types.TransactionID
err = s.Scan(&contractID, decode(&fc.ID), decode(&fc.StateElement.LeafIndex), &fc.Resolved, &fc.Valid, decode(&fc.TransactionID), decodeNull(&confirmationIndex), decodeNull(&confirmationTransactionID), decodeNull(&proofIndex), decodeNull(&proofTransactionID), decode(&fc.FileContract.Filesize), decode(&fc.FileContract.FileMerkleRoot), decode(&fc.FileContract.WindowStart), decode(&fc.FileContract.WindowEnd), decode(&fc.FileContract.Payout), decode(&fc.FileContract.UnlockHash), decode(&fc.FileContract.RevisionNumber))
Expand All @@ -37,7 +37,7 @@ func scanFileContract(s scanner) (contractID int64, fc explorer.FileContract, er
}

// Contracts implements explorer.Store.
func (s *Store) Contracts(ids []types.FileContractID) (result []explorer.FileContract, err error) {
func (s *Store) Contracts(ids []types.FileContractID) (result []explorer.EnhancedFileContract, err error) {
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
Expand All @@ -50,10 +50,10 @@ func (s *Store) Contracts(ids []types.FileContractID) (result []explorer.FileCon
defer rows.Close()

var contractIDs []int64
idContract := make(map[int64]explorer.FileContract)
idContract := make(map[int64]explorer.EnhancedFileContract)
for rows.Next() {
var contractID int64
var fc explorer.FileContract
var fc explorer.EnhancedFileContract

contractID, fc, err := scanFileContract(rows)
if err != nil {
Expand Down Expand Up @@ -82,7 +82,7 @@ func (s *Store) Contracts(ids []types.FileContractID) (result []explorer.FileCon
}

// ContractRevisions implements explorer.Store.
func (s *Store) ContractRevisions(id types.FileContractID) (revisions []explorer.FileContract, err error) {
func (s *Store) ContractRevisions(id types.FileContractID) (revisions []explorer.EnhancedFileContract, err error) {
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
Expand All @@ -98,7 +98,7 @@ func (s *Store) ContractRevisions(id types.FileContractID) (revisions []explorer
// fetch revisions
type fce struct {
ID int64
FileContract explorer.FileContract
FileContract explorer.EnhancedFileContract
}
var fces []fce
var contractIDs []int64
Expand All @@ -119,7 +119,7 @@ func (s *Store) ContractRevisions(id types.FileContractID) (revisions []explorer
}

// merge outputs into revisions
revisions = make([]explorer.FileContract, len(fces))
revisions = make([]explorer.EnhancedFileContract, len(fces))
for i, revision := range fces {
output, found := proofOutputs[revision.ID]
if !found {
Expand All @@ -140,7 +140,7 @@ func (s *Store) ContractRevisions(id types.FileContractID) (revisions []explorer
}

// ContractsKey implements explorer.Store.
func (s *Store) ContractsKey(key types.PublicKey) (result []explorer.FileContract, err error) {
func (s *Store) ContractsKey(key types.PublicKey) (result []explorer.EnhancedFileContract, err error) {
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
Expand All @@ -153,7 +153,7 @@ func (s *Store) ContractsKey(key types.PublicKey) (result []explorer.FileContrac
defer rows.Close()

var contractIDs []int64
idContract := make(map[int64]explorer.FileContract)
idContract := make(map[int64]explorer.EnhancedFileContract)
for rows.Next() {
contractID, fc, err := scanFileContract(rows)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions persist/sqlite/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ CREATE TABLE last_contract_revision (
CREATE TABLE file_contract_valid_proof_outputs (
contract_id INTEGER REFERENCES file_contract_elements(id) ON DELETE CASCADE NOT NULL,
contract_order INTEGER NOT NULL,
id BLOB NOT NULL,
address BLOB NOT NULL,
value BLOB NOT NULL,
UNIQUE(contract_id, contract_order)
Expand All @@ -126,6 +127,7 @@ CREATE INDEX file_contract_valid_proof_outputs_contract_id_index ON file_contrac
CREATE TABLE file_contract_missed_proof_outputs (
contract_id INTEGER REFERENCES file_contract_elements(id) ON DELETE CASCADE NOT NULL,
contract_order INTEGER NOT NULL,
id BLOB NOT NULL,
address BLOB NOT NULL,
value BLOB NOT NULL,
UNIQUE(contract_id, contract_order)
Expand Down
Loading

0 comments on commit 1b9460e

Please sign in to comment.