Skip to content

Commit

Permalink
Skip balance changes for account
Browse files Browse the repository at this point in the history
  • Loading branch information
patrick-ogrady committed Apr 28, 2020
1 parent 88fb127 commit 82ac252
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 20 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ Flags:
--block-concurrency uint concurrency to use while fetching blocks (default 8)
--data-dir string folder used to store logs and any data used to perform validation (default "./validator-data")
--end int block index to stop syncing (default -1)
--exempt-accounts string Absolute path to a file listing all accounts to exempt from balance
tracking and reconciliation. Look at the examples directory for an example of
how to structure this file.
--halt-on-reconciliation-error Determines if block processing should halt on a reconciliation
error. It can be beneficial to collect all reconciliation errors or silence
reconciliation errors during development. (default true)
Expand Down
15 changes: 3 additions & 12 deletions cmd/check_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ package cmd

import (
"context"
"encoding/json"
"io/ioutil"
"log"
"path"

"github.com/coinbase/rosetta-cli/internal/logger"
"github.com/coinbase/rosetta-cli/internal/reconciler"
Expand Down Expand Up @@ -67,22 +64,15 @@ func runCheckAccountCmd(cmd *cobra.Command, args []string) {
// TODO: unify startup logic with stateless
ctx, cancel := context.WithCancel(context.Background())

// Try to load interesting accounts
interestingAccountsRaw, err := ioutil.ReadFile(path.Clean(accountFile))
interestingAccounts, err := loadAccounts(accountFile)
if err != nil {
log.Fatal(err)
}

interestingAccounts := []*reconciler.AccountCurrency{}
if err := json.Unmarshal(interestingAccountsRaw, &interestingAccounts); err != nil {
log.Fatal(err)
}

accts, err := json.MarshalIndent(interestingAccounts, "", " ")
exemptAccounts, err := loadAccounts(ExemptFile)
if err != nil {
log.Fatal(err)
}
log.Printf("Checking: %s\n", string(accts))

fetcher := fetcher.New(
ServerURL,
Expand Down Expand Up @@ -121,6 +111,7 @@ func runCheckAccountCmd(cmd *cobra.Command, args []string) {
syncHandler := syncer.NewBaseHandler(
logger,
r,
exemptAccounts,
)

statelessSyncer := syncer.NewStateless(
Expand Down
6 changes: 6 additions & 0 deletions cmd/check_complete.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ historical balance lookup should set this to false.`,
func runCheckCompleteCmd(cmd *cobra.Command, args []string) {
ctx, cancel := context.WithCancel(context.Background())

exemptAccounts, err := loadAccounts(ExemptFile)
if err != nil {
log.Fatal(err)
}

fetcher := fetcher.New(
ServerURL,
fetcher.WithBlockConcurrency(BlockConcurrency),
Expand Down Expand Up @@ -133,6 +138,7 @@ func runCheckCompleteCmd(cmd *cobra.Command, args []string) {
syncHandler := syncer.NewBaseHandler(
logger,
r,
exemptAccounts,
)

statefulSyncer := syncer.NewStateful(
Expand Down
6 changes: 6 additions & 0 deletions cmd/check_quick.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ use the check:complete command.`,
func runCheckQuickCmd(cmd *cobra.Command, args []string) {
ctx, cancel := context.WithCancel(context.Background())

exemptAccounts, err := loadAccounts(ExemptFile)
if err != nil {
log.Fatal(err)
}

fetcher := fetcher.New(
ServerURL,
fetcher.WithBlockConcurrency(BlockConcurrency),
Expand Down Expand Up @@ -93,6 +98,7 @@ func runCheckQuickCmd(cmd *cobra.Command, args []string) {
syncHandler := syncer.NewBaseHandler(
logger,
r,
exemptAccounts,
)

statelessSyncer := syncer.NewStateless(
Expand Down
31 changes: 31 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@
package cmd

import (
"encoding/json"
"io/ioutil"
"log"
"path"

"github.com/coinbase/rosetta-cli/internal/reconciler"

"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -171,3 +178,27 @@ how to structure this file.`,
rootCmd.AddCommand(checkQuickCmd)
rootCmd.AddCommand(checkAccountCmd)
}

func loadAccounts(filePath string) ([]*reconciler.AccountCurrency, error) {
if len(filePath) == 0 {
return []*reconciler.AccountCurrency{}, nil
}

accountsRaw, err := ioutil.ReadFile(path.Clean(filePath))
if err != nil {
return nil, err
}

accounts := []*reconciler.AccountCurrency{}
if err := json.Unmarshal(accountsRaw, &accounts); err != nil {
return nil, err
}

prettyAccounts, err := json.MarshalIndent(accounts, "", " ")
if err != nil {
return nil, err
}
log.Printf("Found %d accounts at %s: %s\n", len(accounts), filePath, string(prettyAccounts))

return accounts, nil
}
28 changes: 24 additions & 4 deletions internal/syncer/base_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,21 @@ import (
// BaseHandler logs processed blocks
// and reconciles modified balances.
type BaseHandler struct {
logger *logger.Logger
reconciler reconciler.Reconciler
logger *logger.Logger
reconciler reconciler.Reconciler
exemptAccounts []*reconciler.AccountCurrency
}

// NewBaseHandler constructs a basic Handler.
func NewBaseHandler(
logger *logger.Logger,
reconciler reconciler.Reconciler,
exemptAccounts []*reconciler.AccountCurrency,
) Handler {
return &BaseHandler{
logger: logger,
reconciler: reconciler,
logger: logger,
reconciler: reconciler,
exemptAccounts: exemptAccounts,
}
}

Expand Down Expand Up @@ -70,3 +73,20 @@ func (h *BaseHandler) BlockProcessed(
// blocking
return h.reconciler.QueueChanges(ctx, block.BlockIdentifier, balanceChanges)
}

// AccountExempt returns a boolean indicating if the provided
// account and currency are exempt from balance tracking and
// reconciliation.
func (h *BaseHandler) AccountExempt(
ctx context.Context,
account *types.AccountIdentifier,
currency *types.Currency,
) bool {
return reconciler.ContainsAccountCurrency(
h.exemptAccounts,
&reconciler.AccountCurrency{
Account: account,
Currency: currency,
},
)
}
1 change: 1 addition & 0 deletions internal/syncer/stateful_syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ func (s *StatefulSyncer) storeBlockBalanceChanges(
s.fetcher.Asserter,
block,
orphan,
s.handler,
)
if err != nil {
return nil, err
Expand Down
1 change: 1 addition & 0 deletions internal/syncer/stateless_syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func (s *StatelessSyncer) SyncRange(
s.fetcher.Asserter,
block,
false,
s.handler,
)
if err != nil {
return err
Expand Down
15 changes: 15 additions & 0 deletions internal/syncer/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ type Handler interface {
orphan bool,
changes []*storage.BalanceChange,
) error

AccountExempt(
ctx context.Context,
account *types.AccountIdentifier,
current *types.Currency,
) bool
}

// BalanceChanges returns all balance changes for
Expand All @@ -165,6 +171,7 @@ func BalanceChanges(
asserter *asserter.Asserter,
block *types.Block,
orphan bool,
handler Handler,
) ([]*storage.BalanceChange, error) {
balanceChanges := map[string]*storage.BalanceChange{}
for _, tx := range block.Transactions {
Expand All @@ -183,6 +190,14 @@ func BalanceChanges(
continue
}

// Exempting account in BalanceChanges ensures that storage is not updated
// and that the account is not reconciled. If a handler is not provided,
// no account will be marked exempt.
if handler != nil && handler.AccountExempt(ctx, op.Account, op.Amount.Currency) {
log.Printf("Skipping exempt account %+v\n", op.Account)
continue
}

amount := op.Amount
blockIdentifier := block.BlockIdentifier
if orphan {
Expand Down
37 changes: 33 additions & 4 deletions internal/syncer/syncer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"context"
"testing"

"github.com/coinbase/rosetta-cli/internal/reconciler"
"github.com/coinbase/rosetta-cli/internal/storage"

"github.com/coinbase/rosetta-sdk-go/asserter"
Expand Down Expand Up @@ -56,10 +57,11 @@ func simpleTransactionFactory(

func TestBalanceChanges(t *testing.T) {
var tests = map[string]struct {
block *types.Block
orphan bool
changes []*storage.BalanceChange
err error
block *types.Block
orphan bool
changes []*storage.BalanceChange
exemptAccounts []*reconciler.AccountCurrency
err error
}{
"simple block": {
block: &types.Block{
Expand Down Expand Up @@ -90,6 +92,31 @@ func TestBalanceChanges(t *testing.T) {
},
err: nil,
},
"simple block account exempt": {
block: &types.Block{
BlockIdentifier: &types.BlockIdentifier{
Hash: "1",
Index: 1,
},
ParentBlockIdentifier: &types.BlockIdentifier{
Hash: "0",
Index: 0,
},
Transactions: []*types.Transaction{
recipientTransaction,
},
Timestamp: asserter.MinUnixEpoch + 1,
},
orphan: false,
changes: []*storage.BalanceChange{},
exemptAccounts: []*reconciler.AccountCurrency{
{
Account: recipient,
Currency: currency,
},
},
err: nil,
},
"single account sum block": {
block: &types.Block{
BlockIdentifier: &types.BlockIdentifier{
Expand Down Expand Up @@ -191,11 +218,13 @@ func TestBalanceChanges(t *testing.T) {

for name, test := range tests {
t.Run(name, func(t *testing.T) {
handler := NewBaseHandler(nil, nil, test.exemptAccounts)
changes, err := BalanceChanges(
ctx,
asserter,
test.block,
test.orphan,
handler,
)

assert.ElementsMatch(t, test.changes, changes)
Expand Down

0 comments on commit 82ac252

Please sign in to comment.