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

Add usability column to contracts table #1660

Open
wants to merge 9 commits into
base: dev
Choose a base branch
from
6 changes: 6 additions & 0 deletions api/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ const (
ContractStateFailed = "failed"
)

const (
ContractUsabilityBad = "bad"
ContractUsabilityGood = "good"
)

const (
ContractArchivalReasonHostPruned = "hostpruned"
ContractArchivalReasonRemoved = "removed"
Expand Down Expand Up @@ -55,6 +60,7 @@ type (
Size uint64 `json:"size"`
StartHeight uint64 `json:"startHeight"`
State string `json:"state"`
Usability string `json:"usability"`
WindowStart uint64 `json:"windowStart"`
WindowEnd uint64 `json:"windowEnd"`

Expand Down
2 changes: 2 additions & 0 deletions bus/bus.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ func (b *Bus) addContract(ctx context.Context, rev rhpv2.ContractRevision, contr
HostKey: rev.HostKey(),
StartHeight: startHeight,
State: state,
Usability: api.ContractUsabilityGood,
WindowStart: rev.Revision.WindowStart,
WindowEnd: rev.Revision.WindowEnd,
ContractPrice: contractPrice,
Expand Down Expand Up @@ -575,6 +576,7 @@ func (b *Bus) addRenewal(ctx context.Context, renewedFrom types.FileContractID,
RenewedFrom: renewedFrom,
StartHeight: startHeight,
State: state,
Usability: api.ContractUsabilityGood,
WindowStart: rev.Revision.WindowStart,
WindowEnd: rev.Revision.WindowEnd,
ContractPrice: contractPrice,
Expand Down
14 changes: 10 additions & 4 deletions internal/sql/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,12 @@ var (
return performMigration(ctx, tx, migrationsFs, dbIdentifier, "00019_settings", log)
},
},
{
ID: "00019_scan_reset",
Migrate: func(tx Tx) error {
return performMigration(ctx, tx, migrationsFs, dbIdentifier, "00019_scan_reset", log)
},
}, // NOTE: duplicate ID (00019) due to updating core deps directly on master
{
ID: "00020_idx_db_directory",
Migrate: func(tx Tx) error {
Expand Down Expand Up @@ -339,15 +345,15 @@ var (
},
},
{
ID: "00019_scan_reset",
ID: "00026_key_prefix",
Migrate: func(tx Tx) error {
return performMigration(ctx, tx, migrationsFs, dbIdentifier, "00019_scan_reset", log)
return performMigration(ctx, tx, migrationsFs, dbIdentifier, "00026_key_prefix", log)
},
},
{
ID: "00026_key_prefix",
ID: "00027_contract_usability",
Migrate: func(tx Tx) error {
return performMigration(ctx, tx, migrationsFs, dbIdentifier, "00026_key_prefix", log)
return performMigration(ctx, tx, migrationsFs, dbIdentifier, "00027_contract_usability", log)
},
},
}
Expand Down
9 changes: 9 additions & 0 deletions internal/test/e2e/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ func TestNewTestCluster(t *testing.T) {
t.Fatal("wrong endHeight", contract.EndHeight(), revision.EndHeight())
} else if contract.InitialRenterFunds.IsZero() || contract.ContractPrice.IsZero() {
t.Fatal("InitialRenterFunds and ContractPrice shouldn't be zero")
} else if contract.Usability != api.ContractUsabilityGood {
t.Fatal("contract should be good")
}

// Wait for contract set to form
Expand Down Expand Up @@ -234,6 +236,9 @@ func TestNewTestCluster(t *testing.T) {
if contracts[0].State != api.ContractStatePending {
return fmt.Errorf("contract should be pending but was %v", contracts[0].State)
}
if contracts[0].Usability != api.ContractUsabilityGood {
return fmt.Errorf("contract should be good but was %v", contracts[0].Usability)
}
renewalID = contracts[0].ID
return nil
})
Expand Down Expand Up @@ -267,6 +272,9 @@ func TestNewTestCluster(t *testing.T) {
if ac.State != api.ContractStateComplete {
return fmt.Errorf("contract should be complete but was %v", ac.State)
}
if ac.Usability != api.ContractUsabilityBad {
return fmt.Errorf("contract should be bad but was %v", ac.Usability)
}
return nil
})

Expand Down Expand Up @@ -1474,6 +1482,7 @@ func TestUnconfirmedContractArchival(t *testing.T) {
HostKey: c.HostKey,
StartHeight: cs.BlockHeight,
State: api.ContractStatePending,
Usability: api.ContractUsabilityGood,
WindowStart: math.MaxUint32,
WindowEnd: math.MaxUint32 + 10,
ContractPrice: types.NewCurrency64(1),
Expand Down
1 change: 1 addition & 0 deletions stores/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ func (s *SQLStore) AddRenewal(ctx context.Context, c api.ContractMetadata) error
// reinsert renewed contract
renewed.ArchivalReason = api.ContractArchivalReasonRenewed
renewed.RenewedTo = c.ID
renewed.Usability = api.ContractUsabilityBad
return tx.PutContract(ctx, renewed)
})
}
Expand Down
7 changes: 7 additions & 0 deletions stores/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ func TestSQLContractStore(t *testing.T) {
Size: 4,
StartHeight: 5,
State: api.ContractStateActive,
Usability: api.ContractUsabilityGood,
WindowStart: 6,
WindowEnd: 7,

Expand Down Expand Up @@ -574,6 +575,7 @@ func TestAncestorsContracts(t *testing.T) {
expected := newTestContract(fcids[len(fcids)-2-i], hk)
expected.RenewedFrom = renewedFrom
expected.RenewedTo = renewedTo
expected.Usability = api.ContractUsabilityBad
expected.ArchivalReason = api.ContractArchivalReasonRenewed
expected.StartHeight = uint64(len(fcids) - 2 - i)
if !reflect.DeepEqual(contracts[i], expected) {
Expand Down Expand Up @@ -658,6 +660,7 @@ func newTestContract(fcid types.FileContractID, hk types.PublicKey) api.Contract
ID: fcid,
HostKey: hk,
State: api.ContractStatePending,
Usability: api.ContractUsabilityGood,
ContractPrice: types.NewCurrency64(1),
InitialRenterFunds: types.NewCurrency64(2),
}
Expand Down Expand Up @@ -835,6 +838,7 @@ func TestSQLMetadataStore(t *testing.T) {
ID: fcid1,
HostKey: hk1,
State: api.ContractStatePending,
Usability: api.ContractUsabilityGood,
ContractPrice: types.NewCurrency64(1),
InitialRenterFunds: types.NewCurrency64(2),
}
Expand All @@ -857,6 +861,7 @@ func TestSQLMetadataStore(t *testing.T) {
ID: fcid2,
HostKey: hk2,
State: api.ContractStatePending,
Usability: api.ContractUsabilityGood,
ContractPrice: types.NewCurrency64(1),
InitialRenterFunds: types.NewCurrency64(2),
}
Expand Down Expand Up @@ -4783,6 +4788,7 @@ func TestPutContract(t *testing.T) {
Size: 6,
StartHeight: 7,
State: api.ContractStateComplete,
Usability: api.ContractUsabilityGood,
WindowStart: 8,
WindowEnd: 9,

Expand Down Expand Up @@ -4822,6 +4828,7 @@ func TestPutContract(t *testing.T) {
Size: 21,
StartHeight: 22,
State: api.ContractStateFailed,
Usability: api.ContractUsabilityGood,
WindowStart: 23,
WindowEnd: 24,

Expand Down
37 changes: 34 additions & 3 deletions stores/sql/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import (
)

var (
ErrInvalidContractState = errors.New("invalid contract state")
ErrInvalidContractState = errors.New("invalid contract state")
ErrInvalidContractUsability = errors.New("invalid contract usability")
)

type ContractState uint8
Expand Down Expand Up @@ -40,8 +41,6 @@ func ContractStateFromString(state string) ContractState {

func (s *ContractState) LoadString(state string) error {
switch strings.ToLower(state) {
case api.ContractStateInvalid:
*s = contractStateInvalid
case api.ContractStatePending:
*s = contractStatePending
case api.ContractStateActive:
Expand Down Expand Up @@ -73,3 +72,35 @@ func (s ContractState) String() string {
return api.ContractStateUnknown
}
}

type ContractUsability uint8

const (
contractUsabilityInvalid ContractUsability = iota
contractUsabilityBad
contractUsabilityGood
)

func (s *ContractUsability) LoadString(usability string) error {
switch strings.ToLower(usability) {
case api.ContractUsabilityBad:
*s = contractUsabilityBad
case api.ContractUsabilityGood:
*s = contractUsabilityGood
default:
*s = contractUsabilityInvalid
return ErrInvalidContractUsability
}
return nil
}

func (s ContractUsability) String() string {
switch s {
case contractUsabilityBad:
return api.ContractUsabilityBad
case contractUsabilityGood:
return api.ContractUsabilityGood
default:
return "invalid"
}
}
15 changes: 10 additions & 5 deletions stores/sql/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ func AncestorContracts(ctx context.Context, tx sql.Tx, fcid types.FileContractID
)
SELECT
c.fcid, c.host_id, c.host_key, c.v2,
c.archival_reason, c.proof_height, c.renewed_from, c.renewed_to, c.revision_height, c.revision_number, c.size, c.start_height, c.state, c.window_start, c.window_end,
c.archival_reason, c.proof_height, c.renewed_from, c.renewed_to, c.revision_height, c.revision_number, c.size, c.start_height, c.state, c.usability, c.window_start, c.window_end,
c.contract_price, c.initial_renter_funds,
c.delete_spending, c.fund_account_spending, c.sector_roots_spending, c.upload_spending,
"", COALESCE(h.net_address, ""), COALESCE(h.settings->>'$.siamuxport', "")
Expand Down Expand Up @@ -1916,7 +1916,7 @@ func QueryContracts(ctx context.Context, tx sql.Tx, whereExprs []string, whereAr
rows, err := tx.Query(ctx, fmt.Sprintf(`
SELECT
c.fcid, c.host_id, c.host_key, c.v2,
c.archival_reason, c.proof_height, c.renewed_from, c.renewed_to, c.revision_height, c.revision_number, c.size, c.start_height, c.state, c.window_start, c.window_end,
c.archival_reason, c.proof_height, c.renewed_from, c.renewed_to, c.revision_height, c.revision_number, c.size, c.start_height, c.state, c.usability, c.window_start, c.window_end,
c.contract_price, c.initial_renter_funds,
c.delete_spending, c.fund_account_spending, c.sector_roots_spending, c.upload_spending,
COALESCE(cs.name, ""), COALESCE(h.net_address, ""), COALESCE(h.settings->>'$.siamuxport', "") AS siamux_port
Expand Down Expand Up @@ -2165,7 +2165,12 @@ func UpdateContract(ctx context.Context, tx sql.Tx, fcid types.FileContractID, c
var state ContractState
if err := state.LoadString(c.State); err != nil {
return err
} else if c.ID == (types.FileContractID{}) {
}
var usability ContractUsability
if err := usability.LoadString(c.Usability); err != nil {
return err
}
if c.ID == (types.FileContractID{}) {
return errors.New("contract id is required")
} else if c.HostKey == (types.PublicKey{}) {
return errors.New("host key is required")
Expand All @@ -2175,12 +2180,12 @@ func UpdateContract(ctx context.Context, tx sql.Tx, fcid types.FileContractID, c
_, err := tx.Exec(ctx, `
UPDATE contracts SET
created_at = ?, fcid = ?,
proof_height = ?, renewed_from = ?, revision_height = ?, revision_number = ?, size = ?, start_height = ?, state = ?, window_start = ?, window_end = ?,
proof_height = ?, renewed_from = ?, revision_height = ?, revision_number = ?, size = ?, start_height = ?, state = ?, usability = ?, window_start = ?, window_end = ?,
contract_price = ?, initial_renter_funds = ?,
delete_spending = ?, fund_account_spending = ?, sector_roots_spending = ?, upload_spending = ?
WHERE fcid = ?`,
time.Now(), FileContractID(c.ID),
0, FileContractID(c.RenewedFrom), 0, fmt.Sprint(c.RevisionNumber), c.Size, c.StartHeight, state, c.WindowStart, c.WindowEnd,
0, FileContractID(c.RenewedFrom), 0, fmt.Sprint(c.RevisionNumber), c.Size, c.StartHeight, state, usability, c.WindowStart, c.WindowEnd,
Currency(c.ContractPrice), Currency(c.InitialRenterFunds),
ZeroCurrency, ZeroCurrency, ZeroCurrency, ZeroCurrency,
FileContractID(c.RenewedFrom),
Expand Down
15 changes: 10 additions & 5 deletions stores/sql/mysql/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,12 @@ func (tx *MainDatabaseTx) PutContract(ctx context.Context, c api.ContractMetadat
var state ssql.ContractState
if err := state.LoadString(c.State); err != nil {
return err
} else if c.ID == (types.FileContractID{}) {
}
var usability ssql.ContractUsability
if err := usability.LoadString(c.Usability); err != nil {
return err
}
if c.ID == (types.FileContractID{}) {
return errors.New("contract id is required")
} else if c.HostKey == (types.PublicKey{}) {
return errors.New("host key is required")
Expand All @@ -750,17 +755,17 @@ func (tx *MainDatabaseTx) PutContract(ctx context.Context, c api.ContractMetadat
_, err = tx.Exec(ctx, `
INSERT INTO contracts (
created_at, fcid, host_id, host_key, v2,
archival_reason, proof_height, renewed_from, renewed_to, revision_height, revision_number, size, start_height, state, window_start, window_end,
archival_reason, proof_height, renewed_from, renewed_to, revision_height, revision_number, size, start_height, state, usability, window_start, window_end,
contract_price, initial_renter_funds,
delete_spending, fund_account_spending, sector_roots_spending, upload_spending
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
created_at = VALUES(created_at), fcid = VALUES(fcid), host_id = VALUES(host_id), host_key = VALUES(host_key), v2 = VALUES(v2),
archival_reason = VALUES(archival_reason), proof_height = VALUES(proof_height), renewed_from = VALUES(renewed_from), renewed_to = VALUES(renewed_to), revision_height = VALUES(revision_height), revision_number = VALUES(revision_number), size = VALUES(size), start_height = VALUES(start_height), state = VALUES(state), window_start = VALUES(window_start), window_end = VALUES(window_end),
archival_reason = VALUES(archival_reason), proof_height = VALUES(proof_height), renewed_from = VALUES(renewed_from), renewed_to = VALUES(renewed_to), revision_height = VALUES(revision_height), revision_number = VALUES(revision_number), size = VALUES(size), start_height = VALUES(start_height), state = VALUES(state), usability = VALUES(usability), window_start = VALUES(window_start), window_end = VALUES(window_end),
contract_price = VALUES(contract_price), initial_renter_funds = VALUES(initial_renter_funds),
delete_spending = VALUES(delete_spending), fund_account_spending = VALUES(fund_account_spending), sector_roots_spending = VALUES(sector_roots_spending), upload_spending = VALUES(upload_spending)`,
time.Now(), ssql.FileContractID(c.ID), hostID, ssql.PublicKey(c.HostKey), c.V2,
ssql.NullableString(c.ArchivalReason), c.ProofHeight, ssql.FileContractID(c.RenewedFrom), ssql.FileContractID(c.RenewedTo), c.RevisionHeight, c.RevisionNumber, c.Size, c.StartHeight, state, c.WindowStart, c.WindowEnd,
ssql.NullableString(c.ArchivalReason), c.ProofHeight, ssql.FileContractID(c.RenewedFrom), ssql.FileContractID(c.RenewedTo), c.RevisionHeight, c.RevisionNumber, c.Size, c.StartHeight, state, usability, c.WindowStart, c.WindowEnd,
ssql.Currency(c.ContractPrice), ssql.Currency(c.InitialRenterFunds),
ssql.Currency(c.Spending.Deletions), ssql.Currency(c.Spending.FundAccount), ssql.Currency(c.Spending.SectorRoots), ssql.Currency(c.Spending.Uploads),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ALTER TABLE contracts ADD COLUMN usability tinyint unsigned;
CREATE INDEX `idx_contracts_usability` ON contracts (`usability`);
UPDATE contracts SET usability = CASE WHEN archival_reason IS NULL THEN 2 ELSE 1 END;
ALTER TABLE contracts MODIFY COLUMN usability tinyint unsigned NOT NULL;
2 changes: 2 additions & 0 deletions stores/sql/mysql/migrations/main/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ CREATE TABLE `contracts` (
`size` bigint unsigned DEFAULT NULL,
`start_height` bigint unsigned NOT NULL,
`state` tinyint unsigned NOT NULL DEFAULT '0',
`usability` tinyint unsigned NOT NULL,
`window_start` bigint unsigned NOT NULL DEFAULT '0',
`window_end` bigint unsigned NOT NULL DEFAULT '0',

Expand All @@ -110,6 +111,7 @@ CREATE TABLE `contracts` (
KEY `idx_contracts_revision_height` (`revision_height`),
KEY `idx_contracts_start_height` (`start_height`),
KEY `idx_contracts_state` (`state`),
KEY `idx_contracts_usability` (`usability`),
KEY `idx_contracts_window_start` (`window_start`),
KEY `idx_contracts_window_end` (`window_end`),
CONSTRAINT `fk_contracts_host` FOREIGN KEY (`host_id`) REFERENCES `hosts` (`id`)
Expand Down
4 changes: 3 additions & 1 deletion stores/sql/rows.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type ContractRow struct {
Size uint64
StartHeight uint64
State ContractState
Usability ContractUsability
WindowStart uint64
WindowEnd uint64

Expand All @@ -48,7 +49,7 @@ type ContractRow struct {
func (r *ContractRow) Scan(s Scanner) error {
return s.Scan(
&r.FCID, &r.HostID, &r.HostKey, &r.V2,
&r.ArchivalReason, &r.ProofHeight, &r.RenewedFrom, &r.RenewedTo, &r.RevisionHeight, &r.RevisionNumber, &r.Size, &r.StartHeight, &r.State, &r.WindowStart, &r.WindowEnd,
&r.ArchivalReason, &r.ProofHeight, &r.RenewedFrom, &r.RenewedTo, &r.RevisionHeight, &r.RevisionNumber, &r.Size, &r.StartHeight, &r.State, &r.Usability, &r.WindowStart, &r.WindowEnd,
&r.ContractPrice, &r.InitialRenterFunds,
&r.DeleteSpending, &r.FundAccountSpending, &r.SectorRootsSpending, &r.UploadSpending,
&r.ContractSet, &r.NetAddress, &r.SiamuxPort,
Expand Down Expand Up @@ -97,6 +98,7 @@ func (r *ContractRow) ContractMetadata() api.ContractMetadata {
Spending: spending,
StartHeight: r.StartHeight,
State: r.State.String(),
Usability: r.Usability.String(),
WindowStart: r.WindowStart,
WindowEnd: r.WindowEnd,
}
Expand Down
15 changes: 10 additions & 5 deletions stores/sql/sqlite/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,12 @@ func (tx *MainDatabaseTx) PutContract(ctx context.Context, c api.ContractMetadat
var state ssql.ContractState
if err := state.LoadString(c.State); err != nil {
return err
} else if c.ID == (types.FileContractID{}) {
}
var usability ssql.ContractUsability
if err := usability.LoadString(c.Usability); err != nil {
return err
}
if c.ID == (types.FileContractID{}) {
return errors.New("contract id is required")
} else if c.HostKey == (types.PublicKey{}) {
return errors.New("host key is required")
Expand All @@ -760,17 +765,17 @@ func (tx *MainDatabaseTx) PutContract(ctx context.Context, c api.ContractMetadat
_, err = tx.Exec(ctx, `
INSERT INTO contracts (
created_at, fcid, host_id, host_key, v2,
archival_reason, proof_height, renewed_from, renewed_to, revision_height, revision_number, size, start_height, state, window_start, window_end,
archival_reason, proof_height, renewed_from, renewed_to, revision_height, revision_number, size, start_height, state, usability, window_start, window_end,
contract_price, initial_renter_funds,
delete_spending, fund_account_spending, sector_roots_spending, upload_spending
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(fcid) DO UPDATE SET
fcid = EXCLUDED.fcid, host_id = EXCLUDED.host_id, host_key = EXCLUDED.host_key, v2 = EXCLUDED.v2,
archival_reason = EXCLUDED.archival_reason, proof_height = EXCLUDED.proof_height, renewed_from = EXCLUDED.renewed_from, renewed_to = EXCLUDED.renewed_to, revision_height = EXCLUDED.revision_height, revision_number = EXCLUDED.revision_number, size = EXCLUDED.size, start_height = EXCLUDED.start_height, state = EXCLUDED.state, window_start = EXCLUDED.window_start, window_end = EXCLUDED.window_end,
archival_reason = EXCLUDED.archival_reason, proof_height = EXCLUDED.proof_height, renewed_from = EXCLUDED.renewed_from, renewed_to = EXCLUDED.renewed_to, revision_height = EXCLUDED.revision_height, revision_number = EXCLUDED.revision_number, size = EXCLUDED.size, start_height = EXCLUDED.start_height, state = EXCLUDED.state, usability = EXCLUDED.usability, window_start = EXCLUDED.window_start, window_end = EXCLUDED.window_end,
contract_price = EXCLUDED.contract_price, initial_renter_funds = EXCLUDED.initial_renter_funds,
delete_spending = EXCLUDED.delete_spending, fund_account_spending = EXCLUDED.fund_account_spending, sector_roots_spending = EXCLUDED.sector_roots_spending, upload_spending = EXCLUDED.upload_spending`,
time.Now(), ssql.FileContractID(c.ID), hostID, ssql.PublicKey(c.HostKey), c.V2,
ssql.NullableString(c.ArchivalReason), c.ProofHeight, ssql.FileContractID(c.RenewedFrom), ssql.FileContractID(c.RenewedTo), c.RevisionHeight, c.RevisionNumber, c.Size, c.StartHeight, state, c.WindowStart, c.WindowEnd,
ssql.NullableString(c.ArchivalReason), c.ProofHeight, ssql.FileContractID(c.RenewedFrom), ssql.FileContractID(c.RenewedTo), c.RevisionHeight, c.RevisionNumber, c.Size, c.StartHeight, state, usability, c.WindowStart, c.WindowEnd,
ssql.Currency(c.ContractPrice), ssql.Currency(c.InitialRenterFunds),
ssql.Currency(c.Spending.Deletions), ssql.Currency(c.Spending.FundAccount), ssql.Currency(c.Spending.SectorRoots), ssql.Currency(c.Spending.Uploads),
)
Expand Down
Loading
Loading