diff --git a/rpc/chain/client.go b/rpc/chain/client.go index b24ab24d7..01cf49305 100644 --- a/rpc/chain/client.go +++ b/rpc/chain/client.go @@ -498,20 +498,8 @@ func (c *ClientWithFallback) SubscribeNewHead(ctx context.Context, ch chan<- *ty return sub.(ethereum.Subscription), nil } -func (c *ClientWithFallback) NetworkID(ctx context.Context) (*big.Int, error) { - rpcstats.CountCall("eth_NetworkID") - - networkID, err := c.makeCallSingleReturn( - func() (any, error) { return c.main.NetworkID(ctx) }, - func() (any, error) { return c.fallback.NetworkID(ctx) }, - true, - ) - - if err != nil { - return nil, err - } - - return networkID.(*big.Int), nil +func (c *ClientWithFallback) NetworkID() uint64 { + return c.ChainID } func (c *ClientWithFallback) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { diff --git a/services/wallet/balance/balance_cache.go b/services/wallet/balance/balance_cache.go index 161b14897..98486a4cf 100644 --- a/services/wallet/balance/balance_cache.go +++ b/services/wallet/balance/balance_cache.go @@ -2,6 +2,7 @@ package balance import ( "context" + "math" "math/big" "sort" "sync" @@ -23,6 +24,7 @@ type Reader interface { NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) FullTransactionByBlockNumberAndIndex(ctx context.Context, blockNumber *big.Int, index uint) (*chain.FullTransaction, error) + NetworkID() uint64 } // Cacher interface for caching balance to BalanceCache. Requires BalanceReader to fetch balance. @@ -35,28 +37,33 @@ type Cacher interface { // Interface for cache of balances. type CacheIface interface { - GetBalance(account common.Address, blockNumber *big.Int) *big.Int - GetNonce(account common.Address, blockNumber *big.Int) *int64 - AddBalance(account common.Address, blockNumber *big.Int, balance *big.Int) - AddNonce(account common.Address, blockNumber *big.Int, nonce *int64) + GetBalance(account common.Address, chainID uint64, blockNumber *big.Int) *big.Int + GetNonce(account common.Address, chainID uint64, blockNumber *big.Int) *int64 + AddBalance(account common.Address, chainID uint64, blockNumber *big.Int, balance *big.Int) + AddNonce(account common.Address, chainID uint64, blockNumber *big.Int, nonce *int64) Clear() } +type balanceCacheType map[common.Address]map[uint64]map[uint64]*big.Int // address->chainID->blockNumber->balance +type nonceCacheType map[common.Address]map[uint64]map[uint64]*int64 // address->chainID->blockNumber->nonce +type nonceRangesCacheType map[common.Address]map[uint64]map[int64]nonceRange // address->chainID->blockNumber->nonceRange +type sortedNonceRangesCacheType map[common.Address]map[uint64][]nonceRange // address->chainID->[]nonceRange + type Cache struct { // balances maps an address to a map of a block number and the balance of this particular address - balances map[common.Address]map[uint64]*big.Int // we don't care about block number overflow as we use cache only for comparing balances when fetching, not for UI - nonces map[common.Address]map[uint64]*int64 // we don't care about block number overflow as we use cache only for comparing balances when fetching, not for UI - nonceRanges map[common.Address]map[int64]nonceRange - sortedRanges map[common.Address][]nonceRange + balances balanceCacheType + nonces nonceCacheType + nonceRanges nonceRangesCacheType + sortedRanges sortedNonceRangesCacheType rw sync.RWMutex } func NewCache() *Cache { return &Cache{ - balances: make(map[common.Address]map[uint64]*big.Int), - nonces: make(map[common.Address]map[uint64]*int64), - nonceRanges: make(map[common.Address]map[int64]nonceRange), - sortedRanges: make(map[common.Address][]nonceRange), + balances: make(balanceCacheType), + nonces: make(nonceCacheType), + nonceRanges: make(nonceRangesCacheType), + sortedRanges: make(sortedNonceRangesCacheType), } } @@ -64,70 +71,88 @@ func (b *Cache) Clear() { b.rw.Lock() defer b.rw.Unlock() - for address, cache := range b.balances { - if len(cache) == 0 { + for address, chainCache := range b.balances { + if len(chainCache) == 0 { continue } - var maxBlock uint64 = 0 - var minBlock uint64 = 18446744073709551615 - for key := range cache { - if key > maxBlock { - maxBlock = key + for chainID, cache := range chainCache { + if len(cache) == 0 { + continue } - if key < minBlock { - minBlock = key + + var maxBlock uint64 = 0 + var minBlock uint64 = math.MaxUint64 + for key := range cache { + if key > maxBlock { + maxBlock = key + } + if key < minBlock { + minBlock = key + } } + newCache := make(map[uint64]*big.Int) + newCache[maxBlock] = cache[maxBlock] + newCache[minBlock] = cache[minBlock] + b.balances[address][chainID] = newCache } - newCache := make(map[uint64]*big.Int) - newCache[maxBlock] = cache[maxBlock] - newCache[minBlock] = cache[minBlock] - b.balances[address] = newCache } - for address, cache := range b.nonces { - if len(cache) == 0 { + for address, chainCache := range b.nonces { + if len(chainCache) == 0 { continue } - var maxBlock uint64 = 0 - var minBlock uint64 = 18446744073709551615 - for key := range cache { - if key > maxBlock { - maxBlock = key - } - if key < minBlock { - minBlock = key + for chainID, cache := range chainCache { + var maxBlock uint64 = 0 + var minBlock uint64 = math.MaxUint64 + for key := range cache { + if key > maxBlock { + maxBlock = key + } + if key < minBlock { + minBlock = key + } } + newCache := make(map[uint64]*int64) + newCache[maxBlock] = cache[maxBlock] + newCache[minBlock] = cache[minBlock] + b.nonces[address][chainID] = newCache } - newCache := make(map[uint64]*int64) - newCache[maxBlock] = cache[maxBlock] - newCache[minBlock] = cache[minBlock] - b.nonces[address] = newCache } - b.nonceRanges = make(map[common.Address]map[int64]nonceRange) - b.sortedRanges = make(map[common.Address][]nonceRange) + b.nonceRanges = make(nonceRangesCacheType) + b.sortedRanges = make(sortedNonceRangesCacheType) } -func (b *Cache) GetBalance(account common.Address, blockNumber *big.Int) *big.Int { +func (b *Cache) GetBalance(account common.Address, chainID uint64, blockNumber *big.Int) *big.Int { b.rw.RLock() defer b.rw.RUnlock() - return b.balances[account][blockNumber.Uint64()] + if b.balances[account] == nil || b.balances[account][chainID] == nil { + return nil + } + + return b.balances[account][chainID][blockNumber.Uint64()] } -func (b *Cache) AddBalance(account common.Address, blockNumber *big.Int, balance *big.Int) { +func (b *Cache) AddBalance(account common.Address, chainID uint64, blockNumber *big.Int, balance *big.Int) { b.rw.Lock() defer b.rw.Unlock() _, exists := b.balances[account] if !exists { - b.balances[account] = make(map[uint64]*big.Int) + b.balances[account] = make(map[uint64]map[uint64]*big.Int) } - b.balances[account][blockNumber.Uint64()] = balance + + _, exists = b.balances[account][chainID] + if !exists { + b.balances[account][chainID] = make(map[uint64]*big.Int) + } + + b.balances[account][chainID][blockNumber.Uint64()] = balance } func (b *Cache) BalanceAt(ctx context.Context, client Reader, account common.Address, blockNumber *big.Int) (*big.Int, error) { - cachedBalance := b.GetBalance(account, blockNumber) + cachedBalance := b.GetBalance(account, client.NetworkID(), blockNumber) if cachedBalance != nil { return cachedBalance, nil } @@ -135,25 +160,28 @@ func (b *Cache) BalanceAt(ctx context.Context, client Reader, account common.Add if err != nil { return nil, err } - b.AddBalance(account, blockNumber, balance) + b.AddBalance(account, client.NetworkID(), blockNumber, balance) return balance, nil } -func (b *Cache) GetNonce(account common.Address, blockNumber *big.Int) *int64 { +func (b *Cache) GetNonce(account common.Address, chainID uint64, blockNumber *big.Int) *int64 { b.rw.RLock() defer b.rw.RUnlock() - return b.nonces[account][blockNumber.Uint64()] + if b.nonces[account] == nil || b.nonces[account][chainID] == nil { + return nil + } + return b.nonces[account][chainID][blockNumber.Uint64()] } func (b *Cache) Cache() CacheIface { return b } -func (b *Cache) sortRanges(account common.Address) { - keys := make([]int, 0, len(b.nonceRanges[account])) - for k := range b.nonceRanges[account] { +func (b *Cache) sortRanges(account common.Address, chainID uint64) { + keys := make([]int, 0, len(b.nonceRanges[account][chainID])) + for k := range b.nonceRanges[account][chainID] { keys = append(keys, int(k)) } @@ -161,19 +189,24 @@ func (b *Cache) sortRanges(account common.Address) { ranges := []nonceRange{} for _, k := range keys { - r := b.nonceRanges[account][int64(k)] + r := b.nonceRanges[account][chainID][int64(k)] ranges = append(ranges, r) } - b.sortedRanges[account] = ranges + _, exists := b.sortedRanges[account] + if !exists { + b.sortedRanges[account] = make(map[uint64][]nonceRange) + } + + b.sortedRanges[account][chainID] = ranges } -func (b *Cache) findNonceInRange(account common.Address, block *big.Int) *int64 { +func (b *Cache) findNonceInRange(account common.Address, chainID uint64, block *big.Int) *int64 { b.rw.RLock() defer b.rw.RUnlock() - for k := range b.sortedRanges[account] { - nr := b.sortedRanges[account][k] + for k := range b.sortedRanges[account][chainID] { + nr := b.sortedRanges[account][chainID][k] cmpMin := nr.min.Cmp(block) if cmpMin == 1 { return nil @@ -190,19 +223,24 @@ func (b *Cache) findNonceInRange(account common.Address, block *big.Int) *int64 return nil } -func (b *Cache) updateNonceRange(account common.Address, blockNumber *big.Int, nonce *int64) { +func (b *Cache) updateNonceRange(account common.Address, chainID uint64, blockNumber *big.Int, nonce *int64) { _, exists := b.nonceRanges[account] if !exists { - b.nonceRanges[account] = make(map[int64]nonceRange) + b.nonceRanges[account] = make(map[uint64]map[int64]nonceRange) } - nr, exists := b.nonceRanges[account][*nonce] + _, exists = b.nonceRanges[account][chainID] + if !exists { + b.nonceRanges[account][chainID] = make(map[int64]nonceRange) + } + + nr, exists := b.nonceRanges[account][chainID][*nonce] if !exists { r := nonceRange{ max: big.NewInt(0).Set(blockNumber), min: big.NewInt(0).Set(blockNumber), nonce: *nonce, } - b.nonceRanges[account][*nonce] = r + b.nonceRanges[account][chainID][*nonce] = r } else { if nr.max.Cmp(blockNumber) == -1 { nr.max.Set(blockNumber) @@ -212,29 +250,34 @@ func (b *Cache) updateNonceRange(account common.Address, blockNumber *big.Int, n nr.min.Set(blockNumber) } - b.nonceRanges[account][*nonce] = nr - b.sortRanges(account) + b.nonceRanges[account][chainID][*nonce] = nr + b.sortRanges(account, chainID) } } -func (b *Cache) AddNonce(account common.Address, blockNumber *big.Int, nonce *int64) { +func (b *Cache) AddNonce(account common.Address, chainID uint64, blockNumber *big.Int, nonce *int64) { b.rw.Lock() defer b.rw.Unlock() _, exists := b.nonces[account] if !exists { - b.nonces[account] = make(map[uint64]*int64) + b.nonces[account] = make(map[uint64]map[uint64]*int64) } - b.nonces[account][blockNumber.Uint64()] = nonce - b.updateNonceRange(account, blockNumber, nonce) + + _, exists = b.nonces[account][chainID] + if !exists { + b.nonces[account][chainID] = make(map[uint64]*int64) + } + b.nonces[account][chainID][blockNumber.Uint64()] = nonce + b.updateNonceRange(account, chainID, blockNumber, nonce) } func (b *Cache) NonceAt(ctx context.Context, client Reader, account common.Address, blockNumber *big.Int) (*int64, error) { - cachedNonce := b.GetNonce(account, blockNumber) + cachedNonce := b.GetNonce(account, client.NetworkID(), blockNumber) if cachedNonce != nil { return cachedNonce, nil } - rangeNonce := b.findNonceInRange(account, blockNumber) + rangeNonce := b.findNonceInRange(account, client.NetworkID(), blockNumber) if rangeNonce != nil { return rangeNonce, nil } @@ -244,7 +287,7 @@ func (b *Cache) NonceAt(ctx context.Context, client Reader, account common.Addre return nil, err } int64Nonce := int64(nonce) - b.AddNonce(account, blockNumber, &int64Nonce) + b.AddNonce(account, client.NetworkID(), blockNumber, &int64Nonce) return &int64Nonce, nil } diff --git a/services/wallet/transfer/commands.go b/services/wallet/transfer/commands.go index 5b0bfcbed..c0a1355ee 100644 --- a/services/wallet/transfer/commands.go +++ b/services/wallet/transfer/commands.go @@ -82,10 +82,10 @@ func (c *ethHistoricalCommand) Run(ctx context.Context) (err error) { start := time.Now() if c.from.Number != nil && c.from.Balance != nil { - c.balanceCacher.Cache().AddBalance(c.address, c.from.Number, c.from.Balance) + c.balanceCacher.Cache().AddBalance(c.address, c.chainClient.ChainID, c.from.Number, c.from.Balance) } if c.from.Number != nil && c.from.Nonce != nil { - c.balanceCacher.Cache().AddNonce(c.address, c.from.Number, c.from.Nonce) + c.balanceCacher.Cache().AddNonce(c.address, c.chainClient.ChainID, c.from.Number, c.from.Nonce) } from, headers, startBlock, err := findBlocksWithEthTransfers(ctx, c.chainClient, c.balanceCacher, c.address, c.from.Number, c.to, c.noLimit, c.threadLimit) @@ -301,6 +301,7 @@ func (c *controlCommand) Run(parent context.Context) error { event := walletevent.Event{ Type: EventNewTransfers, Accounts: []common.Address{address}, + ChainID: c.chainClient.ChainID, } for _, header := range cmnd.foundHeaders[address] { if event.BlockNumber == nil || header.Number.Cmp(event.BlockNumber) == 1 { @@ -590,6 +591,7 @@ func (c *transfersCommand) notifyOfNewTransfers(transfers []Transfer) { c.feed.Send(walletevent.Event{ Type: EventNewTransfers, Accounts: []common.Address{c.address}, + ChainID: c.chainClient.ChainID, }) } } @@ -691,12 +693,13 @@ func (c *findAndCheckBlockRangeCommand) Run(parent context.Context) error { lastBlockNumber := c.toByAddress[address] log.Debug("saving headers", "len", len(uniqHeaders), "lastBlockNumber", lastBlockNumber, - "balance", c.balanceCacher.Cache().GetBalance(address, lastBlockNumber), "nonce", c.balanceCacher.Cache().GetNonce(address, lastBlockNumber)) + "balance", c.balanceCacher.Cache().GetBalance(address, c.chainClient.ChainID, lastBlockNumber), + "nonce", c.balanceCacher.Cache().GetNonce(address, c.chainClient.ChainID, lastBlockNumber)) to := &Block{ Number: lastBlockNumber, - Balance: c.balanceCacher.Cache().GetBalance(address, lastBlockNumber), - Nonce: c.balanceCacher.Cache().GetNonce(address, lastBlockNumber), + Balance: c.balanceCacher.Cache().GetBalance(address, c.chainClient.ChainID, lastBlockNumber), + Nonce: c.balanceCacher.Cache().GetNonce(address, c.chainClient.ChainID, lastBlockNumber), } log.Debug("uniqHeaders found for account", "address", address, "uniqHeaders.len", len(uniqHeaders)) err = c.db.ProcessBlocks(c.chainClient.ChainID, address, newFromByAddress[address], to, uniqHeaders) diff --git a/services/wallet/transfer/commands_sequential.go b/services/wallet/transfer/commands_sequential.go index fc4717475..6f60307bf 100644 --- a/services/wallet/transfer/commands_sequential.go +++ b/services/wallet/transfer/commands_sequential.go @@ -113,8 +113,8 @@ func (c *findBlocksCommand) Run(parent context.Context) (err error) { if len(headers) > 0 { log.Debug("findBlocksCommand saving headers", "len", len(headers), "lastBlockNumber", to, - "balance", c.balanceCacher.Cache().GetBalance(c.account, to), - "nonce", c.balanceCacher.Cache().GetNonce(c.account, to)) + "balance", c.balanceCacher.Cache().GetBalance(c.account, c.chainClient.ChainID, to), + "nonce", c.balanceCacher.Cache().GetNonce(c.account, c.chainClient.ChainID, to)) err = c.db.SaveBlocks(c.chainClient.ChainID, c.account, headers) if err != nil { @@ -542,6 +542,7 @@ func (c *loadBlocksAndTransfersCommand) notifyHistoryReady() { c.feed.Send(walletevent.Event{ Type: EventRecentHistoryReady, Accounts: []common.Address{c.account}, + ChainID: c.chainClient.ChainID, }) } } diff --git a/services/wallet/transfer/concurrent_test.go b/services/wallet/transfer/concurrent_test.go index 0d4ef5327..69a1d06b1 100644 --- a/services/wallet/transfer/concurrent_test.go +++ b/services/wallet/transfer/concurrent_test.go @@ -79,6 +79,10 @@ func (f balancesFixture) HeaderByHash(ctx context.Context, hash common.Hash) (*t }, nil } +func (f balancesFixture) NetworkID() uint64 { + return 0 +} + func (f balancesFixture) FullTransactionByBlockNumberAndIndex(ctx context.Context, blockNumber *big.Int, index uint) (*chain.FullTransaction, error) { blockHash := common.HexToHash("0x0") return &chain.FullTransaction{