diff --git a/statediff/helpers.go b/statediff/helpers.go index 4d9b886e4..ba809fc06 100644 --- a/statediff/helpers.go +++ b/statediff/helpers.go @@ -90,8 +90,10 @@ func loadWatchedAddresses(db *postgres.DB) error { return fmt.Errorf("error loading watched addresses: %v", err) } - var watchedAddresses []common.Address - var watchedStorageSlots []common.Hash + var ( + watchedAddresses = []common.Address{} + watchedStorageSlots = []common.Hash{} + ) for _, entry := range watched { switch entry.Kind { case types.WatchedAddress.Int(): diff --git a/statediff/service.go b/statediff/service.go index 39e874d98..edff82d67 100644 --- a/statediff/service.go +++ b/statediff/service.go @@ -109,8 +109,6 @@ type IService interface { WriteLoop(chainEventCh chan core.ChainEvent) // Method to change the addresses being watched in write loop params WatchAddress(operation OperationType, args []WatchAddressArg) error - // Method to get currently watched addresses from write loop params - GetWatchedAddresses() []common.Address } // Wraps consructor parameters @@ -825,7 +823,7 @@ func (sds *Service) WatchAddress(operation OperationType, args []WatchAddressArg } // update in-memory params - writeLoopParams.WatchedAddresses = nil + writeLoopParams.WatchedAddresses = []common.Address{} case AddStorageSlots: // filter out args having an already watched storage slot with a warning @@ -902,7 +900,7 @@ func (sds *Service) WatchAddress(operation OperationType, args []WatchAddressArg return err } - writeLoopParams.WatchedStorageSlots = nil + writeLoopParams.WatchedStorageSlots = []common.Hash{} default: return fmt.Errorf("Unexpected operation %s", operation) @@ -910,8 +908,3 @@ func (sds *Service) WatchAddress(operation OperationType, args []WatchAddressArg return nil } - -// Gets currently watched addresses from the in-memory write loop params -func (sds *Service) GetWatchedAddresses() []common.Address { - return writeLoopParams.WatchedAddresses -} diff --git a/statediff/testhelpers/mocks/blockchain.go b/statediff/testhelpers/mocks/blockchain.go index f2834a4a8..f2a77af64 100644 --- a/statediff/testhelpers/mocks/blockchain.go +++ b/statediff/testhelpers/mocks/blockchain.go @@ -39,6 +39,7 @@ type BlockChain struct { Receipts map[common.Hash]types.Receipts TDByHash map[common.Hash]*big.Int TDByNum map[uint64]*big.Int + currentBlock *types.Block } // SetBlocksForHashes mock method @@ -128,9 +129,14 @@ func (bc *BlockChain) GetTd(hash common.Hash, blockNum uint64) *big.Int { return nil } +// SetCurrentBlock test method +func (bc *BlockChain) SetCurrentBlock(block *types.Block) { + bc.currentBlock = block +} + // CurrentBlock mock method func (bc *BlockChain) CurrentBlock() *types.Block { - return nil + return bc.currentBlock } func (bc *BlockChain) SetTd(hash common.Hash, blockNum uint64, td *big.Int) { diff --git a/statediff/testhelpers/mocks/indexer.go b/statediff/testhelpers/mocks/indexer.go new file mode 100644 index 000000000..89459b558 --- /dev/null +++ b/statediff/testhelpers/mocks/indexer.go @@ -0,0 +1,59 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package mocks + +import ( + "math/big" + "time" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/statediff/indexer" + sdtypes "github.com/ethereum/go-ethereum/statediff/types" +) + +// Indexer is a mock state diff indexer +type Indexer struct{} + +func (sdi *Indexer) PushBlock(block *types.Block, receipts types.Receipts, totalDifficulty *big.Int) (*indexer.BlockTx, error) { + return nil, nil +} + +func (sdi *Indexer) PushStateNode(tx *indexer.BlockTx, stateNode sdtypes.StateNode) error { + return nil +} + +func (sdi *Indexer) PushCodeAndCodeHash(tx *indexer.BlockTx, codeAndCodeHash sdtypes.CodeAndCodeHash) error { + return nil +} + +func (sdi *Indexer) ReportDBMetrics(delay time.Duration, quit <-chan bool) {} + +func (sdi *Indexer) InsertWatchedAddresses(addresses []sdtypes.WatchAddressArg, currentBlock *big.Int, kind sdtypes.WatchedAddressType) error { + return nil +} + +func (sdi *Indexer) RemoveWatchedAddresses(addresses []sdtypes.WatchAddressArg, kind sdtypes.WatchedAddressType) error { + return nil +} + +func (sdi *Indexer) SetWatchedAddresses(args []sdtypes.WatchAddressArg, currentBlockNumber *big.Int, kind sdtypes.WatchedAddressType) error { + return nil +} + +func (sdi *Indexer) ClearWatchedAddresses(kind sdtypes.WatchedAddressType) error { + return nil +} diff --git a/statediff/testhelpers/mocks/service.go b/statediff/testhelpers/mocks/service.go index f8d1b6b20..1e5d3c1ba 100644 --- a/statediff/testhelpers/mocks/service.go +++ b/statediff/testhelpers/mocks/service.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" + "github.com/thoas/go-funk" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" @@ -32,9 +33,12 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/statediff" + ind "github.com/ethereum/go-ethereum/statediff/indexer" sdtypes "github.com/ethereum/go-ethereum/statediff/types" ) +var typeAssertionFailed = "type assertion failed" + // MockStateDiffService is a mock state diff service type MockStateDiffService struct { sync.Mutex @@ -47,6 +51,8 @@ type MockStateDiffService struct { QuitChan chan bool Subscriptions map[common.Hash]map[rpc.ID]statediff.Subscription SubscriptionTypes map[common.Hash]statediff.Params + Indexer ind.Indexer + writeLoopParams statediff.ParamsWithMutex } // Protocols mock method @@ -333,10 +339,175 @@ func sendNonBlockingQuit(id rpc.ID, sub statediff.Subscription) { } } +// WatchAddress mock method func (sds *MockStateDiffService) WatchAddress(operation statediff.OperationType, args []sdtypes.WatchAddressArg) error { + // lock writeLoopParams for a write + sds.writeLoopParams.Lock() + defer sds.writeLoopParams.Unlock() + + // get the current block number + currentBlockNumber := sds.BlockChain.CurrentBlock().Number() + + switch operation { + case statediff.AddAddresses: + // filter out args having an already watched address with a warning + filteredArgs, ok := funk.Filter(args, func(arg sdtypes.WatchAddressArg) bool { + if funk.Contains(sds.writeLoopParams.WatchedAddresses, common.HexToAddress(arg.Address)) { + log.Warn("Address already being watched", "address", arg.Address) + return false + } + return true + }).([]sdtypes.WatchAddressArg) + if !ok { + return fmt.Errorf("AddAddresses: filtered args %s", typeAssertionFailed) + } + + // get addresses from the filtered args + filteredAddresses, ok := funk.Map(filteredArgs, func(arg sdtypes.WatchAddressArg) common.Address { + return common.HexToAddress(arg.Address) + }).([]common.Address) + if !ok { + return fmt.Errorf("AddAddresses: filtered addresses %s", typeAssertionFailed) + } + + // update the db + err := sds.Indexer.InsertWatchedAddresses(filteredArgs, currentBlockNumber, sdtypes.WatchedAddress) + if err != nil { + return err + } + + // update in-memory params + sds.writeLoopParams.WatchedAddresses = append(sds.writeLoopParams.WatchedAddresses, filteredAddresses...) + case statediff.RemoveAddresses: + // get addresses from args + argAddresses, ok := funk.Map(args, func(arg sdtypes.WatchAddressArg) common.Address { + return common.HexToAddress(arg.Address) + }).([]common.Address) + if !ok { + return fmt.Errorf("RemoveAddresses: mapped addresses %s", typeAssertionFailed) + } + + // remove the provided addresses from currently watched addresses + addresses, ok := funk.Subtract(sds.writeLoopParams.WatchedAddresses, argAddresses).([]common.Address) + if !ok { + return fmt.Errorf("RemoveAddresses: filtered addresses %s", typeAssertionFailed) + } + + // update the db + err := sds.Indexer.RemoveWatchedAddresses(args, sdtypes.WatchedAddress) + if err != nil { + return err + } + + // update in-memory params + sds.writeLoopParams.WatchedAddresses = addresses + case statediff.SetAddresses: + // get addresses from args + argAddresses, ok := funk.Map(args, func(arg sdtypes.WatchAddressArg) common.Address { + return common.HexToAddress(arg.Address) + }).([]common.Address) + if !ok { + return fmt.Errorf("SetAddresses: mapped addresses %s", typeAssertionFailed) + } + + // update the db + err := sds.Indexer.SetWatchedAddresses(args, currentBlockNumber, sdtypes.WatchedAddress) + if err != nil { + return err + } + + // update in-memory params + sds.writeLoopParams.WatchedAddresses = argAddresses + case statediff.ClearAddresses: + // update the db + err := sds.Indexer.ClearWatchedAddresses(sdtypes.WatchedAddress) + if err != nil { + return err + } + + // update in-memory params + sds.writeLoopParams.WatchedAddresses = []common.Address{} + + case statediff.AddStorageSlots: + // filter out args having an already watched storage slot with a warning + filteredArgs, ok := funk.Filter(args, func(arg sdtypes.WatchAddressArg) bool { + if funk.Contains(sds.writeLoopParams.WatchedStorageSlots, common.HexToHash(arg.Address)) { + log.Warn("StorageSlot already being watched", "address", arg.Address) + return false + } + return true + }).([]sdtypes.WatchAddressArg) + if !ok { + return fmt.Errorf("AddStorageSlots: filtered args %s", typeAssertionFailed) + } + + // get storage slots from the filtered args + filteredStorageSlots, ok := funk.Map(filteredArgs, func(arg sdtypes.WatchAddressArg) common.Hash { + return common.HexToHash(arg.Address) + }).([]common.Hash) + if !ok { + return fmt.Errorf("AddStorageSlots: filtered storage slots %s", typeAssertionFailed) + } + + // update the db + err := sds.Indexer.InsertWatchedAddresses(filteredArgs, currentBlockNumber, sdtypes.WatchedStorageSlot) + if err != nil { + return err + } + + // update in-memory params + sds.writeLoopParams.WatchedStorageSlots = append(sds.writeLoopParams.WatchedStorageSlots, filteredStorageSlots...) + case statediff.RemoveStorageSlots: + // get storage slots from args + argStorageSlots, ok := funk.Map(args, func(arg sdtypes.WatchAddressArg) common.Hash { + return common.HexToHash(arg.Address) + }).([]common.Hash) + if !ok { + return fmt.Errorf("RemoveStorageSlots: mapped storage slots %s", typeAssertionFailed) + } + + // remove the provided storage slots from currently watched storage slots + storageSlots, ok := funk.Subtract(sds.writeLoopParams.WatchedStorageSlots, argStorageSlots).([]common.Hash) + if !ok { + return fmt.Errorf("RemoveStorageSlots: filtered storage slots %s", typeAssertionFailed) + } + + // update the db + err := sds.Indexer.RemoveWatchedAddresses(args, sdtypes.WatchedStorageSlot) + if err != nil { + return err + } + + // update in-memory params + sds.writeLoopParams.WatchedStorageSlots = storageSlots + case statediff.SetStorageSlots: + // get storage slots from args + argStorageSlots, ok := funk.Map(args, func(arg sdtypes.WatchAddressArg) common.Hash { + return common.HexToHash(arg.Address) + }).([]common.Hash) + if !ok { + return fmt.Errorf("SetStorageSlots: mapped storage slots %s", typeAssertionFailed) + } + + // update the db + err := sds.Indexer.SetWatchedAddresses(args, currentBlockNumber, sdtypes.WatchedStorageSlot) + if err != nil { + return err + } + + // update in-memory params + sds.writeLoopParams.WatchedStorageSlots = argStorageSlots + case statediff.ClearStorageSlots: + err := sds.Indexer.ClearWatchedAddresses(sdtypes.WatchedStorageSlot) + if err != nil { + return err + } + + sds.writeLoopParams.WatchedStorageSlots = []common.Hash{} + + default: + return fmt.Errorf("Unexpected operation %s", operation) + } + return nil } - -func (sds *MockStateDiffService) GetWatchedAddresses() []common.Address { - return []common.Address{} -} diff --git a/statediff/testhelpers/mocks/service_test.go b/statediff/testhelpers/mocks/service_test.go index 8c1fd49cf..bc64e2a01 100644 --- a/statediff/testhelpers/mocks/service_test.go +++ b/statediff/testhelpers/mocks/service_test.go @@ -21,12 +21,14 @@ import ( "fmt" "math/big" "os" + "reflect" "sort" "sync" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/statediff" @@ -87,6 +89,7 @@ func init() { func TestAPI(t *testing.T) { testSubscriptionAPI(t) testHTTPAPI(t) + testWatchAddressAPI(t) } func testSubscriptionAPI(t *testing.T) { @@ -246,3 +249,507 @@ func testHTTPAPI(t *testing.T) { t.Errorf("paylaod does not have the expected total difficulty\r\nactual td: %d\r\nexpected td: %d", payload.TotalDifficulty.Int64(), mockTotalDifficulty.Int64()) } } + +func testWatchAddressAPI(t *testing.T) { + blocks, chain := testhelpers.MakeChain(6, testhelpers.Genesis, testhelpers.TestChainGen) + defer chain.Stop() + block6 := blocks[5] + + mockBlockChain := &BlockChain{} + mockBlockChain.SetCurrentBlock(block6) + mockIndexer := Indexer{} + mockService := MockStateDiffService{ + BlockChain: mockBlockChain, + Indexer: &mockIndexer, + } + + var ( + contract1Address = "0x5d663F5269090bD2A7DC2390c911dF6083D7b28F" + contract2Address = "0x6Eb7e5C66DB8af2E96159AC440cbc8CDB7fbD26B" + contract3Address = "0xcfeB164C328CA13EFd3C77E1980d94975aDfedfc" + contract4Address = "0x0Edf0c4f393a628DE4828B228C48175b3EA297fc" + contract1CreatedAt = uint64(1) + contract2CreatedAt = uint64(2) + contract3CreatedAt = uint64(3) + contract4CreatedAt = uint64(4) + + slot1 = common.HexToHash("1") + slot2 = common.HexToHash("2") + slot3 = common.HexToHash("3") + slot4 = common.HexToHash("4") + slot1StorageKey = crypto.Keccak256Hash(slot1.Bytes()) + slot2StorageKey = crypto.Keccak256Hash(slot2.Bytes()) + slot3StorageKey = crypto.Keccak256Hash(slot3.Bytes()) + slot4StorageKey = crypto.Keccak256Hash(slot4.Bytes()) + slot1StorageKeyHex = crypto.Keccak256Hash(slot1.Bytes()).Hex() + slot2StorageKeyHex = crypto.Keccak256Hash(slot2.Bytes()).Hex() + slot3StorageKeyHex = crypto.Keccak256Hash(slot3.Bytes()).Hex() + slot4StorageKeyHex = crypto.Keccak256Hash(slot4.Bytes()).Hex() + slot1CreatedAt = uint64(1) + slot2CreatedAt = uint64(2) + slot3CreatedAt = uint64(3) + slot4CreatedAt = uint64(4) + + args1 = []sdtypes.WatchAddressArg{ + { + Address: contract1Address, + CreatedAt: contract1CreatedAt, + }, + { + Address: contract2Address, + CreatedAt: contract2CreatedAt, + }, + } + startingParams1 = statediff.Params{ + WatchedAddresses: []common.Address{}, + } + expectedParams1 = statediff.Params{ + WatchedAddresses: []common.Address{ + common.HexToAddress(contract1Address), + common.HexToAddress(contract2Address), + }, + } + + args2 = []sdtypes.WatchAddressArg{ + { + Address: contract3Address, + CreatedAt: contract3CreatedAt, + }, + { + Address: contract2Address, + CreatedAt: contract2CreatedAt, + }, + } + startingParams2 = expectedParams1 + expectedParams2 = statediff.Params{ + WatchedAddresses: []common.Address{ + common.HexToAddress(contract1Address), + common.HexToAddress(contract2Address), + common.HexToAddress(contract3Address), + }, + } + + args3 = []sdtypes.WatchAddressArg{ + { + Address: contract3Address, + CreatedAt: contract3CreatedAt, + }, + { + Address: contract2Address, + CreatedAt: contract2CreatedAt, + }, + } + startingParams3 = expectedParams2 + expectedParams3 = statediff.Params{ + WatchedAddresses: []common.Address{ + common.HexToAddress(contract1Address), + }, + } + + args4 = []sdtypes.WatchAddressArg{ + { + Address: contract1Address, + CreatedAt: contract1CreatedAt, + }, + { + Address: contract2Address, + CreatedAt: contract2CreatedAt, + }, + } + startingParams4 = expectedParams3 + expectedParams4 = statediff.Params{ + WatchedAddresses: []common.Address{}, + } + + args5 = []sdtypes.WatchAddressArg{ + { + Address: contract1Address, + CreatedAt: contract1CreatedAt, + }, + { + Address: contract2Address, + CreatedAt: contract2CreatedAt, + }, + { + Address: contract3Address, + CreatedAt: contract3CreatedAt, + }, + } + startingParams5 = expectedParams4 + expectedParams5 = statediff.Params{ + WatchedAddresses: []common.Address{ + common.HexToAddress(contract1Address), + common.HexToAddress(contract2Address), + common.HexToAddress(contract3Address), + }, + } + + args6 = []sdtypes.WatchAddressArg{ + { + Address: contract4Address, + CreatedAt: contract4CreatedAt, + }, + { + Address: contract2Address, + CreatedAt: contract2CreatedAt, + }, + { + Address: contract3Address, + CreatedAt: contract3CreatedAt, + }, + } + startingParams6 = expectedParams5 + expectedParams6 = statediff.Params{ + WatchedAddresses: []common.Address{ + common.HexToAddress(contract4Address), + common.HexToAddress(contract2Address), + common.HexToAddress(contract3Address), + }, + } + + args7 = []sdtypes.WatchAddressArg{} + startingParams7 = expectedParams6 + expectedParams7 = statediff.Params{ + WatchedAddresses: []common.Address{}, + } + + args8 = []sdtypes.WatchAddressArg{} + startingParams8 = expectedParams6 + expectedParams8 = statediff.Params{ + WatchedAddresses: []common.Address{}, + } + + args9 = []sdtypes.WatchAddressArg{} + startingParams9 = expectedParams8 + expectedParams9 = statediff.Params{ + WatchedAddresses: []common.Address{}, + } + + args10 = []sdtypes.WatchAddressArg{ + { + Address: slot1StorageKeyHex, + CreatedAt: slot1CreatedAt, + }, + { + Address: slot2StorageKeyHex, + CreatedAt: slot2CreatedAt, + }, + } + startingParams10 = statediff.Params{ + WatchedStorageSlots: []common.Hash{}, + } + expectedParams10 = statediff.Params{ + WatchedStorageSlots: []common.Hash{ + slot1StorageKey, + slot2StorageKey, + }, + } + + args11 = []sdtypes.WatchAddressArg{ + { + Address: slot3StorageKeyHex, + CreatedAt: slot3CreatedAt, + }, + { + Address: slot2StorageKeyHex, + CreatedAt: slot2CreatedAt, + }, + } + startingParams11 = expectedParams10 + expectedParams11 = statediff.Params{ + WatchedStorageSlots: []common.Hash{ + slot1StorageKey, + slot2StorageKey, + slot3StorageKey, + }, + } + + args12 = []sdtypes.WatchAddressArg{ + { + Address: slot3StorageKeyHex, + CreatedAt: slot3CreatedAt, + }, + { + Address: slot2StorageKeyHex, + CreatedAt: slot2CreatedAt, + }, + } + startingParams12 = expectedParams11 + expectedParams12 = statediff.Params{ + WatchedStorageSlots: []common.Hash{ + slot1StorageKey, + }, + } + + args13 = []sdtypes.WatchAddressArg{ + { + Address: slot1StorageKeyHex, + CreatedAt: slot1CreatedAt, + }, + { + Address: slot2StorageKeyHex, + CreatedAt: slot2CreatedAt, + }, + } + startingParams13 = expectedParams12 + expectedParams13 = statediff.Params{ + WatchedStorageSlots: []common.Hash{}, + } + + args14 = []sdtypes.WatchAddressArg{ + { + Address: slot1StorageKeyHex, + CreatedAt: slot1CreatedAt, + }, + { + Address: slot2StorageKeyHex, + CreatedAt: slot2CreatedAt, + }, + { + Address: slot3StorageKeyHex, + CreatedAt: slot3CreatedAt, + }, + } + startingParams14 = expectedParams13 + expectedParams14 = statediff.Params{ + WatchedStorageSlots: []common.Hash{ + slot1StorageKey, + slot2StorageKey, + slot3StorageKey, + }, + } + + args15 = []sdtypes.WatchAddressArg{ + { + Address: slot4StorageKeyHex, + CreatedAt: slot4CreatedAt, + }, + { + Address: slot2StorageKeyHex, + CreatedAt: slot2CreatedAt, + }, + { + Address: slot3StorageKeyHex, + CreatedAt: slot3CreatedAt, + }, + } + startingParams15 = expectedParams14 + expectedParams15 = statediff.Params{ + WatchedStorageSlots: []common.Hash{ + slot4StorageKey, + slot2StorageKey, + slot3StorageKey, + }, + } + + args16 = []sdtypes.WatchAddressArg{} + startingParams16 = expectedParams15 + expectedParams16 = statediff.Params{ + WatchedStorageSlots: []common.Hash{}, + } + + args17 = []sdtypes.WatchAddressArg{} + startingParams17 = expectedParams15 + expectedParams17 = statediff.Params{ + WatchedStorageSlots: []common.Hash{}, + } + + args18 = []sdtypes.WatchAddressArg{} + startingParams18 = expectedParams17 + expectedParams18 = statediff.Params{ + WatchedStorageSlots: []common.Hash{}, + } + ) + + tests := []struct { + name string + operation statediff.OperationType + args []sdtypes.WatchAddressArg + startingParams statediff.Params + expectedParams statediff.Params + expectedErr error + }{ + // addresses tests + { + "testAddAddresses", + statediff.AddAddresses, + args1, + startingParams1, + expectedParams1, + nil, + }, + { + "testAddAddressesSomeWatched", + statediff.AddAddresses, + args2, + startingParams2, + expectedParams2, + nil, + }, + { + "testRemoveAddresses", + statediff.RemoveAddresses, + args3, + startingParams3, + expectedParams3, + nil, + }, + { + "testRemoveAddressesSomeWatched", + statediff.RemoveAddresses, + args4, + startingParams4, + expectedParams4, + nil, + }, + { + "testSetAddresses", + statediff.SetAddresses, + args5, + startingParams5, + expectedParams5, + nil, + }, + { + "testSetAddressesSomeWatched", + statediff.SetAddresses, + args6, + startingParams6, + expectedParams6, + nil, + }, + { + "testSetAddressesEmtpyArgs", + statediff.SetAddresses, + args7, + startingParams7, + expectedParams7, + nil, + }, + { + "testClearAddresses", + statediff.ClearAddresses, + args8, + startingParams8, + expectedParams8, + nil, + }, + { + "testClearAddressesEmpty", + statediff.ClearAddresses, + args9, + startingParams9, + expectedParams9, + nil, + }, + + // storage slots tests + { + "testAddStorageSlots", + statediff.AddStorageSlots, + args10, + startingParams10, + expectedParams10, + nil, + }, + { + "testAddStorageSlotsSomeWatched", + statediff.AddStorageSlots, + args11, + startingParams11, + expectedParams11, + nil, + }, + { + "testRemoveStorageSlots", + statediff.RemoveStorageSlots, + args12, + startingParams12, + expectedParams12, + nil, + }, + { + "testRemoveStorageSlotsSomeWatched", + statediff.RemoveStorageSlots, + args13, + startingParams13, + expectedParams13, + nil, + }, + { + "testSetStorageSlots", + statediff.SetStorageSlots, + args14, + startingParams14, + expectedParams14, + nil, + }, + { + "testSetStorageSlotsSomeWatched", + statediff.SetStorageSlots, + args15, + startingParams15, + expectedParams15, + nil, + }, + { + "testSetStorageSlotsEmtpyArgs", + statediff.SetStorageSlots, + args16, + startingParams16, + expectedParams16, + nil, + }, + { + "testClearStorageSlots", + statediff.ClearStorageSlots, + args17, + startingParams17, + expectedParams17, + nil, + }, + { + "testClearStorageSlotsEmpty", + statediff.ClearStorageSlots, + args18, + startingParams18, + expectedParams18, + nil, + }, + + // invalid args + { + "testInvalidOperation", + "WrongOp", + args18, + startingParams18, + statediff.Params{}, + fmt.Errorf("Unexpected operation WrongOp"), + }, + } + + for _, test := range tests { + mockService.writeLoopParams = statediff.ParamsWithMutex{ + Params: test.startingParams, + } + + err := mockService.WatchAddress(test.operation, test.args) + if test.expectedErr != nil { + if err.Error() != test.expectedErr.Error() { + t.Logf("Test failed: %s", test.name) + t.Errorf("actual err: %+v\nexpected err: %+v", err, test.expectedErr) + } + + continue + } + if err != nil { + t.Error(err) + } + + updatedParams := mockService.writeLoopParams.Params + if !reflect.DeepEqual(updatedParams, test.expectedParams) { + t.Logf("Test failed: %s", test.name) + t.Errorf("actual params: %+v\nexpected params: %+v", updatedParams, test.expectedParams) + } + } +}