ipld-eth-server/libraries/shared/storage/backfill.go

126 lines
4.2 KiB
Go
Raw Normal View History

2019-09-30 17:34:43 +00:00
// VulcanizeDB
// Copyright © 2019 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program 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 Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package storage
import (
"bytes"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/statediff"
"github.com/sirupsen/logrus"
"github.com/vulcanize/vulcanizedb/libraries/shared/fetcher"
2019-09-30 17:34:43 +00:00
"github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
)
2019-09-30 17:37:16 +00:00
// IBackFiller is the backfilling interface
2019-09-30 17:34:43 +00:00
type IBackFiller interface {
BackFill(bfa BackFillerArgs) (map[common.Hash][]utils.StorageDiff, error)
}
2019-09-30 17:37:16 +00:00
// BackFiller is the backfilling struct
2019-09-30 17:34:43 +00:00
type BackFiller struct {
sdf fetcher.IStateDiffFetcher
2019-09-30 17:34:43 +00:00
}
2019-09-30 17:37:16 +00:00
// BackFillerArgs are used to pass configuration params to the backfiller
2019-09-30 17:34:43 +00:00
type BackFillerArgs struct {
// mapping of hashed addresses to a list of the storage key hashes we want to collect at that address
WantedStorage map[common.Hash][]common.Hash
StartingBlock uint64
EndingBlock uint64
}
2019-09-30 17:37:16 +00:00
// NewStorageBackFiller returns a IBackFiller
func NewStorageBackFiller(bc fetcher.BatchClient) IBackFiller {
2019-09-30 17:34:43 +00:00
return &BackFiller{
sdf: fetcher.NewStateDiffFetcher(bc),
2019-09-30 17:34:43 +00:00
}
}
// BackFill uses the provided config to fetch and return the state diff at the specified blocknumber
// StateDiffAt(ctx context.Context, blockNumber uint64) (*Payload, error)
func (bf *BackFiller) BackFill(bfa BackFillerArgs) (map[common.Hash][]utils.StorageDiff, error) {
results := make(map[common.Hash][]utils.StorageDiff, len(bfa.WantedStorage))
if bfa.EndingBlock < bfa.StartingBlock {
return nil, errors.New("backfill: ending block number needs to be greater than starting block number")
}
blockHeights := make([]uint64, 0, bfa.EndingBlock-bfa.StartingBlock+1)
2019-09-30 17:34:43 +00:00
for i := bfa.StartingBlock; i <= bfa.EndingBlock; i++ {
blockHeights = append(blockHeights, i)
2019-09-30 17:34:43 +00:00
}
payloads, err := bf.sdf.FetchStateDiffsAt(blockHeights)
if err != nil {
return nil, err
2019-09-30 17:34:43 +00:00
}
for _, payload := range payloads {
2019-09-30 17:34:43 +00:00
block := new(types.Block)
blockDecodeErr := rlp.DecodeBytes(payload.BlockRlp, block)
if blockDecodeErr != nil {
return nil, blockDecodeErr
}
stateDiff := new(statediff.StateDiff)
stateDiffDecodeErr := rlp.DecodeBytes(payload.StateDiffRlp, stateDiff)
if stateDiffDecodeErr != nil {
return nil, stateDiffDecodeErr
}
accounts := utils.GetAccountsFromDiff(*stateDiff)
for _, account := range accounts {
if wantedHashedAddress(bfa.WantedStorage, common.BytesToHash(account.Key)) {
logrus.Trace(fmt.Sprintf("iterating through %d Storage values on account", len(account.Storage)))
for _, storage := range account.Storage {
if wantedHashedStorageKey(bfa.WantedStorage[common.BytesToHash(account.Key)], storage.Key) {
diff, formatErr := utils.FromGethStateDiff(account, stateDiff, storage)
logrus.Trace("adding storage diff to out channel",
"keccak of address: ", diff.HashedAddress.Hex(),
"block height: ", diff.BlockHeight,
"storage key: ", diff.StorageKey.Hex(),
"storage value: ", diff.StorageValue.Hex())
if formatErr != nil {
return nil, formatErr
}
results[diff.HashedAddress] = append(results[diff.HashedAddress], diff)
}
}
}
}
}
return results, nil
}
func wantedHashedAddress(wantedStorage map[common.Hash][]common.Hash, hashedKey common.Hash) bool {
for addrHash := range wantedStorage {
if bytes.Equal(addrHash.Bytes(), hashedKey.Bytes()) {
return true
}
}
return false
}
func wantedHashedStorageKey(wantedKeys []common.Hash, keyBytes []byte) bool {
for _, key := range wantedKeys {
if bytes.Equal(key.Bytes(), keyBytes) {
return true
}
}
return false
}