From bfd3819b5c3c79fb943a321c0f2c81872392e8a6 Mon Sep 17 00:00:00 2001 From: Alan Orwick Date: Wed, 3 Apr 2024 12:00:03 -0500 Subject: [PATCH] Fix indexer and add method to get outpoints --- common/proto_common.pb.go | 2 +- consensus/blake3pow/consensus.go | 6 +- consensus/consensus.go | 3 + consensus/progpow/consensus.go | 6 +- core/bloom_indexer.go | 4 +- core/chain_indexer.go | 186 ++++++++- core/core.go | 33 +- core/genesis.go | 13 +- core/headerchain.go | 71 +--- core/rawdb/accessors_chain.go | 71 +++- core/rawdb/db.pb.go | 2 +- core/rawdb/schema.go | 4 - core/slice.go | 4 +- core/state_processor.go | 5 - core/types/proto_block.pb.go | 393 ++++++++++++++++---- core/types/proto_block.proto | 14 + core/types/utxo.go | 44 +++ internal/quaiapi/api.go | 72 ++-- internal/quaiapi/backend.go | 3 +- internal/quaiapi/quai_api.go | 66 ++-- p2p/node/peerManager/peerdb/peer_info.pb.go | 2 +- p2p/pb/quai_messages.pb.go | 2 +- params/config.go | 5 +- quai/api_backend.go | 8 +- quai/backend.go | 13 +- quaiclient/ethclient/ethclient.go | 7 - trie/proto_trienode.pb.go | 2 +- 27 files changed, 767 insertions(+), 274 deletions(-) diff --git a/common/proto_common.pb.go b/common/proto_common.pb.go index b070827015..30b8fc2958 100644 --- a/common/proto_common.pb.go +++ b/common/proto_common.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v4.25.3 +// protoc v4.25.2 // source: common/proto_common.proto package common diff --git a/consensus/blake3pow/consensus.go b/consensus/blake3pow/consensus.go index f1dc0901c6..f3943f3722 100644 --- a/consensus/blake3pow/consensus.go +++ b/consensus/blake3pow/consensus.go @@ -627,7 +627,11 @@ func (blake3pow *Blake3pow) Finalize(chain consensus.ChainHeaderReader, header * continue } } - core.AddGenesisUtxos(state, nodeLocation, blake3pow.logger) + addressOutpointMap := make(map[string]map[string]*types.OutpointAndDenomination) + core.AddGenesisUtxos(state, nodeLocation, addressOutpointMap, blake3pow.logger) + if chain.Config().IndexAddressUtxos { + chain.WriteAddressOutpoints(addressOutpointMap) + } } header.Header().SetUTXORoot(state.UTXORoot()) header.Header().SetEVMRoot(state.IntermediateRoot(true)) diff --git a/consensus/consensus.go b/consensus/consensus.go index 8749db87ad..7a07165e10 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -58,6 +58,9 @@ type ChainHeaderReader interface { // UpdateEtxEligibleSlices updates the etx eligible slice for the given zone location UpdateEtxEligibleSlices(header *types.WorkObject, location common.Location) common.Hash + + // WriteAddressOutpoints writes the address outpoints to the database + WriteAddressOutpoints(outpointsMap map[string]map[string]*types.OutpointAndDenomination) error } // ChainReader defines a small collection of methods needed to access the local diff --git a/consensus/progpow/consensus.go b/consensus/progpow/consensus.go index 80a5550520..2255b00eb8 100644 --- a/consensus/progpow/consensus.go +++ b/consensus/progpow/consensus.go @@ -677,7 +677,11 @@ func (progpow *Progpow) Finalize(chain consensus.ChainHeaderReader, header *type continue } } - core.AddGenesisUtxos(state, nodeLocation, progpow.logger) + addressOutpointMap := make(map[string]map[string]*types.OutpointAndDenomination) + core.AddGenesisUtxos(state, nodeLocation, addressOutpointMap, progpow.logger) + if chain.Config().IndexAddressUtxos { + chain.WriteAddressOutpoints(addressOutpointMap) + } } header.Header().SetUTXORoot(state.UTXORoot()) header.Header().SetEVMRoot(state.IntermediateRoot(true)) diff --git a/core/bloom_indexer.go b/core/bloom_indexer.go index 57f52a6c5b..d226a9b556 100644 --- a/core/bloom_indexer.go +++ b/core/bloom_indexer.go @@ -49,7 +49,7 @@ type BloomIndexer struct { // NewBloomIndexer returns a chain indexer that generates bloom bits data for the // canonical chain for fast logs filtering. -func NewBloomIndexer(db ethdb.Database, size, confirms uint64, nodeCtx int, logger *log.Logger) *ChainIndexer { +func NewBloomIndexer(db ethdb.Database, size, confirms uint64, nodeCtx int, logger *log.Logger, indexAddressUtxos bool) *ChainIndexer { backend := &BloomIndexer{ db: db, size: size, @@ -57,7 +57,7 @@ func NewBloomIndexer(db ethdb.Database, size, confirms uint64, nodeCtx int, logg } table := rawdb.NewTable(db, string(rawdb.BloomBitsIndexPrefix), db.Location(), db.Logger()) - return NewChainIndexer(db, table, backend, size, confirms, bloomThrottling, "bloombits", nodeCtx, logger) + return NewChainIndexer(db, table, backend, size, confirms, bloomThrottling, "bloombits", nodeCtx, logger, indexAddressUtxos) } // Reset implements core.ChainIndexerBackend, starting a new bloombits index diff --git a/core/chain_indexer.go b/core/chain_indexer.go index bfec3c4c16..599682bdb0 100644 --- a/core/chain_indexer.go +++ b/core/chain_indexer.go @@ -27,10 +27,13 @@ import ( "github.com/dominant-strategies/go-quai/common" "github.com/dominant-strategies/go-quai/core/rawdb" + "github.com/dominant-strategies/go-quai/core/state" "github.com/dominant-strategies/go-quai/core/types" + "github.com/dominant-strategies/go-quai/crypto" "github.com/dominant-strategies/go-quai/ethdb" "github.com/dominant-strategies/go-quai/event" "github.com/dominant-strategies/go-quai/log" + "github.com/dominant-strategies/go-quai/params" ) // ChainIndexerBackend defines the methods needed to process chain segments in @@ -62,6 +65,8 @@ type ChainIndexerChain interface { SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription // NodeCtx returns the context of the chain NodeCtx() int + // StateAt returns the state for a state trie root and utxo root + StateAt(root common.Hash, utxoRoot common.Hash, etxRoot common.Hash) (*state.StateDB, error) } // ChainIndexer does a post-processing job for equally sized sections of the @@ -79,6 +84,7 @@ type ChainIndexer struct { backend ChainIndexerBackend // Background processor generating the index data content children []*ChainIndexer // Child indexers to cascade chain updates to GetBloom func(common.Hash) (*types.Bloom, error) + StateAt func(common.Hash, common.Hash, common.Hash) (*state.StateDB, error) active uint32 // Flag whether the event loop was started update chan struct{} // Notification channel that headers should be processed quit chan chan error // Quit channel to tear down running goroutines @@ -96,22 +102,25 @@ type ChainIndexer struct { logger *log.Logger lock sync.Mutex + + indexAddressUtxos bool } // NewChainIndexer creates a new chain indexer to do background processing on // chain segments of a given size after certain number of confirmations passed. // The throttling parameter might be used to prevent database thrashing. -func NewChainIndexer(chainDb ethdb.Database, indexDb ethdb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string, nodeCtx int, logger *log.Logger) *ChainIndexer { +func NewChainIndexer(chainDb ethdb.Database, indexDb ethdb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string, nodeCtx int, logger *log.Logger, indexAddressUtxos bool) *ChainIndexer { c := &ChainIndexer{ - chainDb: chainDb, - indexDb: indexDb, - backend: backend, - update: make(chan struct{}, 1), - quit: make(chan chan error), - sectionSize: section, - confirmsReq: confirm, - throttling: throttling, - logger: logger, + chainDb: chainDb, + indexDb: indexDb, + backend: backend, + update: make(chan struct{}, 1), + quit: make(chan chan error), + sectionSize: section, + confirmsReq: confirm, + throttling: throttling, + logger: logger, + indexAddressUtxos: indexAddressUtxos, } // Initialize database dependent fields and start the updater c.loadValidSections() @@ -125,11 +134,12 @@ func NewChainIndexer(chainDb ethdb.Database, indexDb ethdb.Database, backend Cha // Start creates a goroutine to feed chain head events into the indexer for // cascading background processing. Children do not need to be started, they // are notified about new events by their parents. -func (c *ChainIndexer) Start(chain ChainIndexerChain) { +func (c *ChainIndexer) Start(chain ChainIndexerChain, config params.ChainConfig) { events := make(chan ChainHeadEvent, 10) sub := chain.SubscribeChainHeadEvent(events) c.GetBloom = chain.GetBloom - go c.eventLoop(chain.CurrentHeader(), events, sub, chain.NodeCtx()) + c.StateAt = chain.StateAt + go c.eventLoop(chain.CurrentHeader(), events, sub, chain.NodeCtx(), config) } // Close tears down all goroutines belonging to the indexer and returns any error @@ -174,13 +184,13 @@ func (c *ChainIndexer) Close() error { // eventLoop is a secondary - optional - event loop of the indexer which is only // started for the outermost indexer to push chain head events into a processing // queue. -func (c *ChainIndexer) eventLoop(currentHeader *types.WorkObject, events chan ChainHeadEvent, sub event.Subscription, nodeCtx int) { +func (c *ChainIndexer) eventLoop(currentHeader *types.WorkObject, events chan ChainHeadEvent, sub event.Subscription, nodeCtx int, config params.ChainConfig) { defer func() { if r := recover(); r != nil { c.logger.WithFields(log.Fields{ "error": r, "stacktrace": string(debug.Stack()), - }).Fatal("Go-Quai Panicked") + }).Error("Go-Quai Panicked") } }() // Mark the chain indexer as active, requiring an additional teardown @@ -210,18 +220,61 @@ func (c *ChainIndexer) eventLoop(currentHeader *types.WorkObject, events chan Ch return } header := ev.Block + + var validUtxoIndex bool + var addressOutpoints map[string]map[string]*types.OutpointAndDenomination + if c.indexAddressUtxos { + validUtxoIndex = true + addressOutpoints = rawdb.ReadAddressOutpoints(c.chainDb, config.Location) + } + if header.ParentHash(nodeCtx) != prevHash { // Reorg to the common ancestor if needed (might not exist in light sync mode, skip reorg then) // TODO: This seems a bit brittle, can we detect this case explicitly? if rawdb.ReadCanonicalHash(c.chainDb, prevHeader.NumberU64(nodeCtx)) != prevHash { if h := rawdb.FindCommonAncestor(c.chainDb, prevHeader, header, nodeCtx); h != nil { + + // If indexAddressUtxos flag is enabled, update the address utxo map + // TODO: Need to be able to turn on/off indexer and fix corrupted state + if c.indexAddressUtxos { + reorgHeaders := make([]*types.WorkObject, 0) + for prev := prevHeader; prev.Hash() != h.Hash(); prev = rawdb.ReadHeader(c.chainDb, prev.ParentHash(nodeCtx)) { + reorgHeaders = append(reorgHeaders, h) + } + + // Reorg out all outpoints of the reorg headers + err := c.reorgUtxoIndexer(reorgHeaders, addressOutpoints, nodeCtx, config) + if err != nil { + c.logger.Error("Failed to reorg utxo indexer", "err", err) + validUtxoIndex = false + } + + // Add new blocks from current hash back to common ancestor + for curr := header; curr.Hash() != h.Hash(); curr = rawdb.ReadHeader(c.chainDb, curr.ParentHash(nodeCtx)) { + block := rawdb.ReadWorkObject(c.chainDb, curr.Hash(), types.BlockObject) + c.addOutpointsToIndexer(addressOutpoints, nodeCtx, config, block) + } + } + c.newHead(h.NumberU64(nodeCtx), true) } } } + + if c.indexAddressUtxos { + c.addOutpointsToIndexer(addressOutpoints, nodeCtx, config, ev.Block) + } + c.newHead(header.NumberU64(nodeCtx), false) + if c.indexAddressUtxos && validUtxoIndex { + err := rawdb.WriteAddressOutpoints(c.chainDb, addressOutpoints) + if err != nil { + panic(err) + } + } + prevHeader, prevHash = header, header.Hash() } } @@ -490,3 +543,108 @@ func (c *ChainIndexer) removeSectionHead(section uint64) { c.indexDb.Delete(append([]byte("shead"), data[:]...)) } + +// addOutpointsToIndexer removes the spent outpoints and adds new utxos to the indexer. +func (c *ChainIndexer) addOutpointsToIndexer(addressOutpoints map[string]map[string]*types.OutpointAndDenomination, nodeCtx int, config params.ChainConfig, block *types.WorkObject) { + utxos := block.QiTransactions() + + for _, tx := range utxos { + for _, in := range tx.TxIn() { + + // Skip Coinbase TxIns since they do not have a previous outpoint + if types.IsCoinBaseTx(tx, block.ParentHash(nodeCtx), config.Location) { + continue + } + + outpoint := in.PreviousOutPoint + + address := crypto.PubkeyBytesToAddress(in.PubKey, config.Location).Hex() + outpointsForAddress := addressOutpoints[address] + + delete(outpointsForAddress, outpoint.Key()) + } + + for i, out := range tx.TxOut() { + + addrBytes := out.Address + outpoint := types.OutPoint{ + TxHash: tx.Hash(), + Index: uint16(i), + } + + address := common.BytesToAddress(addrBytes, config.Location).Hex() + + outpointAndDenom := &types.OutpointAndDenomination{ + TxHash: outpoint.TxHash, + Index: outpoint.Index, + Denomination: out.Denomination, + } + + if _, ok := addressOutpoints[address]; !ok { + addressOutpoints[address] = make(map[string]*types.OutpointAndDenomination) + } + addressOutpoints[address][outpointAndDenom.Key()] = outpointAndDenom + } + } +} + +// reorgUtxoIndexer adds back previously removed outpoints and removes newly added outpoints. +// This is done in reverse order from the old header to the common ancestor. +func (c *ChainIndexer) reorgUtxoIndexer(headers []*types.WorkObject, addressOutpoints map[string]map[string]*types.OutpointAndDenomination, nodeCtx int, config params.ChainConfig) error { + for _, header := range headers { + block := rawdb.ReadWorkObject(c.chainDb, header.Hash(), types.BlockObject) + + for _, tx := range block.QiTransactions() { + for i, out := range tx.TxOut() { + + address := out.Address + + addr := common.BytesToAddress(address, config.Location).Hex() + outpointsForAddress := addressOutpoints[addr] + + // reconstruct outpoint to remove it via outpoint.Key() + outpoint := types.OutPoint{ + TxHash: tx.Hash(), + Index: uint16(i), + } + + delete(outpointsForAddress, outpoint.Key()) + } + + if types.IsCoinBaseTx(tx, block.ParentHash(nodeCtx), config.Location) { + continue + } + + for _, in := range tx.TxIn() { + outpoint := in.PreviousOutPoint + address := crypto.PubkeyBytesToAddress(in.PubKey, config.Location).Hex() + + parent := rawdb.ReadHeader(c.chainDb, block.ParentHash(nodeCtx)) + + state, err := c.StateAt(parent.EVMRoot(), parent.UTXORoot(), parent.EtxSetRoot()) + if err != nil { + return err + } + + entry := state.GetUTXO(outpoint.TxHash, outpoint.Index) + if entry == nil { + // missing entry while tryig to add back outpoint + continue + } + + outpointAndDenom := &types.OutpointAndDenomination{ + TxHash: outpoint.TxHash, + Index: outpoint.Index, + Denomination: entry.Denomination, + } + + if _, ok := addressOutpoints[address]; !ok { + addressOutpoints[address] = make(map[string]*types.OutpointAndDenomination) + } + addressOutpoints[address][outpointAndDenom.Key()] = outpointAndDenom + } + + } + } + return nil +} diff --git a/core/core.go b/core/core.go index f0ba3266b0..b28165dfd1 100644 --- a/core/core.go +++ b/core/core.go @@ -78,12 +78,8 @@ type Core struct { logger *log.Logger } -type IndexerConfig struct { - IndexAddressUtxos bool -} - -func NewCore(db ethdb.Database, config *Config, isLocalBlock func(block *types.WorkObject) bool, txConfig *TxPoolConfig, txLookupLimit *uint64, chainConfig *params.ChainConfig, slicesRunning []common.Location, currentExpansionNumber uint8, genesisBlock *types.WorkObject, domClientUrl string, subClientUrls []string, engine consensus.Engine, cacheConfig *CacheConfig, vmConfig vm.Config, indexerConfig *IndexerConfig, genesis *Genesis, logger *log.Logger) (*Core, error) { - slice, err := NewSlice(db, config, txConfig, txLookupLimit, isLocalBlock, chainConfig, slicesRunning, currentExpansionNumber, genesisBlock, domClientUrl, subClientUrls, engine, cacheConfig, indexerConfig, vmConfig, genesis, logger) +func NewCore(db ethdb.Database, config *Config, isLocalBlock func(block *types.WorkObject) bool, txConfig *TxPoolConfig, txLookupLimit *uint64, chainConfig *params.ChainConfig, slicesRunning []common.Location, currentExpansionNumber uint8, genesisBlock *types.WorkObject, domClientUrl string, subClientUrls []string, engine consensus.Engine, cacheConfig *CacheConfig, vmConfig vm.Config, genesis *Genesis, logger *log.Logger) (*Core, error) { + slice, err := NewSlice(db, config, txConfig, txLookupLimit, isLocalBlock, chainConfig, slicesRunning, currentExpansionNumber, genesisBlock, domClientUrl, subClientUrls, engine, cacheConfig, vmConfig, genesis, logger) if err != nil { return nil, err } @@ -942,6 +938,10 @@ func (c *Core) CheckIfEtxIsEligible(etxEligibleSlices common.Hash, location comm return c.sl.hc.CheckIfEtxIsEligible(etxEligibleSlices, location) } +func (c *Core) WriteAddressOutpoints(outpoints map[string]map[string]*types.OutpointAndDenomination) error { + return c.sl.hc.WriteAddressOutpoints(outpoints) +} + //--------------------// // BlockChain methods // //--------------------// @@ -1142,8 +1142,25 @@ func (c *Core) TrieNode(hash common.Hash) ([]byte, error) { return c.sl.hc.bc.processor.TrieNode(hash) } -func (c *Core) GetUTXOsByAddress(addr common.Address) ([]*types.UtxoEntry, error) { - return c.sl.hc.bc.processor.GetUTXOsByAddress(addr) +func (c *Core) GetOutpointsByAddress(address common.Address) map[string]*types.OutpointAndDenomination { + outpoints := rawdb.ReadAddressOutpoints(c.sl.hc.bc.db, c.sl.hc.NodeLocation()) + outpointsForAddress := outpoints[address.Hex()] + return outpointsForAddress +} + +func (c *Core) GetUTXOsByAddressAtState(state *state.StateDB, address common.Address) ([]*types.UtxoEntry, error) { + outpointsForAddress := c.GetOutpointsByAddress(address) + utxos := make([]*types.UtxoEntry, 0, len(outpointsForAddress)) + + for _, outpoint := range outpointsForAddress { + entry := state.GetUTXO(outpoint.TxHash, outpoint.Index) + if entry == nil { + return nil, errors.New("failed to get UTXO for address") + } + utxos = append(utxos, entry) + } + + return utxos, nil } //----------------// diff --git a/core/genesis.go b/core/genesis.go index 2c5a71a09f..4b6b030a0e 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -512,7 +512,7 @@ func ReadGenesisQiAlloc(filename string, logger *log.Logger) map[string]GenesisU } // WriteGenesisUtxoSet writes the genesis utxo set to the database -func AddGenesisUtxos(state *state.StateDB, nodeLocation common.Location, logger *log.Logger) { +func AddGenesisUtxos(state *state.StateDB, nodeLocation common.Location, addressOutpointMap map[string]map[string]*types.OutpointAndDenomination, logger *log.Logger) { qiAlloc := ReadGenesisQiAlloc("genallocs/gen_alloc_qi_"+nodeLocation.Name()+".json", logger) // logger.WithField("alloc", len(qiAlloc)).Info("Allocating genesis accounts") for addressString, utxo := range qiAlloc { @@ -537,5 +537,16 @@ func AddGenesisUtxos(state *state.StateDB, nodeLocation common.Location, logger if err := state.CreateUTXO(hash, uint16(utxo.Index), newUtxo); err != nil { panic(fmt.Sprintf("Failed to create genesis UTXO: %v", err)) } + + outpointAndDenomination := &types.OutpointAndDenomination{ + TxHash: hash, + Index: uint16(utxo.Index), + Denomination: uint8(utxo.Denomination), + } + + if _, ok := addressOutpointMap[addr.Hex()]; !ok { + addressOutpointMap[addr.Hex()] = make(map[string]*types.OutpointAndDenomination) + } + addressOutpointMap[addr.Hex()][outpointAndDenomination.Key()] = outpointAndDenomination } } diff --git a/core/headerchain.go b/core/headerchain.go index 0f2ebb4b97..27e8a145ef 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -78,13 +78,11 @@ type HeaderChain struct { processingState bool logger *log.Logger - - indexerConfig *IndexerConfig } // NewHeaderChain creates a new HeaderChain structure. ProcInterrupt points // to the parent's interrupt semaphore. -func NewHeaderChain(db ethdb.Database, engine consensus.Engine, pEtxsRollupFetcher getPendingEtxsRollup, pEtxsFetcher getPendingEtxs, chainConfig *params.ChainConfig, cacheConfig *CacheConfig, txLookupLimit *uint64, indexerConfig *IndexerConfig, vmConfig vm.Config, slicesRunning []common.Location, currentExpansionNumber uint8, logger *log.Logger) (*HeaderChain, error) { +func NewHeaderChain(db ethdb.Database, engine consensus.Engine, pEtxsRollupFetcher getPendingEtxsRollup, pEtxsFetcher getPendingEtxs, chainConfig *params.ChainConfig, cacheConfig *CacheConfig, txLookupLimit *uint64, vmConfig vm.Config, slicesRunning []common.Location, currentExpansionNumber uint8, logger *log.Logger) (*HeaderChain, error) { headerCache, _ := lru.New[common.Hash, types.WorkObject](headerCacheLimit) numberCache, _ := lru.New[common.Hash, uint64](numberCacheLimit) nodeCtx := chainConfig.Location.Context() @@ -99,7 +97,6 @@ func NewHeaderChain(db ethdb.Database, engine consensus.Engine, pEtxsRollupFetch fetchPEtxRollup: pEtxsRollupFetcher, fetchPEtx: pEtxsFetcher, logger: logger, - indexerConfig: indexerConfig, currentExpansionNumber: currentExpansionNumber, } @@ -147,9 +144,6 @@ func NewHeaderChain(db ethdb.Database, engine consensus.Engine, pEtxsRollupFetch heads := make([]*types.WorkObject, 0) hc.heads = heads - // Initialize the UTXO cache - hc.InitializeAddressUtxoCache() - return hc, nil } @@ -1006,65 +1000,6 @@ func (hc *HeaderChain) SlicesRunning() []common.Location { return hc.slicesRunning } -// InitializeAddressUtxoCache initializes the address utxo cache. -// It is important to manage this set since nodes can enable / disable -// address indexing between start ups. When a node disables address utxo -// indexing, prior entries in the table will need to be removed. -func (hc *HeaderChain) InitializeAddressUtxoCache() error { - - start := time.Now() - - it := hc.bc.db.NewIterator(rawdb.AddressUtxosPrefix, nil) - defer it.Release() - - utxoCount := 0 - for it.Next() { - utxoCount += 1 - // If we are no longer running with address utxo indexing, - // delete all prior entries - if !hc.indexerConfig.IndexAddressUtxos { - err := hc.bc.db.Delete(it.Key()) - if err != nil { - return err - } - } else { - break - } - } - - // If we are no longer running with address utxo indexing, - // delete all prior entries and compact the table - if !hc.indexerConfig.IndexAddressUtxos { - end := common.CopyBytes(rawdb.AddressUtxosPrefix) - end[len(end)-1]++ - - hc.bc.db.Compact(rawdb.AddressUtxosPrefix, end) - return nil - } - - // If there are utxos in the database, then we need to index them - // into the address utxo table - if utxoCount == 0 { - it := hc.bc.db.NewIterator([]byte("ut"), nil) - defer it.Release() - - for it.Next() { - data := it.Value() - utxo := new(types.UtxoEntry) - if err := rlp.Decode(bytes.NewReader(data), utxo); err != nil { - return nil - } - address := common.BytesToAddress(utxo.Address, hc.NodeLocation()) - addressUtxos := rawdb.ReadAddressUtxos(hc.bc.db, address) - addressUtxos = append(addressUtxos, utxo) - rawdb.WriteAddressUtxos(hc.bc.db, address, addressUtxos) - } - } - - hc.logger.Info("Finished initializing address utxo cache", "duration", time.Since(start)) - return nil -} - // ComputeEfficiencyScore calculates the efficiency score for the given header func (hc *HeaderChain) ComputeEfficiencyScore(parent *types.WorkObject) uint16 { deltaS := new(big.Int).Add(parent.ParentDeltaS(common.REGION_CTX), parent.ParentDeltaS(common.ZONE_CTX)) @@ -1144,3 +1079,7 @@ func (hc *HeaderChain) GetExpansionNumber() uint8 { func (hc *HeaderChain) GetPrimeTerminus(header *types.WorkObject) *types.WorkObject { return hc.GetHeaderByHash(header.PrimeTerminus()) } + +func (hc *HeaderChain) WriteAddressOutpoints(outpoints map[string]map[string]*types.OutpointAndDenomination) error { + return rawdb.WriteAddressOutpoints(hc.bc.db, outpoints) +} diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 94c0c38c52..e63ec7232d 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -28,7 +28,6 @@ import ( "github.com/dominant-strategies/go-quai/ethdb" "github.com/dominant-strategies/go-quai/log" "github.com/dominant-strategies/go-quai/params" - "github.com/dominant-strategies/go-quai/rlp" "google.golang.org/protobuf/proto" ) @@ -1600,32 +1599,78 @@ func DeleteInboundEtxs(db ethdb.KeyValueWriter, hash common.Hash) { } } -func WriteAddressUtxos(db ethdb.KeyValueWriter, address common.Address, utxos []*types.UtxoEntry) { - data, err := rlp.EncodeToBytes(utxos) +func WriteAddressOutpoints(db ethdb.KeyValueWriter, outpointMap map[string]map[string]*types.OutpointAndDenomination) error { + outpointsProto := &types.ProtoOutPointsMap{ + Entries: make(map[string]*types.ProtoAddressOutPoints), + } + + for address, outpoints := range outpointMap { + addressOutpointsProto := &types.ProtoAddressOutPoints{ + OutPoints: make(map[string]*types.ProtoOutPointAndDenomination, len(outpoints)), + } + + for _, outpoint := range outpoints { + outpointProto, err := outpoint.ProtoEncode() + if err != nil { + return err + } + + addressOutpointsProto.OutPoints[outpoint.Key()] = outpointProto + } + + // Correctly assign the address's UTXOs to the Entries map within the utxosProto struct + outpointsProto.Entries[address] = addressOutpointsProto + } + + // Now, marshal utxosProto to protobuf bytes + data, err := proto.Marshal(outpointsProto) if err != nil { db.Logger().WithField("err", err).Fatal("Failed to rlp encode utxos") } - if err := db.Put(addressUtxosKey(address), data); err != nil { + if err := db.Put(AddressUtxosPrefix, data); err != nil { db.Logger().WithField("err", err).Fatal("Failed to store utxos") } + + // And finally, store the data in the database under the appropriate key + return db.Put(AddressUtxosPrefix, data) } -func ReadAddressUtxos(db ethdb.Reader, address common.Address) []*types.UtxoEntry { +func ReadAddressOutpoints(db ethdb.Reader, location common.Location) map[string]map[string]*types.OutpointAndDenomination { // Try to look up the data in leveldb. - data, _ := db.Get(addressUtxosKey(address)) + data, _ := db.Get(AddressUtxosPrefix) if len(data) == 0 { - return nil + return make(map[string]map[string]*types.OutpointAndDenomination) } - utxos := []*types.UtxoEntry{} - if err := rlp.Decode(bytes.NewReader(data), &utxos); err != nil { - db.Logger().WithField("err", err).Error("Invalid utxos RLP") + outpointsProto := &types.ProtoOutPointsMap{} + if err := proto.Unmarshal(data, outpointsProto); err != nil { return nil } - return utxos + + outpointsMap := make(map[string]map[string]*types.OutpointAndDenomination) + for addrStr, addressOutpointsProto := range outpointsProto.Entries { + outpoints := map[string]*types.OutpointAndDenomination{} + + for _, outpointProto := range addressOutpointsProto.OutPoints { + outpoint := new(types.OutpointAndDenomination) + err := outpoint.ProtoDecode(outpointProto) + if err != nil { + db.Logger().WithFields(log.Fields{ + "err": err, + "outpoint": outpointProto, + }).Error("Invalid outpointProto") + return nil + } + outpoints[outpoint.Key()] = outpoint + } + + outpointsMap[addrStr] = outpoints + } + + return outpointsMap } -func DeleteAddressUtxos(db ethdb.KeyValueWriter, address common.Address) { - if err := db.Delete(addressUtxosKey(address)); err != nil { +func DeleteAddressUtxos(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { + if err := db.Delete(AddressUtxosPrefix); err != nil { db.Logger().WithField("err", err).Fatal("Failed to delete utxos") } } diff --git a/core/rawdb/db.pb.go b/core/rawdb/db.pb.go index efb89f164a..0f2560b344 100644 --- a/core/rawdb/db.pb.go +++ b/core/rawdb/db.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v4.25.3 +// protoc v4.25.2 // source: core/rawdb/db.proto package rawdb diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index fa1dd034a3..5af28094d6 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -383,7 +383,3 @@ func bloomKey(hash common.Hash) []byte { func inboundEtxsKey(hash common.Hash) []byte { return append(inboundEtxsPrefix, hash.Bytes()...) } - -func addressUtxosKey(address common.Address) []byte { - return append(AddressUtxosPrefix, address.Bytes()...) -} diff --git a/core/slice.go b/core/slice.go index 941e668255..285a9f002f 100644 --- a/core/slice.go +++ b/core/slice.go @@ -87,7 +87,7 @@ type Slice struct { logger *log.Logger } -func NewSlice(db ethdb.Database, config *Config, txConfig *TxPoolConfig, txLookupLimit *uint64, isLocalBlock func(block *types.WorkObject) bool, chainConfig *params.ChainConfig, slicesRunning []common.Location, currentExpansionNumber uint8, genesisBlock *types.WorkObject, domClientUrl string, subClientUrls []string, engine consensus.Engine, cacheConfig *CacheConfig, indexerConfig *IndexerConfig, vmConfig vm.Config, genesis *Genesis, logger *log.Logger) (*Slice, error) { +func NewSlice(db ethdb.Database, config *Config, txConfig *TxPoolConfig, txLookupLimit *uint64, isLocalBlock func(block *types.WorkObject) bool, chainConfig *params.ChainConfig, slicesRunning []common.Location, currentExpansionNumber uint8, genesisBlock *types.WorkObject, domClientUrl string, subClientUrls []string, engine consensus.Engine, cacheConfig *CacheConfig, vmConfig vm.Config, genesis *Genesis, logger *log.Logger) (*Slice, error) { nodeCtx := chainConfig.Location.Context() sl := &Slice{ config: chainConfig, @@ -103,7 +103,7 @@ func NewSlice(db ethdb.Database, config *Config, txConfig *TxPoolConfig, txLooku sl.AddGenesisHash(genesisBlock.Hash()) } var err error - sl.hc, err = NewHeaderChain(db, engine, sl.GetPEtxRollupAfterRetryThreshold, sl.GetPEtxAfterRetryThreshold, chainConfig, cacheConfig, txLookupLimit, indexerConfig, vmConfig, slicesRunning, currentExpansionNumber, logger) + sl.hc, err = NewHeaderChain(db, engine, sl.GetPEtxRollupAfterRetryThreshold, sl.GetPEtxAfterRetryThreshold, chainConfig, cacheConfig, txLookupLimit, vmConfig, slicesRunning, currentExpansionNumber, logger) if err != nil { return nil, err } diff --git a/core/state_processor.go b/core/state_processor.go index 44b5b3da52..b519f4f7f5 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -1473,8 +1473,3 @@ func prepareApplyETX(statedb *state.StateDB, value *big.Int, nodeLocation common statedb.SetBalance(common.ZeroInternal(nodeLocation), value) // Use zero address at temp placeholder and set it to value return prevZeroBal } - -func (p *StateProcessor) GetUTXOsByAddress(address common.Address) ([]*types.UtxoEntry, error) { - utxos := rawdb.ReadAddressUtxos(p.hc.bc.db, address) - return utxos, nil -} diff --git a/core/types/proto_block.pb.go b/core/types/proto_block.pb.go index cb0261d5c2..73ae24f619 100644 --- a/core/types/proto_block.pb.go +++ b/core/types/proto_block.pb.go @@ -2009,6 +2009,163 @@ func (x *ProtoTxOut) GetLock() []byte { return nil } +type ProtoOutPointAndDenomination struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hash *common.ProtoHash `protobuf:"bytes,1,opt,name=hash,proto3,oneof" json:"hash,omitempty"` + Index *uint32 `protobuf:"varint,2,opt,name=index,proto3,oneof" json:"index,omitempty"` + Denomination *uint32 `protobuf:"varint,3,opt,name=denomination,proto3,oneof" json:"denomination,omitempty"` +} + +func (x *ProtoOutPointAndDenomination) Reset() { + *x = ProtoOutPointAndDenomination{} + if protoimpl.UnsafeEnabled { + mi := &file_core_types_proto_block_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProtoOutPointAndDenomination) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProtoOutPointAndDenomination) ProtoMessage() {} + +func (x *ProtoOutPointAndDenomination) ProtoReflect() protoreflect.Message { + mi := &file_core_types_proto_block_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProtoOutPointAndDenomination.ProtoReflect.Descriptor instead. +func (*ProtoOutPointAndDenomination) Descriptor() ([]byte, []int) { + return file_core_types_proto_block_proto_rawDescGZIP(), []int{28} +} + +func (x *ProtoOutPointAndDenomination) GetHash() *common.ProtoHash { + if x != nil { + return x.Hash + } + return nil +} + +func (x *ProtoOutPointAndDenomination) GetIndex() uint32 { + if x != nil && x.Index != nil { + return *x.Index + } + return 0 +} + +func (x *ProtoOutPointAndDenomination) GetDenomination() uint32 { + if x != nil && x.Denomination != nil { + return *x.Denomination + } + return 0 +} + +type ProtoAddressOutPoints struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OutPoints map[string]*ProtoOutPointAndDenomination `protobuf:"bytes,1,rep,name=out_points,json=outPoints,proto3" json:"out_points,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *ProtoAddressOutPoints) Reset() { + *x = ProtoAddressOutPoints{} + if protoimpl.UnsafeEnabled { + mi := &file_core_types_proto_block_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProtoAddressOutPoints) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProtoAddressOutPoints) ProtoMessage() {} + +func (x *ProtoAddressOutPoints) ProtoReflect() protoreflect.Message { + mi := &file_core_types_proto_block_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProtoAddressOutPoints.ProtoReflect.Descriptor instead. +func (*ProtoAddressOutPoints) Descriptor() ([]byte, []int) { + return file_core_types_proto_block_proto_rawDescGZIP(), []int{29} +} + +func (x *ProtoAddressOutPoints) GetOutPoints() map[string]*ProtoOutPointAndDenomination { + if x != nil { + return x.OutPoints + } + return nil +} + +type ProtoOutPointsMap struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Entries map[string]*ProtoAddressOutPoints `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *ProtoOutPointsMap) Reset() { + *x = ProtoOutPointsMap{} + if protoimpl.UnsafeEnabled { + mi := &file_core_types_proto_block_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProtoOutPointsMap) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProtoOutPointsMap) ProtoMessage() {} + +func (x *ProtoOutPointsMap) ProtoReflect() protoreflect.Message { + mi := &file_core_types_proto_block_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProtoOutPointsMap.ProtoReflect.Descriptor instead. +func (*ProtoOutPointsMap) Descriptor() ([]byte, []int) { + return file_core_types_proto_block_proto_rawDescGZIP(), []int{30} +} + +func (x *ProtoOutPointsMap) GetEntries() map[string]*ProtoAddressOutPoints { + if x != nil { + return x.Entries + } + return nil +} + var File_core_types_proto_block_proto protoreflect.FileDescriptor var file_core_types_proto_block_proto_rawDesc = []byte{ @@ -2443,11 +2600,46 @@ var file_core_types_proto_block_proto_rawDesc = []byte{ 0x17, 0x0a, 0x04, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x02, 0x52, 0x04, 0x6c, 0x6f, 0x63, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x33, - 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x6f, 0x6d, - 0x69, 0x6e, 0x61, 0x6e, 0x74, 0x2d, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x69, 0x65, 0x73, - 0x2f, 0x67, 0x6f, 0x2d, 0x71, 0x75, 0x61, 0x69, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0xb2, + 0x01, 0x0a, 0x1c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, + 0x41, 0x6e, 0x64, 0x44, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x2a, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, + 0x48, 0x00, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x05, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x0c, + 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, + 0x07, 0x0a, 0x05, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0xc6, 0x01, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4a, 0x0a, + 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2b, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x2e, + 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, + 0x6f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0x61, 0x0a, 0x0e, 0x4f, 0x75, 0x74, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x39, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x41, 0x6e, 0x64, 0x44, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xae, 0x01, 0x0a, + 0x11, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x4d, + 0x61, 0x70, 0x12, 0x3f, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x4d, 0x61, 0x70, 0x2e, 0x45, 0x6e, + 0x74, 0x72, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, + 0x69, 0x65, 0x73, 0x1a, 0x58, 0x0a, 0x0c, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x33, 0x5a, + 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x6f, 0x6d, 0x69, + 0x6e, 0x61, 0x6e, 0x74, 0x2d, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x69, 0x65, 0x73, 0x2f, + 0x67, 0x6f, 0x2d, 0x71, 0x75, 0x61, 0x69, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2462,79 +2654,84 @@ func file_core_types_proto_block_proto_rawDescGZIP() []byte { return file_core_types_proto_block_proto_rawDescData } -var file_core_types_proto_block_proto_msgTypes = make([]protoimpl.MessageInfo, 28) +var file_core_types_proto_block_proto_msgTypes = make([]protoimpl.MessageInfo, 33) var file_core_types_proto_block_proto_goTypes = []interface{}{ - (*ProtoHeader)(nil), // 0: block.ProtoHeader - (*ProtoTransaction)(nil), // 1: block.ProtoTransaction - (*ProtoTransactions)(nil), // 2: block.ProtoTransactions - (*ProtoHeaders)(nil), // 3: block.ProtoHeaders - (*ProtoManifest)(nil), // 4: block.ProtoManifest - (*ProtoAccessList)(nil), // 5: block.ProtoAccessList - (*ProtoWorkObjectHeader)(nil), // 6: block.ProtoWorkObjectHeader - (*ProtoWorkObjectHeaders)(nil), // 7: block.ProtoWorkObjectHeaders - (*ProtoWorkObjectBody)(nil), // 8: block.ProtoWorkObjectBody - (*ProtoWorkObject)(nil), // 9: block.ProtoWorkObject - (*ProtoWorkObjects)(nil), // 10: block.ProtoWorkObjects - (*ProtoWorkObjectBlockView)(nil), // 11: block.ProtoWorkObjectBlockView - (*ProtoWorkObjectHeaderView)(nil), // 12: block.ProtoWorkObjectHeaderView - (*ProtoAccessTuple)(nil), // 13: block.ProtoAccessTuple - (*ProtoReceiptForStorage)(nil), // 14: block.ProtoReceiptForStorage - (*ProtoReceiptsForStorage)(nil), // 15: block.ProtoReceiptsForStorage - (*ProtoLogForStorage)(nil), // 16: block.ProtoLogForStorage - (*ProtoLogsForStorage)(nil), // 17: block.ProtoLogsForStorage - (*ProtoPendingHeader)(nil), // 18: block.ProtoPendingHeader - (*ProtoTermini)(nil), // 19: block.ProtoTermini - (*ProtoEtxSet)(nil), // 20: block.ProtoEtxSet - (*ProtoPendingEtxs)(nil), // 21: block.ProtoPendingEtxs - (*ProtoPendingEtxsRollup)(nil), // 22: block.ProtoPendingEtxsRollup - (*ProtoTxIns)(nil), // 23: block.ProtoTxIns - (*ProtoTxOuts)(nil), // 24: block.ProtoTxOuts - (*ProtoTxIn)(nil), // 25: block.ProtoTxIn - (*ProtoOutPoint)(nil), // 26: block.ProtoOutPoint - (*ProtoTxOut)(nil), // 27: block.ProtoTxOut - (*common.ProtoHash)(nil), // 28: common.ProtoHash - (*common.ProtoLocation)(nil), // 29: common.ProtoLocation - (*common.ProtoHashes)(nil), // 30: common.ProtoHashes - (*common.ProtoAddress)(nil), // 31: common.ProtoAddress + (*ProtoHeader)(nil), // 0: block.ProtoHeader + (*ProtoTransaction)(nil), // 1: block.ProtoTransaction + (*ProtoTransactions)(nil), // 2: block.ProtoTransactions + (*ProtoHeaders)(nil), // 3: block.ProtoHeaders + (*ProtoManifest)(nil), // 4: block.ProtoManifest + (*ProtoAccessList)(nil), // 5: block.ProtoAccessList + (*ProtoWorkObjectHeader)(nil), // 6: block.ProtoWorkObjectHeader + (*ProtoWorkObjectHeaders)(nil), // 7: block.ProtoWorkObjectHeaders + (*ProtoWorkObjectBody)(nil), // 8: block.ProtoWorkObjectBody + (*ProtoWorkObject)(nil), // 9: block.ProtoWorkObject + (*ProtoWorkObjects)(nil), // 10: block.ProtoWorkObjects + (*ProtoWorkObjectBlockView)(nil), // 11: block.ProtoWorkObjectBlockView + (*ProtoWorkObjectHeaderView)(nil), // 12: block.ProtoWorkObjectHeaderView + (*ProtoAccessTuple)(nil), // 13: block.ProtoAccessTuple + (*ProtoReceiptForStorage)(nil), // 14: block.ProtoReceiptForStorage + (*ProtoReceiptsForStorage)(nil), // 15: block.ProtoReceiptsForStorage + (*ProtoLogForStorage)(nil), // 16: block.ProtoLogForStorage + (*ProtoLogsForStorage)(nil), // 17: block.ProtoLogsForStorage + (*ProtoPendingHeader)(nil), // 18: block.ProtoPendingHeader + (*ProtoTermini)(nil), // 19: block.ProtoTermini + (*ProtoEtxSet)(nil), // 20: block.ProtoEtxSet + (*ProtoPendingEtxs)(nil), // 21: block.ProtoPendingEtxs + (*ProtoPendingEtxsRollup)(nil), // 22: block.ProtoPendingEtxsRollup + (*ProtoTxIns)(nil), // 23: block.ProtoTxIns + (*ProtoTxOuts)(nil), // 24: block.ProtoTxOuts + (*ProtoTxIn)(nil), // 25: block.ProtoTxIn + (*ProtoOutPoint)(nil), // 26: block.ProtoOutPoint + (*ProtoTxOut)(nil), // 27: block.ProtoTxOut + (*ProtoOutPointAndDenomination)(nil), // 28: block.ProtoOutPointAndDenomination + (*ProtoAddressOutPoints)(nil), // 29: block.ProtoAddressOutPoints + (*ProtoOutPointsMap)(nil), // 30: block.ProtoOutPointsMap + nil, // 31: block.ProtoAddressOutPoints.OutPointsEntry + nil, // 32: block.ProtoOutPointsMap.EntriesEntry + (*common.ProtoHash)(nil), // 33: common.ProtoHash + (*common.ProtoLocation)(nil), // 34: common.ProtoLocation + (*common.ProtoHashes)(nil), // 35: common.ProtoHashes + (*common.ProtoAddress)(nil), // 36: common.ProtoAddress } var file_core_types_proto_block_proto_depIdxs = []int32{ - 28, // 0: block.ProtoHeader.parent_hash:type_name -> common.ProtoHash - 28, // 1: block.ProtoHeader.uncle_hash:type_name -> common.ProtoHash - 28, // 2: block.ProtoHeader.evm_root:type_name -> common.ProtoHash - 28, // 3: block.ProtoHeader.tx_hash:type_name -> common.ProtoHash - 28, // 4: block.ProtoHeader.etx_hash:type_name -> common.ProtoHash - 28, // 5: block.ProtoHeader.etx_rollup_hash:type_name -> common.ProtoHash - 28, // 6: block.ProtoHeader.manifest_hash:type_name -> common.ProtoHash - 28, // 7: block.ProtoHeader.receipt_hash:type_name -> common.ProtoHash - 29, // 8: block.ProtoHeader.location:type_name -> common.ProtoLocation - 28, // 9: block.ProtoHeader.mix_hash:type_name -> common.ProtoHash - 28, // 10: block.ProtoHeader.utxo_root:type_name -> common.ProtoHash - 28, // 11: block.ProtoHeader.etx_set_hash:type_name -> common.ProtoHash - 28, // 12: block.ProtoHeader.etx_eligible_slices:type_name -> common.ProtoHash - 28, // 13: block.ProtoHeader.prime_terminus:type_name -> common.ProtoHash - 28, // 14: block.ProtoHeader.interlink_root_hash:type_name -> common.ProtoHash + 33, // 0: block.ProtoHeader.parent_hash:type_name -> common.ProtoHash + 33, // 1: block.ProtoHeader.uncle_hash:type_name -> common.ProtoHash + 33, // 2: block.ProtoHeader.evm_root:type_name -> common.ProtoHash + 33, // 3: block.ProtoHeader.tx_hash:type_name -> common.ProtoHash + 33, // 4: block.ProtoHeader.etx_hash:type_name -> common.ProtoHash + 33, // 5: block.ProtoHeader.etx_rollup_hash:type_name -> common.ProtoHash + 33, // 6: block.ProtoHeader.manifest_hash:type_name -> common.ProtoHash + 33, // 7: block.ProtoHeader.receipt_hash:type_name -> common.ProtoHash + 34, // 8: block.ProtoHeader.location:type_name -> common.ProtoLocation + 33, // 9: block.ProtoHeader.mix_hash:type_name -> common.ProtoHash + 33, // 10: block.ProtoHeader.utxo_root:type_name -> common.ProtoHash + 33, // 11: block.ProtoHeader.etx_set_root:type_name -> common.ProtoHash + 33, // 12: block.ProtoHeader.etx_eligible_slices:type_name -> common.ProtoHash + 33, // 13: block.ProtoHeader.prime_terminus:type_name -> common.ProtoHash + 33, // 14: block.ProtoHeader.interlink_root_hash:type_name -> common.ProtoHash 5, // 15: block.ProtoTransaction.access_list:type_name -> block.ProtoAccessList - 28, // 16: block.ProtoTransaction.originating_tx_hash:type_name -> common.ProtoHash + 33, // 16: block.ProtoTransaction.originating_tx_hash:type_name -> common.ProtoHash 23, // 17: block.ProtoTransaction.tx_ins:type_name -> block.ProtoTxIns 24, // 18: block.ProtoTransaction.tx_outs:type_name -> block.ProtoTxOuts - 28, // 19: block.ProtoTransaction.parent_hash:type_name -> common.ProtoHash - 28, // 20: block.ProtoTransaction.mix_hash:type_name -> common.ProtoHash + 33, // 19: block.ProtoTransaction.parent_hash:type_name -> common.ProtoHash + 33, // 20: block.ProtoTransaction.mix_hash:type_name -> common.ProtoHash 1, // 21: block.ProtoTransactions.transactions:type_name -> block.ProtoTransaction 0, // 22: block.ProtoHeaders.headers:type_name -> block.ProtoHeader - 28, // 23: block.ProtoManifest.manifest:type_name -> common.ProtoHash + 33, // 23: block.ProtoManifest.manifest:type_name -> common.ProtoHash 13, // 24: block.ProtoAccessList.access_tuples:type_name -> block.ProtoAccessTuple - 28, // 25: block.ProtoWorkObjectHeader.header_hash:type_name -> common.ProtoHash - 28, // 26: block.ProtoWorkObjectHeader.parent_hash:type_name -> common.ProtoHash - 28, // 27: block.ProtoWorkObjectHeader.tx_hash:type_name -> common.ProtoHash - 29, // 28: block.ProtoWorkObjectHeader.location:type_name -> common.ProtoLocation - 28, // 29: block.ProtoWorkObjectHeader.mix_hash:type_name -> common.ProtoHash + 33, // 25: block.ProtoWorkObjectHeader.header_hash:type_name -> common.ProtoHash + 33, // 26: block.ProtoWorkObjectHeader.parent_hash:type_name -> common.ProtoHash + 33, // 27: block.ProtoWorkObjectHeader.tx_hash:type_name -> common.ProtoHash + 34, // 28: block.ProtoWorkObjectHeader.location:type_name -> common.ProtoLocation + 33, // 29: block.ProtoWorkObjectHeader.mix_hash:type_name -> common.ProtoHash 6, // 30: block.ProtoWorkObjectHeaders.wo_headers:type_name -> block.ProtoWorkObjectHeader 0, // 31: block.ProtoWorkObjectBody.header:type_name -> block.ProtoHeader 2, // 32: block.ProtoWorkObjectBody.transactions:type_name -> block.ProtoTransactions 7, // 33: block.ProtoWorkObjectBody.uncles:type_name -> block.ProtoWorkObjectHeaders 2, // 34: block.ProtoWorkObjectBody.ext_transactions:type_name -> block.ProtoTransactions 4, // 35: block.ProtoWorkObjectBody.manifest:type_name -> block.ProtoManifest - 30, // 36: block.ProtoWorkObjectBody.interlink_hashes:type_name -> common.ProtoHashes + 35, // 36: block.ProtoWorkObjectBody.interlink_hashes:type_name -> common.ProtoHashes 6, // 37: block.ProtoWorkObject.wo_header:type_name -> block.ProtoWorkObjectHeader 8, // 38: block.ProtoWorkObject.wo_body:type_name -> block.ProtoWorkObjectBody 1, // 39: block.ProtoWorkObject.tx:type_name -> block.ProtoTransaction @@ -2543,19 +2740,19 @@ var file_core_types_proto_block_proto_depIdxs = []int32{ 8, // 42: block.ProtoWorkObjectBlockView.wo_body:type_name -> block.ProtoWorkObjectBody 6, // 43: block.ProtoWorkObjectHeaderView.wo_header:type_name -> block.ProtoWorkObjectHeader 8, // 44: block.ProtoWorkObjectHeaderView.wo_body:type_name -> block.ProtoWorkObjectBody - 28, // 45: block.ProtoAccessTuple.storage_key:type_name -> common.ProtoHash - 28, // 46: block.ProtoReceiptForStorage.tx_hash:type_name -> common.ProtoHash - 31, // 47: block.ProtoReceiptForStorage.contract_address:type_name -> common.ProtoAddress + 33, // 45: block.ProtoAccessTuple.storage_key:type_name -> common.ProtoHash + 33, // 46: block.ProtoReceiptForStorage.tx_hash:type_name -> common.ProtoHash + 36, // 47: block.ProtoReceiptForStorage.contract_address:type_name -> common.ProtoAddress 17, // 48: block.ProtoReceiptForStorage.logs:type_name -> block.ProtoLogsForStorage 2, // 49: block.ProtoReceiptForStorage.etxs:type_name -> block.ProtoTransactions 14, // 50: block.ProtoReceiptsForStorage.receipts:type_name -> block.ProtoReceiptForStorage - 31, // 51: block.ProtoLogForStorage.address:type_name -> common.ProtoAddress - 28, // 52: block.ProtoLogForStorage.topics:type_name -> common.ProtoHash + 36, // 51: block.ProtoLogForStorage.address:type_name -> common.ProtoAddress + 33, // 52: block.ProtoLogForStorage.topics:type_name -> common.ProtoHash 16, // 53: block.ProtoLogsForStorage.logs:type_name -> block.ProtoLogForStorage 9, // 54: block.ProtoPendingHeader.wo:type_name -> block.ProtoWorkObject 19, // 55: block.ProtoPendingHeader.termini:type_name -> block.ProtoTermini - 28, // 56: block.ProtoTermini.dom_termini:type_name -> common.ProtoHash - 28, // 57: block.ProtoTermini.sub_termini:type_name -> common.ProtoHash + 33, // 56: block.ProtoTermini.dom_termini:type_name -> common.ProtoHash + 33, // 57: block.ProtoTermini.sub_termini:type_name -> common.ProtoHash 9, // 58: block.ProtoPendingEtxs.header:type_name -> block.ProtoWorkObject 2, // 59: block.ProtoPendingEtxs.etxs:type_name -> block.ProtoTransactions 9, // 60: block.ProtoPendingEtxsRollup.header:type_name -> block.ProtoWorkObject @@ -2563,12 +2760,17 @@ var file_core_types_proto_block_proto_depIdxs = []int32{ 25, // 62: block.ProtoTxIns.tx_ins:type_name -> block.ProtoTxIn 27, // 63: block.ProtoTxOuts.tx_outs:type_name -> block.ProtoTxOut 26, // 64: block.ProtoTxIn.previous_out_point:type_name -> block.ProtoOutPoint - 28, // 65: block.ProtoOutPoint.hash:type_name -> common.ProtoHash - 66, // [66:66] is the sub-list for method output_type - 66, // [66:66] is the sub-list for method input_type - 66, // [66:66] is the sub-list for extension type_name - 66, // [66:66] is the sub-list for extension extendee - 0, // [0:66] is the sub-list for field type_name + 33, // 65: block.ProtoOutPoint.hash:type_name -> common.ProtoHash + 33, // 66: block.ProtoOutPointAndDenomination.hash:type_name -> common.ProtoHash + 31, // 67: block.ProtoAddressOutPoints.out_points:type_name -> block.ProtoAddressOutPoints.OutPointsEntry + 32, // 68: block.ProtoOutPointsMap.entries:type_name -> block.ProtoOutPointsMap.EntriesEntry + 28, // 69: block.ProtoAddressOutPoints.OutPointsEntry.value:type_name -> block.ProtoOutPointAndDenomination + 29, // 70: block.ProtoOutPointsMap.EntriesEntry.value:type_name -> block.ProtoAddressOutPoints + 71, // [71:71] is the sub-list for method output_type + 71, // [71:71] is the sub-list for method input_type + 71, // [71:71] is the sub-list for extension type_name + 71, // [71:71] is the sub-list for extension extendee + 0, // [0:71] is the sub-list for field type_name } func init() { file_core_types_proto_block_proto_init() } @@ -2913,6 +3115,42 @@ func file_core_types_proto_block_proto_init() { return nil } } + file_core_types_proto_block_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProtoOutPointAndDenomination); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_core_types_proto_block_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProtoAddressOutPoints); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_core_types_proto_block_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProtoOutPointsMap); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_core_types_proto_block_proto_msgTypes[0].OneofWrappers = []interface{}{} file_core_types_proto_block_proto_msgTypes[1].OneofWrappers = []interface{}{} @@ -2928,13 +3166,14 @@ func file_core_types_proto_block_proto_init() { file_core_types_proto_block_proto_msgTypes[25].OneofWrappers = []interface{}{} file_core_types_proto_block_proto_msgTypes[26].OneofWrappers = []interface{}{} file_core_types_proto_block_proto_msgTypes[27].OneofWrappers = []interface{}{} + file_core_types_proto_block_proto_msgTypes[28].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_core_types_proto_block_proto_rawDesc, NumEnums: 0, - NumMessages: 28, + NumMessages: 33, NumExtensions: 0, NumServices: 0, }, diff --git a/core/types/proto_block.proto b/core/types/proto_block.proto index 90071c4d39..729c6b0224 100644 --- a/core/types/proto_block.proto +++ b/core/types/proto_block.proto @@ -182,3 +182,17 @@ message ProtoTxOut { optional bytes address = 2; optional bytes lock = 3; } + +message ProtoOutPointAndDenomination { + optional common.ProtoHash hash = 1; + optional uint32 index = 2; + optional uint32 denomination = 3; +} + +message ProtoAddressOutPoints { + map out_points = 1; +} + +message ProtoOutPointsMap { + map entries = 1; +} \ No newline at end of file diff --git a/core/types/utxo.go b/core/types/utxo.go index befd810c63..d05ee474f6 100644 --- a/core/types/utxo.go +++ b/core/types/utxo.go @@ -1,6 +1,7 @@ package types import ( + "encoding/binary" "errors" "math" "math/big" @@ -102,6 +103,13 @@ type OutPoint struct { Index uint16 } +func (outPoint OutPoint) Key() string { + indexBytes := make([]byte, 2) + binary.BigEndian.PutUint16(indexBytes, outPoint.Index) + bytes := append(indexBytes, outPoint.TxHash.Bytes()...) + return common.Bytes2Hex(bytes) +} + func (outPoint OutPoint) ProtoEncode() (*ProtoOutPoint, error) { protoOutPoint := &ProtoOutPoint{} protoOutPoint.Hash = outPoint.TxHash.ProtoEncode() @@ -116,6 +124,37 @@ func (outPoint *OutPoint) ProtoDecode(protoOutPoint *ProtoOutPoint) error { return nil } +type OutpointAndDenomination struct { + TxHash common.Hash + Index uint16 + Denomination uint8 +} + +func (outPoint OutpointAndDenomination) Key() string { + indexBytes := make([]byte, 2) + binary.BigEndian.PutUint16(indexBytes, outPoint.Index) + bytes := append(indexBytes, outPoint.TxHash.Bytes()...) + return common.Bytes2Hex(bytes) +} + +func (outPoint OutpointAndDenomination) ProtoEncode() (*ProtoOutPointAndDenomination, error) { + protoOutPoint := &ProtoOutPointAndDenomination{} + protoOutPoint.Hash = outPoint.TxHash.ProtoEncode() + index := uint32(outPoint.Index) + protoOutPoint.Index = &index + denomination := uint32(outPoint.Denomination) + protoOutPoint.Denomination = &denomination + + return protoOutPoint, nil +} + +func (outPoint *OutpointAndDenomination) ProtoDecode(protoOutPoint *ProtoOutPointAndDenomination) error { + outPoint.TxHash.ProtoDecode(protoOutPoint.Hash) + outPoint.Index = uint16(*protoOutPoint.Index) + outPoint.Denomination = uint8(*protoOutPoint.Denomination) + return nil +} + // NewOutPoint returns a new Qi transaction outpoint point with the // provided hash and index. func NewOutPoint(txHash *common.Hash, index uint16) *OutPoint { @@ -234,3 +273,8 @@ func NewUtxoEntry(txOut *TxOut) *UtxoEntry { Lock: txOut.Lock, } } + +type AddressUtxos struct { + Address common.Address + Utxos []*UtxoEntry +} diff --git a/internal/quaiapi/api.go b/internal/quaiapi/api.go index c3afee1bf0..b9214b29a1 100644 --- a/internal/quaiapi/api.go +++ b/internal/quaiapi/api.go @@ -241,39 +241,57 @@ func (s *PublicBlockChainAPI) BlockNumber() hexutil.Uint64 { // given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta // block numbers are also allowed. func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.AddressBytes, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { - state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) - if state == nil || err != nil { - return nil, err - } addr := common.Bytes20ToAddress(address, s.b.NodeLocation()) - internal, err := addr.InternalAndQuaiAddress() - if err != nil { - return nil, err - } - return (*hexutil.Big)(state.GetBalance(internal)), state.Error() -} + if addr.IsInQiLedgerScope() { + state, header, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + if state == nil || err != nil { + return nil, err + } -func (s *PublicBlockChainAPI) GetQiBalance(ctx context.Context, address common.Address) (*hexutil.Big, error) { - utxos, err := s.b.UTXOsByAddress(ctx, address) - if utxos == nil || err != nil { - return nil, err - } + currHeader := s.b.CurrentHeader() + if header.Hash() != currHeader.Hash() { + return (*hexutil.Big)(big.NewInt(0)), errors.New("qi balance query is only supported for the current block") + } - if len(utxos) == 0 { - return (*hexutil.Big)(big.NewInt(0)), nil - } + utxos, err := s.b.UTXOsByAddressAtState(ctx, state, addr) + if utxos == nil || err != nil { + return nil, err + } - var balance *big.Int - for _, utxo := range utxos { - denomination := utxo.Denomination - value := types.Denominations[denomination] - if balance == nil { - balance = new(big.Int).Set(value) - } else { - balance.Add(balance, value) + if len(utxos) == 0 { + return (*hexutil.Big)(big.NewInt(0)), nil + } + + balance := big.NewInt(0) + for _, utxo := range utxos { + denomination := utxo.Denomination + value := types.Denominations[denomination] + if balance == nil { + balance = new(big.Int).Set(value) + } else { + balance.Add(balance, value) + } } + return (*hexutil.Big)(balance), nil + } else { + state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + if state == nil || err != nil { + return nil, err + } + internal, err := addr.InternalAndQuaiAddress() + if err != nil { + return nil, err + } + return (*hexutil.Big)(state.GetBalance(internal)), state.Error() + } +} + +func (s *PublicBlockChainAPI) GetOutpointsByAddressAtBlock(ctx context.Context, address common.Address) (map[string]*types.OutpointAndDenomination, error) { + outpoints, err := s.b.AddressOutpoints(ctx, address) + if err != nil { + return nil, err } - return (*hexutil.Big)(balance), nil + return outpoints, nil } // Result structs for GetProof diff --git a/internal/quaiapi/backend.go b/internal/quaiapi/backend.go index 518f6c1e79..f0f199f44b 100644 --- a/internal/quaiapi/backend.go +++ b/internal/quaiapi/backend.go @@ -67,7 +67,8 @@ type Backend interface { BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.WorkObject, error) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.WorkObject, error) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.WorkObject, error) - UTXOsByAddress(ctx context.Context, address common.Address) ([]*types.UtxoEntry, error) + AddressOutpoints(ctx context.Context, address common.Address) (map[string]*types.OutpointAndDenomination, error) + UTXOsByAddressAtState(ctx context.Context, state *state.StateDB, address common.Address) ([]*types.UtxoEntry, error) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.WorkObject, vmConfig *vm.Config) (*vm.EVM, func() error, error) SetCurrentExpansionNumber(expansionNumber uint8) diff --git a/internal/quaiapi/quai_api.go b/internal/quaiapi/quai_api.go index 581a43bb48..e3b7736e10 100644 --- a/internal/quaiapi/quai_api.go +++ b/internal/quaiapi/quai_api.go @@ -135,42 +135,54 @@ func (s *PublicBlockChainQuaiAPI) GetBalance(ctx context.Context, address common if !s.b.ProcessingState() { return nil, errors.New("getBalance call can only be made on chain processing the state") } - state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + + state, header, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err } + addr := common.Bytes20ToAddress(address.Address().Bytes20(), s.b.NodeLocation()) - internal, err := addr.InternalAndQuaiAddress() - if err != nil { - return nil, err - } - return (*hexutil.Big)(state.GetBalance(internal)), state.Error() -} + if addr.IsInQiLedgerScope() { + currHeader := s.b.CurrentHeader() + if header.Hash() != currHeader.Hash() { + return (*hexutil.Big)(big.NewInt(0)), errors.New("qi balance query is only supported for the current block") + } -func (s *PublicBlockChainQuaiAPI) GetQiBalance(ctx context.Context, address common.MixedcaseAddress) (*hexutil.Big, error) { - if !address.ValidChecksum() { - return nil, errors.New("address has invalid checksum") - } - utxos, err := s.b.UTXOsByAddress(ctx, address.Address()) - if utxos == nil || err != nil { - return nil, err - } + utxos, err := s.b.UTXOsByAddressAtState(ctx, state, addr) + if utxos == nil || err != nil { + return nil, err + } - if len(utxos) == 0 { - return (*hexutil.Big)(big.NewInt(0)), nil - } + if len(utxos) == 0 { + return (*hexutil.Big)(big.NewInt(0)), nil + } - var balance *big.Int - for _, utxo := range utxos { - denomination := utxo.Denomination - value := types.Denominations[denomination] - if balance == nil { - balance = new(big.Int).Set(value) - } else { - balance.Add(balance, value) + var balance *big.Int + for _, utxo := range utxos { + denomination := utxo.Denomination + value := types.Denominations[denomination] + if balance == nil { + balance = new(big.Int).Set(value) + } else { + balance.Add(balance, value) + } } + return (*hexutil.Big)(balance), nil + } else { + internal, err := addr.InternalAndQuaiAddress() + if err != nil { + return nil, err + } + return (*hexutil.Big)(state.GetBalance(internal)), state.Error() + } +} + +func (s *PublicBlockChainQuaiAPI) GetOutpointsByAddress(ctx context.Context, address common.Address) (map[string]*types.OutpointAndDenomination, error) { + outpints, err := s.b.AddressOutpoints(ctx, address) + if err != nil { + return nil, err } - return (*hexutil.Big)(balance), nil + return outpints, nil } // GetProof returns the Merkle-proof for a given account and optionally some storage keys. diff --git a/p2p/node/peerManager/peerdb/peer_info.pb.go b/p2p/node/peerManager/peerdb/peer_info.pb.go index cea9a3aa49..d20bc0c18a 100644 --- a/p2p/node/peerManager/peerdb/peer_info.pb.go +++ b/p2p/node/peerManager/peerdb/peer_info.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v4.25.3 +// protoc v4.25.2 // source: p2p/node/peerManager/peerdb/peer_info.proto package peerdb diff --git a/p2p/pb/quai_messages.pb.go b/p2p/pb/quai_messages.pb.go index 7a8dcc15fe..10b83255d3 100644 --- a/p2p/pb/quai_messages.pb.go +++ b/p2p/pb/quai_messages.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v4.25.3 +// protoc v4.25.2 // source: p2p/pb/quai_messages.proto package pb diff --git a/params/config.go b/params/config.go index ac09690064..324b4f3b50 100644 --- a/params/config.go +++ b/params/config.go @@ -112,9 +112,9 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllProgpowProtocolChanges = &ChainConfig{big.NewInt(1337), "progpow", new(Blake3powConfig), new(ProgpowConfig), common.Location{}, common.Hash{}} + AllProgpowProtocolChanges = &ChainConfig{big.NewInt(1337), "progpow", new(Blake3powConfig), new(ProgpowConfig), common.Location{}, common.Hash{}, false} - TestChainConfig = &ChainConfig{big.NewInt(1), "progpow", new(Blake3powConfig), new(ProgpowConfig), common.Location{}, common.Hash{}} + TestChainConfig = &ChainConfig{big.NewInt(1), "progpow", new(Blake3powConfig), new(ProgpowConfig), common.Location{}, common.Hash{}, false} TestRules = TestChainConfig.Rules(new(big.Int)) ) @@ -131,6 +131,7 @@ type ChainConfig struct { Progpow *ProgpowConfig `json:"progpow,omitempty"` Location common.Location DefaultGenesisHash common.Hash + IndexAddressUtxos bool } // SetLocation sets the location on the chain config diff --git a/quai/api_backend.go b/quai/api_backend.go index 081718a340..64ecfe2ad2 100644 --- a/quai/api_backend.go +++ b/quai/api_backend.go @@ -216,8 +216,12 @@ func (b *QuaiAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, block return nil, nil, errors.New("invalid arguments; neither block nor hash specified") } -func (b *QuaiAPIBackend) UTXOsByAddress(ctx context.Context, address common.Address) ([]*types.UtxoEntry, error) { - return b.quai.core.GetUTXOsByAddress(address) +func (b *QuaiAPIBackend) AddressOutpoints(ctx context.Context, address common.Address) (map[string]*types.OutpointAndDenomination, error) { + return b.quai.core.GetOutpointsByAddress(address), nil +} + +func (b *QuaiAPIBackend) UTXOsByAddressAtState(ctx context.Context, state *state.StateDB, address common.Address) ([]*types.UtxoEntry, error) { + return b.quai.core.GetUTXOsByAddressAtState(state, address) } func (b *QuaiAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { diff --git a/quai/backend.go b/quai/backend.go index fe505dd8e8..92a6c8b8da 100644 --- a/quai/backend.go +++ b/quai/backend.go @@ -176,6 +176,7 @@ func New(stack *node.Node, p2p NetworkingAPI, config *quaiconfig.Config, nodeCtx logger.WithField("chainConfig", config.NodeLocation).Info("Chain Config") chainConfig.Location = config.NodeLocation // TODO: See why this is necessary chainConfig.DefaultGenesisHash = config.DefaultGenesisHash + chainConfig.IndexAddressUtxos = config.IndexAddressUtxos logger.WithFields(log.Fields{ "Ctx": nodeCtx, "NodeLocation": config.NodeLocation, @@ -238,26 +239,20 @@ func New(stack *node.Node, p2p NetworkingAPI, config *quaiconfig.Config, nodeCtx } ) - var ( - indexerConfig = &core.IndexerConfig{ - IndexAddressUtxos: config.IndexAddressUtxos, - } - ) - if config.TxPool.Journal != "" { config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal) } logger.WithField("url", quai.config.DomUrl).Info("Dom client") - quai.core, err = core.NewCore(chainDb, &config.Miner, quai.isLocalBlock, &config.TxPool, &config.TxLookupLimit, chainConfig, quai.config.SlicesRunning, currentExpansionNumber, genesisBlock, quai.config.DomUrl, quai.config.SubUrls, quai.engine, cacheConfig, vmConfig, indexerConfig, config.Genesis, logger) + quai.core, err = core.NewCore(chainDb, &config.Miner, quai.isLocalBlock, &config.TxPool, &config.TxLookupLimit, chainConfig, quai.config.SlicesRunning, currentExpansionNumber, genesisBlock, quai.config.DomUrl, quai.config.SubUrls, quai.engine, cacheConfig, vmConfig, config.Genesis, logger) if err != nil { return nil, err } // Only index bloom if processing state if quai.core.ProcessingState() && nodeCtx == common.ZONE_CTX { - quai.bloomIndexer = core.NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms, chainConfig.Location.Context(), logger) - quai.bloomIndexer.Start(quai.Core().Slice().HeaderChain()) + quai.bloomIndexer = core.NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms, chainConfig.Location.Context(), logger, config.IndexAddressUtxos) + quai.bloomIndexer.Start(quai.Core().Slice().HeaderChain(), newChainConfig) } // Set the p2p Networking API diff --git a/quaiclient/ethclient/ethclient.go b/quaiclient/ethclient/ethclient.go index 7ee94fb6cc..6d8d71b89f 100644 --- a/quaiclient/ethclient/ethclient.go +++ b/quaiclient/ethclient/ethclient.go @@ -357,13 +357,6 @@ func (ec *Client) BalanceAt(ctx context.Context, account common.MixedcaseAddress return (*big.Int)(&result), err } -// QiBalance returns the qi balance of the given account. -func (ec *Client) QiBalance(ctx context.Context, account common.MixedcaseAddress) (*big.Int, error) { - var result hexutil.Big - err := ec.c.CallContext(ctx, &result, "eth_getQiBalance", account.Original()) - return (*big.Int)(&result), err -} - // StorageAt returns the value of key in the contract storage of the given account. // The block number can be nil, in which case the value is taken from the latest known block. func (ec *Client) StorageAt(ctx context.Context, account common.MixedcaseAddress, key common.Hash, blockNumber *big.Int) ([]byte, error) { diff --git a/trie/proto_trienode.pb.go b/trie/proto_trienode.pb.go index 67796299d9..c306fde3df 100644 --- a/trie/proto_trienode.pb.go +++ b/trie/proto_trienode.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v4.25.3 +// protoc v4.25.2 // source: trie/proto_trienode.proto package trie