From 3ee79e69a3b88aa43d3e8ea6aa97a7324eb92bcb Mon Sep 17 00:00:00 2001 From: Nate Maninger Date: Wed, 10 Jan 2024 11:17:07 -0800 Subject: [PATCH] ci: fix lint errors --- .github/actions/test/action.yml | 8 +- .golangci.yml | 161 ++++++++++++++++++++++++++++++++ persist/sqlite/consensus.go | 3 + persist/sqlite/types.go | 1 + persist/sqlite/wallet.go | 12 +++ wallet/manager.go | 2 + wallet/state.go | 83 ---------------- wallet/wallet.go | 16 +++- 8 files changed, 196 insertions(+), 90 deletions(-) create mode 100644 .golangci.yml delete mode 100644 wallet/state.go diff --git a/.github/actions/test/action.yml b/.github/actions/test/action.yml index c44a2c0..9631727 100644 --- a/.github/actions/test/action.yml +++ b/.github/actions/test/action.yml @@ -7,10 +7,10 @@ runs: - name: Configure git # required for golangci-lint on Windows shell: bash run: git config --global core.autocrlf false -# - name: Lint -# uses: golangci/golangci-lint-action@v3 -# with: -# skip-cache: true + - name: Lint + uses: golangci/golangci-lint-action@v3 + with: + skip-cache: true - name: Analyze uses: SiaFoundation/action-golang-analysis@HEAD with: diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..ca4188f --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,161 @@ +# Based off of the example file at https://github.com/golangci/golangci-lint + +# options for analysis running +run: + # default concurrency is a available CPU number + concurrency: 4 + + # timeout for analysis, e.g. 30s, 5m, default is 1m + timeout: 600s + + # exit code when at least one issue was found, default is 1 + issues-exit-code: 1 + + # include test files or not, default is true + tests: true + + # list of build tags, all linters use it. Default is empty list. + build-tags: [] + + # which dirs to skip: issues from them won't be reported; + # can use regexp here: generated.*, regexp is applied on full path; + # default value is empty list, but default dirs are skipped independently + # from this option's value (see skip-dirs-use-default). + skip-dirs: + - cover + + # default is true. Enables skipping of directories: + # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ + skip-dirs-use-default: true + + # which files to skip: they will be analyzed, but issues from them + # won't be reported. Default value is empty list, but there is + # no need to include all autogenerated files, we confidently recognize + # autogenerated files. If it's not please let us know. + skip-files: [] + +# output configuration options +output: + # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" + format: colored-line-number + + # print lines of code with issue, default is true + print-issued-lines: true + + # print linter name in the end of issue text, default is true + print-linter-name: true + +# all available settings of specific linters +linters-settings: + ## Enabled linters: + govet: + # report about shadowed variables + check-shadowing: false + disable-all: false + + tagliatelle: + case: + rules: + json: goCamel + yaml: goCamel + + + gocritic: + # Which checks should be enabled; can't be combined with 'disabled-checks'; + # See https://go-critic.github.io/overview#checks-overview + # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` + # By default list of stable checks is used. + enabled-checks: + - argOrder # Diagnostic options + - badCond + - caseOrder + - dupArg + - dupBranchBody + - dupCase + - dupSubExpr + - nilValReturn + - offBy1 + - weakCond + - boolExprSimplify # Style options here and below. + - builtinShadow + - emptyFallthrough + - hexLiteral + - underef + - equalFold + revive: + ignore-generated-header: true + rules: + - name: blank-imports + disabled: false + - name: bool-literal-in-expr + disabled: false + - name: confusing-results + disabled: false + - name: constant-logical-expr + disabled: false + - name: context-as-argument + disabled: false + - name: exported + disabled: false + - name: errorf + disabled: false + - name: if-return + disabled: false + - name: indent-error-flow + disabled: false + - name: increment-decrement + disabled: false + - name: modifies-value-receiver + disabled: false + - name: optimize-operands-order + disabled: false + - name: range-val-in-closure + disabled: false + - name: struct-tag + disabled: false + - name: superfluous-else + disabled: false + - name: time-equal + disabled: false + - name: unexported-naming + disabled: false + - name: unexported-return + disabled: false + - name: unnecessary-stmt + disabled: false + - name: unreachable-code + disabled: false + - name: package-comments + disabled: true + +linters: + disable-all: true + fast: false + enable: + - tagliatelle + - gocritic + - gofmt + - revive + - govet + - misspell + - typecheck + - whitespace + +issues: + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-issues-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 + + # List of regexps of issue texts to exclude, empty list by default. + # But independently from this option we use default exclude patterns, + # it can be disabled by `exclude-use-default: false`. To list all + # excluded by default patterns execute `golangci-lint run --help` + exclude: [] + + # Independently from option `exclude` we use default exclude patterns, + # it can be disabled by this option. To list all + # excluded by default patterns execute `golangci-lint run --help`. + # Default value for this option is true. + exclude-use-default: false \ No newline at end of file diff --git a/persist/sqlite/consensus.go b/persist/sqlite/consensus.go index 0978dab..986b2da 100644 --- a/persist/sqlite/consensus.go +++ b/persist/sqlite/consensus.go @@ -314,6 +314,7 @@ func updateElementProofs(tx txn, table string, updater proofUpdater) error { return nil } +// applyChainUpdates applies the given chain updates to the database. func applyChainUpdates(tx txn, updates []*chain.ApplyUpdate) error { stmt, err := tx.Prepare(`SELECT id FROM sia_addresses WHERE sia_address=$1 LIMIT 1`) if err != nil { @@ -399,6 +400,7 @@ func applyChainUpdates(tx txn, updates []*chain.ApplyUpdate) error { return nil } +// ProcessChainApplyUpdate implements chain.Subscriber func (s *Store) ProcessChainApplyUpdate(cau *chain.ApplyUpdate, mayCommit bool) error { s.updates = append(s.updates, cau) @@ -414,6 +416,7 @@ func (s *Store) ProcessChainApplyUpdate(cau *chain.ApplyUpdate, mayCommit bool) return nil } +// ProcessChainRevertUpdate implements chain.Subscriber func (s *Store) ProcessChainRevertUpdate(cru *chain.RevertUpdate) error { // update hasn't been committed yet if len(s.updates) > 0 && s.updates[len(s.updates)-1].Block.ID() == cru.Block.ID() { diff --git a/persist/sqlite/types.go b/persist/sqlite/types.go index 44f1f10..31083c6 100644 --- a/persist/sqlite/types.go +++ b/persist/sqlite/types.go @@ -115,6 +115,7 @@ type decodable[T types.DecoderFrom] struct { n int64 } +// Scan implements the sql.Scanner interface. func (d *decodable[T]) Scan(src any) error { switch src := src.(type) { case []byte: diff --git a/persist/sqlite/wallet.go b/persist/sqlite/wallet.go index 56ba3fd..8306e1c 100644 --- a/persist/sqlite/wallet.go +++ b/persist/sqlite/wallet.go @@ -19,6 +19,7 @@ RETURNING id` return } +// WalletEvents returns the events relevant to a wallet, sorted by height descending. func (s *Store) WalletEvents(walletID string, offset, limit int) (events []wallet.Event, err error) { err = s.transaction(func(tx txn) error { const query = `SELECT ev.id, ev.date_created, ci.height, ci.block_id, ev.event_type, ev.event_data @@ -76,6 +77,7 @@ LIMIT $2 OFFSET $3` return } +// AddWallet adds a wallet to the database. func (s *Store) AddWallet(name string, info json.RawMessage) error { return s.transaction(func(tx txn) error { const query = `INSERT INTO wallets (id, extra_data) VALUES ($1, $2)` @@ -88,6 +90,8 @@ func (s *Store) AddWallet(name string, info json.RawMessage) error { }) } +// DeleteWallet deletes a wallet from the database. This does not stop tracking +// addresses that were previously associated with the wallet. func (s *Store) DeleteWallet(name string) error { return s.transaction(func(tx txn) error { _, err := tx.Exec(`DELETE FROM wallets WHERE id=$1`, name) @@ -95,6 +99,7 @@ func (s *Store) DeleteWallet(name string) error { }) } +// Wallets returns a map of wallet names to wallet extra data. func (s *Store) Wallets() (map[string]json.RawMessage, error) { wallets := make(map[string]json.RawMessage) err := s.transaction(func(tx txn) error { @@ -119,6 +124,7 @@ func (s *Store) Wallets() (map[string]json.RawMessage, error) { return wallets, err } +// AddAddress adds an address to a wallet. func (s *Store) AddAddress(walletID string, address types.Address, info json.RawMessage) error { return s.transaction(func(tx txn) error { addressID, err := insertAddress(tx, address) @@ -130,6 +136,8 @@ func (s *Store) AddAddress(walletID string, address types.Address, info json.Raw }) } +// RemoveAddress removes an address from a wallet. This does not stop tracking +// the address. func (s *Store) RemoveAddress(walletID string, address types.Address) error { return s.transaction(func(tx txn) error { const query = `DELETE FROM wallet_addresses WHERE wallet_id=$1 AND address_id=(SELECT id FROM sia_addresses WHERE sia_address=$2)` @@ -138,6 +146,7 @@ func (s *Store) RemoveAddress(walletID string, address types.Address) error { }) } +// Addresses returns a map of addresses to their extra data for a wallet. func (s *Store) Addresses(walletID string) (map[types.Address]json.RawMessage, error) { addresses := make(map[types.Address]json.RawMessage) err := s.transaction(func(tx txn) error { @@ -165,6 +174,7 @@ WHERE wa.wallet_id=$1` return addresses, err } +// UnspentSiacoinOutputs returns the unspent siacoin outputs for a wallet. func (s *Store) UnspentSiacoinOutputs(walletID string) (siacoins []types.SiacoinElement, err error) { err = s.transaction(func(tx txn) error { const query = `SELECT se.id, se.leaf_index, se.merkle_proof, se.siacoin_value, sa.sia_address, se.maturity_height @@ -193,6 +203,7 @@ func (s *Store) UnspentSiacoinOutputs(walletID string) (siacoins []types.Siacoin return } +// UnspentSiafundOutputs returns the unspent siafund outputs for a wallet. func (s *Store) UnspentSiafundOutputs(walletID string) (siafunds []types.SiafundElement, err error) { err = s.transaction(func(tx txn) error { const query = `SELECT se.id, se.leaf_index, se.merkle_proof, se.siafund_value, se.claim_start, sa.sia_address @@ -257,6 +268,7 @@ func (s *Store) AddressBalance(address types.Address) (sc types.Currency, sf uin return } +// Annotate annotates a list of transactions using the wallet's addresses. func (s *Store) Annotate(walletID string, txns []types.Transaction) (annotated []wallet.PoolTransaction, err error) { err = s.transaction(func(tx txn) error { stmt, err := tx.Prepare(`SELECT sia_address FROM wallet_addresses WHERE wallet_id=$1 AND sia_address=$2 LIMIT 1`) diff --git a/wallet/manager.go b/wallet/manager.go index e4a2380..e59eb15 100644 --- a/wallet/manager.go +++ b/wallet/manager.go @@ -13,6 +13,7 @@ import ( ) type ( + // A ChainManager manages the consensus state ChainManager interface { AddSubscriber(chain.Subscriber, types.ChainIndex) error RemoveSubscriber(chain.Subscriber) @@ -20,6 +21,7 @@ type ( BestIndex(height uint64) (types.ChainIndex, bool) } + // A Store is a persistent store of wallet data. Store interface { chain.Subscriber diff --git a/wallet/state.go b/wallet/state.go deleted file mode 100644 index 4c0afb9..0000000 --- a/wallet/state.go +++ /dev/null @@ -1,83 +0,0 @@ -package wallet - -import ( - "go.sia.tech/core/chain" - "go.sia.tech/core/types" -) - -// A Midstate is a snapshot of unapplied consensus changes. -type Midstate struct { - SpentSiacoinOutputs map[types.Hash256]bool - SpentSiafundOutputs map[types.Hash256]bool - - NewSiacoinOutputs map[types.Hash256]types.SiacoinElement - NewSiafundOutputs map[types.Hash256]types.SiafundElement - - Events []Event -} - -func (ms *Midstate) Apply(cau *chain.ApplyUpdate, ownsAddress func(types.Address) bool) { - events := AppliedEvents(cau.State, cau.Block, cau, ownsAddress) - ms.Events = append(ms.Events, events...) - - cau.ForEachSiacoinElement(func(se types.SiacoinElement, spent bool) { - if !ownsAddress(se.SiacoinOutput.Address) { - return - } - - if spent { - ms.SpentSiacoinOutputs[se.ID] = true - delete(ms.NewSiacoinOutputs, se.ID) - } else { - ms.NewSiacoinOutputs[se.ID] = se - } - }) - - cau.ForEachSiafundElement(func(sf types.SiafundElement, spent bool) { - if !ownsAddress(sf.SiafundOutput.Address) { - return - } - - if spent { - ms.SpentSiafundOutputs[sf.ID] = true - delete(ms.NewSiafundOutputs, sf.ID) - } else { - ms.NewSiafundOutputs[sf.ID] = sf - } - }) -} - -func (ms *Midstate) Revert(cru *chain.RevertUpdate, ownsAddress func(types.Address) bool) { - revertedBlockID := cru.Block.ID() - for i := len(ms.Events) - 1; i >= 0; i-- { - // working backwards, revert all events until the block ID no longer - // matches. - if ms.Events[i].Index.ID != revertedBlockID { - break - } - ms.Events = ms.Events[:i] - } - - cru.ForEachSiacoinElement(func(se types.SiacoinElement, spent bool) { - if !ownsAddress(se.SiacoinOutput.Address) { - return - } - - if !spent { - delete(ms.SpentSiacoinOutputs, se.ID) - } - }) - - cru.ForEachSiafundElement(func(sf types.SiafundElement, spent bool) { - if !ownsAddress(sf.SiafundOutput.Address) { - return - } - - if spent { - ms.SpentSiafundOutputs[sf.ID] = true - delete(ms.NewSiafundOutputs, sf.ID) - } else { - ms.NewSiafundOutputs[sf.ID] = sf - } - }) -} diff --git a/wallet/wallet.go b/wallet/wallet.go index d1a458c..7a806b1 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -9,8 +9,8 @@ import ( "go.sia.tech/core/types" ) +// event type constants const ( - // transactions EventTypeTransaction = "transaction" EventTypeMinerPayout = "miner payout" EventTypeMissedFileContract = "missed file contract" @@ -153,8 +153,13 @@ type Event struct { Val interface{ EventType() string } } -func (*EventTransaction) EventType() string { return EventTypeTransaction } -func (*EventMinerPayout) EventType() string { return EventTypeMinerPayout } +// EventType implements Event. +func (*EventTransaction) EventType() string { return EventTypeTransaction } + +// EventType implements Event. +func (*EventMinerPayout) EventType() string { return EventTypeMinerPayout } + +// EventType implements Event. func (*EventMissedFileContract) EventType() string { return EventTypeMissedFileContract } // MarshalJSON implements json.Marshaler. @@ -235,6 +240,7 @@ type V2FileContract struct { Outputs []types.SiacoinElement `json:"outputs,omitempty"` } +// An EventTransaction represents a transaction that affects the wallet. type EventTransaction struct { ID types.TransactionID `json:"id"` SiacoinInputs []types.SiacoinElement `json:"siacoinInputs"` @@ -247,15 +253,19 @@ type EventTransaction struct { Fee types.Currency `json:"fee"` } +// An EventMinerPayout represents a miner payout from a block. type EventMinerPayout struct { SiacoinOutput types.SiacoinElement `json:"siacoinOutput"` } +// An EventMissedFileContract represents a file contract that has expired +// without a storage proof type EventMissedFileContract struct { FileContract types.FileContractElement `json:"fileContract"` MissedOutputs []types.SiacoinElement `json:"missedOutputs"` } +// A ChainUpdate is a set of changes to the consensus state. type ChainUpdate interface { ForEachSiacoinElement(func(sce types.SiacoinElement, spent bool)) ForEachSiafundElement(func(sfe types.SiafundElement, spent bool))