diff --git a/consensus/blake3pow/consensus.go b/consensus/blake3pow/consensus.go index 1364a2c866..d516881f66 100644 --- a/consensus/blake3pow/consensus.go +++ b/consensus/blake3pow/consensus.go @@ -461,6 +461,15 @@ func (blake3pow *Blake3pow) verifyHeader(chain consensus.ChainHeaderReader, head return fmt.Errorf("invalid baseFee: have %s, want %s, parentBaseFee %s, parentGasUsed %d", expectedBaseFee, header.BaseFee(), parent.BaseFee(), parent.GasUsed()) } + // Verify that the stateUsed is <= stateLimit + if header.StateUsed() > header.StateLimit() { + return fmt.Errorf("invalid stateUsed: have %d, stateLimit %d", header.StateUsed(), header.StateLimit()) + } + // Verify the stateLimit is correct based on the parent header. + expectedStateLimit := misc.CalcStateLimit(parent, params.StateCeil) + if header.StateLimit() != expectedStateLimit { + return fmt.Errorf("invalid stateLimit: have %v, want %v, parentStateLimit %v", expectedStateLimit, header.StateLimit(), parent.StateLimit()) + } var expectedPrimeTerminus common.Hash _, parentOrder, _ := blake3pow.CalcOrder(chain, parent) if parentOrder == common.PRIME_CTX { @@ -642,6 +651,7 @@ func (blake3pow *Blake3pow) Finalize(chain consensus.ChainHeaderReader, header * header.Header().SetUTXORoot(state.UTXORoot()) header.Header().SetEVMRoot(state.IntermediateRoot(true)) header.Header().SetEtxSetRoot(state.ETXRoot()) + header.Header().SetQuaiStateSize(state.GetQuaiTrieSize()) } // FinalizeAndAssemble implements consensus.Engine, accumulating the block and diff --git a/consensus/misc/statefee.go b/consensus/misc/statefee.go new file mode 100644 index 0000000000..fd209eb820 --- /dev/null +++ b/consensus/misc/statefee.go @@ -0,0 +1,43 @@ +package misc + +import ( + "github.com/dominant-strategies/go-quai/common" + "github.com/dominant-strategies/go-quai/core/types" + "github.com/dominant-strategies/go-quai/params" +) + +func CalcStateLimit(parent *types.WorkObject, stateCeil uint64) uint64 { + // No Gas for TimeToStartTx days worth of zone blocks, this gives enough time to + // onboard new miners into the slice + if parent.NumberU64(common.ZONE_CTX) < params.TimeToStartTx { + return 0 + } + + // If parent gas is zero and we have passed the 5 day threshold, we can set the first block gas limit to min gas limit + if parent.StateLimit() == 0 { + return params.MinGasLimit + } + + parentStateLimit := parent.StateLimit() + + delta := parentStateLimit/params.StateLimitBoundDivisor - 1 + limit := parentStateLimit + + var desiredLimit uint64 + percentStateUsed := parent.StateUsed() * 100 / parent.StateLimit() + if percentStateUsed > params.PercentStateUsedThreshold { + desiredLimit = stateCeil + if limit+delta > desiredLimit { + return desiredLimit + } else { + return limit + delta + } + } else { + desiredLimit = params.MinGasLimit + if limit-delta/2 < desiredLimit { + return desiredLimit + } else { + return limit - delta/2 + } + } +} diff --git a/consensus/progpow/consensus.go b/consensus/progpow/consensus.go index 27b9fb3e83..b891a3bf4e 100644 --- a/consensus/progpow/consensus.go +++ b/consensus/progpow/consensus.go @@ -459,6 +459,15 @@ func (progpow *Progpow) verifyHeader(chain consensus.ChainHeaderReader, header, return fmt.Errorf("invalid baseFee: have %s, want %s, parentBaseFee %s, parentGasUsed %d", expectedBaseFee, header.BaseFee(), parent.BaseFee(), parent.GasUsed()) } + // Verify that the stateUsed is <= stateLimit + if header.StateUsed() > header.StateLimit() { + return fmt.Errorf("invalid stateUsed: have %d, stateLimit %d", header.StateUsed(), header.StateLimit()) + } + // Verify the StateLimit is correct based on the parent header. + expectedStateLimit := misc.CalcStateLimit(parent, params.StateCeil) + if header.StateLimit() != expectedStateLimit { + return fmt.Errorf("invalid StateLimit: have %d, want %d, parentStateLimit %d", expectedStateLimit, header.StateLimit(), parent.StateLimit()) + } var expectedPrimeTerminus common.Hash var expectedPrimeTerminusNumber *big.Int _, parentOrder, _ := progpow.CalcOrder(chain, parent) @@ -698,6 +707,7 @@ func (progpow *Progpow) Finalize(chain consensus.ChainHeaderReader, header *type header.Header().SetUTXORoot(state.UTXORoot()) header.Header().SetEVMRoot(state.IntermediateRoot(true)) header.Header().SetEtxSetRoot(state.ETXRoot()) + header.Header().SetQuaiStateSize(state.GetQuaiTrieSize()) } // FinalizeAndAssemble implements consensus.Engine, accumulating the block and diff --git a/core/block_validator.go b/core/block_validator.go index e7d6cdb313..072264c709 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -261,13 +261,16 @@ func (v *BlockValidator) SanityCheckWorkObjectShareViewBody(wo *types.WorkObject // transition, such as amount of used gas, the receipt roots and the state root // itself. ValidateState returns a database batch if the validation was a success // otherwise nil and an error is returned. -func (v *BlockValidator) ValidateState(block *types.WorkObject, statedb *state.StateDB, receipts types.Receipts, etxs types.Transactions, usedGas uint64) error { +func (v *BlockValidator) ValidateState(block *types.WorkObject, statedb *state.StateDB, receipts types.Receipts, etxs types.Transactions, usedGas uint64, usedState uint64) error { start := time.Now() header := types.CopyHeader(block.Header()) time1 := common.PrettyDuration(time.Since(start)) if block.GasUsed() != usedGas { return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas) } + if block.StateUsed() != usedState { + return fmt.Errorf("invalid state used (remote: %d local: %d)", block.StateUsed(), usedState) + } time2 := common.PrettyDuration(time.Since(start)) time3 := common.PrettyDuration(time.Since(start)) // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]])) @@ -281,6 +284,9 @@ func (v *BlockValidator) ValidateState(block *types.WorkObject, statedb *state.S if root := statedb.IntermediateRoot(true); header.EVMRoot() != root { return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.EVMRoot(), root) } + if stateSize := statedb.GetQuaiTrieSize(); header.QuaiStateSize().Cmp(stateSize) != 0 { + return fmt.Errorf("invalid quai trie size (remote: %x local: %x)", header.QuaiStateSize(), stateSize) + } if root := statedb.UTXORoot(); header.UTXORoot() != root { return fmt.Errorf("invalid utxo root (remote: %x local: %x)", header.UTXORoot(), root) } diff --git a/core/chain_indexer.go b/core/chain_indexer.go index 02e63c9c5e..8ae24243ce 100644 --- a/core/chain_indexer.go +++ b/core/chain_indexer.go @@ -21,6 +21,7 @@ import ( "encoding/binary" "errors" "fmt" + "math/big" "runtime/debug" "sync" "sync/atomic" @@ -67,7 +68,7 @@ type ChainIndexerChain interface { // 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) + StateAt(root common.Hash, utxoRoot common.Hash, etxRoot common.Hash, quaiStateSize *big.Int) (*state.StateDB, error) } // ChainIndexer does a post-processing job for equally sized sections of the @@ -85,7 +86,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) + StateAt func(common.Hash, common.Hash, common.Hash, *big.Int) (*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 @@ -666,7 +667,7 @@ func (c *ChainIndexer) reorgUtxoIndexer(headers []*types.WorkObject, addressOutp } parent := rawdb.ReadHeader(c.chainDb, *parentNumber, block.ParentHash(nodeCtx)) - state, err := c.StateAt(parent.EVMRoot(), parent.UTXORoot(), parent.EtxSetRoot()) + state, err := c.StateAt(parent.EVMRoot(), parent.UTXORoot(), parent.EtxSetRoot(), parent.QuaiStateSize()) if err != nil { return err } diff --git a/core/core.go b/core/core.go index c044089db5..b8a25cb07b 100644 --- a/core/core.go +++ b/core/core.go @@ -1146,8 +1146,8 @@ func (c *Core) State() (*state.StateDB, error) { } // StateAt returns a new mutable state based on a particular point in time. -func (c *Core) StateAt(root, utxoRoot, etxRoot common.Hash) (*state.StateDB, error) { - return c.sl.hc.bc.processor.StateAt(root, utxoRoot, etxRoot) +func (c *Core) StateAt(root, utxoRoot, etxRoot common.Hash, quaiStateSize *big.Int) (*state.StateDB, error) { + return c.sl.hc.bc.processor.StateAt(root, utxoRoot, etxRoot, quaiStateSize) } // StateCache returns the caching database underpinning the blockchain instance. diff --git a/core/evm.go b/core/evm.go index 14f8cb4f1f..5272804fcd 100644 --- a/core/evm.go +++ b/core/evm.go @@ -112,6 +112,7 @@ func NewEVMBlockContext(header *types.WorkObject, parent *types.WorkObject, chai GasLimit: header.GasLimit(), CheckIfEtxEligible: chain.CheckIfEtxIsEligible, EtxEligibleSlices: etxEligibleSlices, + QuaiStateSize: parent.QuaiStateSize(), // using the state size at the parent for all the gas calculations }, nil } diff --git a/core/genesis.go b/core/genesis.go index 52cfb086e6..7551365784 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -186,7 +186,7 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, nodeLoca // We have the genesis block in database(perhaps in ancient database) // but the corresponding state is missing. header := rawdb.ReadHeader(db, 0, stored) - if _, err := state.New(header.EVMRoot(), header.UTXORoot(), header.EtxSetRoot(), state.NewDatabaseWithConfig(db, nil), state.NewDatabaseWithConfig(db, nil), state.NewDatabaseWithConfig(db, nil), nil, nodeLocation, logger); err != nil { + if _, err := state.New(header.EVMRoot(), header.UTXORoot(), header.EtxSetRoot(), header.QuaiStateSize(), state.NewDatabaseWithConfig(db, nil), state.NewDatabaseWithConfig(db, nil), state.NewDatabaseWithConfig(db, nil), nil, nodeLocation, logger); err != nil { if genesis == nil { genesis = DefaultGenesisBlock() } @@ -288,6 +288,8 @@ func (g *Genesis) ToBlock(startingExpansionNumber uint64) *types.WorkObject { wo.Header().SetEtxEligibleSlices(common.Hash{}) } wo.Header().SetBaseFee(new(big.Int).SetUint64(params.InitialBaseFee)) + wo.Header().SetStateLimit(params.InitialStateLimit) + wo.Header().SetStateUsed(0) wo.Header().SetEtxSetRoot(types.EmptyRootHash) if g.GasLimit == 0 { wo.Header().SetGasLimit(params.GenesisGasLimit) diff --git a/core/headerchain.go b/core/headerchain.go index 12ad00368b..06bd5fdd8f 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -1011,8 +1011,8 @@ func (hc *HeaderChain) SubscribeChainSideEvent(ch chan<- ChainSideEvent) event.S return hc.scope.Track(hc.chainSideFeed.Subscribe(ch)) } -func (hc *HeaderChain) StateAt(root, utxoRoot, etxRoot common.Hash) (*state.StateDB, error) { - return hc.bc.processor.StateAt(root, utxoRoot, etxRoot) +func (hc *HeaderChain) StateAt(root, utxoRoot, etxRoot common.Hash, quaiStateSize *big.Int) (*state.StateDB, error) { + return hc.bc.processor.StateAt(root, utxoRoot, etxRoot, quaiStateSize) } func (hc *HeaderChain) SlicesRunning() []common.Location { diff --git a/core/slice.go b/core/slice.go index 8d2c40ec31..e62dc3fefd 100644 --- a/core/slice.go +++ b/core/slice.go @@ -1405,6 +1405,7 @@ func (sl *Slice) combinePendingHeader(header *types.WorkObject, slPendingHeader combinedPendingHeader.Header().SetEtxRollupHash(header.EtxRollupHash()) combinedPendingHeader.Header().SetUncledS(header.Header().UncledS()) + combinedPendingHeader.Header().SetQuaiStateSize(header.Header().QuaiStateSize()) combinedPendingHeader.Header().SetUncleHash(header.UncleHash()) combinedPendingHeader.Header().SetTxHash(header.Header().TxHash()) combinedPendingHeader.Header().SetEtxHash(header.EtxHash()) @@ -1414,6 +1415,8 @@ func (sl *Slice) combinePendingHeader(header *types.WorkObject, slPendingHeader combinedPendingHeader.Header().SetUTXORoot(header.UTXORoot()) combinedPendingHeader.Header().SetEtxSetRoot(header.EtxSetRoot()) combinedPendingHeader.Header().SetBaseFee(header.BaseFee()) + combinedPendingHeader.Header().SetStateLimit(header.StateLimit()) + combinedPendingHeader.Header().SetStateUsed(header.StateUsed()) combinedPendingHeader.Header().SetGasLimit(header.GasLimit()) combinedPendingHeader.Header().SetGasUsed(header.GasUsed()) combinedPendingHeader.Header().SetExtra(header.Extra()) diff --git a/core/state/dump.go b/core/state/dump.go index 64331b9997..09bb6842c0 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -53,6 +53,7 @@ type DumpAccount struct { Root hexutil.Bytes `json:"root"` CodeHash hexutil.Bytes `json:"codeHash"` Code hexutil.Bytes `json:"code,omitempty"` + Size string `json:"size"` Storage map[common.Hash]string `json:"storage,omitempty"` Address *common.InternalAddress `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key @@ -149,6 +150,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] Nonce: data.Nonce, Root: data.Root[:], CodeHash: data.CodeHash, + Size: data.Size.String(), SecureKey: it.Key, } addrBytes := s.trie.GetKey(it.Key) diff --git a/core/state/journal.go b/core/state/journal.go index 1fd179dd43..5813cde1cc 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -116,6 +116,10 @@ type ( account *common.InternalAddress prevcode, prevhash []byte } + sizeChange struct { + account *common.InternalAddress + prev *big.Int + } // Changes to other state values. refundChange struct { @@ -211,6 +215,14 @@ func (ch storageChange) dirtied() *common.InternalAddress { return ch.account } +func (ch sizeChange) revert(s *StateDB) { + s.getStateObject(*ch.account).SetSize(ch.prev) +} + +func (ch sizeChange) dirtied() *common.InternalAddress { + return ch.account +} + func (ch refundChange) revert(s *StateDB) { s.refund = ch.prev } diff --git a/core/state/snapshot/account.go b/core/state/snapshot/account.go index de6a642bad..dd640a1239 100644 --- a/core/state/snapshot/account.go +++ b/core/state/snapshot/account.go @@ -33,13 +33,15 @@ type Account struct { Balance *big.Int Root []byte CodeHash []byte + Size *big.Int } // SlimAccount converts a state.Account content into a slim snapshot account -func SlimAccount(nonce uint64, balance *big.Int, root common.Hash, codehash []byte) Account { +func SlimAccount(nonce uint64, balance *big.Int, root common.Hash, codehash []byte, size *big.Int) Account { slim := Account{ Nonce: nonce, Balance: balance, + Size: size, } if root != emptyRoot { slim.Root = root[:] @@ -52,8 +54,8 @@ func SlimAccount(nonce uint64, balance *big.Int, root common.Hash, codehash []by // SlimAccountRLP converts a state.Account content into a slim snapshot // version RLP encoded. -func SlimAccountRLP(nonce uint64, balance *big.Int, root common.Hash, codehash []byte) []byte { - data, err := rlp.EncodeToBytes(SlimAccount(nonce, balance, root, codehash)) +func SlimAccountRLP(nonce uint64, balance *big.Int, root common.Hash, codehash []byte, size *big.Int) []byte { + data, err := rlp.EncodeToBytes(SlimAccount(nonce, balance, root, codehash, size)) if err != nil { panic(err) } diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go index 916d08641e..0775e3fab5 100644 --- a/core/state/snapshot/generate.go +++ b/core/state/snapshot/generate.go @@ -566,6 +566,7 @@ func (dl *diskLayer) generate(stats *generatorStats) { Balance *big.Int Root common.Hash CodeHash []byte + Size *big.Int } if err := rlp.DecodeBytes(val, &acc); err != nil { stats.logger.WithField("err", err).Fatal("Invalid account encountered during snapshot creation") @@ -581,7 +582,7 @@ func (dl *diskLayer) generate(stats *generatorStats) { dataLen -= 32 } } else { - data := SlimAccountRLP(acc.Nonce, acc.Balance, acc.Root, acc.CodeHash) + data := SlimAccountRLP(acc.Nonce, acc.Balance, acc.Root, acc.CodeHash, acc.Size) dataLen = len(data) rawdb.WriteAccountSnapshot(batch, accountHash, data) } diff --git a/core/state/state_object.go b/core/state/state_object.go index 1551f3daf1..1a3eb478a4 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -85,6 +85,8 @@ type stateObject struct { dirtyStorage Storage // Storage entries that have been modified in the current transaction execution fakeStorage Storage // Fake storage which constructed by caller for debugging purpose. + uniqueNewKeysStorage Storage //Storage cache for the new entries being considered to be added to the storage trie + // Cache flags. // When an object is marked suicided it will be delete from the trie // during the "update" phase of the state transition. @@ -95,7 +97,7 @@ type stateObject struct { // empty returns whether the account is considered empty. func (s *stateObject) empty() bool { - return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash) + return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash) && s.data.Size.Sign() == 0 } // Account is the Quai consensus representation of accounts. @@ -105,6 +107,7 @@ type Account struct { Balance *big.Int Root common.Hash // merkle root of the storage trie CodeHash []byte + Size *big.Int // size of the storage trie for the contract } // newObject creates a state object. @@ -118,14 +121,18 @@ func newObject(db *StateDB, address common.InternalAddress, data Account) *state if data.Root == (common.Hash{}) { data.Root = emptyRoot } + if data.Size == nil { + data.Size = new(big.Int) + } return &stateObject{ - db: db, - address: address, - addrHash: crypto.Keccak256Hash(address[:]), - data: data, - originStorage: make(Storage), - pendingStorage: make(Storage), - dirtyStorage: make(Storage), + db: db, + address: address, + addrHash: crypto.Keccak256Hash(address[:]), + data: data, + originStorage: make(Storage), + pendingStorage: make(Storage), + dirtyStorage: make(Storage), + uniqueNewKeysStorage: make(Storage), } } @@ -220,6 +227,11 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has }() } + if enc, err = s.getTrie(db).TryGet(key.Bytes()); len(enc) == 0 && err == nil { + // This key didnt exist in the database, we need to add it to the uniqueKeys + s.uniqueNewKeysStorage[key] = common.Hash{} + } + if s.db.snap != nil { // If the object was destructed in *this* block (and potentially resurrected), // the storage has been cleared out, and we should *not* consult the previous @@ -347,10 +359,23 @@ func (s *stateObject) updateTrie(db Database) Trie { var v []byte if (value == common.Hash{}) { s.setError(tr.TryDelete(key[:])) + // While writing the value is nil check in the uniqueNewKeysStorage + // and if the key doesnt exist that means we are deleting key that + // previously existed in the storage trie lower the size of the + // storage trie + _, exists := s.uniqueNewKeysStorage[key] + if !exists { + s.SubSize() + } } else { // Encoding []byte cannot fail, ok to ignore the error. v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:])) s.setError(tr.TryUpdate(key[:], v)) + // While writing the values to the trie, check if the key exists in the uniqueKeys + _, exists := s.uniqueNewKeysStorage[key] + if exists { + s.AddSize() + } } // If state snapshotting is active, cache the data til commit if s.db.snap != nil { @@ -371,6 +396,9 @@ func (s *stateObject) updateTrie(db Database) Trie { if len(s.pendingStorage) > 0 { s.pendingStorage = make(Storage) } + if len(s.uniqueNewKeysStorage) > 0 { + s.uniqueNewKeysStorage = make(Storage) + } return tr } @@ -441,6 +469,26 @@ func (s *stateObject) setBalance(amount *big.Int) { s.data.Balance = amount } +func (s *stateObject) SetSize(size *big.Int) { + s.db.journal.append(sizeChange{ + account: &s.address, + prev: new(big.Int).Set(s.data.Size), + }) + s.setSize(size) +} + +func (s *stateObject) AddSize() { + s.SetSize(new(big.Int).Add(s.Size(), common.Big1)) +} + +func (s *stateObject) SubSize() { + s.SetSize(new(big.Int).Sub(s.Size(), common.Big1)) +} + +func (s *stateObject) setSize(size *big.Int) { + s.data.Size = size +} + func (s *stateObject) deepCopy(db *StateDB) *stateObject { stateObject := newObject(db, s.address, s.data) if s.trie != nil { @@ -450,6 +498,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject { stateObject.dirtyStorage = s.dirtyStorage.Copy() stateObject.originStorage = s.originStorage.Copy() stateObject.pendingStorage = s.pendingStorage.Copy() + stateObject.uniqueNewKeysStorage = s.uniqueNewKeysStorage.Copy() stateObject.suicided = s.suicided stateObject.dirtyCode = s.dirtyCode stateObject.deleted = s.deleted @@ -538,6 +587,10 @@ func (s *stateObject) Nonce() uint64 { return s.data.Nonce } +func (s *stateObject) Size() *big.Int { + return s.data.Size +} + // Never called, but must be present to allow stateObject to be used // as a vm.Account interface that also satisfies the vm.ContractRef // interface. Interfaces are awesome. diff --git a/core/state/statedb.go b/core/state/statedb.go index e0f6837e78..56183e2913 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -105,6 +105,9 @@ type StateDB struct { etxTrie Trie hasher crypto.KeccakState + newAccountsAdded map[common.AddressBytes]bool + size *big.Int + logger *log.Logger nodeLocation common.Location @@ -161,7 +164,7 @@ type StateDB struct { } // New creates a new state from a given trie. -func New(root common.Hash, utxoRoot common.Hash, etxRoot common.Hash, db Database, utxoDb Database, etxDb Database, snaps *snapshot.Tree, nodeLocation common.Location, logger *log.Logger) (*StateDB, error) { +func New(root common.Hash, utxoRoot common.Hash, etxRoot common.Hash, quaiStateSize *big.Int, db Database, utxoDb Database, etxDb Database, snaps *snapshot.Tree, nodeLocation common.Location, logger *log.Logger) (*StateDB, error) { tr, err := db.OpenTrie(root) if err != nil { return nil, err @@ -183,6 +186,8 @@ func New(root common.Hash, utxoRoot common.Hash, etxRoot common.Hash, db Databas etxTrie: etxTr, originalRoot: root, snaps: snaps, + size: quaiStateSize, + newAccountsAdded: make(map[common.AddressBytes]bool), logger: logger, stateObjects: make(map[common.InternalAddress]*stateObject), stateObjectsPending: make(map[common.InternalAddress]struct{}), @@ -295,6 +300,16 @@ func (s *StateDB) SubRefund(gas uint64) { s.refund -= gas } +// AddSize increases the size variable for the statedb by 1 +func (s *StateDB) AddSize() { + s.size = new(big.Int).Add(s.size, common.Big1) +} + +// SubSize decreases the size variable for the statedb by 1 +func (s *StateDB) SubSize() { + s.size = new(big.Int).Sub(s.size, common.Big1) +} + // Exist reports whether the given account address exists in the state. // Notably this also returns true for suicided accounts. func (s *StateDB) Exist(addr common.InternalAddress) bool { @@ -324,6 +339,17 @@ func (s *StateDB) GetNonce(addr common.InternalAddress) uint64 { return 0 } +func (s *StateDB) GetSize(addr common.InternalAddress) *big.Int { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.Size() + } + return common.Big0 +} + +func (s *StateDB) GetQuaiTrieSize() *big.Int { + return s.size +} // TxIndex returns the current transaction index set by Prepare. func (s *StateDB) TxIndex() int { @@ -502,6 +528,7 @@ func (s *StateDB) Suicide(addr common.InternalAddress) bool { }) stateObject.markSuicided() stateObject.data.Balance = new(big.Int) + stateObject.data.Size = new(big.Int) return true } @@ -532,7 +559,7 @@ func (s *StateDB) updateStateObject(obj *stateObject) { // enough to track account updates at commit time, deletions need tracking // at transaction boundary level to ensure we capture state clearing. if s.snap != nil { - s.snapAccounts[obj.addrHash] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) + s.snapAccounts[obj.addrHash] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash, obj.data.Size) } } @@ -801,6 +828,7 @@ func (s *StateDB) getDeletedStateObject(addr common.InternalAddress) *stateObjec Balance: acc.Balance, CodeHash: acc.CodeHash, Root: common.BytesToHash(acc.Root), + Size: acc.Size, } if len(data.CodeHash) == 0 { data.CodeHash = emptyCodeHash @@ -873,6 +901,9 @@ func (s *StateDB) createObject(addr common.InternalAddress) (newobj, prev *state } newobj = newObject(s, addr, Account{}) if prev == nil { + // Add this new account to the collection of accounts that might be + // created during the execution of the state in this block + s.newAccountsAdded[addr.Bytes20()] = true s.journal.append(createObjectChange{account: &addr}) } else { s.journal.append(resetObjectChange{prev: prev, prevdestruct: prevdestruct}) @@ -898,6 +929,7 @@ func (s *StateDB) CreateAccount(addr common.InternalAddress) { newObj, prev := s.createObject(addr) if prev != nil { newObj.setBalance(prev.data.Balance) + newObj.setSize(prev.data.Size) } } @@ -941,6 +973,8 @@ func (s *StateDB) Copy() *StateDB { utxoDb: s.utxoDb, etxTrie: s.etxDb.CopyTrie(s.etxTrie), etxDb: s.etxDb, + size: new(big.Int).Set(s.size), + newAccountsAdded: make(map[common.AddressBytes]bool, len(s.newAccountsAdded)), stateObjects: make(map[common.InternalAddress]*stateObject, len(s.journal.dirties)), stateObjectsPending: make(map[common.InternalAddress]struct{}, len(s.stateObjectsPending)), stateObjectsDirty: make(map[common.InternalAddress]struct{}, len(s.journal.dirties)), @@ -1007,6 +1041,13 @@ func (s *StateDB) Copy() *StateDB { if s.prefetcher != nil { state.prefetcher = s.prefetcher.copy() } + // If len of the new accounts added is non zero, we can copy the map into + // the state variable + if len(s.newAccountsAdded) > 0 { + for acc, val := range s.newAccountsAdded { + state.newAccountsAdded[acc] = val + } + } if s.snaps != nil { // In order for the miner to be able to use and make additions // to the snapshot tree, we need to copy that aswell. @@ -1149,8 +1190,22 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { for addr := range s.stateObjectsPending { if obj := s.stateObjects[addr]; obj.deleted { s.deleteStateObject(obj) + // In the case of deletion because the balance was nil or that the + // address was deleted using opSuicide we need to lower the size of + // the quai state trie if this was not a newAccont created during the + // execution of this block + _, exists := s.newAccountsAdded[addr.Bytes20()] + if !exists { + s.SubSize() + } } else { s.updateStateObject(obj) + // If this address exists in the newAccountsAdded map, we can + // increase the size of the statedb + _, exists := s.newAccountsAdded[addr.Bytes20()] + if exists { + s.AddSize() + } } usedAddrs = append(usedAddrs, common.CopyBytes(addr[:])) // Copy needed for closure } @@ -1160,10 +1215,14 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { if len(s.stateObjectsPending) > 0 { s.stateObjectsPending = make(map[common.InternalAddress]struct{}) } + if len(s.newAccountsAdded) > 0 { + s.newAccountsAdded = make(map[common.AddressBytes]bool) + } // Track the amount of time wasted on hashing the account trie if metrics_config.MetricsEnabled() { defer func(start time.Time) { stateMetrics.WithLabelValues("AccountHashes").Add(float64(time.Since(start))) }(time.Now()) } + return s.trie.Hash() } diff --git a/core/state_processor.go b/core/state_processor.go index 7b99d1e85b..4c02b0f41e 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -216,10 +216,11 @@ func NewStateProcessor(config *params.ChainConfig, hc *HeaderChain, engine conse // Process returns the receipts and logs accumulated during the process and // returns the amount of gas that was used in the process. If any of the // transactions failed to execute due to insufficient gas it will return an error. -func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*types.Transaction, []*types.Log, *state.StateDB, uint64, error) { +func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*types.Transaction, []*types.Log, *state.StateDB, uint64, uint64, error) { var ( receipts types.Receipts usedGas = new(uint64) + usedState = new(uint64) header = types.CopyWorkObject(block) blockHash = block.Hash() nodeLocation = p.hc.NodeLocation() @@ -231,7 +232,7 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty start := time.Now() parent := p.hc.GetBlock(block.ParentHash(nodeCtx), block.NumberU64(nodeCtx)-1) if parent == nil { - return types.Receipts{}, []*types.Transaction{}, []*types.Log{}, nil, 0, errors.New("parent block is nil for the block given to process") + return types.Receipts{}, []*types.Transaction{}, []*types.Log{}, nil, 0, 0, errors.New("parent block is nil for the block given to process") } time1 := common.PrettyDuration(time.Since(start)) @@ -244,15 +245,15 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty parentEtxSetRoot = types.EmptyRootHash } // Initialize a statedb - statedb, err := state.New(parentEvmRoot, parentUtxoRoot, parentEtxSetRoot, p.stateCache, p.utxoCache, p.etxCache, p.snaps, nodeLocation, p.logger) + statedb, err := state.New(parentEvmRoot, parentUtxoRoot, parentEtxSetRoot, parent.QuaiStateSize(), p.stateCache, p.utxoCache, p.etxCache, p.snaps, nodeLocation, p.logger) if err != nil { - return types.Receipts{}, []*types.Transaction{}, []*types.Log{}, nil, 0, err + return types.Receipts{}, []*types.Transaction{}, []*types.Log{}, nil, 0, 0, err } // Apply the previous inbound ETXs to the ETX set state prevInboundEtxs := rawdb.ReadInboundEtxs(p.hc.bc.db, header.ParentHash(nodeCtx)) if len(prevInboundEtxs) > 0 { if err := statedb.PushETXs(prevInboundEtxs); err != nil { - return nil, nil, nil, nil, 0, fmt.Errorf("could not push prev inbound etxs: %w", err) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("could not push prev inbound etxs: %w", err) } } time2 := common.PrettyDuration(time.Since(start)) @@ -282,7 +283,7 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty blockContext, err := NewEVMBlockContext(header, parent, p.hc, nil) if err != nil { - return nil, nil, nil, nil, 0, err + return nil, nil, nil, nil, 0, 0, err } vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, p.vmConfig) time3 := common.PrettyDuration(time.Since(start)) @@ -337,7 +338,7 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty } fees, etxs, err, timing := ProcessQiTx(tx, p.hc, true, checkSig, header, statedb, gp, usedGas, p.hc.pool.signer, p.hc.NodeLocation(), *p.config.ChainID, &etxRLimit, &etxPLimit) if err != nil { - return nil, nil, nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } startEtxAppend := time.Now() for _, etx := range etxs { @@ -350,7 +351,7 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty } else { primeTerminus := p.hc.GetHeaderByHash(header.PrimeTerminus()) if primeTerminus == nil { - return nil, nil, nil, nil, 0, fmt.Errorf("could not find prime terminus header %032x", header.PrimeTerminus()) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("could not find prime terminus header %032x", header.PrimeTerminus()) } totalFees.Add(totalFees, misc.QiToQuai(primeTerminus.WorkObjectHeader(), fees)) } @@ -367,7 +368,7 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty msg, err := tx.AsMessageWithSender(types.MakeSigner(p.config, header.Number(nodeCtx)), header.BaseFee(), senders[tx.Hash()]) if err != nil { - return nil, nil, nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } timeSignDelta := time.Since(startProcess) timeSign += timeSignDelta @@ -385,13 +386,13 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty // ETXs MUST be included in order, so popping the first from the queue must equal the first in the block etx, err := statedb.PopETX() if err != nil { - return nil, nil, nil, nil, 0, fmt.Errorf("could not pop etx from statedb: %w", err) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("could not pop etx from statedb: %w", err) } if etx == nil { - return nil, nil, nil, nil, 0, fmt.Errorf("etx %x is nil", tx.Hash()) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("etx %x is nil", tx.Hash()) } if etx.Hash() != tx.Hash() { - return nil, nil, nil, nil, 0, fmt.Errorf("invalid external transaction: etx %x is not in order or not found in unspent etx set", tx.Hash()) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("invalid external transaction: etx %x is not in order or not found in unspent etx set", tx.Hash()) } // check if the tx is a coinbase tx // coinbase tx @@ -402,7 +403,7 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty if types.IsCoinBaseTx(tx) { iAddr, err := tx.To().InternalAddress() if err != nil { - return nil, nil, nil, nil, 0, errors.New("coinbase address is not in the chain scope") + return nil, nil, nil, nil, 0, 0, errors.New("coinbase address is not in the chain scope") } if tx.To().IsInQiLedgerScope() { value := tx.Value() @@ -421,7 +422,7 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty } // the ETX hash is guaranteed to be unique if err := statedb.CreateUTXO(etx.Hash(), outputIndex, types.NewUtxoEntry(types.NewTxOut(uint8(denomination), tx.To().Bytes(), block.Number(nodeCtx)))); err != nil { - return nil, nil, nil, nil, 0, err + return nil, nil, nil, nil, 0, 0, err } p.logger.Debugf("Creating UTXO for coinbase %032x with denomination %d index %d\n", tx.Hash(), denomination, outputIndex) outputIndex++ @@ -440,7 +441,7 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty lock := new(big.Int).Add(header.Number(nodeCtx), big.NewInt(params.ConversionLockPeriod)) primeTerminus := p.hc.GetHeaderByHash(header.PrimeTerminus()) if primeTerminus == nil { - return nil, nil, nil, nil, 0, fmt.Errorf("could not find prime terminus header %032x", header.PrimeTerminus()) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("could not find prime terminus header %032x", header.PrimeTerminus()) } value := misc.QuaiToQi(primeTerminus.WorkObjectHeader(), etx.Value()) // convert Quai to Qi txGas := etx.Gas() @@ -449,7 +450,7 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty } txGas -= params.TxGas if err := gp.SubGas(params.TxGas); err != nil { - return nil, nil, nil, nil, 0, err + return nil, nil, nil, nil, 0, 0, err } *usedGas += params.TxGas totalEtxGas += params.TxGas @@ -468,13 +469,13 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty } txGas -= params.CallValueTransferGas if err := gp.SubGas(params.CallValueTransferGas); err != nil { - return nil, nil, nil, nil, 0, err + return nil, nil, nil, nil, 0, 0, err } *usedGas += params.CallValueTransferGas // In the future we may want to determine what a fair gas cost is totalEtxGas += params.CallValueTransferGas // In the future we may want to determine what a fair gas cost is // the ETX hash is guaranteed to be unique if err := statedb.CreateUTXO(etx.Hash(), outputIndex, types.NewUtxoEntry(types.NewTxOut(uint8(denomination), etx.To().Bytes(), lock))); err != nil { - return nil, nil, nil, nil, 0, err + return nil, nil, nil, nil, 0, 0, err } p.logger.Infof("Converting Quai to Qi %032x with denomination %d index %d lock %d\n", tx.Hash(), denomination, outputIndex, lock) outputIndex++ @@ -483,11 +484,11 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty } else { // There are no more checks to be made as the ETX is worked so add it to the set if err := statedb.CreateUTXO(etx.OriginatingTxHash(), etx.ETXIndex(), types.NewUtxoEntry(types.NewTxOut(uint8(etx.Value().Uint64()), etx.To().Bytes(), big.NewInt(0)))); err != nil { - return nil, nil, nil, nil, 0, err + return nil, nil, nil, nil, 0, 0, err } // This Qi ETX should cost more gas if err := gp.SubGas(params.CallValueTransferGas); err != nil { - return nil, nil, nil, nil, 0, err + return nil, nil, nil, nil, 0, 0, err } *usedGas += params.CallValueTransferGas // In the future we may want to determine what a fair gas cost is totalEtxGas += params.CallValueTransferGas // In the future we may want to determine what a fair gas cost is @@ -500,7 +501,7 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty msg.SetLock(new(big.Int).Add(header.Number(nodeCtx), big.NewInt(params.ConversionLockPeriod))) primeTerminus := p.hc.GetHeaderByHash(header.PrimeTerminus()) if primeTerminus == nil { - return nil, nil, nil, nil, 0, fmt.Errorf("could not find prime terminus header %032x", header.PrimeTerminus()) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("could not find prime terminus header %032x", header.PrimeTerminus()) } // Convert Qi to Quai msg.SetValue(misc.QiToQuai(primeTerminus.WorkObjectHeader(), etx.Value())) @@ -508,10 +509,10 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty p.logger.Infof("Converting Qi to Quai for ETX %032x with value %d lock %d\n", tx.Hash(), msg.Value().Uint64(), msg.Lock().Uint64()) } prevZeroBal := prepareApplyETX(statedb, msg.Value(), nodeLocation) - receipt, quaiFees, err = applyTransaction(msg, parent, p.config, p.hc, nil, gp, statedb, blockNumber, blockHash, etx, usedGas, vmenv, &etxRLimit, &etxPLimit, p.logger) + receipt, quaiFees, err = applyTransaction(msg, parent, p.config, p.hc, nil, gp, statedb, blockNumber, blockHash, etx, usedGas, usedState, vmenv, &etxRLimit, &etxPLimit, p.logger) statedb.SetBalance(common.ZeroInternal(nodeLocation), prevZeroBal) // Reset the balance to what it previously was. Residual balance will be lost if err != nil { - return nil, nil, nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } addReceipt = true if block.Coinbase().IsInQuaiLedgerScope() { @@ -519,7 +520,7 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty } else { primeTerminus := p.hc.GetHeaderByHash(header.PrimeTerminus()) if primeTerminus == nil { - return nil, nil, nil, nil, 0, fmt.Errorf("could not find prime terminus header %032x", header.PrimeTerminus()) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("could not find prime terminus header %032x", header.PrimeTerminus()) } totalFees.Add(totalFees, misc.QuaiToQi(primeTerminus.WorkObjectHeader(), quaiFees)) } @@ -530,9 +531,9 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty } else if tx.Type() == types.QuaiTxType { startTimeTx := time.Now() - receipt, quaiFees, err = applyTransaction(msg, parent, p.config, p.hc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, &etxRLimit, &etxPLimit, p.logger) + receipt, quaiFees, err = applyTransaction(msg, parent, p.config, p.hc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, usedState, vmenv, &etxRLimit, &etxPLimit, p.logger) if err != nil { - return nil, nil, nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } addReceipt = true timeTxDelta := time.Since(startTimeTx) @@ -542,12 +543,12 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty } else { primeTerminus := p.hc.GetHeaderByHash(header.PrimeTerminus()) if primeTerminus == nil { - return nil, nil, nil, nil, 0, fmt.Errorf("could not find prime terminus header %032x", header.PrimeTerminus()) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("could not find prime terminus header %032x", header.PrimeTerminus()) } totalFees.Add(totalFees, misc.QuaiToQi(primeTerminus.WorkObjectHeader(), quaiFees)) } } else { - return nil, nil, nil, nil, 0, ErrTxTypeNotSupported + return nil, nil, nil, nil, 0, 0, ErrTxTypeNotSupported } for _, etx := range receipt.Etxs { if receipt.Status == types.ReceiptStatusSuccessful { @@ -564,19 +565,19 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty etxAvailable := false oldestIndex, err := statedb.GetOldestIndex() if err != nil { - return nil, nil, nil, nil, 0, fmt.Errorf("could not get oldest index: %w", err) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("could not get oldest index: %w", err) } // Check if there is at least one ETX in the set etx, err := statedb.ReadETX(oldestIndex) if err != nil { - return nil, nil, nil, nil, 0, fmt.Errorf("could not read etx: %w", err) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("could not read etx: %w", err) } if etx != nil { etxAvailable = true } if (etxAvailable && totalEtxGas < minimumEtxGas) || totalEtxGas > maximumEtxGas { p.logger.Errorf("prevInboundEtxs: %d, oldestIndex: %d, etxHash: %s", len(prevInboundEtxs), oldestIndex.Int64(), etx.Hash().Hex()) - return nil, nil, nil, nil, 0, fmt.Errorf("total gas used by ETXs %d is not within the range %d to %d", totalEtxGas, minimumEtxGas, maximumEtxGas) + return nil, nil, nil, nil, 0, 0, fmt.Errorf("total gas used by ETXs %d is not within the range %d to %d", totalEtxGas, minimumEtxGas, maximumEtxGas) } coinbaseReward := misc.CalculateReward(block.WorkObjectHeader()) @@ -629,10 +630,10 @@ func (p *StateProcessor) Process(block *types.WorkObject) (types.Receipts, []*ty "numTxs": len(block.Transactions()), }).Info("Total Tx Processing Time") - return receipts, emittedEtxs, allLogs, statedb, *usedGas, nil + return receipts, emittedEtxs, allLogs, statedb, *usedGas, *usedState, nil } -func applyTransaction(msg types.Message, parent *types.WorkObject, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *types.GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, etxRLimit, etxPLimit *int, logger *log.Logger) (*types.Receipt, *big.Int, error) { +func applyTransaction(msg types.Message, parent *types.WorkObject, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *types.GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, usedState *uint64, evm *vm.EVM, etxRLimit, etxPLimit *int, logger *log.Logger) (*types.Receipt, *big.Int, error) { nodeLocation := config.Location // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) @@ -670,6 +671,7 @@ func applyTransaction(msg types.Message, parent *types.WorkObject, config *param statedb.Finalise(true) *usedGas += result.UsedGas + *usedState += result.UsedState // Create a new receipt for the transaction, storing the intermediate root and gas used // by the tx. @@ -1193,7 +1195,7 @@ func (p *StateProcessor) Apply(batch ethdb.Batch, block *types.WorkObject) ([]*t time1 := common.PrettyDuration(time.Since(start)) time2 := common.PrettyDuration(time.Since(start)) // Process our block - receipts, etxs, logs, statedb, usedGas, err := p.Process(block) + receipts, etxs, logs, statedb, usedGas, usedState, err := p.Process(block) if err != nil { return nil, err } @@ -1204,7 +1206,7 @@ func (p *StateProcessor) Apply(batch ethdb.Batch, block *types.WorkObject) ([]*t }).Warn("Block hash changed after Processing the block") } time3 := common.PrettyDuration(time.Since(start)) - err = p.validator.ValidateState(block, statedb, receipts, etxs, usedGas) + err = p.validator.ValidateState(block, statedb, receipts, etxs, usedGas, usedState) if err != nil { return nil, err } @@ -1264,7 +1266,7 @@ func (p *StateProcessor) Apply(batch ethdb.Batch, block *types.WorkObject) ([]*t // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. -func ApplyTransaction(config *params.ChainConfig, parent *types.WorkObject, parentOrder int, bc ChainContext, author *common.Address, gp *types.GasPool, statedb *state.StateDB, header *types.WorkObject, tx *types.Transaction, usedGas *uint64, cfg vm.Config, etxRLimit, etxPLimit *int, logger *log.Logger) (*types.Receipt, *big.Int, error) { +func ApplyTransaction(config *params.ChainConfig, parent *types.WorkObject, parentOrder int, bc ChainContext, author *common.Address, gp *types.GasPool, statedb *state.StateDB, header *types.WorkObject, tx *types.Transaction, usedGas *uint64, usedState *uint64, cfg vm.Config, etxRLimit, etxPLimit *int, logger *log.Logger) (*types.Receipt, *big.Int, error) { nodeCtx := config.Location.Context() msg, err := tx.AsMessage(types.MakeSigner(config, header.Number(nodeCtx)), header.BaseFee()) if err != nil { @@ -1293,11 +1295,11 @@ func ApplyTransaction(config *params.ChainConfig, parent *types.WorkObject, pare vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) if tx.Type() == types.ExternalTxType { prevZeroBal := prepareApplyETX(statedb, msg.Value(), config.Location) - receipt, quaiFees, err := applyTransaction(msg, parent, config, bc, author, gp, statedb, header.Number(nodeCtx), header.Hash(), tx, usedGas, vmenv, etxRLimit, etxPLimit, logger) + receipt, quaiFees, err := applyTransaction(msg, parent, config, bc, author, gp, statedb, header.Number(nodeCtx), header.Hash(), tx, usedGas, usedState, vmenv, etxRLimit, etxPLimit, logger) statedb.SetBalance(common.ZeroInternal(config.Location), prevZeroBal) // Reset the balance to what it previously was (currently a failed external transaction removes all the sent coins from the supply and any residual balance is gone as well) return receipt, quaiFees, err } - return applyTransaction(msg, parent, config, bc, author, gp, statedb, header.Number(nodeCtx), header.Hash(), tx, usedGas, vmenv, etxRLimit, etxPLimit, logger) + return applyTransaction(msg, parent, config, bc, author, gp, statedb, header.Number(nodeCtx), header.Hash(), tx, usedGas, usedState, vmenv, etxRLimit, etxPLimit, logger) } // GetVMConfig returns the block chain VM config. @@ -1307,12 +1309,12 @@ func (p *StateProcessor) GetVMConfig() *vm.Config { // State returns a new mutable state based on the current HEAD block. func (p *StateProcessor) State() (*state.StateDB, error) { - return p.StateAt(p.hc.CurrentHeader().EVMRoot(), p.hc.CurrentHeader().UTXORoot(), p.hc.CurrentHeader().EtxSetRoot()) + return p.StateAt(p.hc.CurrentHeader().EVMRoot(), p.hc.CurrentHeader().UTXORoot(), p.hc.CurrentHeader().EtxSetRoot(), p.hc.CurrentHeader().QuaiStateSize()) } // StateAt returns a new mutable state based on a particular point in time. -func (p *StateProcessor) StateAt(root, utxoRoot, etxRoot common.Hash) (*state.StateDB, error) { - return state.New(root, utxoRoot, etxRoot, p.stateCache, p.utxoCache, p.etxCache, p.snaps, p.hc.NodeLocation(), p.logger) +func (p *StateProcessor) StateAt(root, utxoRoot, etxRoot common.Hash, quaiStateSize *big.Int) (*state.StateDB, error) { + return state.New(root, utxoRoot, etxRoot, quaiStateSize, p.stateCache, p.utxoCache, p.etxCache, p.snaps, p.hc.NodeLocation(), p.logger) } // StateCache returns the caching database underpinning the blockchain instance. @@ -1419,7 +1421,7 @@ func (p *StateProcessor) StateAtBlock(block *types.WorkObject, reexec uint64, ba ) // Check the live database first if we have the state fully available, use that. if checkLive { - statedb, err = p.StateAt(block.EVMRoot(), block.UTXORoot(), block.EtxSetRoot()) + statedb, err = p.StateAt(block.EVMRoot(), block.UTXORoot(), block.EtxSetRoot(), block.QuaiStateSize()) if err == nil { return statedb, nil } @@ -1448,7 +1450,7 @@ func (p *StateProcessor) StateAtBlock(block *types.WorkObject, reexec uint64, ba // we would rewind past a persisted block (specific corner case is chain // tracing from the genesis). if !checkLive { - statedb, err = state.New(current.EVMRoot(), current.UTXORoot(), current.EtxSetRoot(), database, utxoDatabase, etxDatabase, nil, nodeLocation, p.logger) + statedb, err = state.New(current.EVMRoot(), current.UTXORoot(), current.EtxSetRoot(), current.QuaiStateSize(), database, utxoDatabase, etxDatabase, nil, nodeLocation, p.logger) if err == nil { return statedb, nil } @@ -1465,7 +1467,7 @@ func (p *StateProcessor) StateAtBlock(block *types.WorkObject, reexec uint64, ba } current = types.CopyWorkObject(parent) - statedb, err = state.New(current.EVMRoot(), current.UTXORoot(), current.EtxSetRoot(), database, utxoDatabase, etxDatabase, nil, nodeLocation, p.logger) + statedb, err = state.New(current.EVMRoot(), current.UTXORoot(), current.EtxSetRoot(), current.QuaiStateSize(), database, utxoDatabase, etxDatabase, nil, nodeLocation, p.logger) if err == nil { break } @@ -1502,7 +1504,7 @@ func (p *StateProcessor) StateAtBlock(block *types.WorkObject, reexec uint64, ba if currentBlock == nil { return nil, errors.New("detached block found trying to regenerate state") } - _, _, _, _, _, err := p.Process(currentBlock) + _, _, _, _, _, _, err := p.Process(currentBlock) if err != nil { return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(nodeCtx), err) } @@ -1522,7 +1524,7 @@ func (p *StateProcessor) StateAtBlock(block *types.WorkObject, reexec uint64, ba return nil, fmt.Errorf("stateAtBlock commit failed, number %d root %v: %w", current.NumberU64(nodeCtx), current.EVMRoot().Hex(), err) } - statedb, err = state.New(root, utxoRoot, etxRoot, database, utxoDatabase, etxDatabase, nil, nodeLocation, p.logger) + statedb, err = state.New(root, utxoRoot, etxRoot, currentBlock.QuaiStateSize(), database, utxoDatabase, etxDatabase, nil, nodeLocation, p.logger) if err != nil { return nil, fmt.Errorf("state reset after block %d failed: %v", current.NumberU64(nodeCtx), err) } diff --git a/core/state_transition.go b/core/state_transition.go index 2ad9b62ea9..cd23e05b6b 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -17,6 +17,7 @@ package core import ( + "bytes" "fmt" "math" "math/big" @@ -31,6 +32,7 @@ import ( ) var emptyCodeHash = crypto.Keccak256Hash(nil) +var suicide = []byte("Suicide") /* The State Transitioning Model @@ -90,6 +92,7 @@ type Message interface { // message no matter the execution itself is successful or not. type ExecutionResult struct { UsedGas uint64 // Total used gas but include the refunded gas + UsedState uint64 // Total used state Err error // Any error encountered during the execution(listed in core/vm/errors.go) ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) Etxs []*types.Transaction // External transactions generated from opETX @@ -317,6 +320,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if strings.Contains(err.Error(), ErrEtxGasLimitReached.Error()) { return &ExecutionResult{ UsedGas: params.TxGas, + UsedState: params.EtxStateUsed, Err: err, ReturnData: []byte{}, Etxs: nil, @@ -354,14 +358,42 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber) st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules, st.evm.ChainConfig().Location), msg.AccessList()) + if !st.msg.IsETX() && !contractCreation && len(st.data) == 27 && bytes.Equal(st.data[:7], suicide) && st.to().Equal(st.msg.From()) { + // Caller requests self-destruct + beneficiary, err := common.BytesToAddress(st.data[7:27], st.evm.ChainConfig().Location).InternalAndQuaiAddress() + if err != nil { + return nil, fmt.Errorf("Unable to self-destruct: %v", err) + } + fromInternal, err := msg.From().InternalAndQuaiAddress() + if err != nil { + return nil, fmt.Errorf("Unable to self-destruct: %v", err) + } + balance := st.evm.StateDB.GetBalance(fromInternal) + st.evm.StateDB.Suicide(fromInternal) + refund := new(big.Int).Mul(st.evm.Context.BaseFee, new(big.Int).SetUint64(params.CallNewAccountGas(st.evm.Context.QuaiStateSize))) + balance.Add(balance, refund) + st.evm.StateDB.AddBalance(beneficiary, balance) + + effectiveTip := cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee)) + return &ExecutionResult{ + UsedGas: st.gasUsed(), + UsedState: 0, + Err: nil, + ReturnData: []byte{}, + Etxs: nil, + QuaiFees: new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip), + ContractAddr: nil, + }, nil + } var ( ret []byte vmerr error // vm errors do not effect consensus and are therefore not assigned to err contractAddr *common.Address ) + var stateUsed uint64 if contractCreation { var contract common.Address - ret, contract, st.gas, vmerr = st.evm.Create(sender, st.data, st.gas, st.value) + ret, contract, st.gas, stateUsed, vmerr = st.evm.Create(sender, st.data, st.gas, st.value) contractAddr = &contract } else { // Increment the nonce for the next transaction @@ -374,7 +406,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { return nil, err } st.state.SetNonce(from, st.state.GetNonce(addr)+1) - ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value, st.msg.Lock()) + ret, st.gas, stateUsed, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value, st.msg.Lock()) } // At this point, the execution completed, so the ETX cache can be dumped and reset @@ -400,6 +432,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { return &ExecutionResult{ UsedGas: st.gasUsed(), + UsedState: stateUsed, Err: vmerr, ReturnData: ret, Etxs: etxs, diff --git a/core/tx_pool.go b/core/tx_pool.go index 4ecbfba18f..99038aafcd 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -154,7 +154,7 @@ const ( type blockChain interface { CurrentBlock() *types.WorkObject GetBlock(hash common.Hash, number uint64) *types.WorkObject - StateAt(root, utxoRoot, etxRoot common.Hash) (*state.StateDB, error) + StateAt(root, utxoRoot, etxRoot common.Hash, quaiStateSize *big.Int) (*state.StateDB, error) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription IsGenesisHash(hash common.Hash) bool CheckIfEtxIsEligible(hash common.Hash, location common.Location) bool @@ -1831,12 +1831,14 @@ func (pool *TxPool) reset(oldHead, newHead *types.WorkObject) { evmRoot := newHead.EVMRoot() utxoRoot := newHead.UTXORoot() etxRoot := newHead.EtxSetRoot() + quaiStateSize := newHead.QuaiStateSize() if pool.chain.IsGenesisHash(newHead.Hash()) { evmRoot = types.EmptyRootHash utxoRoot = types.EmptyRootHash etxRoot = types.EmptyRootHash + quaiStateSize = big.NewInt(0) } - statedb, err := pool.chain.StateAt(evmRoot, utxoRoot, etxRoot) + statedb, err := pool.chain.StateAt(evmRoot, utxoRoot, etxRoot, quaiStateSize) if err != nil { pool.logger.WithField("err", err).Error("Failed to reset txpool state") return diff --git a/core/types.go b/core/types.go index 34f962c4ec..ef76f92cd3 100644 --- a/core/types.go +++ b/core/types.go @@ -37,7 +37,7 @@ type Validator interface { // ValidateState validates the given statedb and optionally the receipts and // gas used. - ValidateState(block *types.WorkObject, state *state.StateDB, receipts types.Receipts, etxs types.Transactions, usedGas uint64) error + ValidateState(block *types.WorkObject, state *state.StateDB, receipts types.Receipts, etxs types.Transactions, usedGas uint64, usedState uint64) error } // Prefetcher is an interface for pre-caching transaction signatures and state. diff --git a/core/types/block.go b/core/types/block.go index 7de66c3014..03fc059421 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -91,6 +91,7 @@ type Header struct { uncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` evmRoot common.Hash `json:"evmRoot" gencodec:"required"` utxoRoot common.Hash `json:"utxoRoot" gencodec:"required"` + quaiStateSize *big.Int `json:"quaiStateSize" gencodec:"required"` txHash common.Hash `json:"transactionsRoot" gencodec:"required"` etxHash common.Hash `json:"extTransactionsRoot" gencodec:"required"` etxSetRoot common.Hash `json:"etxSetRoot" gencodec:"required"` @@ -102,7 +103,7 @@ type Header struct { parentUncledSubDeltaS []*big.Int `json:"parentUncledSubDeltaS" gencodec:"required"` efficiencyScore uint16 `json:"efficiencyScore" gencodec:"required"` thresholdCount uint16 `json:"thresholdCount" gencodec:"required"` - expansionNumber uint8 `json:"expansionNumber" gencodec:"required"` + expansionNumber uint8 `json:"expansionNumber" gencodec:"required"` etxEligibleSlices common.Hash `json:"etxEligibleSlices" gencodec:"required"` primeTerminus common.Hash `json:"primeTerminus" gencodec:"required"` interlinkRootHash common.Hash `json:"interlinkRootHash" gencodec:"required"` @@ -112,6 +113,8 @@ type Header struct { gasUsed uint64 `json:"gasUsed" gencodec:"required"` baseFee *big.Int `json:"baseFeePerGas" gencodec:"required"` extra []byte `json:"extraData" gencodec:"required"` + stateLimit uint64 `json:"stateLimit" gencodec:"required"` + stateUsed uint64 `json:"stateUsed" gencodec:"required"` // caches hash atomic.Value @@ -146,12 +149,15 @@ func EmptyHeader() *Header { h.uncledS = big.NewInt(0) h.evmRoot = EmptyRootHash h.utxoRoot = EmptyRootHash + h.quaiStateSize = big.NewInt(0) h.txHash = EmptyRootHash h.etxHash = EmptyRootHash h.etxSetRoot = EmptyRootHash h.etxRollupHash = EmptyRootHash h.uncleHash = EmptyUncleHash h.baseFee = big.NewInt(0) + h.stateLimit = 0 + h.stateUsed = 0 h.extra = []byte{} h.efficiencyScore = 0 h.thresholdCount = 0 @@ -214,6 +220,8 @@ func (h *Header) ProtoEncode() (*ProtoHeader, error) { interlinkRootHash := common.ProtoHash{Value: h.InterlinkRootHash().Bytes()} gasLimit := h.GasLimit() gasUsed := h.GasUsed() + stateLimit := h.StateLimit() + stateUsed := h.StateUsed() efficiencyScore := uint64(h.EfficiencyScore()) thresholdCount := uint64(h.ThresholdCount()) expansionNumber := uint64(h.ExpansionNumber()) @@ -225,6 +233,7 @@ func (h *Header) ProtoEncode() (*ProtoHeader, error) { TxHash: &txHash, EtxHash: &etxhash, EtxSetRoot: &etxSetRoot, + QuaiStateSize: h.QuaiStateSize().Bytes(), EtxRollupHash: &etxRollupHash, ReceiptHash: &receiptHash, PrimeTerminus: &primeTerminus, @@ -237,6 +246,8 @@ func (h *Header) ProtoEncode() (*ProtoHeader, error) { ThresholdCount: &thresholdCount, ExpansionNumber: &expansionNumber, BaseFee: h.BaseFee().Bytes(), + StateLimit: &stateLimit, + StateUsed: &stateUsed, Extra: h.Extra(), } @@ -333,6 +344,9 @@ func (h *Header) ProtoDecode(protoHeader *ProtoHeader, location common.Location) if protoHeader.PrimeTerminus == nil { return errors.New("missing required field 'PrimeTerminus' in Header") } + if protoHeader.QuaiStateSize == nil { + return errors.New("missing required field 'QuaiStateSize' in Header") + } // Initialize the array fields before setting h.parentHash = make([]common.Hash, common.HierarchyDepth-1) @@ -355,6 +369,7 @@ func (h *Header) ProtoDecode(protoHeader *ProtoHeader, location common.Location) h.SetUncleHash(common.BytesToHash(protoHeader.GetUncleHash().GetValue())) h.SetEVMRoot(common.BytesToHash(protoHeader.GetEvmRoot().GetValue())) + h.SetQuaiStateSize(new(big.Int).SetBytes(protoHeader.GetQuaiStateSize())) h.SetUTXORoot(common.BytesToHash(protoHeader.GetUtxoRoot().GetValue())) h.SetTxHash(common.BytesToHash(protoHeader.GetTxHash().GetValue())) h.SetReceiptHash(common.BytesToHash(protoHeader.GetReceiptHash().GetValue())) @@ -367,6 +382,8 @@ func (h *Header) ProtoDecode(protoHeader *ProtoHeader, location common.Location) h.SetGasLimit(protoHeader.GetGasLimit()) h.SetGasUsed(protoHeader.GetGasUsed()) h.SetBaseFee(new(big.Int).SetBytes(protoHeader.GetBaseFee())) + h.SetStateLimit((protoHeader.GetStateLimit())) + h.SetStateUsed((protoHeader.GetStateUsed())) h.SetExtra(protoHeader.GetExtra()) h.SetEfficiencyScore(uint16(protoHeader.GetEfficiencyScore())) h.SetThresholdCount(uint16(protoHeader.GetThresholdCount())) @@ -389,6 +406,7 @@ func (h *Header) RPCMarshalHeader() map[string]interface{} { "hash": h.Hash(), "parentHash": h.ParentHashArray(), "uncledS": (*hexutil.Big)(h.UncledS()), + "quaiStateSize": (*hexutil.Big)(h.QuaiStateSize()), "sha3Uncles": h.UncleHash(), "evmRoot": h.EVMRoot(), "utxoRoot": h.UTXORoot(), @@ -408,6 +426,8 @@ func (h *Header) RPCMarshalHeader() map[string]interface{} { "thresholdCount": hexutil.Uint64(h.ThresholdCount()), "expansionNumber": hexutil.Uint64(h.ExpansionNumber()), "etxEligibleSlices": h.EtxEligibleSlices(), + "stateLimit": hexutil.Uint64(h.StateLimit()), + "stateUsed": hexutil.Uint64(h.StateUsed()), } number := make([]*hexutil.Big, common.HierarchyDepth) @@ -450,6 +470,9 @@ func (h *Header) EVMRoot() common.Hash { func (h *Header) UTXORoot() common.Hash { return h.utxoRoot } +func (h *Header) QuaiStateSize() *big.Int { + return h.quaiStateSize +} func (h *Header) TxHash() common.Hash { return h.txHash } @@ -507,6 +530,12 @@ func (h *Header) EtxEligibleSlices() common.Hash { func (h *Header) BaseFee() *big.Int { return h.baseFee } +func (h *Header) StateLimit() uint64 { + return h.stateLimit +} +func (h *Header) StateUsed() uint64 { + return h.stateUsed +} func (h *Header) Extra() []byte { return common.CopyBytes(h.extra) } func (h *Header) PrimeTerminus() common.Hash { return h.primeTerminus } func (h *Header) InterlinkRootHash() common.Hash { return h.interlinkRootHash } @@ -526,6 +555,11 @@ func (h *Header) SetEVMRoot(val common.Hash) { h.sealHash = atomic.Value{} // clear sealHash cache h.evmRoot = val } +func (h *Header) SetQuaiStateSize(val *big.Int) { + h.hash = atomic.Value{} // clear hash cache + h.sealHash = atomic.Value{} // clear sealHash cache + h.quaiStateSize = val +} func (h *Header) SetUTXORoot(val common.Hash) { h.hash = atomic.Value{} // clear hash cache h.sealHash = atomic.Value{} // clear sealHash cache @@ -635,6 +669,16 @@ func (h *Header) SetBaseFee(val *big.Int) { h.sealHash = atomic.Value{} // clear sealHash cache h.baseFee = new(big.Int).Set(val) } +func (h *Header) SetStateLimit(val uint64) { + h.hash = atomic.Value{} // clear hash cache + h.sealHash = atomic.Value{} // clear sealHash cache + h.stateLimit = val +} +func (h *Header) SetStateUsed(val uint64) { + h.hash = atomic.Value{} // clear hash cache + h.sealHash = atomic.Value{} // clear sealHash cache + h.stateUsed = val +} func (h *Header) SetExtra(val []byte) { h.hash = atomic.Value{} // clear hash cache h.sealHash = atomic.Value{} // clear sealHash cache @@ -668,6 +712,8 @@ func (h *Header) SealEncode() *ProtoHeader { expansionNumber := uint64(h.ExpansionNumber()) gasLimit := h.GasLimit() gasUsed := h.GasUsed() + stateLimit := h.StateLimit() + stateUsed := h.StateUsed() protoSealData := &ProtoHeader{ UncleHash: &uncleHash, @@ -680,7 +726,10 @@ func (h *Header) SealEncode() *ProtoHeader { ReceiptHash: &receiptHash, GasLimit: &gasLimit, GasUsed: &gasUsed, + QuaiStateSize: h.QuaiStateSize().Bytes(), BaseFee: h.BaseFee().Bytes(), + StateLimit: &stateLimit, + StateUsed: &stateUsed, UncledS: h.UncledS().Bytes(), PrimeTerminus: &primeTerminus, InterlinkRootHash: &interlinkRootHash, @@ -849,6 +898,7 @@ func CopyHeader(h *Header) *Header { cpy.SetParentHash(h.ParentHash(i), i) cpy.SetNumber(h.Number(i), i) } + cpy.SetQuaiStateSize(h.QuaiStateSize()) cpy.SetUncledS(h.UncledS()) cpy.SetUncleHash(h.UncleHash()) cpy.SetEVMRoot(h.EVMRoot()) @@ -870,6 +920,8 @@ func CopyHeader(h *Header) *Header { cpy.SetExpansionNumber(h.ExpansionNumber()) cpy.SetEtxEligibleSlices(h.EtxEligibleSlices()) cpy.SetBaseFee(h.BaseFee()) + cpy.SetStateLimit(h.StateLimit()) + cpy.SetStateUsed(h.StateUsed()) return &cpy } diff --git a/core/types/block_test.go b/core/types/block_test.go index 587ef4e431..d1e39774ac 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -36,6 +36,7 @@ func headerTestData() (*Header, common.Hash) { parentHash: []common.Hash{common.HexToHash("0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0"), common.HexToHash("0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0")}, uncleHash: common.HexToHash("0x23456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1"), evmRoot: common.HexToHash("0x456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef3"), + quaiStateSize: big.NewInt(1000), utxoRoot: common.HexToHash("0x56789abcdef0123456789abcdef0123456789abcdef0123456789abcdef4"), txHash: common.HexToHash("0x6789abcdef0123456789abcdef0123456789abcdef0123456789abcdef5"), etxHash: common.HexToHash("0x789abcdef0123456789abcdef0123456789abcdef0123456789abcdef6"), @@ -56,6 +57,8 @@ func headerTestData() (*Header, common.Hash) { gasLimit: 123456789, gasUsed: 987654321, baseFee: big.NewInt(123456789), + stateLimit: 1234567, + stateUsed: 1234567, extra: []byte("SGVsbG8gd29ybGQ="), } @@ -64,7 +67,7 @@ func headerTestData() (*Header, common.Hash) { func TestHeaderHash(t *testing.T) { _, hash := headerTestData() - correctHash := common.HexToHash("0x87fedc319b0a7c3a64136d0eba5fe861e0abd1e873a8fd10bf958a1be875f374") + correctHash := common.HexToHash("0xc8110a0bb8fe2f98081d1202ce0dcf299dc42a14df271a994e1925c78931177b") require.Equal(t, hash, correctHash, "Hash not equal to expected hash") } @@ -291,6 +294,21 @@ func FuzzHeaderBaseFeeHash(f *testing.F) { func(h *Header, bi *big.Int) { h.baseFee = bi }) } +func FuzzHeaderStateLimitHash(f *testing.F) { + fuzzHeaderUint64Hash(f, + func(h *Header) uint64 { return h.stateLimit }, + func(h *Header, bi uint64) { h.stateLimit = bi }) +} +func FuzzHeaderStateUsedHash(f *testing.F) { + fuzzHeaderUint64Hash(f, + func(h *Header) uint64 { return h.stateUsed }, + func(h *Header, bi uint64) { h.stateUsed = bi }) +} +func FuzzHeaderQuaiStateSize(f *testing.F) { + fuzzHeaderBigIntHash(f, + func(h *Header) *big.Int { return h.quaiStateSize }, + func(h *Header, bi *big.Int) { h.quaiStateSize = bi }) +} func FuzzHeaderExtraHash(f *testing.F) { header, _ := headerTestData() f.Add(testByte) diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index 946884fab8..2d3ea5a5e5 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -19,6 +19,7 @@ func (h Header) MarshalJSON() ([]byte, error) { ParentHash []common.Hash `json:"parentHash" gencodec:"required"` UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` EVMRoot common.Hash `json:"evmRoot" gencodec:"required"` + QuaiStateSize *hexutil.Big `json:"quaiStateSize" gencodec:"required"` UTXORoot common.Hash `json:"utxoRoot" gencodec:"required"` TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` @@ -40,6 +41,8 @@ func (h Header) MarshalJSON() ([]byte, error) { GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` BaseFee *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` + StateLimit hexutil.Uint64 `json:"stateLimit" gencodec:"required"` + StateUsed hexutil.Uint64 `json:"stateUsed" gencodec:"required"` Extra hexutil.Bytes `json:"extraData" gencodec:"required"` } // Initialize the enc struct @@ -61,6 +64,7 @@ func (h Header) MarshalJSON() ([]byte, error) { } enc.UncleHash = h.UncleHash() enc.EVMRoot = h.EVMRoot() + enc.QuaiStateSize = (*hexutil.Big)(h.QuaiStateSize()) enc.UTXORoot = h.UTXORoot() enc.TxHash = h.TxHash() enc.EtxHash = h.EtxHash() @@ -77,6 +81,8 @@ func (h Header) MarshalJSON() ([]byte, error) { enc.ExpansionNumber = hexutil.Uint64(h.ExpansionNumber()) enc.EtxEligibleSlices = h.EtxEligibleSlices() enc.BaseFee = (*hexutil.Big)(h.BaseFee()) + enc.StateLimit = hexutil.Uint64(h.StateLimit()) + enc.StateUsed = hexutil.Uint64(h.StateUsed()) enc.Extra = hexutil.Bytes(h.Extra()) raw, err := json.Marshal(&enc) return raw, err @@ -88,6 +94,7 @@ func (h *Header) UnmarshalJSON(input []byte) error { ParentHash []common.Hash `json:"parentHash" gencodec:"required"` UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` EVMRoot *common.Hash `json:"evmRoot" gencodec:"required"` + QuaiStateSize *hexutil.Big `json:"quaiStateSize" gencodec:"required"` UTXORoot *common.Hash `json:"utxoRoot" gencodec:"required"` TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` @@ -109,6 +116,8 @@ func (h *Header) UnmarshalJSON(input []byte) error { GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` BaseFee *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` + StateLimit *hexutil.Uint64 `json:"stateLimit" gencodec:"required"` + StateUsed *hexutil.Uint64 `json:"stateUsed" gencodec:"required"` Extra hexutil.Bytes `json:"extraData" gencodec:"required"` } if err := json.Unmarshal(input, &dec); err != nil { @@ -183,9 +192,18 @@ func (h *Header) UnmarshalJSON(input []byte) error { if dec.BaseFee == nil { return errors.New("missing required field 'baseFee' for Header") } + if dec.StateLimit == nil { + return errors.New("missing required field 'stateLimit' for Header") + } + if dec.StateUsed == nil { + return errors.New("missing required field 'stateUsed' for Header") + } if dec.Extra == nil { return errors.New("missing required field 'extraData' for Header") } + if dec.QuaiStateSize == nil { + return errors.New("missing required field 'quaiStateSize' for Header") + } // Initialize the header h.parentHash = make([]common.Hash, common.HierarchyDepth-1) h.manifestHash = make([]common.Hash, common.HierarchyDepth) @@ -220,6 +238,7 @@ func (h *Header) UnmarshalJSON(input []byte) error { h.SetUncleHash(*dec.UncleHash) h.SetEVMRoot(*dec.EVMRoot) + h.SetQuaiStateSize((*big.Int)(dec.QuaiStateSize)) h.SetUTXORoot(*dec.UTXORoot) h.SetTxHash(*dec.TxHash) h.SetReceiptHash(*dec.ReceiptHash) @@ -236,6 +255,8 @@ func (h *Header) UnmarshalJSON(input []byte) error { h.SetExpansionNumber(uint8(*dec.ExpansionNumber)) h.SetEtxEligibleSlices(*dec.EtxEligibleSlices) h.SetBaseFee((*big.Int)(dec.BaseFee)) + h.SetStateLimit(uint64(*dec.StateLimit)) + h.SetStateUsed(uint64(*dec.StateUsed)) h.SetExtra(dec.Extra) return nil } diff --git a/core/types/proto_block.pb.go b/core/types/proto_block.pb.go index fb4d047fa7..b94a742da9 100644 --- a/core/types/proto_block.pb.go +++ b/core/types/proto_block.pb.go @@ -55,6 +55,9 @@ type ProtoHeader struct { EtxEligibleSlices *common.ProtoHash `protobuf:"bytes,27,opt,name=etx_eligible_slices,json=etxEligibleSlices,proto3,oneof" json:"etx_eligible_slices,omitempty"` PrimeTerminus *common.ProtoHash `protobuf:"bytes,28,opt,name=prime_terminus,json=primeTerminus,proto3,oneof" json:"prime_terminus,omitempty"` InterlinkRootHash *common.ProtoHash `protobuf:"bytes,29,opt,name=interlink_root_hash,json=interlinkRootHash,proto3,oneof" json:"interlink_root_hash,omitempty"` + StateLimit *uint64 `protobuf:"varint,30,opt,name=state_limit,json=stateLimit,proto3,oneof" json:"state_limit,omitempty"` + StateUsed *uint64 `protobuf:"varint,31,opt,name=state_used,json=stateUsed,proto3,oneof" json:"state_used,omitempty"` + QuaiStateSize []byte `protobuf:"bytes,32,opt,name=quai_state_size,json=quaiStateSize,proto3,oneof" json:"quai_state_size,omitempty"` } func (x *ProtoHeader) Reset() { @@ -292,6 +295,27 @@ func (x *ProtoHeader) GetInterlinkRootHash() *common.ProtoHash { return nil } +func (x *ProtoHeader) GetStateLimit() uint64 { + if x != nil && x.StateLimit != nil { + return *x.StateLimit + } + return 0 +} + +func (x *ProtoHeader) GetStateUsed() uint64 { + if x != nil && x.StateUsed != nil { + return *x.StateUsed + } + return 0 +} + +func (x *ProtoHeader) GetQuaiStateSize() []byte { + if x != nil { + return x.QuaiStateSize + } + return nil +} + type ProtoTransaction struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2267,7 +2291,7 @@ var file_core_types_proto_block_proto_rawDesc = []byte{ 0x74, 0x6f, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x1a, 0x19, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0xe1, 0x0d, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x22, 0x8b, 0x0f, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, @@ -2354,400 +2378,410 @@ var file_core_types_proto_block_proto_rawDesc = []byte{ 0x6f, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x48, 0x16, 0x52, 0x11, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x6b, 0x52, 0x6f, 0x6f, - 0x74, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x75, 0x6e, 0x63, - 0x6c, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x76, 0x6d, 0x5f, - 0x72, 0x6f, 0x6f, 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, - 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x12, 0x0a, - 0x10, 0x5f, 0x65, 0x74, 0x78, 0x5f, 0x72, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x5f, 0x68, 0x61, 0x73, - 0x68, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x5f, 0x68, 0x61, - 0x73, 0x68, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, - 0x79, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x75, 0x6e, 0x63, 0x6c, 0x65, 0x64, 0x5f, 0x73, 0x42, 0x0c, - 0x0a, 0x0a, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x0b, 0x0a, 0x09, - 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x62, 0x61, - 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x42, 0x0b, 0x0a, - 0x09, 0x5f, 0x6d, 0x69, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6e, - 0x6f, 0x6e, 0x63, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x5f, 0x72, 0x6f, - 0x6f, 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x65, 0x74, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x72, - 0x6f, 0x6f, 0x74, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x65, 0x66, 0x66, 0x69, 0x63, 0x69, 0x65, 0x6e, - 0x63, 0x79, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x74, 0x68, 0x72, - 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x13, 0x0a, 0x11, - 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, - 0x72, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x65, 0x74, 0x78, 0x5f, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, - 0x6c, 0x65, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x70, 0x72, - 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x42, 0x16, 0x0a, 0x14, - 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x5f, - 0x68, 0x61, 0x73, 0x68, 0x22, 0xe9, 0x08, 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x88, - 0x01, 0x01, 0x12, 0x13, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, - 0x52, 0x02, 0x74, 0x6f, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x48, 0x02, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x88, - 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0c, 0x48, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x88, 0x01, 0x01, 0x12, 0x15, 0x0a, - 0x03, 0x67, 0x61, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x48, 0x04, 0x52, 0x03, 0x67, 0x61, - 0x73, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0c, 0x48, 0x05, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, - 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x48, - 0x06, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, - 0x0b, 0x67, 0x61, 0x73, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x63, 0x61, 0x70, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0c, 0x48, 0x07, 0x52, 0x09, 0x67, 0x61, 0x73, 0x46, 0x65, 0x65, 0x43, 0x61, 0x70, 0x88, - 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0b, 0x67, 0x61, 0x73, 0x5f, 0x74, 0x69, 0x70, 0x5f, 0x63, 0x61, - 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x08, 0x52, 0x09, 0x67, 0x61, 0x73, 0x54, 0x69, - 0x70, 0x43, 0x61, 0x70, 0x88, 0x01, 0x01, 0x12, 0x3c, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x4c, 0x69, 0x73, 0x74, 0x48, 0x09, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x69, - 0x73, 0x74, 0x88, 0x01, 0x01, 0x12, 0x11, 0x0a, 0x01, 0x76, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, - 0x48, 0x0a, 0x52, 0x01, 0x76, 0x88, 0x01, 0x01, 0x12, 0x11, 0x0a, 0x01, 0x72, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x0c, 0x48, 0x0b, 0x52, 0x01, 0x72, 0x88, 0x01, 0x01, 0x12, 0x11, 0x0a, 0x01, 0x73, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x0c, 0x52, 0x01, 0x73, 0x88, 0x01, 0x01, 0x12, 0x46, - 0x0a, 0x13, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x48, 0x0d, - 0x52, 0x11, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x48, - 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x65, 0x74, 0x78, 0x5f, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x0e, 0x52, 0x08, 0x65, 0x74, 0x78, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x88, 0x01, 0x01, 0x12, 0x2d, 0x0a, 0x06, 0x74, 0x78, 0x5f, 0x69, - 0x6e, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x78, 0x49, 0x6e, 0x73, 0x48, 0x0f, 0x52, 0x05, 0x74, - 0x78, 0x49, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x6f, 0x75, - 0x74, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x78, 0x4f, 0x75, 0x74, 0x73, 0x48, 0x10, 0x52, 0x06, - 0x74, 0x78, 0x4f, 0x75, 0x74, 0x73, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x11, 0x52, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, - 0x65, 0x74, 0x78, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0c, - 0x48, 0x12, 0x52, 0x09, 0x65, 0x74, 0x78, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, - 0x12, 0x37, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, - 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x48, 0x13, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x69, 0x78, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x48, 0x14, - 0x52, 0x07, 0x6d, 0x69, 0x78, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, - 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x04, - 0x48, 0x15, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x88, 0x01, 0x01, - 0x12, 0x24, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x18, - 0x17, 0x20, 0x01, 0x28, 0x08, 0x48, 0x16, 0x52, 0x0a, 0x69, 0x73, 0x43, 0x6f, 0x69, 0x6e, 0x62, - 0x61, 0x73, 0x65, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, - 0x05, 0x0a, 0x03, 0x5f, 0x74, 0x6f, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, - 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x67, - 0x61, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0b, 0x0a, 0x09, 0x5f, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x67, 0x61, 0x73, - 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x63, 0x61, 0x70, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x67, 0x61, 0x73, - 0x5f, 0x74, 0x69, 0x70, 0x5f, 0x63, 0x61, 0x70, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x42, 0x04, 0x0a, 0x02, 0x5f, 0x76, 0x42, 0x04, - 0x0a, 0x02, 0x5f, 0x72, 0x42, 0x04, 0x0a, 0x02, 0x5f, 0x73, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x6f, - 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x61, - 0x73, 0x68, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x65, 0x74, 0x78, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, - 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x74, 0x78, 0x5f, 0x69, 0x6e, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, - 0x74, 0x78, 0x5f, 0x6f, 0x75, 0x74, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x65, 0x74, 0x78, 0x5f, 0x73, 0x65, - 0x6e, 0x64, 0x65, 0x72, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, - 0x68, 0x61, 0x73, 0x68, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6d, 0x69, 0x78, 0x5f, 0x68, 0x61, 0x73, - 0x68, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, - 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, - 0x22, 0x50, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3b, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x22, 0x3c, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, - 0x22, 0x3e, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, - 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x52, 0x08, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, - 0x22, 0x4f, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, - 0x69, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x75, - 0x70, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x75, - 0x70, 0x6c, 0x65, 0x52, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x75, 0x70, 0x6c, 0x65, - 0x73, 0x22, 0xa5, 0x05, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x0b, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 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, 0x0a, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x61, 0x73, - 0x68, 0x88, 0x01, 0x01, 0x12, 0x37, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, - 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x48, 0x01, 0x52, 0x0a, - 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, - 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x02, 0x52, - 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x64, 0x69, - 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x03, - 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x88, 0x01, 0x01, 0x12, - 0x2f, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, - 0x61, 0x73, 0x68, 0x48, 0x04, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, - 0x12, 0x19, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x48, - 0x05, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x08, 0x6c, - 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4c, 0x6f, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x06, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x74, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x04, 0x48, 0x17, 0x52, + 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x12, 0x22, + 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x1f, 0x20, 0x01, + 0x28, 0x04, 0x48, 0x18, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x64, 0x88, + 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x0f, 0x71, 0x75, 0x61, 0x69, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x19, 0x52, 0x0d, 0x71, + 0x75, 0x61, 0x69, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x42, + 0x0d, 0x0a, 0x0b, 0x5f, 0x75, 0x6e, 0x63, 0x6c, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x0b, + 0x0a, 0x09, 0x5f, 0x65, 0x76, 0x6d, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x5f, + 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x74, 0x78, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x65, 0x74, 0x78, 0x5f, 0x72, 0x6f, 0x6c, + 0x6c, 0x75, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x63, + 0x65, 0x69, 0x70, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x69, + 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x75, 0x6e, 0x63, + 0x6c, 0x65, 0x64, 0x5f, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, + 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x42, 0x0b, 0x0a, + 0x09, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x65, + 0x78, 0x74, 0x72, 0x61, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6d, 0x69, 0x78, 0x5f, 0x68, 0x61, 0x73, + 0x68, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, + 0x75, 0x74, 0x78, 0x6f, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x65, 0x74, + 0x78, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x65, + 0x66, 0x66, 0x69, 0x63, 0x69, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x42, + 0x12, 0x0a, 0x10, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x65, 0x74, 0x78, + 0x5f, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x73, + 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x69, + 0x6e, 0x75, 0x73, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x69, 0x6e, + 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x71, + 0x75, 0x61, 0x69, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xe9, + 0x08, 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x48, 0x00, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x13, 0x0a, 0x02, + 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x02, 0x74, 0x6f, 0x88, 0x01, + 0x01, 0x12, 0x19, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x48, 0x02, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x03, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x88, 0x01, 0x01, 0x12, 0x15, 0x0a, 0x03, 0x67, 0x61, 0x73, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x04, 0x48, 0x04, 0x52, 0x03, 0x67, 0x61, 0x73, 0x88, 0x01, 0x01, 0x12, 0x17, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x05, 0x52, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x06, 0x52, 0x07, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0b, 0x67, 0x61, 0x73, 0x5f, 0x66, + 0x65, 0x65, 0x5f, 0x63, 0x61, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x07, 0x52, 0x09, + 0x67, 0x61, 0x73, 0x46, 0x65, 0x65, 0x43, 0x61, 0x70, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0b, + 0x67, 0x61, 0x73, 0x5f, 0x74, 0x69, 0x70, 0x5f, 0x63, 0x61, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x0c, 0x48, 0x08, 0x52, 0x09, 0x67, 0x61, 0x73, 0x54, 0x69, 0x70, 0x43, 0x61, 0x70, 0x88, 0x01, + 0x01, 0x12, 0x3c, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x69, 0x73, 0x74, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x09, + 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x88, 0x01, 0x01, 0x12, + 0x11, 0x0a, 0x01, 0x76, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x0a, 0x52, 0x01, 0x76, 0x88, + 0x01, 0x01, 0x12, 0x11, 0x0a, 0x01, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x0b, 0x52, + 0x01, 0x72, 0x88, 0x01, 0x01, 0x12, 0x11, 0x0a, 0x01, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, + 0x48, 0x0c, 0x52, 0x01, 0x73, 0x88, 0x01, 0x01, 0x12, 0x46, 0x0a, 0x13, 0x6f, 0x72, 0x69, 0x67, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, + 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x48, 0x0d, 0x52, 0x11, 0x6f, 0x72, 0x69, 0x67, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, + 0x12, 0x20, 0x0a, 0x09, 0x65, 0x74, 0x78, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0f, 0x20, + 0x01, 0x28, 0x0d, 0x48, 0x0e, 0x52, 0x08, 0x65, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x88, + 0x01, 0x01, 0x12, 0x2d, 0x0a, 0x06, 0x74, 0x78, 0x5f, 0x69, 0x6e, 0x73, 0x18, 0x10, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x54, 0x78, 0x49, 0x6e, 0x73, 0x48, 0x0f, 0x52, 0x05, 0x74, 0x78, 0x49, 0x6e, 0x73, 0x88, 0x01, + 0x01, 0x12, 0x30, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x6f, 0x75, 0x74, 0x73, 0x18, 0x11, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x54, 0x78, 0x4f, 0x75, 0x74, 0x73, 0x48, 0x10, 0x52, 0x06, 0x74, 0x78, 0x4f, 0x75, 0x74, 0x73, + 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x11, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x65, 0x74, 0x78, 0x5f, 0x73, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x12, 0x52, 0x09, 0x65, 0x74, + 0x78, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x37, 0x0a, 0x0b, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, + 0x73, 0x68, 0x48, 0x13, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x69, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x48, 0x07, 0x52, 0x07, 0x6d, 0x69, 0x78, 0x48, - 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x04, 0x48, 0x08, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, - 0x37, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x75, - 0x73, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x09, - 0x52, 0x13, 0x70, 0x72, 0x69, 0x6d, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x4e, - 0x75, 0x6d, 0x62, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x08, 0x63, 0x6f, 0x69, 0x6e, - 0x62, 0x61, 0x73, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x48, 0x0a, 0x52, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x88, 0x01, 0x01, 0x42, - 0x0e, 0x0a, 0x0c, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, - 0x0e, 0x0a, 0x0c, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, - 0x09, 0x0a, 0x07, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, - 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x74, 0x78, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x42, - 0x0b, 0x0a, 0x09, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0b, 0x0a, 0x09, - 0x5f, 0x6d, 0x69, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x42, 0x0b, 0x0a, 0x09, - 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x22, 0x55, 0x0a, 0x16, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x73, 0x12, 0x3b, 0x0a, 0x0a, 0x77, 0x6f, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x48, 0x14, 0x52, 0x07, 0x6d, 0x69, 0x78, 0x48, + 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6e, + 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x04, 0x48, 0x15, 0x52, 0x09, 0x77, 0x6f, + 0x72, 0x6b, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x69, 0x73, + 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x48, + 0x16, 0x52, 0x0a, 0x69, 0x73, 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x88, 0x01, 0x01, + 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x05, 0x0a, 0x03, 0x5f, 0x74, 0x6f, + 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x67, 0x61, 0x73, 0x42, 0x07, 0x0a, 0x05, + 0x5f, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, + 0x69, 0x64, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x63, + 0x61, 0x70, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x74, 0x69, 0x70, 0x5f, 0x63, + 0x61, 0x70, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x69, + 0x73, 0x74, 0x42, 0x04, 0x0a, 0x02, 0x5f, 0x76, 0x42, 0x04, 0x0a, 0x02, 0x5f, 0x72, 0x42, 0x04, + 0x0a, 0x02, 0x5f, 0x73, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x0c, 0x0a, 0x0a, + 0x5f, 0x65, 0x74, 0x78, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x74, + 0x78, 0x5f, 0x69, 0x6e, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x74, 0x78, 0x5f, 0x6f, 0x75, 0x74, + 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, + 0x0d, 0x0a, 0x0b, 0x5f, 0x65, 0x74, 0x78, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x42, 0x0e, + 0x0a, 0x0c, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x0b, + 0x0a, 0x09, 0x5f, 0x6d, 0x69, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, + 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x69, + 0x73, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x22, 0x50, 0x0a, 0x11, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x3b, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x3c, 0x0a, 0x0c, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x07, + 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x22, 0x3e, 0x0a, 0x0d, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x6d, + 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, + 0x52, 0x08, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x22, 0x4f, 0x0a, 0x0f, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3c, 0x0a, + 0x0d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x52, 0x0c, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x22, 0xa5, 0x05, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x09, 0x77, 0x6f, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, - 0x22, 0xe9, 0x03, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x2f, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, - 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x0c, 0x74, 0x72, 0x61, - 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x01, 0x52, 0x0c, 0x74, 0x72, 0x61, - 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x06, - 0x75, 0x6e, 0x63, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x48, 0x02, 0x52, 0x06, 0x75, - 0x6e, 0x63, 0x6c, 0x65, 0x73, 0x88, 0x01, 0x01, 0x12, 0x48, 0x0a, 0x10, 0x65, 0x78, 0x74, 0x5f, - 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x03, 0x52, 0x0f, - 0x65, 0x78, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x88, - 0x01, 0x01, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x48, 0x04, 0x52, 0x08, 0x6d, 0x61, - 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x88, 0x01, 0x01, 0x12, 0x43, 0x0a, 0x10, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x48, 0x05, 0x52, 0x0f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6c, 0x69, 0x6e, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, - 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x75, - 0x6e, 0x63, 0x6c, 0x65, 0x73, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x65, 0x78, 0x74, 0x5f, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6d, - 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0xda, 0x01, 0x0a, - 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x12, 0x3e, 0x0a, 0x09, 0x77, 0x6f, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x48, 0x00, 0x52, 0x08, 0x77, 0x6f, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, - 0x12, 0x38, 0x0a, 0x07, 0x77, 0x6f, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, - 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x48, 0x01, 0x52, - 0x06, 0x77, 0x6f, 0x42, 0x6f, 0x64, 0x79, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x02, 0x74, 0x78, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, - 0x02, 0x52, 0x02, 0x74, 0x78, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x77, 0x6f, 0x5f, - 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x77, 0x6f, 0x5f, 0x62, 0x6f, - 0x64, 0x79, 0x42, 0x05, 0x0a, 0x03, 0x5f, 0x74, 0x78, 0x22, 0x4d, 0x0a, 0x10, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x39, 0x0a, - 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x0b, 0x77, 0x6f, 0x72, - 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0x68, 0x0a, 0x18, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, + 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, + 0x0a, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x37, + 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x48, 0x01, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x02, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, + 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x03, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, + 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x07, 0x74, 0x78, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x48, 0x04, 0x52, + 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x6e, 0x6f, + 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x48, 0x05, 0x52, 0x05, 0x6e, 0x6f, 0x6e, + 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x06, + 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, + 0x08, 0x6d, 0x69, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, + 0x73, 0x68, 0x48, 0x07, 0x52, 0x07, 0x6d, 0x69, 0x78, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, + 0x12, 0x17, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x48, 0x08, + 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x37, 0x0a, 0x15, 0x70, 0x72, 0x69, + 0x6d, 0x65, 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x5f, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x09, 0x52, 0x13, 0x70, 0x72, 0x69, 0x6d, + 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x88, + 0x01, 0x01, 0x12, 0x35, 0x0a, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x0a, 0x52, 0x08, 0x63, 0x6f, + 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x68, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, + 0x6c, 0x74, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, + 0x08, 0x0a, 0x06, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6d, 0x69, 0x78, 0x5f, 0x68, + 0x61, 0x73, 0x68, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x18, 0x0a, 0x16, + 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x5f, + 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x62, + 0x61, 0x73, 0x65, 0x22, 0x55, 0x0a, 0x16, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x3b, 0x0a, + 0x0a, 0x77, 0x6f, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, + 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, + 0x09, 0x77, 0x6f, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x22, 0xe9, 0x03, 0x0a, 0x13, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x6f, + 0x64, 0x79, 0x12, 0x2f, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x48, 0x01, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x06, 0x75, 0x6e, 0x63, 0x6c, 0x65, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x73, 0x48, 0x02, 0x52, 0x06, 0x75, 0x6e, 0x63, 0x6c, 0x65, 0x73, 0x88, + 0x01, 0x01, 0x12, 0x48, 0x0a, 0x10, 0x65, 0x78, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x03, 0x52, 0x0f, 0x65, 0x78, 0x74, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x08, + 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4d, 0x61, 0x6e, 0x69, + 0x66, 0x65, 0x73, 0x74, 0x48, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, + 0x88, 0x01, 0x01, 0x12, 0x43, 0x0a, 0x10, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x6b, + 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, + 0x65, 0x73, 0x48, 0x05, 0x52, 0x0f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x6b, 0x48, + 0x61, 0x73, 0x68, 0x65, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x75, 0x6e, 0x63, 0x6c, 0x65, 0x73, 0x42, + 0x13, 0x0a, 0x11, 0x5f, 0x65, 0x78, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, + 0x74, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0xda, 0x01, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x3e, 0x0a, 0x09, 0x77, 0x6f, + 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x08, 0x77, + 0x6f, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x38, 0x0a, 0x07, 0x77, 0x6f, + 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x48, 0x01, 0x52, 0x06, 0x77, 0x6f, 0x42, 0x6f, 0x64, + 0x79, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x02, 0x74, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x02, 0x52, 0x02, 0x74, 0x78, 0x88, + 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x77, 0x6f, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x77, 0x6f, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x42, 0x05, 0x0a, 0x03, + 0x5f, 0x74, 0x78, 0x22, 0x4d, 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x39, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x5f, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x22, 0x68, 0x0a, 0x18, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x69, 0x65, 0x77, 0x12, 0x3c, + 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x77, + 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, + 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x5f, 0x0a, 0x19, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x56, 0x69, 0x65, 0x77, 0x12, 0x42, 0x0a, 0x0c, 0x77, 0x6f, 0x72, + 0x6b, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, + 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x69, 0x65, 0x77, + 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0x69, 0x0a, + 0x19, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, 0x12, 0x3c, 0x0a, 0x0b, 0x77, 0x6f, + 0x72, 0x6b, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, + 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x77, 0x6f, 0x72, + 0x6b, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x68, 0x0a, 0x18, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x68, 0x61, 0x72, 0x65, 0x56, 0x69, 0x65, 0x77, 0x12, 0x3c, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x22, 0x5f, 0x0a, 0x19, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x56, 0x69, 0x65, 0x77, 0x12, - 0x42, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x56, 0x69, 0x65, 0x77, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x73, 0x22, 0x69, 0x0a, 0x19, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, - 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x69, 0x65, 0x77, - 0x12, 0x3c, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, - 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0e, - 0x0a, 0x0c, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x68, - 0x0a, 0x18, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x53, 0x68, 0x61, 0x72, 0x65, 0x56, 0x69, 0x65, 0x77, 0x12, 0x3c, 0x0a, 0x0b, 0x77, 0x6f, - 0x72, 0x6b, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, - 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x4f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x77, 0x6f, 0x72, - 0x6b, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x60, 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x32, 0x0a, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, - 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x52, 0x0a, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x22, 0xdf, 0x02, 0x0a, 0x16, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x46, 0x6f, 0x72, 0x53, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x14, 0x70, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x11, 0x70, 0x6f, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4f, 0x72, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, - 0x74, 0x69, 0x76, 0x65, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x47, - 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x4c, 0x6f, 0x67, 0x73, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, - 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, - 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, - 0x73, 0x68, 0x12, 0x3f, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x2c, - 0x0a, 0x04, 0x65, 0x74, 0x78, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x04, 0x65, 0x74, 0x78, 0x73, 0x22, 0x54, 0x0a, 0x17, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, 0x46, 0x6f, 0x72, - 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, - 0x70, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x46, 0x6f, - 0x72, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, - 0x74, 0x73, 0x22, 0x83, 0x01, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4c, 0x6f, 0x67, 0x46, - 0x6f, 0x72, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x74, 0x6f, 0x70, - 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x52, 0x06, 0x74, 0x6f, - 0x70, 0x69, 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x44, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x4c, 0x6f, 0x67, 0x73, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, - 0x2d, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4c, 0x6f, 0x67, 0x46, 0x6f, - 0x72, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x22, 0x88, - 0x01, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x02, 0x77, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, - 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x02, 0x77, 0x6f, 0x88, - 0x01, 0x01, 0x12, 0x32, 0x0a, 0x07, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x48, 0x01, 0x52, 0x07, 0x74, 0x65, 0x72, 0x6d, - 0x69, 0x6e, 0x69, 0x88, 0x01, 0x01, 0x42, 0x05, 0x0a, 0x03, 0x5f, 0x77, 0x6f, 0x42, 0x0a, 0x0a, - 0x08, 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x22, 0x76, 0x0a, 0x0c, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x12, 0x32, 0x0a, 0x0b, 0x64, 0x6f, 0x6d, - 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, - 0x68, 0x52, 0x0a, 0x64, 0x6f, 0x6d, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x12, 0x32, 0x0a, - 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x18, 0x02, 0x20, 0x03, + 0x63, 0x74, 0x22, 0x60, 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x32, 0x0a, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x52, 0x0a, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x4b, 0x65, 0x79, 0x22, 0xdf, 0x02, 0x0a, 0x16, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x65, + 0x63, 0x65, 0x69, 0x70, 0x74, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, + 0x2f, 0x0a, 0x14, 0x70, 0x6f, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x72, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x70, + 0x6f, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x67, + 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, + 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, + 0x12, 0x2e, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4c, 0x6f, 0x67, 0x73, + 0x46, 0x6f, 0x72, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, + 0x12, 0x2a, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x48, 0x61, 0x73, 0x68, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x3f, 0x0a, 0x10, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0f, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x19, 0x0a, + 0x08, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x07, 0x67, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x2c, 0x0a, 0x04, 0x65, 0x74, 0x78, 0x73, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x04, 0x65, 0x74, 0x78, 0x73, 0x22, 0x54, 0x0a, 0x17, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, + 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x12, 0x39, 0x0a, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x52, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, 0x22, 0x83, 0x01, 0x0a, + 0x12, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4c, 0x6f, 0x67, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x52, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x22, 0x44, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4c, 0x6f, 0x67, 0x73, 0x46, + 0x6f, 0x72, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x2d, 0x0a, 0x04, 0x6c, 0x6f, 0x67, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4c, 0x6f, 0x67, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x22, 0x88, 0x01, 0x0a, 0x12, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, + 0x2b, 0x0a, 0x02, 0x77, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x02, 0x77, 0x6f, 0x88, 0x01, 0x01, 0x12, 0x32, 0x0a, 0x07, + 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x65, 0x72, 0x6d, 0x69, + 0x6e, 0x69, 0x48, 0x01, 0x52, 0x07, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x88, 0x01, 0x01, + 0x42, 0x05, 0x0a, 0x03, 0x5f, 0x77, 0x6f, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x74, 0x65, 0x72, 0x6d, + 0x69, 0x6e, 0x69, 0x22, 0x76, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x65, 0x72, 0x6d, + 0x69, 0x6e, 0x69, 0x12, 0x32, 0x0a, 0x0b, 0x64, 0x6f, 0x6d, 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x69, + 0x6e, 0x69, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x52, 0x0a, 0x64, 0x6f, 0x6d, + 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x12, 0x32, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x74, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x52, + 0x0a, 0x73, 0x75, 0x62, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x22, 0x40, 0x0a, 0x0b, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x45, 0x74, 0x78, 0x53, 0x65, 0x74, 0x12, 0x22, 0x0a, 0x0a, 0x65, 0x74, + 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, + 0x52, 0x09, 0x65, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x88, 0x01, 0x01, 0x42, 0x0d, + 0x0a, 0x0b, 0x5f, 0x65, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0x8e, 0x01, + 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x74, + 0x78, 0x73, 0x12, 0x33, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x04, 0x65, 0x74, 0x78, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, + 0x01, 0x52, 0x04, 0x65, 0x74, 0x78, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x65, 0x74, 0x78, 0x73, 0x22, 0xa8, + 0x01, 0x0a, 0x16, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x45, + 0x74, 0x78, 0x73, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x12, 0x33, 0x0a, 0x06, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x3e, + 0x0a, 0x0b, 0x65, 0x74, 0x78, 0x73, 0x5f, 0x72, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x01, 0x52, + 0x0a, 0x65, 0x74, 0x78, 0x73, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x88, 0x01, 0x01, 0x42, 0x09, + 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x65, 0x74, + 0x78, 0x73, 0x5f, 0x72, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x22, 0x35, 0x0a, 0x0a, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x54, 0x78, 0x49, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x06, 0x74, 0x78, 0x5f, 0x69, 0x6e, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x78, 0x49, 0x6e, 0x52, 0x05, 0x74, 0x78, 0x49, 0x6e, 0x73, + 0x22, 0x39, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x78, 0x4f, 0x75, 0x74, 0x73, 0x12, + 0x2a, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x6f, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x11, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x78, + 0x4f, 0x75, 0x74, 0x52, 0x06, 0x74, 0x78, 0x4f, 0x75, 0x74, 0x73, 0x22, 0x95, 0x01, 0x0a, 0x09, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x78, 0x49, 0x6e, 0x12, 0x47, 0x0a, 0x12, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x10, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x88, + 0x01, 0x01, 0x12, 0x1c, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, + 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x6f, 0x75, + 0x74, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x70, 0x75, 0x62, 0x5f, + 0x6b, 0x65, 0x79, 0x22, 0x69, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4f, 0x75, 0x74, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 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, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x69, 0x22, 0x40, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x45, 0x74, 0x78, 0x53, 0x65, 0x74, - 0x12, 0x22, 0x0a, 0x0a, 0x65, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x65, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, - 0x73, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x65, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, - 0x68, 0x65, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x65, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x45, 0x74, 0x78, 0x73, 0x12, 0x33, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, - 0x04, 0x65, 0x74, 0x78, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x01, 0x52, 0x04, 0x65, 0x74, 0x78, 0x73, 0x88, 0x01, 0x01, - 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x5f, - 0x65, 0x74, 0x78, 0x73, 0x22, 0xa8, 0x01, 0x0a, 0x16, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x74, 0x78, 0x73, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x12, - 0x33, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x57, 0x6f, 0x72, - 0x6b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x88, 0x01, 0x01, 0x12, 0x3e, 0x0a, 0x0b, 0x65, 0x74, 0x78, 0x73, 0x5f, 0x72, 0x6f, 0x6c, - 0x6c, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x48, 0x01, 0x52, 0x0a, 0x65, 0x74, 0x78, 0x73, 0x52, 0x6f, 0x6c, 0x6c, 0x75, - 0x70, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, - 0x0e, 0x0a, 0x0c, 0x5f, 0x65, 0x74, 0x78, 0x73, 0x5f, 0x72, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x22, - 0x35, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x78, 0x49, 0x6e, 0x73, 0x12, 0x27, 0x0a, - 0x06, 0x74, 0x78, 0x5f, 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x78, 0x49, 0x6e, 0x52, - 0x05, 0x74, 0x78, 0x49, 0x6e, 0x73, 0x22, 0x39, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, - 0x78, 0x4f, 0x75, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x6f, 0x75, 0x74, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x78, 0x4f, 0x75, 0x74, 0x52, 0x06, 0x74, 0x78, 0x4f, 0x75, 0x74, - 0x73, 0x22, 0x95, 0x01, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x78, 0x49, 0x6e, 0x12, - 0x47, 0x0a, 0x12, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x6f, 0x75, 0x74, 0x5f, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x48, 0x00, 0x52, 0x10, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x4f, 0x75, 0x74, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x1c, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x06, 0x70, 0x75, 0x62, - 0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, - 0x6f, 0x75, 0x73, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x0a, 0x0a, - 0x08, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x22, 0x69, 0x0a, 0x0d, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 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, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x22, 0x93, 0x01, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x78, - 0x4f, 0x75, 0x74, 0x12, 0x27, 0x0a, 0x0c, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x0c, 0x64, 0x65, 0x6e, - 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, - 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 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, 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, + 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, 0x42, 0x07, 0x0a, 0x05, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x93, + 0x01, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x78, 0x4f, 0x75, 0x74, 0x12, 0x27, 0x0a, + 0x0c, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x0c, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 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, 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 ( diff --git a/core/types/proto_block.proto b/core/types/proto_block.proto index 51bf64537f..b26ff4c3cc 100644 --- a/core/types/proto_block.proto +++ b/core/types/proto_block.proto @@ -35,6 +35,9 @@ message ProtoHeader { optional common.ProtoHash etx_eligible_slices = 27; optional common.ProtoHash prime_terminus = 28; optional common.ProtoHash interlink_root_hash = 29; + optional uint64 state_limit = 30; + optional uint64 state_used = 31; + optional bytes quai_state_size = 32; } message ProtoTransaction { diff --git a/core/types/qi_tx.go b/core/types/qi_tx.go index 11ac6dd1b9..21ac00315f 100644 --- a/core/types/qi_tx.go +++ b/core/types/qi_tx.go @@ -144,7 +144,7 @@ func CalculateQiTxGas(transaction *Transaction, location common.Location) uint64 txGas += params.ETXGas + params.TxGas } else if location.Equal(*toAddr.Location()) && toAddr.IsInQuaiLedgerScope() { // This output creates a conversion - txGas += params.ETXGas + params.TxGas + params.ColdSloadCost + params.ColdSloadCost + params.SstoreSetGas + params.SstoreSetGas + txGas += params.ETXGas + params.TxGas + params.ColdSloadCost(big.NewInt(0), big.NewInt(0)) + params.ColdSloadCost(big.NewInt(0), big.NewInt(0)) + params.SstoreSetGas(big.NewInt(0), big.NewInt(0)) + params.SstoreSetGas(big.NewInt(0), big.NewInt(0)) } } return txGas diff --git a/core/types/wo.go b/core/types/wo.go index 584c89f870..cd4fa86aa7 100644 --- a/core/types/wo.go +++ b/core/types/wo.go @@ -215,6 +215,10 @@ func (wo *WorkObject) NonceU64() uint64 { return wo.WorkObjectHeader().Nonce().Uint64() } +func (wo *WorkObject) QuaiStateSize() *big.Int { + return wo.Header().QuaiStateSize() +} + func (wo *WorkObject) UncledS() *big.Int { return wo.Header().UncledS() } @@ -239,6 +243,14 @@ func (wo *WorkObject) BaseFee() *big.Int { return wo.Header().BaseFee() } +func (wo *WorkObject) StateLimit() uint64 { + return wo.Header().StateLimit() +} + +func (wo *WorkObject) StateUsed() uint64 { + return wo.Header().StateUsed() +} + func (wo *WorkObject) GasUsed() uint64 { return wo.Header().GasUsed() } diff --git a/core/vm/contract.go b/core/vm/contract.go index c97bb0ca2e..20da322ddb 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -58,8 +58,9 @@ type Contract struct { CodeAddr *common.Address Input []byte - Gas uint64 - value *big.Int + StateGas uint64 + Gas uint64 + value *big.Int } // NewContract returns a new contract environment for the execution of EVM. @@ -79,6 +80,9 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin // ensures a value is set c.value = value + // Create a state gas + c.StateGas = 0 + return c } @@ -172,6 +176,11 @@ func (c *Contract) UseGas(gas uint64) (ok bool) { return true } +// UseState keeps a counter on the gas used for state operation +func (c *Contract) UseState(gas uint64) { + c.StateGas += gas +} + // Address returns the contracts address func (c *Contract) Address() common.Address { return c.self.Address() diff --git a/core/vm/contracts.go b/core/vm/contracts.go index a685bc8192..460e281d5f 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -495,31 +495,36 @@ func RequiredGas(input []byte) uint64 { } // RedeemQuai executes the lockup contract to redeem the locked balance(s) for the sender -func RedeemQuai(statedb StateDB, sender common.Address, gas *types.GasPool, blockHeight *big.Int, lockupContractAddress common.Address) (uint64, error) { +func RedeemQuai(blockContext BlockContext, statedb StateDB, sender common.Address, gas *types.GasPool, blockHeight *big.Int, lockupContractAddress common.Address) (uint64, uint64, error) { internalContractAddress, err := lockupContractAddress.InternalAndQuaiAddress() if err != nil { - return 0, err + return 0, 0, err } + stateSize := blockContext.QuaiStateSize + contractSize := statedb.GetSize(internalContractAddress) // The current lock is the next available lock to redeem (in order of creation) currentLockHash := statedb.GetState(internalContractAddress, sender.Hash()) - gasUsed := params.ColdSloadCost - if gas.SubGas(params.ColdSloadCost) != nil { + coldSloadCost := params.ColdSloadCost(stateSize, contractSize) + sstoreResetGas := params.SstoreResetGas(stateSize, contractSize) + gasUsed := coldSloadCost + stateGas := coldSloadCost + if gas.SubGas(coldSloadCost) != nil { // This contract does not revert. If the caller runs out of gas, we just stop - return gasUsed, ErrOutOfGas + return gasUsed, stateGas, ErrOutOfGas } if (currentLockHash == common.Hash{}) { - return gasUsed, errors.New("lockup not found") + return gasUsed, stateGas, errors.New("lockup not found") } currentLockNumber := new(big.Int).SetBytes(currentLockHash[:]) if !currentLockNumber.IsUint64() { - return gasUsed, errors.New("account has locked too many times, overflows uint64") + return gasUsed, stateGas, errors.New("account has locked too many times, overflows uint64") } for i := int64(0); i < math.MaxInt64; i++ { // TODO: We should decide on a reasonable limit // Ensure we have enough gas to complete this step entirely - requiredGas := params.ColdSloadCost + params.SstoreResetGas + params.ColdSloadCost + params.SstoreResetGas + params.SstoreResetGas + params.CallValueTransferGas + requiredGas := coldSloadCost + sstoreResetGas + coldSloadCost + sstoreResetGas + sstoreResetGas + params.CallValueTransferGas if gas.Gas() < requiredGas { - return gasUsed, fmt.Errorf("insufficient gas to complete lockup redemption, required %d, have %d", requiredGas, gas.Gas()) + return gasUsed, stateGas, fmt.Errorf("insufficient gas to complete lockup redemption, required %d, have %d", requiredGas, gas.Gas()) } // The key is zero padded + sender's address + current lock pointer + 1 key := sender.Bytes() @@ -527,133 +532,148 @@ func RedeemQuai(statedb StateDB, sender common.Address, gas *types.GasPool, bloc key = binary.BigEndian.AppendUint64(key, currentLockNumber.Uint64()) key = append(key, byte(1)) // Set the 29th byte of the key to 1 to get lock height if len(key) > common.HashLength { - return gasUsed, errors.New("lockup key is too long, math is broken") + return gasUsed, stateGas, errors.New("lockup key is too long, math is broken") } lockHash := statedb.GetState(internalContractAddress, common.BytesToHash(key)) - gasUsed += params.ColdSloadCost - if gas.SubGas(params.ColdSloadCost) != nil { + gasUsed += coldSloadCost + stateGas += coldSloadCost + if gas.SubGas(coldSloadCost) != nil { // This contract does not revert. If the caller runs out of gas, we just stop - return gasUsed, ErrOutOfGas + return gasUsed, stateGas, ErrOutOfGas } if (lockHash == common.Hash{}) { // Lock doesn't exist, so we're done - return gasUsed, nil + return gasUsed, stateGas, nil } lock := new(big.Int).SetBytes(lockHash[:]) if lock.Cmp(blockHeight) > 0 { // lock not ready yet. Lockups are stored in FIFO order, so we don't have to go through the rest - return gasUsed, fmt.Errorf("lockup not ready yet, lock height: %d, current block height: %d", lock, blockHeight) + return gasUsed, stateGas, fmt.Errorf("lockup not ready yet, lock height: %d, current block height: %d", lock, blockHeight) } // Set the lock to zero statedb.SetState(internalContractAddress, common.BytesToHash(key), common.Hash{}) - gasUsed += params.SstoreResetGas - if gas.SubGas(params.SstoreResetGas) != nil { + gasUsed += sstoreResetGas + stateGas += sstoreResetGas + if gas.SubGas(sstoreResetGas) != nil { // This contract does not revert. If the caller runs out of gas, we just stop - return gasUsed, ErrOutOfGas + return gasUsed, stateGas, ErrOutOfGas } key[28] = 0 // Set the 29th byte of the key to 0 for balance balanceHash := statedb.GetState(internalContractAddress, common.BytesToHash(key)) - gasUsed += params.ColdSloadCost - if gas.SubGas(params.ColdSloadCost) != nil { - return gasUsed, ErrOutOfGas + gasUsed += coldSloadCost + stateGas += coldSloadCost + if gas.SubGas(coldSloadCost) != nil { + return gasUsed, stateGas, ErrOutOfGas } if (balanceHash == common.Hash{}) { // If locked balance after covnert is zero, either it doesn't exist or something is broken - return gasUsed, errors.New("balance not found") + return gasUsed, stateGas, errors.New("balance not found") } // Set the locked balance to zero statedb.SetState(internalContractAddress, common.BytesToHash(key), common.Hash{}) - gasUsed += params.SstoreResetGas - if gas.SubGas(params.SstoreResetGas) != nil { - return gasUsed, ErrOutOfGas + gasUsed += sstoreResetGas + stateGas += sstoreResetGas + if gas.SubGas(sstoreResetGas) != nil { + return gasUsed, stateGas, ErrOutOfGas } // Increment the current lock counter currentLockNumber.Add(currentLockNumber, big1) currentLockHash = common.BytesToHash(binary.BigEndian.AppendUint64([]byte{}, currentLockNumber.Uint64())) statedb.SetState(internalContractAddress, sender.Hash(), currentLockHash) - gasUsed += params.SstoreResetGas - if gas.SubGas(params.SstoreResetGas) != nil { - return gasUsed, ErrOutOfGas + gasUsed += sstoreResetGas + stateGas += sstoreResetGas + if gas.SubGas(sstoreResetGas) != nil { + return gasUsed, stateGas, ErrOutOfGas } // Redeem the balance for the sender balance := new(big.Int).SetBytes(balanceHash[:]) internal, err := sender.InternalAndQuaiAddress() if err != nil { - return gasUsed, err + return gasUsed, stateGas, err } statedb.AddBalance(internal, balance) gasUsed += params.CallValueTransferGas if gas.SubGas(params.CallValueTransferGas) != nil { - return gasUsed, ErrOutOfGas + return gasUsed, stateGas, ErrOutOfGas } } - return gasUsed, errors.New("account has locked too many times, overflows int64") + return gasUsed, stateGas, errors.New("account has locked too many times, overflows int64") } // AddNewLock adds a new locked balance to the lockup contract -func AddNewLock(statedb StateDB, toAddr common.Address, gas *types.GasPool, lock *big.Int, balance *big.Int, lockupContractAddress common.Address) (uint64, error) { +func AddNewLock(blockContext BlockContext, statedb StateDB, toAddr common.Address, gas *types.GasPool, lock *big.Int, balance *big.Int, lockupContractAddress common.Address) (uint64, uint64, error) { internalContractAddress, err := lockupContractAddress.InternalAndQuaiAddress() if err != nil { - return 0, err + return 0, 0, err } if len(lock.Bytes()) > common.HashLength || len(balance.Bytes()) > common.HashLength { - return 0, errors.New("lock or balance is too large") + return 0, 0, errors.New("lock or balance is too large") } + stateSize := blockContext.QuaiStateSize + contractSize := statedb.GetSize(internalContractAddress) currentLockHash := statedb.GetState(internalContractAddress, common.BytesToHash(toAddr.Bytes())) - gasUsed := params.ColdSloadCost - if gas.SubGas(params.ColdSloadCost) != nil { + coldSloadCost := params.ColdSloadCost(stateSize, contractSize) + sstoreSetGas := params.SstoreSetGas(stateSize, contractSize) + gasUsed := coldSloadCost + stateGas := coldSloadCost + if gas.SubGas(coldSloadCost) != nil { // This contract does not revert. If the caller runs out of gas, we just stop - return gasUsed, ErrOutOfGas + return gasUsed, stateGas, ErrOutOfGas } if (currentLockHash == common.Hash{}) { // No lock found, create a new one statedb.SetState(internalContractAddress, common.BytesToHash(toAddr.Bytes()), common.BytesToHash([]byte{1})) + gasUsed += sstoreSetGas + stateGas += sstoreSetGas currentLockHash = common.BytesToHash([]byte{1}) } currentLockNumber := new(big.Int).SetBytes(currentLockHash[:]) if !currentLockNumber.IsUint64() { - return gasUsed, errors.New("account has locked too many times, overflows uint64") + return gasUsed, stateGas, errors.New("account has locked too many times, overflows uint64") } for i := int64(0); i < math.MaxInt64; i++ { // TODO: We should decide on a reasonable limit // Ensure we have enough gas to complete this step entirely - requiredGas := params.ColdSloadCost + params.SstoreSetGas + params.SstoreSetGas + requiredGas := coldSloadCost + sstoreSetGas + sstoreSetGas if gas.Gas() < requiredGas { - return gasUsed, fmt.Errorf("insufficient gas to add new lock, required %d, got %d", requiredGas, gas.Gas()) + return gasUsed, stateGas, fmt.Errorf("insufficient gas to add new lock, required %d, got %d", requiredGas, gas.Gas()) } key := toAddr.Bytes() // Append current lock to the key key = binary.BigEndian.AppendUint64(key, currentLockNumber.Uint64()) key = append(key, byte(1)) // Set the 29th byte of the key to 1 for lockup if len(key) > common.HashLength { - return gasUsed, errors.New("lockup key is too long, math is broken") + return gasUsed, stateGas, errors.New("lockup key is too long, math is broken") } lockHash := statedb.GetState(internalContractAddress, common.BytesToHash(key)) - gasUsed += params.ColdSloadCost - if gas.SubGas(params.ColdSloadCost) != nil { + gasUsed += coldSloadCost + stateGas += coldSloadCost + if gas.SubGas(coldSloadCost) != nil { // This contract does not revert. If the caller runs out of gas, we just stop - return gasUsed, ErrOutOfGas + return gasUsed, stateGas, ErrOutOfGas } if (lockHash == common.Hash{}) { // Lock doesn't exist, so add the new one here statedb.SetState(internalContractAddress, common.BytesToHash(key), common.BytesToHash(lock.Bytes())) - gasUsed += params.SstoreSetGas - if gas.SubGas(params.SstoreSetGas) != nil { - return gasUsed, ErrOutOfGas + gasUsed += sstoreSetGas + stateGas += sstoreSetGas + if gas.SubGas(sstoreSetGas) != nil { + return gasUsed, stateGas, ErrOutOfGas } key[28] = 0 // Set the 29th byte of the key to 0 for balance statedb.SetState(internalContractAddress, common.BytesToHash(key), common.BytesToHash(balance.Bytes())) - gasUsed += params.SstoreSetGas - if gas.SubGas(params.SstoreSetGas) != nil { - return gasUsed, ErrOutOfGas + gasUsed += sstoreSetGas + stateGas += sstoreSetGas + if gas.SubGas(sstoreSetGas) != nil { + return gasUsed, stateGas, ErrOutOfGas } // Addition of new lock successful - return gasUsed, nil + return gasUsed, stateGas, nil } // Lock exists, increment the current lock counter (but don't store it) currentLockNumber.Add(currentLockNumber, big1) } - return gasUsed, errors.New("account has locked too many times, overflows int64") + return gasUsed, stateGas, errors.New("account has locked too many times, overflows int64") } diff --git a/core/vm/evm.go b/core/vm/evm.go index 9f1c7a27bc..1cb3267b89 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -79,12 +79,13 @@ type BlockContext struct { CheckIfEtxEligible CheckIfEtxEligibleFunc // Block information - Coinbase common.Address // Provides information for COINBASE - GasLimit uint64 // Provides information for GASLIMIT - BlockNumber *big.Int // Provides information for NUMBER - Time *big.Int // Provides information for TIME - Difficulty *big.Int // Provides information for DIFFICULTY - BaseFee *big.Int // Provides information for BASEFEE + Coinbase common.Address // Provides information for COINBASE + GasLimit uint64 // Provides information for GASLIMIT + BlockNumber *big.Int // Provides information for NUMBER + Time *big.Int // Provides information for TIME + Difficulty *big.Int // Provides information for DIFFICULTY + BaseFee *big.Int // Provides information for BASEFEE + QuaiStateSize *big.Int // Provides information for QUAISTATESIZE // Prime Terminus information for the given block EtxEligibleSlices common.Hash @@ -186,44 +187,46 @@ func (evm *EVM) Interpreter() *EVMInterpreter { // parameters. It also handles any necessary value transfer required and takes // the necessary steps to create accounts and reverses the state in case of an // execution error or failed value transfer. -func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int, lock *big.Int) (ret []byte, leftOverGas uint64, err error) { +func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int, lock *big.Int) (ret []byte, leftOverGas uint64, stateGas uint64, err error) { if evm.Config.NoRecursion && evm.depth > 0 { - return nil, gas, nil + return nil, gas, 0, nil } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { - return nil, gas, ErrDepth + return nil, gas, 0, ErrDepth } // Fail if we're trying to transfer more than the available balance if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { - return nil, gas, ErrInsufficientBalance + return nil, gas, 0, ErrInsufficientBalance } lockupContractAddress := LockupContractAddresses[[2]byte{evm.chainConfig.Location[0], evm.chainConfig.Location[1]}] if addr.Equal(lockupContractAddress) { - gasUsed, err := RedeemQuai(evm.StateDB, caller.Address(), new(types.GasPool).AddGas(gas), evm.Context.BlockNumber, lockupContractAddress) + gasUsed, stateGasUsed, err := RedeemQuai(evm.Context, evm.StateDB, caller.Address(), new(types.GasPool).AddGas(gas), evm.Context.BlockNumber, lockupContractAddress) if gas > gasUsed { gas = gas - gasUsed } else { gas = 0 } + stateGas += stateGasUsed if err != nil { log.Global.Error("RedeemQuai failed", "err", err) } - return []byte{}, gas, err + return []byte{}, gas, stateGas, err } else if lock != nil && lock.Sign() != 0 { if err := evm.Context.Transfer(evm.StateDB, caller.Address(), lockupContractAddress, value); err != nil { - return nil, gas, err + return nil, gas, stateGas, err } - gasUsed, err := AddNewLock(evm.StateDB, addr, new(types.GasPool).AddGas(gas), lock, evm.Context.BlockNumber, lockupContractAddress) + gasUsed, stateGasUsed, err := AddNewLock(evm.Context, evm.StateDB, addr, new(types.GasPool).AddGas(gas), lock, evm.Context.BlockNumber, lockupContractAddress) if gas > gasUsed { gas = gas - gasUsed } else { gas = 0 } + stateGas += stateGasUsed if err != nil { log.Global.Error("AddNewLock failed", "err", err) } - return []byte{}, gas, err + return []byte{}, gas, stateGas, err } snapshot := evm.StateDB.Snapshot() p, isPrecompile, addr := evm.precompile(addr) @@ -238,12 +241,19 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil) } - return nil, gas, nil + return nil, gas, stateGas, nil } + newAccountCreationGas := params.CallNewAccountGas(evm.Context.QuaiStateSize) + if gas > newAccountCreationGas { + gas = gas - newAccountCreationGas + } else { + return nil, gas, stateGas, ErrInsufficientBalance + } + stateGas += params.CallNewAccountGas(evm.Context.QuaiStateSize) evm.StateDB.CreateAccount(internalAddr) } if err := evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value); err != nil { - return nil, gas, err + return nil, gas, stateGas, err } // Capture the tracer start/end events in debug mode @@ -270,6 +280,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(internalAddr), code) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas + stateGas += contract.StateGas } } // When an error was returned by the EVM or when setting the creation code @@ -284,7 +295,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas //} else { // evm.StateDB.DiscardSnapshot(snapshot) } - return ret, gas, err + return ret, gas, stateGas, err } // CallCode executes the contract associated with the addr with the given input @@ -438,10 +449,10 @@ func (c *codeAndHash) Hash() common.Hash { } // create creates a new contract using code as deployment code. -func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) { +func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, uint64, error) { internalCallerAddr, err := caller.Address().InternalAndQuaiAddress() if err != nil { - return nil, common.Zero, 0, err + return nil, common.Zero, 0, 0, err } nonce := evm.StateDB.GetNonce(internalCallerAddr) evm.StateDB.SetNonce(internalCallerAddr, nonce+1) @@ -449,15 +460,22 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Depth check execution. Fail if we're trying to execute above the // limit. if evm.depth > int(params.CallCreateDepth) { - return nil, common.Zero, gas, ErrDepth + return nil, common.Zero, gas, 0, ErrDepth + } + newAccountCreationGas := params.CallNewAccountGas(evm.Context.QuaiStateSize) + if gas > newAccountCreationGas { + gas = gas - newAccountCreationGas + } else { + return nil, common.Zero, gas, 0, ErrInsufficientBalance } + stateUsed := newAccountCreationGas if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { - return nil, common.Zero, gas, ErrInsufficientBalance + return nil, common.Zero, gas, 0, ErrInsufficientBalance } internalContractAddr, err := address.InternalAndQuaiAddress() if err != nil { - return nil, common.Zero, 0, err + return nil, common.Zero, 0, 0, err } // We add this to the access list _before_ taking a snapshot. Even if the creation fails, @@ -467,7 +485,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Ensure there's no existing contract already at the designated address contractHash := evm.StateDB.GetCodeHash(internalContractAddr) if evm.StateDB.GetNonce(internalContractAddr) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { - return nil, common.Zero, 0, ErrContractAddressCollision + return nil, common.Zero, 0, 0, ErrContractAddressCollision } // Create a new account on the state snapshot := evm.StateDB.Snapshot() @@ -476,7 +494,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, evm.StateDB.SetNonce(internalContractAddr, 1) if err := evm.Context.Transfer(evm.StateDB, caller.Address(), address, value); err != nil { - return nil, common.Zero, 0, err + return nil, common.Zero, 0, stateUsed, err } // Initialise a new contract and set the code that is to be used by the EVM. @@ -485,7 +503,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, contract.SetCodeOptionalHash(&address, codeAndHash) if evm.Config.NoRecursion && evm.depth > 0 { - return nil, address, gas, nil + return nil, address, gas, stateUsed, nil } if evm.Config.Debug && evm.depth == 0 { @@ -531,14 +549,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.Config.Debug && evm.depth == 0 { evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) } - return ret, address, contract.Gas, err + return ret, address, contract.Gas, stateUsed, err } // Create creates a new contract using code as deployment code. -func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { +func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, stateUsed uint64, err error) { internalAddr, err := caller.Address().InternalAndQuaiAddress() if err != nil { - return nil, common.Zero, 0, err + return nil, common.Zero, 0, 0, err } nonce := evm.StateDB.GetNonce(internalAddr) @@ -551,13 +569,13 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I // Calculate the gas required for the keccak256 computation of the input data. gasCost, err := calculateKeccakGas(code) if err != nil { - return nil, common.Zero, 0, err + return nil, common.Zero, 0, 0, err } // attempt to grind the address contractAddr, remainingGas, err := evm.attemptGrindContractCreation(caller, nonce, gas, gasCost, code) if err != nil { - return nil, common.Zero, 0, err + return nil, common.Zero, 0, 0, err } gas = remainingGas @@ -614,44 +632,44 @@ func (evm *EVM) attemptGrindContractCreation(caller ContractRef, nonce uint64, g // // The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:] // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. -func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { +func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, stateUsed uint64, err error) { codeAndHash := &codeAndHash{code: code} contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes(), evm.chainConfig.Location) return evm.create(caller, codeAndHash, gas, endowment, contractAddr) } -func (evm *EVM) CreateETX(toAddr common.Address, fromAddr common.Address, gas uint64, value *big.Int, data []byte) (ret []byte, leftOverGas uint64, err error) { +func (evm *EVM) CreateETX(toAddr common.Address, fromAddr common.Address, gas uint64, value *big.Int, data []byte) (ret []byte, leftOverGas uint64, stateGas uint64, err error) { // Verify address is not in context if toAddr.IsInQuaiLedgerScope() && common.IsInChainScope(toAddr.Bytes(), evm.chainConfig.Location) { - return []byte{}, 0, fmt.Errorf("%x is in chain scope, but CreateETX was called", toAddr) + return []byte{}, 0, 0, fmt.Errorf("%x is in chain scope, but CreateETX was called", toAddr) } conversion := false if toAddr.IsInQiLedgerScope() && common.IsInChainScope(toAddr.Bytes(), evm.chainConfig.Location) { conversion = true } if toAddr.IsInQiLedgerScope() && !common.IsInChainScope(toAddr.Bytes(), evm.chainConfig.Location) { - return []byte{}, 0, fmt.Errorf("%x is in qi scope and is not in the same location, but CreateETX was called", toAddr) + return []byte{}, 0, 0, fmt.Errorf("%x is in qi scope and is not in the same location, but CreateETX was called", toAddr) } else if conversion && value.Cmp(params.MinQuaiConversionAmount) < 0 { - return []byte{}, 0, fmt.Errorf("CreateETX conversion error: %d is not sufficient value, required amount: %d", value, params.MinQuaiConversionAmount) + return []byte{}, 0, 0, fmt.Errorf("CreateETX conversion error: %d is not sufficient value, required amount: %d", value, params.MinQuaiConversionAmount) } if gas < params.ETXGas { - return []byte{}, 0, fmt.Errorf("CreateETX error: %d is not sufficient gas, required amount: %d", gas, params.ETXGas) + return []byte{}, 0, 0, fmt.Errorf("CreateETX error: %d is not sufficient gas, required amount: %d", gas, params.ETXGas) } fromInternal, err := fromAddr.InternalAndQuaiAddress() if err != nil { - return []byte{}, 0, fmt.Errorf("CreateETX error: %s", err.Error()) + return []byte{}, 0, 0, fmt.Errorf("CreateETX error: %s", err.Error()) } gas = gas - params.ETXGas if gas < params.TxGas { // ETX must have enough gas to create a transaction - return []byte{}, 0, fmt.Errorf("CreateETX error: %d is not sufficient gas for ETX, required amount: %d", gas, params.TxGas) + return []byte{}, 0, 0, fmt.Errorf("CreateETX error: %d is not sufficient gas for ETX, required amount: %d", gas, params.TxGas) } // Fail if we're trying to transfer more than the available balance if !evm.Context.CanTransfer(evm.StateDB, fromAddr, value) { - return []byte{}, 0, fmt.Errorf("CreateETX: %x cannot transfer %d", fromAddr, value.Uint64()) + return []byte{}, 0, 0, fmt.Errorf("CreateETX: %x cannot transfer %d", fromAddr, value.Uint64()) } evm.StateDB.SubBalance(fromInternal, value) @@ -660,7 +678,7 @@ func (evm *EVM) CreateETX(toAddr common.Address, fromAddr common.Address, gas ui index := len(evm.ETXCache) // this is virtually guaranteed to be zero, but the logic is the same as opETX evm.ETXCacheLock.RUnlock() if index > math.MaxUint16 { - return []byte{}, 0, fmt.Errorf("CreateETX overflow error: too many ETXs in cache") + return []byte{}, 0, 0, fmt.Errorf("CreateETX overflow error: too many ETXs in cache") } // create external transaction etxInner := types.ExternalTx{Value: value, To: &toAddr, Sender: fromAddr, OriginatingTxHash: evm.Hash, ETXIndex: uint16(index), Gas: gas, Data: data, AccessList: evm.AccessList} @@ -668,14 +686,14 @@ func (evm *EVM) CreateETX(toAddr common.Address, fromAddr common.Address, gas ui // check if the etx is eligible to be sent to the to location if !conversion && !evm.Context.CheckIfEtxEligible(evm.Context.EtxEligibleSlices, *etx.To().Location()) { - return []byte{}, 0, fmt.Errorf("CreateETX error: ETX is not eligible to be sent to %x", etx.To()) + return []byte{}, 0, 0, fmt.Errorf("CreateETX error: ETX is not eligible to be sent to %x", etx.To()) } evm.ETXCacheLock.Lock() evm.ETXCache = append(evm.ETXCache, etx) evm.ETXCacheLock.Unlock() - return []byte{}, 0, nil // all leftover gas goes to the ETX + return []byte{}, 0, 0, nil // all leftover gas goes to the ETX } // Emitted ETXs must include some multiple of BaseFee as miner tip, to diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 7610c8a9e0..81f7b36fed 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -17,8 +17,6 @@ package vm import ( - "errors" - "github.com/dominant-strategies/go-quai/common" "github.com/dominant-strategies/go-quai/common/math" "github.com/dominant-strategies/go-quai/params" @@ -26,9 +24,9 @@ import ( // memoryGasCost calculates the quadratic gas for memory expansion. It does so // only for the memory region that is expanded, not the total memory. -func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { +func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, uint64, error) { if newMemSize == 0 { - return 0, nil + return 0, 0, nil } // The maximum that will fit in a uint64 is max_word_count - 1. Anything above // that will result in an overflow. Additionally, a newMemSize which results in @@ -36,7 +34,7 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { // overflow. The constant 0x1FFFFFFFE0 is the highest number that can be used // without overflowing the gas calculation. if newMemSize > 0x1FFFFFFFE0 { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } newMemSizeWords := toWordSize(newMemSize) newMemSize = newMemSizeWords * 32 @@ -50,9 +48,9 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { fee := newTotalFee - mem.lastGasCost mem.lastGasCost = newTotalFee - return fee, nil + return fee, 0, nil } - return 0, nil + return 0, 0, nil } // memoryCopierGas creates the gas functions for the following opcodes, and takes @@ -63,26 +61,26 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { // EXTCODECOPY (stack poition 3) // RETURNDATACOPY (stack position 2) func memoryCopierGas(stackpos int) gasFunc { - return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { // Gas for expanding the memory - gas, err := memoryGasCost(mem, memorySize) + gas, _, err := memoryGasCost(mem, memorySize) if err != nil { - return 0, err + return 0, 0, err } // And gas for copying data, charged per word at param.CopyGas words, overflow := stack.Back(stackpos).Uint64WithOverflow() if overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } if gas, overflow = math.SafeAdd(gas, words); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } - return gas, nil + return gas, 0, nil } } @@ -92,117 +90,59 @@ var ( gasReturnDataCopy = memoryCopierGas(2) ) -// 0. If *gasleft* is less than or equal to 2300, fail the current call. -// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted. -// 2. If current value does not equal new value: -// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context): -// 2.1.1. If original value is 0, SSTORE_SET_GAS (20K) gas is deducted. -// 2.1.2. Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter. -// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses: -// 2.2.1. If original value is not 0: -// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter. -// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter. -// 2.2.2. If original value equals new value (this storage slot is reset): -// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter. -// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter. -func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - // If we fail the minimum gas availability invariant, fail (0) - if contract.Gas <= params.SstoreSentryGas { - return 0, errors.New("not enough gas for reentrancy sentry") - } - // Gas sentry honoured, do the actual gas calculation based on the stored value - var ( - y, x = stack.Back(1), stack.Back(0) - internalContractAddr, err = contract.Address().InternalAndQuaiAddress() - ) - if err != nil { - return 0, err - } - current := evm.StateDB.GetState(internalContractAddr, x.Bytes32()) - value := common.Hash(y.Bytes32()) - - if current == value { // noop (1) - return params.SloadGas, nil - } - original := evm.StateDB.GetCommittedState(internalContractAddr, x.Bytes32()) - if original == current { - if original == (common.Hash{}) { // create slot (2.1.1) - return params.SstoreSetGas, nil - } - if value == (common.Hash{}) { // delete slot (2.1.2b) - evm.StateDB.AddRefund(params.SstoreClearsScheduleRefund) - } - return params.SstoreResetGas, nil // write existing slot (2.1.2) - } - if original != (common.Hash{}) { - if current == (common.Hash{}) { // recreate slot (2.2.1.1) - evm.StateDB.SubRefund(params.SstoreClearsScheduleRefund) - } else if value == (common.Hash{}) { // delete slot (2.2.1.2) - evm.StateDB.AddRefund(params.SstoreClearsScheduleRefund) - } - } - if original == value { - if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) - evm.StateDB.AddRefund(params.SstoreSetGas - params.SloadGas) - } else { // reset to original existing slot (2.2.2.2) - evm.StateDB.AddRefund(params.SstoreResetGas - params.SloadGas) - } - } - return params.SloadGas, nil // dirty update (2.2) -} - func makeGasLog(n uint64) gasFunc { - return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { requestedSize, overflow := stack.Back(1).Uint64WithOverflow() if overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } - gas, err := memoryGasCost(mem, memorySize) + gas, _, err := memoryGasCost(mem, memorySize) if err != nil { - return 0, err + return 0, 0, err } if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } var memorySizeGas uint64 if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } - return gas, nil + // make gas log doesnt use any state operation, so return 0 for stateGas used + return gas, 0, nil } } -func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) +func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { + gas, _, err := memoryGasCost(mem, memorySize) if err != nil { - return 0, err + return 0, 0, err } wordGas, overflow := stack.Back(1).Uint64WithOverflow() if overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } if gas, overflow = math.SafeAdd(gas, wordGas); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } - return gas, nil + return gas, 0, nil } // pureMemoryGascost is used by several operations, which aside from their // static cost have a dynamic cost which is solely based on the memory // expansion -func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { +func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { return memoryGasCost(mem, memorySize) } @@ -215,25 +155,25 @@ var ( gasCreate = pureMemoryGascost ) -func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) +func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { + gas, _, err := memoryGasCost(mem, memorySize) if err != nil { - return 0, err + return 0, 0, err } wordGas, overflow := stack.Back(2).Uint64WithOverflow() if overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } if gas, overflow = math.SafeAdd(gas, wordGas); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } - return gas, nil + return gas, 0, nil } -func gasExp(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { +func gasExp(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8) var ( @@ -241,100 +181,103 @@ func gasExp(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize overflow bool ) if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } - return gas, nil + return gas, 0, nil } -func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { +func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { var ( gas uint64 + stateGas uint64 transfersValue = !stack.Back(2).IsZero() address, err = common.Bytes20ToAddress(stack.Back(1).Bytes20(), evm.chainConfig.Location).InternalAndQuaiAddress() ) if err != nil { - return 0, err + return 0, 0, err } if transfersValue && evm.StateDB.Empty(address) { - gas += params.CallNewAccountGas + gas += params.CallNewAccountGas(evm.Context.QuaiStateSize) + stateGas = gas } if transfersValue { gas += params.CallValueTransferGas } - memoryGas, err := memoryGasCost(mem, memorySize) + memoryGas, _, err := memoryGasCost(mem, memorySize) if err != nil { - return 0, err + return 0, 0, err } var overflow bool if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } evm.callGasTemp, err = callGas(contract.Gas, gas, stack.Back(0)) if err != nil { - return 0, err + return 0, 0, err } if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } - return gas, nil + return gas, stateGas, nil } -func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - memoryGas, err := memoryGasCost(mem, memorySize) +func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { + memoryGas, _, err := memoryGasCost(mem, memorySize) if err != nil { - return 0, err + return 0, 0, err } var ( gas uint64 + stateGas uint64 overflow bool ) if stack.Back(2).Sign() != 0 { gas += params.CallValueTransferGas } if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } evm.callGasTemp, err = callGas(contract.Gas, gas, stack.Back(0)) if err != nil { - return 0, err + return 0, 0, err } if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } - return gas, nil + return gas, stateGas, nil } -func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) +func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { + gas, _, err := memoryGasCost(mem, memorySize) if err != nil { - return 0, err + return 0, 0, err } evm.callGasTemp, err = callGas(contract.Gas, gas, stack.Back(0)) if err != nil { - return 0, err + return 0, 0, err } var overflow bool if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } - return gas, nil + return gas, 0, nil } -func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) +func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { + gas, _, err := memoryGasCost(mem, memorySize) if err != nil { - return 0, err + return 0, 0, err } evm.callGasTemp, err = callGas(contract.Gas, gas, stack.Back(0)) if err != nil { - return 0, err + return 0, 0, err } var overflow bool if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { - return 0, ErrGasUintOverflow + return 0, 0, ErrGasUintOverflow } - return gas, nil + return gas, 0, nil } func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { @@ -358,3 +301,56 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me } return gas, nil } + +func gasWarmStorageRead(evm *EVM, contract *Contract) (uint64, uint64, error) { + contractAddr, err := contract.Address().InternalAddress() + if err != nil { + return 0, 0, err + } + contractSize := evm.StateDB.GetSize(contractAddr) + warmStorageGasCost := params.WarmStorageReadCost(evm.Context.QuaiStateSize, contractSize) + return warmStorageGasCost, warmStorageGasCost, nil +} + +// wrapping the constant gas values in the constantGasFunc signature, all these +// functions dont use the stateGas +func gasZero(evm *EVM, contract *Contract) (uint64, uint64, error) { + return 0, 0, nil +} +func gasQuickStep(evm *EVM, contract *Contract) (uint64, uint64, error) { + return GasQuickStep, 0, nil +} +func gasFastestStep(evm *EVM, contract *Contract) (uint64, uint64, error) { + return GasFastestStep, 0, nil +} +func gasFastStep(evm *EVM, contract *Contract) (uint64, uint64, error) { + return GasFastStep, 0, nil +} +func gasMidStep(evm *EVM, contract *Contract) (uint64, uint64, error) { + return GasMidStep, 0, nil +} +func gasSlowStep(evm *EVM, contract *Contract) (uint64, uint64, error) { + return GasSlowStep, 0, nil +} +func gasExtStep(evm *EVM, contract *Contract) (uint64, uint64, error) { + return GasExtStep, 0, nil +} + +func gasCreateConstant(evm *EVM, contract *Contract) (uint64, uint64, error) { + return params.CreateGas, 0, nil +} +func gasCreate2Constant(evm *EVM, contract *Contract) (uint64, uint64, error) { + return params.Create2Gas, 0, nil +} +func gasSha3Constant(evm *EVM, contract *Contract) (uint64, uint64, error) { + return params.Sha3Gas, 0, nil +} +func gasJumpDestGas(evm *EVM, contract *Contract) (uint64, uint64, error) { + return params.JumpdestGas, 0, nil +} +func gasSelfdestructConstant(evm *EVM, contract *Contract) (uint64, uint64, error) { + return params.SelfdestructGas, 0, nil +} +func gasEtx(evm *EVM, contract *Contract) (uint64, uint64, error) { + return params.ETXGas, 0, nil +} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 8bb88ddee4..07b6f5b0d9 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -19,6 +19,7 @@ package vm import ( "fmt" "math" + "math/big" "github.com/dominant-strategies/go-quai/common" "github.com/dominant-strategies/go-quai/core/types" @@ -613,7 +614,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b bigVal = value.ToBig() } - res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, bigVal) + res, addr, returnGas, stateUsed, suberr := interpreter.evm.Create(scope.Contract, input, gas, bigVal) // Push item on the stack based on the returned error. if suberr != nil { stackvalue.Clear() @@ -622,6 +623,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } scope.Stack.push(&stackvalue) scope.Contract.Gas += returnGas + scope.Contract.StateGas += stateUsed if suberr == ErrExecutionReverted { return res, nil @@ -646,7 +648,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] if !endowment.IsZero() { bigEndowment = endowment.ToBig() } - res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas, + res, addr, returnGas, stateUsed, suberr := interpreter.evm.Create2(scope.Contract, input, gas, bigEndowment, &salt) // Push item on the stack based on the returned error. if suberr != nil { @@ -656,6 +658,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] } scope.Stack.push(&stackvalue) scope.Contract.Gas += returnGas + scope.Contract.StateGas += stateUsed if suberr == ErrExecutionReverted { return res, nil @@ -684,7 +687,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt bigVal = value.ToBig() } - ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal, nil) + ret, returnGas, stateGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal, nil) if err != nil { temp.Clear() @@ -699,6 +702,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt return nil, err } scope.Contract.Gas += returnGas + scope.Contract.StateGas += stateGas return ret, nil } @@ -830,6 +834,8 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] } balance := interpreter.evm.StateDB.GetBalance(addr) interpreter.evm.StateDB.AddBalance(beneficiaryAddr, balance) + refund := new(big.Int).Mul(interpreter.evm.Context.BaseFee, new(big.Int).SetUint64(params.CallNewAccountGas(interpreter.evm.Context.QuaiStateSize))) + interpreter.evm.StateDB.AddBalance(beneficiaryAddr, refund) interpreter.evm.StateDB.Suicide(addr) return nil, nil } diff --git a/core/vm/interface.go b/core/vm/interface.go index 91ed3cd227..9c980c541d 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -30,6 +30,7 @@ type StateDB interface { SubBalance(common.InternalAddress, *big.Int) AddBalance(common.InternalAddress, *big.Int) GetBalance(common.InternalAddress) *big.Int + GetSize(common.InternalAddress) *big.Int GetNonce(common.InternalAddress) uint64 SetNonce(common.InternalAddress, uint64) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index b0e7435f31..d64d17b7a0 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -185,12 +185,17 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( return nil, ErrWriteProtection } } - // Static portion of gas - cost = operation.constantGas // For tracing - if !contract.UseGas(operation.constantGas) { + // Static portion of gas, the static portion now only applies to the compute operations + // previously considered state access static costs are modified based on the size of the + // state trie and the contract storage size trie + staticCost, stateCost, err := operation.constantGas(in.evm, contract) + if !contract.UseGas(staticCost) { return nil, ErrOutOfGas } + // Add the stateGas used into the contract.StateGas + contract.UseState(stateCost) + var memorySize uint64 // calculate the new memory size and expand the memory to fit // the operation @@ -212,11 +217,16 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // cost is explicitly set so that the capture state defer method can get the proper cost if operation.dynamicGas != nil { var dynamicCost uint64 - dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize) + var stateCost uint64 + dynamicCost, stateCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize) cost += dynamicCost // total cost, for debug tracing + if err != nil || !contract.UseGas(dynamicCost) { return nil, ErrOutOfGas } + + // Add the stateGas used into the contract.StateGas + contract.UseState(stateCost) } if memorySize > 0 { mem.Resize(memorySize) diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index e292dae62f..c11437d34b 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -16,13 +16,10 @@ package vm -import ( - "github.com/dominant-strategies/go-quai/params" -) - type ( - executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) - gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 + executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) + gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, uint64, error) // last parameter is the requested memory size as a uint64 + constantGasFunc func(*EVM, *Contract) (uint64, uint64, error) // memorySizeFunc returns the required size, and whether the operation overflowed a uint64 memorySizeFunc func(*Stack) (size uint64, overflow bool) ) @@ -30,7 +27,7 @@ type ( type operation struct { // execute is the operation function execute executionFunc - constantGas uint64 + constantGas constantGasFunc dynamicGas gasFunc // minStack tells how many stack items are required minStack int @@ -61,7 +58,7 @@ func NewInstructionSet() JumpTable { instructionSet[DELEGATECALL] = &operation{ execute: opDelegateCall, dynamicGas: gasDelegateCallVariant, - constantGas: params.WarmStorageReadCost, + constantGas: gasWarmStorageRead, minStack: minStack(6, 1), maxStack: maxStack(6, 1), memorySize: memoryDelegateCall, @@ -70,7 +67,7 @@ func NewInstructionSet() JumpTable { instructionSet[EXP].dynamicGas = gasExp instructionSet[STATICCALL] = &operation{ execute: opStaticCall, - constantGas: params.WarmStorageReadCost, + constantGas: gasWarmStorageRead, dynamicGas: gasStaticCallVariant, minStack: minStack(6, 1), maxStack: maxStack(6, 1), @@ -79,55 +76,56 @@ func NewInstructionSet() JumpTable { } instructionSet[RETURNDATASIZE] = &operation{ execute: opReturnDataSize, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), } instructionSet[RETURNDATACOPY] = &operation{ execute: opReturnDataCopy, - constantGas: GasFastestStep, + constantGas: gasFastestStep, dynamicGas: gasReturnDataCopy, minStack: minStack(3, 0), maxStack: maxStack(3, 0), memorySize: memoryReturnDataCopy, } instructionSet[REVERT] = &operation{ - execute: opRevert, - dynamicGas: gasRevert, - minStack: minStack(2, 0), - maxStack: maxStack(2, 0), - memorySize: memoryRevert, - reverts: true, - returns: true, + execute: opRevert, + constantGas: gasZero, + dynamicGas: gasRevert, + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + memorySize: memoryRevert, + reverts: true, + returns: true, } instructionSet[SHL] = &operation{ execute: opSHL, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), } instructionSet[SHR] = &operation{ execute: opSHR, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), } instructionSet[SAR] = &operation{ execute: opSAR, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), } instructionSet[EXTCODEHASH] = &operation{ execute: opExtCodeHash, - constantGas: params.WarmStorageReadCost, + constantGas: gasWarmStorageRead, dynamicGas: gasAccountCheck, minStack: minStack(1, 1), maxStack: maxStack(1, 1), } instructionSet[CREATE2] = &operation{ execute: opCreate2, - constantGas: params.Create2Gas, + constantGas: gasCreate2Constant, dynamicGas: gasCreate2, minStack: minStack(4, 1), maxStack: maxStack(4, 1), @@ -137,13 +135,13 @@ func NewInstructionSet() JumpTable { } instructionSet[CHAINID] = &operation{ execute: opChainID, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), } instructionSet[SELFBALANCE] = &operation{ execute: opSelfBalance, - constantGas: GasFastStep, + constantGas: gasFastStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), } @@ -154,146 +152,147 @@ func newInstructionSet() JumpTable { return JumpTable{ STOP: { execute: opStop, - constantGas: 0, + constantGas: gasZero, minStack: minStack(0, 0), maxStack: maxStack(0, 0), halts: true, }, ADD: { execute: opAdd, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, MUL: { execute: opMul, - constantGas: GasFastStep, + constantGas: gasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, SUB: { execute: opSub, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, DIV: { execute: opDiv, - constantGas: GasFastStep, + constantGas: gasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, SDIV: { execute: opSdiv, - constantGas: GasFastStep, + constantGas: gasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, MOD: { execute: opMod, - constantGas: GasFastStep, + constantGas: gasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, SMOD: { execute: opSmod, - constantGas: GasFastStep, + constantGas: gasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, ADDMOD: { execute: opAddmod, - constantGas: GasMidStep, + constantGas: gasMidStep, minStack: minStack(3, 1), maxStack: maxStack(3, 1), }, MULMOD: { execute: opMulmod, - constantGas: GasMidStep, + constantGas: gasMidStep, minStack: minStack(3, 1), maxStack: maxStack(3, 1), }, EXP: { - execute: opExp, - dynamicGas: gasExp, - minStack: minStack(2, 1), - maxStack: maxStack(2, 1), + execute: opExp, + constantGas: gasZero, + dynamicGas: gasExp, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), }, SIGNEXTEND: { execute: opSignExtend, - constantGas: GasFastStep, + constantGas: gasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, LT: { execute: opLt, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, GT: { execute: opGt, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, SLT: { execute: opSlt, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, SGT: { execute: opSgt, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, EQ: { execute: opEq, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, ISZERO: { execute: opIszero, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), }, AND: { execute: opAnd, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, XOR: { execute: opXor, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, OR: { execute: opOr, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, NOT: { execute: opNot, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), }, BYTE: { execute: opByte, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, SHA3: { execute: opSha3, - constantGas: params.Sha3Gas, + constantGas: gasSha3Constant, dynamicGas: gasSha3, minStack: minStack(2, 1), maxStack: maxStack(2, 1), @@ -301,50 +300,50 @@ func newInstructionSet() JumpTable { }, ADDRESS: { execute: opAddress, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, BALANCE: { execute: opBalance, - constantGas: params.WarmStorageReadCost, + constantGas: gasWarmStorageRead, dynamicGas: gasAccountCheck, minStack: minStack(1, 1), maxStack: maxStack(1, 1), }, ORIGIN: { execute: opOrigin, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, CALLER: { execute: opCaller, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, CALLVALUE: { execute: opCallValue, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, CALLDATALOAD: { execute: opCallDataLoad, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), }, CALLDATASIZE: { execute: opCallDataSize, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, CALLDATACOPY: { execute: opCallDataCopy, - constantGas: GasFastestStep, + constantGas: gasFastestStep, dynamicGas: gasCallDataCopy, minStack: minStack(3, 0), maxStack: maxStack(3, 0), @@ -352,13 +351,13 @@ func newInstructionSet() JumpTable { }, CODESIZE: { execute: opCodeSize, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, CODECOPY: { execute: opCodeCopy, - constantGas: GasFastestStep, + constantGas: gasFastestStep, dynamicGas: gasCodeCopy, minStack: minStack(3, 0), maxStack: maxStack(3, 0), @@ -366,20 +365,20 @@ func newInstructionSet() JumpTable { }, GASPRICE: { execute: opGasprice, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, EXTCODESIZE: { execute: opExtCodeSize, - constantGas: params.WarmStorageReadCost, + constantGas: gasWarmStorageRead, dynamicGas: gasAccountCheck, minStack: minStack(1, 1), maxStack: maxStack(1, 1), }, EXTCODECOPY: { execute: opExtCodeCopy, - constantGas: params.WarmStorageReadCost, + constantGas: gasWarmStorageRead, dynamicGas: gasExtCodeCopy, minStack: minStack(4, 0), maxStack: maxStack(4, 0), @@ -387,49 +386,49 @@ func newInstructionSet() JumpTable { }, BLOCKHASH: { execute: opBlockhash, - constantGas: GasExtStep, + constantGas: gasExtStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), }, COINBASE: { execute: opCoinbase, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, TIMESTAMP: { execute: opTimestamp, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, NUMBER: { execute: opNumber, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, DIFFICULTY: { execute: opDifficulty, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, GASLIMIT: { execute: opGasLimit, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, POP: { execute: opPop, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(1, 0), maxStack: maxStack(1, 0), }, MLOAD: { execute: opMload, - constantGas: GasFastestStep, + constantGas: gasFastestStep, dynamicGas: gasMLoad, minStack: minStack(1, 1), maxStack: maxStack(1, 1), @@ -437,7 +436,7 @@ func newInstructionSet() JumpTable { }, MSTORE: { execute: opMstore, - constantGas: GasFastestStep, + constantGas: gasFastestStep, dynamicGas: gasMStore, minStack: minStack(2, 0), maxStack: maxStack(2, 0), @@ -445,7 +444,7 @@ func newInstructionSet() JumpTable { }, MSTORE8: { execute: opMstore8, - constantGas: GasFastestStep, + constantGas: gasFastestStep, dynamicGas: gasMStore8, memorySize: memoryMStore8, minStack: minStack(2, 0), @@ -453,483 +452,489 @@ func newInstructionSet() JumpTable { }, SLOAD: { execute: opSload, - constantGas: 0, + constantGas: gasZero, dynamicGas: gasSLoad, minStack: minStack(1, 1), maxStack: maxStack(1, 1), }, SSTORE: { - execute: opSstore, - dynamicGas: gasSStoreVariant, - minStack: minStack(2, 0), - maxStack: maxStack(2, 0), - writes: true, + execute: opSstore, + constantGas: gasZero, + dynamicGas: gasSStoreVariant, + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + writes: true, }, JUMP: { execute: opJump, - constantGas: GasMidStep, + constantGas: gasMidStep, minStack: minStack(1, 0), maxStack: maxStack(1, 0), jumps: true, }, JUMPI: { execute: opJumpi, - constantGas: GasSlowStep, + constantGas: gasSlowStep, minStack: minStack(2, 0), maxStack: maxStack(2, 0), jumps: true, }, PC: { execute: opPc, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, MSIZE: { execute: opMsize, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, GAS: { execute: opGas, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, JUMPDEST: { execute: opJumpdest, - constantGas: params.JumpdestGas, + constantGas: gasJumpDestGas, minStack: minStack(0, 0), maxStack: maxStack(0, 0), }, PUSH1: { execute: opPush1, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH2: { execute: makePush(2, 2), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH3: { execute: makePush(3, 3), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH4: { execute: makePush(4, 4), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH5: { execute: makePush(5, 5), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH6: { execute: makePush(6, 6), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH7: { execute: makePush(7, 7), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH8: { execute: makePush(8, 8), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH9: { execute: makePush(9, 9), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH10: { execute: makePush(10, 10), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH11: { execute: makePush(11, 11), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH12: { execute: makePush(12, 12), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH13: { execute: makePush(13, 13), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH14: { execute: makePush(14, 14), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH15: { execute: makePush(15, 15), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH16: { execute: makePush(16, 16), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH17: { execute: makePush(17, 17), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH18: { execute: makePush(18, 18), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH19: { execute: makePush(19, 19), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH20: { execute: makePush(20, 20), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH21: { execute: makePush(21, 21), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH22: { execute: makePush(22, 22), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH23: { execute: makePush(23, 23), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH24: { execute: makePush(24, 24), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH25: { execute: makePush(25, 25), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH26: { execute: makePush(26, 26), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH27: { execute: makePush(27, 27), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH28: { execute: makePush(28, 28), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH29: { execute: makePush(29, 29), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH30: { execute: makePush(30, 30), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH31: { execute: makePush(31, 31), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, PUSH32: { execute: makePush(32, 32), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, DUP1: { execute: makeDup(1), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(1), maxStack: maxDupStack(1), }, DUP2: { execute: makeDup(2), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(2), maxStack: maxDupStack(2), }, DUP3: { execute: makeDup(3), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(3), maxStack: maxDupStack(3), }, DUP4: { execute: makeDup(4), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(4), maxStack: maxDupStack(4), }, DUP5: { execute: makeDup(5), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(5), maxStack: maxDupStack(5), }, DUP6: { execute: makeDup(6), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(6), maxStack: maxDupStack(6), }, DUP7: { execute: makeDup(7), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(7), maxStack: maxDupStack(7), }, DUP8: { execute: makeDup(8), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(8), maxStack: maxDupStack(8), }, DUP9: { execute: makeDup(9), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(9), maxStack: maxDupStack(9), }, DUP10: { execute: makeDup(10), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(10), maxStack: maxDupStack(10), }, DUP11: { execute: makeDup(11), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(11), maxStack: maxDupStack(11), }, DUP12: { execute: makeDup(12), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(12), maxStack: maxDupStack(12), }, DUP13: { execute: makeDup(13), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(13), maxStack: maxDupStack(13), }, DUP14: { execute: makeDup(14), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(14), maxStack: maxDupStack(14), }, DUP15: { execute: makeDup(15), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(15), maxStack: maxDupStack(15), }, DUP16: { execute: makeDup(16), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minDupStack(16), maxStack: maxDupStack(16), }, SWAP1: { execute: makeSwap(1), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(2), maxStack: maxSwapStack(2), }, SWAP2: { execute: makeSwap(2), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(3), maxStack: maxSwapStack(3), }, SWAP3: { execute: makeSwap(3), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(4), maxStack: maxSwapStack(4), }, SWAP4: { execute: makeSwap(4), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(5), maxStack: maxSwapStack(5), }, SWAP5: { execute: makeSwap(5), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(6), maxStack: maxSwapStack(6), }, SWAP6: { execute: makeSwap(6), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(7), maxStack: maxSwapStack(7), }, SWAP7: { execute: makeSwap(7), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(8), maxStack: maxSwapStack(8), }, SWAP8: { execute: makeSwap(8), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(9), maxStack: maxSwapStack(9), }, SWAP9: { execute: makeSwap(9), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(10), maxStack: maxSwapStack(10), }, SWAP10: { execute: makeSwap(10), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(11), maxStack: maxSwapStack(11), }, SWAP11: { execute: makeSwap(11), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(12), maxStack: maxSwapStack(12), }, SWAP12: { execute: makeSwap(12), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(13), maxStack: maxSwapStack(13), }, SWAP13: { execute: makeSwap(13), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(14), maxStack: maxSwapStack(14), }, SWAP14: { execute: makeSwap(14), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(15), maxStack: maxSwapStack(15), }, SWAP15: { execute: makeSwap(15), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(16), maxStack: maxSwapStack(16), }, SWAP16: { execute: makeSwap(16), - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minSwapStack(17), maxStack: maxSwapStack(17), }, LOG0: { - execute: makeLog(0), - dynamicGas: makeGasLog(0), - minStack: minStack(2, 0), - maxStack: maxStack(2, 0), - memorySize: memoryLog, - writes: true, + execute: makeLog(0), + constantGas: gasZero, + dynamicGas: makeGasLog(0), + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + memorySize: memoryLog, + writes: true, }, LOG1: { - execute: makeLog(1), - dynamicGas: makeGasLog(1), - minStack: minStack(3, 0), - maxStack: maxStack(3, 0), - memorySize: memoryLog, - writes: true, + execute: makeLog(1), + constantGas: gasZero, + dynamicGas: makeGasLog(1), + minStack: minStack(3, 0), + maxStack: maxStack(3, 0), + memorySize: memoryLog, + writes: true, }, LOG2: { - execute: makeLog(2), - dynamicGas: makeGasLog(2), - minStack: minStack(4, 0), - maxStack: maxStack(4, 0), - memorySize: memoryLog, - writes: true, + execute: makeLog(2), + constantGas: gasZero, + dynamicGas: makeGasLog(2), + minStack: minStack(4, 0), + maxStack: maxStack(4, 0), + memorySize: memoryLog, + writes: true, }, LOG3: { - execute: makeLog(3), - dynamicGas: makeGasLog(3), - minStack: minStack(5, 0), - maxStack: maxStack(5, 0), - memorySize: memoryLog, - writes: true, + execute: makeLog(3), + constantGas: gasZero, + dynamicGas: makeGasLog(3), + minStack: minStack(5, 0), + maxStack: maxStack(5, 0), + memorySize: memoryLog, + writes: true, }, LOG4: { - execute: makeLog(4), - dynamicGas: makeGasLog(4), - minStack: minStack(6, 0), - maxStack: maxStack(6, 0), - memorySize: memoryLog, - writes: true, + execute: makeLog(4), + constantGas: gasZero, + dynamicGas: makeGasLog(4), + minStack: minStack(6, 0), + maxStack: maxStack(6, 0), + memorySize: memoryLog, + writes: true, }, CREATE: { execute: opCreate, - constantGas: params.CreateGas, + constantGas: gasCreateConstant, dynamicGas: gasCreate, minStack: minStack(3, 1), maxStack: maxStack(3, 1), @@ -939,7 +944,7 @@ func newInstructionSet() JumpTable { }, CALL: { execute: opCall, - constantGas: params.WarmStorageReadCost, + constantGas: gasWarmStorageRead, dynamicGas: gasCallVariant, minStack: minStack(7, 1), maxStack: maxStack(7, 1), @@ -948,7 +953,7 @@ func newInstructionSet() JumpTable { }, CALLCODE: { execute: opCallCode, - constantGas: params.WarmStorageReadCost, + constantGas: gasWarmStorageRead, dynamicGas: gasCallCodeVariant, minStack: minStack(7, 1), maxStack: maxStack(7, 1), @@ -956,16 +961,17 @@ func newInstructionSet() JumpTable { returns: true, }, RETURN: { - execute: opReturn, - dynamicGas: gasReturn, - minStack: minStack(2, 0), - maxStack: maxStack(2, 0), - memorySize: memoryReturn, - halts: true, + execute: opReturn, + constantGas: gasZero, + dynamicGas: gasReturn, + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + memorySize: memoryReturn, + halts: true, }, SELFDESTRUCT: { execute: opSuicide, - constantGas: params.SelfdestructGas, + constantGas: gasSelfdestructConstant, dynamicGas: gasSelfdestructVariant, minStack: minStack(1, 0), maxStack: maxStack(1, 0), @@ -974,13 +980,13 @@ func newInstructionSet() JumpTable { }, BASEFEE: { execute: opBaseFee, - constantGas: GasQuickStep, + constantGas: gasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), }, ETX: { execute: opETX, - constantGas: params.ETXGas, + constantGas: gasEtx, minStack: minStack(10, 1), maxStack: maxStack(10, 1), memorySize: memoryETX, @@ -988,13 +994,13 @@ func newInstructionSet() JumpTable { }, ISADDRINTERNAL: { execute: opIsAddressInternal, - constantGas: GasFastestStep, + constantGas: gasFastestStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), }, CONVERT: { execute: opConvert, - constantGas: params.ETXGas, + constantGas: gasEtx, minStack: minStack(4, 1), maxStack: maxStack(4, 1), writes: true, diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index a196e9cc1f..e13a5a27e0 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -18,32 +18,37 @@ package vm import ( "errors" + "math/big" "github.com/dominant-strategies/go-quai/common" "github.com/dominant-strategies/go-quai/common/math" "github.com/dominant-strategies/go-quai/params" ) -func makeGasSStoreFunc(clearingRefund uint64) gasFunc { - return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - // If we fail the minimum gas availability invariant, fail (0) - if contract.Gas <= params.SstoreSentryGas { - return 0, errors.New("not enough gas for reentrancy sentry") - } +func makeGasSStoreFunc(clearingRefund func(*big.Int, *big.Int) uint64) gasFunc { + return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { // Gas sentry honoured, do the actual gas calculation based on the stored value var ( y, x = stack.Back(1), stack.peek() slot = common.Hash(x.Bytes32()) cost = uint64(0) + stateGas = uint64(0) internalAddr, err = contract.Address().InternalAndQuaiAddress() ) if err != nil { - return 0, err + return 0, stateGas, err + } + storageSizeOfContract := evm.StateDB.GetSize(internalAddr) + // If we fail the minimum gas availability invariant, fail (0) + if contract.Gas <= params.SstoreSentryGas(evm.Context.QuaiStateSize, storageSizeOfContract) { + return 0, 0, errors.New("not enough gas for reentrancy sentry") } current := evm.StateDB.GetState(internalAddr, slot) // Check slot presence in the access list if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent { - cost = params.ColdSloadCost + coldSLoadCost := params.ColdSloadCost(evm.Context.QuaiStateSize, storageSizeOfContract) + cost = coldSLoadCost + stateGas = coldSLoadCost // If the caller cannot afford the cost, this change will be rolled back evm.StateDB.AddSlotToAccessList(contract.Address(), slot) if !addrPresent { @@ -56,33 +61,33 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc { value := common.Hash(y.Bytes32()) if current == value { // noop (1) - return cost + params.WarmStorageReadCost, nil // SLOAD_GAS + return cost + params.WarmStorageReadCost(evm.Context.QuaiStateSize, storageSizeOfContract), stateGas, nil // SLOAD_GAS } original := evm.StateDB.GetCommittedState(internalAddr, x.Bytes32()) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) - return cost + params.SstoreSetGas, nil + return cost + params.SstoreSetGas(evm.Context.QuaiStateSize, storageSizeOfContract), stateGas, nil } if value == (common.Hash{}) { // delete slot (2.1.2b) - evm.StateDB.AddRefund(clearingRefund) + evm.StateDB.AddRefund(clearingRefund(evm.Context.QuaiStateSize, storageSizeOfContract)) } - return cost + (params.SstoreResetGas - params.ColdSloadCost), nil // write existing slot (2.1.2) + return cost + (params.SstoreResetGas(evm.Context.QuaiStateSize, storageSizeOfContract) - params.ColdSloadCost(evm.Context.QuaiStateSize, storageSizeOfContract)), stateGas, nil // write existing slot (2.1.2) } if original != (common.Hash{}) { if current == (common.Hash{}) { // recreate slot (2.2.1.1) - evm.StateDB.SubRefund(clearingRefund) + evm.StateDB.SubRefund(clearingRefund(evm.Context.QuaiStateSize, storageSizeOfContract)) } else if value == (common.Hash{}) { // delete slot (2.2.1.2) - evm.StateDB.AddRefund(clearingRefund) + evm.StateDB.AddRefund(clearingRefund(evm.Context.QuaiStateSize, storageSizeOfContract)) } } if original == value { if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) - evm.StateDB.AddRefund(params.SstoreSetGas - params.WarmStorageReadCost) + evm.StateDB.AddRefund(params.SstoreSetGas(evm.Context.QuaiStateSize, storageSizeOfContract) - params.WarmStorageReadCost(evm.Context.QuaiStateSize, storageSizeOfContract)) } else { // reset to original existing slot (2.2.2.2) - evm.StateDB.AddRefund((params.SstoreResetGas - params.ColdSloadCost) - params.WarmStorageReadCost) + evm.StateDB.AddRefund((params.SstoreResetGas(evm.Context.QuaiStateSize, storageSizeOfContract) - params.ColdSloadCost(evm.Context.QuaiStateSize, storageSizeOfContract)) - params.WarmStorageReadCost(evm.Context.QuaiStateSize, storageSizeOfContract)) } } - return cost + params.WarmStorageReadCost, nil // dirty update (2.2) + return cost + params.WarmStorageReadCost(evm.Context.QuaiStateSize, storageSizeOfContract), stateGas, nil // dirty update (2.2) } } @@ -91,41 +96,57 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc { // whose storage is being read) is not yet in accessed_storage_keys, // charge 2100 gas and add the pair to accessed_storage_keys. // If the pair is already in accessed_storage_keys, charge 100 gas. -func gasSLoad(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { +func gasSLoad(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { loc := stack.peek() slot := common.Hash(loc.Bytes32()) + internalAddr, err := contract.Address().InternalAddress() + if err != nil { + return 0, 0, err + } + storageSizeOfContract := evm.StateDB.GetSize(internalAddr) + var stateGas uint64 // TODO: check the calculation // Check slot presence in the access list if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent { // If the caller cannot afford the cost, this change will be rolled back // If he does afford it, we can skip checking the same thing later on, during execution evm.StateDB.AddSlotToAccessList(contract.Address(), slot) - return params.ColdSloadCost, nil + return params.ColdSloadCost(evm.Context.QuaiStateSize, storageSizeOfContract), stateGas, nil } - return params.WarmStorageReadCost, nil + return params.WarmStorageReadCost(evm.Context.QuaiStateSize, storageSizeOfContract), stateGas, nil } // gasExtCodeCopy implements extcodecopy gas calculation // > If the target is not in accessed_addresses, // > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses. // > Otherwise, charge WARM_STORAGE_READ_COST gas. -func gasExtCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { +func gasExtCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { // memory expansion first - gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize) + // TODO: check the stateGas value + gas, stateGas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize) if err != nil { - return 0, err + return 0, 0, err } addr := common.Bytes20ToAddress(stack.peek().Bytes20(), evm.chainConfig.Location) // Check slot presence in the access list if !evm.StateDB.AddressInAccessList(addr) { evm.StateDB.AddAddressToAccessList(addr) var overflow bool + // contract internal address + contractIAddr, err := contract.Address().InternalAddress() + if err != nil { + return 0, 0, err + } + contractSize := evm.StateDB.GetSize(contractIAddr) + coldAccountAccessCost := params.ColdAccountAccessCost(evm.Context.QuaiStateSize, contractSize) + warmStorageReadCost := params.WarmStorageReadCost(evm.Context.QuaiStateSize, contractSize) // We charge (cold-warm), since 'warm' is already charged as constantGas - if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCost-params.WarmStorageReadCost); overflow { - return 0, ErrGasUintOverflow + if gas, overflow = math.SafeAdd(gas, coldAccountAccessCost-warmStorageReadCost); overflow { + return 0, 0, ErrGasUintOverflow } - return gas, nil + stateGas += coldAccountAccessCost + warmStorageReadCost + return gas, stateGas, nil } - return gas, nil + return gas, stateGas, nil } // gasAccountCheck checks whether the first stack item (as address) is present in the access list. @@ -135,32 +156,49 @@ func gasExtCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem // - extcodehash, // - extcodesize, // - (ext) balance -func gasAccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { +func gasAccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { addr := common.Bytes20ToAddress(stack.peek().Bytes20(), evm.chainConfig.Location) // Check slot presence in the access list if !evm.StateDB.AddressInAccessList(addr) { // If the caller cannot afford the cost, this change will be rolled back evm.StateDB.AddAddressToAccessList(addr) // The warm storage read cost is already charged as constantGas - return params.ColdAccountAccessCost - params.WarmStorageReadCost, nil + // contract internal address + contractIAddr, err := contract.Address().InternalAddress() + if err != nil { + return 0, 0, err + } + contractSize := evm.StateDB.GetSize(contractIAddr) + coldAccountAccessCost := params.ColdAccountAccessCost(evm.Context.QuaiStateSize, contractSize) + warmStorageReadCost := params.WarmStorageReadCost(evm.Context.QuaiStateSize, contractSize) + stateGas := coldAccountAccessCost - warmStorageReadCost + return coldAccountAccessCost - warmStorageReadCost, stateGas, nil } - return 0, nil + return 0, 0, nil } func makeCallVariantGasCall(oldCalculator gasFunc) gasFunc { - return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { addr := common.Bytes20ToAddress(stack.Back(1).Bytes20(), evm.chainConfig.Location) // Check slot presence in the access list warmAccess := evm.StateDB.AddressInAccessList(addr) // The WarmStorageReadCost (100) is already deducted in the form of a constant cost, so // the cost to charge for cold access, if any, is Cold - Warm - coldCost := params.ColdAccountAccessCost - params.WarmStorageReadCost + // contract internal address + contractIAddr, err := contract.Address().InternalAddress() + if err != nil { + return 0, 0, err + } + contractSize := evm.StateDB.GetSize(contractIAddr) + coldAccountAccessCost := params.ColdAccountAccessCost(evm.Context.QuaiStateSize, contractSize) + warmStorageReadCost := params.WarmStorageReadCost(evm.Context.QuaiStateSize, contractSize) + coldCost := coldAccountAccessCost - warmStorageReadCost if !warmAccess { evm.StateDB.AddAddressToAccessList(addr) // Charge the remaining difference here already, to correctly calculate available // gas for call if !contract.UseGas(coldCost) { - return 0, ErrOutOfGas + return 0, 0, ErrOutOfGas } } // Now call the old calculator, which takes into account @@ -168,16 +206,17 @@ func makeCallVariantGasCall(oldCalculator gasFunc) gasFunc { // - transfer value // - memory expansion // - 63/64ths rule - gas, err := oldCalculator(evm, contract, stack, mem, memorySize) + gas, _, err := oldCalculator(evm, contract, stack, mem, memorySize) if warmAccess || err != nil { - return gas, err + return gas, 0, err } // In case of a cold access, we temporarily add the cold charge back, and also // add it to the returned gas. By adding it to the return, it will be charged // outside of this function, as part of the dynamic gas, and that will make it // also become correctly reported to tracers. contract.Gas += coldCost - return gas + coldCost, nil + contract.StateGas += coldCost + return gas + coldCost, coldCost, nil } } @@ -196,23 +235,25 @@ var ( // makeSelfdestructGasFn can create the selfdestruct dynamic gas function func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { - gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, uint64, error) { var ( gas uint64 + stateGas uint64 address = common.Bytes20ToAddress(stack.peek().Bytes20(), evm.chainConfig.Location) internalAddress, err = address.InternalAndQuaiAddress() ) if err != nil { - return 0, err + return 0, 0, err } contractAddress, err := contract.Address().InternalAndQuaiAddress() if err != nil { - return 0, err + return 0, 0, err } + storageSizeOfContract := evm.StateDB.GetSize(contractAddress) if !evm.StateDB.AddressInAccessList(address) { // If the caller cannot afford the cost, this change will be rolled back evm.StateDB.AddAddressToAccessList(address) - gas = params.ColdAccountAccessCost + gas = params.ColdAccountAccessCost(evm.Context.QuaiStateSize, storageSizeOfContract) } // if empty and transfers value if evm.StateDB.Empty(internalAddress) && evm.StateDB.GetBalance(contractAddress).Sign() != 0 { @@ -221,7 +262,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { if refundsEnabled && !evm.StateDB.HasSuicided(contractAddress) { evm.StateDB.AddRefund(params.SelfdestructRefundGas) } - return gas, nil + return gas, stateGas, nil } return gasFunc } diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 462f6fa959..6588a7b750 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -27,15 +27,16 @@ func NewEnv(cfg *Config) *vm.EVM { GasPrice: cfg.GasPrice, } blockContext := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - GetHash: cfg.GetHashFn, - Coinbase: cfg.Coinbase, - BlockNumber: cfg.BlockNumber, - Time: cfg.Time, - Difficulty: cfg.Difficulty, - GasLimit: cfg.GasLimit, - BaseFee: cfg.BaseFee, + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + GetHash: cfg.GetHashFn, + Coinbase: cfg.Coinbase, + BlockNumber: cfg.BlockNumber, + Time: cfg.Time, + Difficulty: cfg.Difficulty, + GasLimit: cfg.GasLimit, + BaseFee: cfg.BaseFee, + QuaiStateSize: cfg.QuaiStateSize, } return vm.NewEVM(blockContext, txContext, cfg.State, cfg.ChainConfig, cfg.EVMConfig) diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 23994d1f23..5099a6702b 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -35,20 +35,21 @@ import ( // Config is a basic type specifying certain configuration flags for running // the EVM. type Config struct { - ChainConfig *params.ChainConfig - Difficulty *big.Int - Origin common.Address - Coinbase common.Address - BlockNumber *big.Int - Time *big.Int - GasLimit uint64 - GasPrice *big.Int - Value *big.Int - Lock *big.Int - Debug bool - EVMConfig vm.Config - BaseFee *big.Int - Logger *logrus.Logger + ChainConfig *params.ChainConfig + Difficulty *big.Int + Origin common.Address + Coinbase common.Address + BlockNumber *big.Int + Time *big.Int + GasLimit uint64 + GasPrice *big.Int + Value *big.Int + Lock *big.Int + Debug bool + EVMConfig vm.Config + BaseFee *big.Int + QuaiStateSize *big.Int + Logger *logrus.Logger State *state.StateDB GetHashFn func(n uint64) common.Hash @@ -71,6 +72,9 @@ func setDefaults(cfg *Config) { if cfg.GasLimit == 0 { cfg.GasLimit = math.MaxUint64 } + if cfg.QuaiStateSize == nil { + cfg.QuaiStateSize = new(big.Int) + } if cfg.GasPrice == nil { cfg.GasPrice = new(big.Int) } @@ -109,7 +113,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { setDefaults(cfg) if cfg.State == nil { - cfg.State, _ = state.New(common.Hash{}, common.Hash{}, common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase(cfg.Logger)), state.NewDatabase(rawdb.NewMemoryDatabase(cfg.Logger)), state.NewDatabase(rawdb.NewMemoryDatabase(cfg.Logger)), &snapshot.Tree{}, cfg.ChainConfig.Location, cfg.Logger) + cfg.State, _ = state.New(common.Hash{}, common.Hash{}, common.Hash{}, big.NewInt(0), state.NewDatabase(rawdb.NewMemoryDatabase(cfg.Logger)), state.NewDatabase(rawdb.NewMemoryDatabase(cfg.Logger)), state.NewDatabase(rawdb.NewMemoryDatabase(cfg.Logger)), &snapshot.Tree{}, cfg.ChainConfig.Location, cfg.Logger) } var ( address = common.BytesToAddress([]byte("contract"), cfg.ChainConfig.Location) @@ -127,7 +131,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { // set the receiver's (the executing contract) code for execution. cfg.State.SetCode(internal, code) // Call the code with the given configuration. - ret, _, err := vmenv.Call( + ret, _, _, err := vmenv.Call( sender, common.BytesToAddress([]byte("contract"), cfg.ChainConfig.Location), input, @@ -140,14 +144,14 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { } // Create executes the code using the EVM create method -func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { +func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, uint64, error) { if cfg == nil { cfg = new(Config) } setDefaults(cfg) if cfg.State == nil { - cfg.State, _ = state.New(common.Hash{}, common.Hash{}, common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase(cfg.Logger)), state.NewDatabase(rawdb.NewMemoryDatabase(cfg.Logger)), state.NewDatabase(rawdb.NewMemoryDatabase(cfg.Logger)), &snapshot.Tree{}, cfg.ChainConfig.Location, cfg.Logger) + cfg.State, _ = state.New(common.Hash{}, common.Hash{}, common.Hash{}, big.NewInt(0), state.NewDatabase(rawdb.NewMemoryDatabase(cfg.Logger)), state.NewDatabase(rawdb.NewMemoryDatabase(cfg.Logger)), state.NewDatabase(rawdb.NewMemoryDatabase(cfg.Logger)), &snapshot.Tree{}, cfg.ChainConfig.Location, cfg.Logger) } var ( vmenv = NewEnv(cfg) @@ -157,13 +161,13 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules, cfg.ChainConfig.Location), nil) // Call the code with the given configuration. - code, address, leftOverGas, err := vmenv.Create( + code, address, leftOverGas, stateUsed, err := vmenv.Create( sender, input, cfg.GasLimit, cfg.Value, ) - return code, address, leftOverGas, err + return code, address, leftOverGas, stateUsed, err } // Call executes the code given by the contract's address. It will return the @@ -171,13 +175,13 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { // // Call, unlike Execute, requires a config and also requires the State field to // be set. -func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, error) { +func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, uint64, error) { setDefaults(cfg) vmenv := NewEnv(cfg) _, err := cfg.Origin.InternalAndQuaiAddress() if err != nil { - return []byte{}, 0, err + return []byte{}, 0, 0, err } statedb := cfg.State @@ -186,7 +190,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules, cfg.ChainConfig.Location), nil) // Call the code with the given configuration. - ret, leftOverGas, err := vmenv.Call( + ret, leftOverGas, stateGas, err := vmenv.Call( vm.AccountRef(cfg.Origin), address, input, @@ -194,5 +198,5 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er cfg.Value, cfg.Lock, ) - return ret, leftOverGas, err + return ret, leftOverGas, stateGas, err } diff --git a/core/worker.go b/core/worker.go index 52e4c91dda..035f9a744d 100644 --- a/core/worker.go +++ b/core/worker.go @@ -662,12 +662,14 @@ func (w *worker) makeEnv(parent *types.WorkObject, proposedWo *types.WorkObject, evmRoot := parent.EVMRoot() utxoRoot := parent.UTXORoot() etxRoot := parent.EtxSetRoot() + quaiStateSize := parent.QuaiStateSize() if w.hc.IsGenesisHash(parent.Hash()) { evmRoot = types.EmptyRootHash utxoRoot = types.EmptyRootHash etxRoot = types.EmptyRootHash + quaiStateSize = big.NewInt(0) } - state, err := w.hc.bc.processor.StateAt(evmRoot, utxoRoot, etxRoot) + state, err := w.hc.bc.processor.StateAt(evmRoot, utxoRoot, etxRoot, quaiStateSize) if err != nil { return nil, err } @@ -850,7 +852,8 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t snap := env.state.Snapshot() // retrieve the gas used int and pass in the reference to the ApplyTransaction gasUsed := env.wo.GasUsed() - receipt, quaiFees, err := ApplyTransaction(w.chainConfig, parent, *env.parentOrder, w.hc, &env.coinbase, env.gasPool, env.state, env.wo, tx, &gasUsed, *w.hc.bc.processor.GetVMConfig(), &env.etxRLimit, &env.etxPLimit, w.logger) + stateUsed := env.wo.StateUsed() + receipt, quaiFees, err := ApplyTransaction(w.chainConfig, parent, *env.parentOrder, w.hc, &env.coinbase, env.gasPool, env.state, env.wo, tx, &gasUsed, &stateUsed, *w.hc.bc.processor.GetVMConfig(), &env.etxRLimit, &env.etxPLimit, w.logger) if err != nil { w.logger.WithFields(log.Fields{ "err": err, @@ -868,6 +871,7 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t // This extra step is needed because previously the GasUsed was a public method and direct update of the value // was possible. env.wo.Header().SetGasUsed(gasUsed) + env.wo.Header().SetStateUsed(stateUsed) env.txs = append(env.txs, tx) env.quaiFees = new(big.Int).Add(env.quaiFees, quaiFees) env.receipts = append(env.receipts, receipt) @@ -1246,6 +1250,7 @@ func (w *worker) prepareWork(genParams *generateParams, wo *types.WorkObject) (* if nodeCtx == common.ZONE_CTX && w.hc.ProcessingState() { newWo.Header().SetExtra(w.extra) newWo.Header().SetBaseFee(misc.CalcBaseFee(w.chainConfig, parent)) + newWo.Header().SetStateLimit(misc.CalcStateLimit(parent, w.config.GasCeil)) if w.isRunning() { if w.coinbase.Equal(common.Zero) { w.logger.Error("Refusing to mine without etherbase") diff --git a/internal/quaiapi/quai_api.go b/internal/quaiapi/quai_api.go index 8e5be29b36..decf0318c8 100644 --- a/internal/quaiapi/quai_api.go +++ b/internal/quaiapi/quai_api.go @@ -19,6 +19,7 @@ package quaiapi import ( "context" "errors" + "fmt" "math/big" "time" @@ -490,6 +491,24 @@ func (s *PublicBlockChainQuaiAPI) EstimateGas(ctx context.Context, args Transact } } +// GetContractSize gives the size of the contract at the block hash or number +func (s *PublicBlockChainQuaiAPI) GetContractSize(ctx context.Context, address common.AddressBytes, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { + addr := common.Bytes20ToAddress(address, s.b.NodeLocation()) + if addr.IsInQuaiLedgerScope() { + 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.GetSize(internal)), state.Error() + } else { + return nil, errors.New("getContractSize cannot be called on a Qi Address") + } +} + // BaseFee returns the base fee for a tx to be included in the next block. // If txType is set to "true" returns the Quai base fee in units of Wei. // If txType is set to "false" returns the Qi base fee in units of Qit. @@ -910,7 +929,7 @@ func (s *PublicBlockChainQuaiAPI) CalcOrder(ctx context.Context, raw hexutil.Byt } _, order, err := s.b.CalcOrder(woHeader) if err != nil { - return 0, errors.New("cannot calculate prime terminus order") + return 0, fmt.Errorf("cannot calculate prime terminus order: %v", err) } return hexutil.Uint(order), nil } diff --git a/params/protocol_params.go b/params/protocol_params.go index 87d9af678d..de6f6bdf7f 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -17,6 +17,7 @@ package params import ( + "math" "math/big" "github.com/dominant-strategies/go-quai/common" @@ -31,10 +32,14 @@ const ( MinGasLimit uint64 = 40000000 // Minimum the gas limit may ever be. GenesisGasLimit uint64 = 5000000 // Gas limit of the Genesis block. + StateCeil uint64 = 40000000 // Maximum the StateCeil may ever be + StateLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations. + PercentStateUsedThreshold uint64 = 90 // Percent Gas used threshold at which the gas limit adjusts + + EtxStateUsed uint64 = 10000 // state used by a simple etx + MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis. - ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction. CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero. - CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior. TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions. TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. @@ -52,30 +57,6 @@ const ( Sha3Gas uint64 = 30 // Once per SHA3 operation. Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data. - SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. - SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. - - NetSstoreNoopGas uint64 = 200 // Once per SSTORE operation if the value doesn't change. - NetSstoreInitGas uint64 = 20000 // Once per SSTORE operation from clean zero. - NetSstoreCleanGas uint64 = 5000 // Once per SSTORE operation from clean non-zero. - NetSstoreDirtyGas uint64 = 200 // Once per SSTORE operation from dirty. - - NetSstoreClearRefund uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot - NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value - NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value - - SstoreSentryGas uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed - SstoreSetGas uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero - SstoreResetGas uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else - - ColdAccountAccessCost = uint64(2600) // COLD_ACCOUNT_ACCESS_COST - ColdSloadCost = uint64(2100) // COLD_SLOAD_COST - WarmStorageReadCost = uint64(100) // WARM_STORAGE_READ_COST - - // SSTORE_CLEARS_SCHEDULE is defined as SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST - // Which becomes: 5000 - 2100 + 1900 = 4800 - SstoreClearsScheduleRefund uint64 = SstoreResetGas - ColdSloadCost + TxAccessListStorageKeyGas // Once per SSTORE operation for clearing an originally existing storage slot - JumpdestGas uint64 = 1 // Once per JUMPDEST operation. EpochDuration uint64 = 30000 // Duration between proof-of-work epochs. @@ -97,11 +78,7 @@ const ( TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in access list // These have been changed during the course of the chain - CallGas uint64 = 700 // Static portion of gas for CALL-derivates - BalanceGas uint64 = 700 // The cost of a BALANCE operation - ExtcodeSizeGas uint64 = 700 // Cost of EXTCODESIZE - SloadGas uint64 = 800 - ExtcodeHashGas uint64 = 700 // Cost of EXTCODEHASH + SloadGas uint64 = 800 // This is only used in the Qi tx processing SelfdestructGas uint64 = 5000 // Cost of SELFDESTRUCT // EXP has a dynamic portion depending on the size of the exponent @@ -119,6 +96,7 @@ const ( ElasticityMultiplier = 2 // Bounds the maximum gas limit a block may have. InitialBaseFee = 1 * GWei // Initial base fee for blocks. MaxBaseFee = 100 * GWei // Maximum base fee for blocks. + InitialStateLimit = 5000000 // Initial state fee for blocks. MaxCodeSize = 24576 // Maximum bytecode to permit for a contract @@ -218,3 +196,53 @@ func RegionEntropyTarget(expansionNum uint8) *big.Int { timeFactor := int64(max(numZones, 2)) return new(big.Int).Mul(big.NewInt(timeFactor), new(big.Int).SetUint64(numZones)) } + +// Gas calculation functions + +func SstoreSetGas(stateSize, contractSize *big.Int) uint64 { + return CalculateGasWithStateScaling(stateSize, contractSize, 20000) // Once per SSTORE operation from clean zero to non-zero +} + +func SstoreSentryGas(stateSize, contractSize *big.Int) uint64 { + return CalculateGasWithStateScaling(stateSize, contractSize, 2300) // Minimum gas required to be present for an SSTORE call, not consumed +} + +func ColdAccountAccessCost(stateSize, contractSize *big.Int) uint64 { + return CalculateGasWithStateScaling(stateSize, contractSize, 2600) // COLD_ACCOUNT_ACCESS_COST +} + +func WarmStorageReadCost(stateSize, contractSize *big.Int) uint64 { + return CalculateGasWithStateScaling(stateSize, contractSize, 100) // WARM_STORAGE_READ_COST +} + +func ColdSloadCost(stateSize, contractSize *big.Int) uint64 { + return CalculateGasWithStateScaling(stateSize, contractSize, 2100) // COLD_SLOAD_COST +} + +func SstoreResetGas(stateSize, contractSize *big.Int) uint64 { + return CalculateGasWithStateScaling(stateSize, contractSize, 5000) // Once per SSTORE operation from clean non-zero to something else +} + +func CallNewAccountGas(stateSize *big.Int) uint64 { + return CalculateGasWithStateScaling(stateSize, big.NewInt(0), 25000) // Paid for CALL when the destination address didn't exist prior. +} + +// SSTORE_CLEARS_SCHEDULE is defined as SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST +// Which becomes: 5000 - 2100 + 1900 = 4800 +func SstoreClearsScheduleRefund(stateSize, contractSize *big.Int) uint64 { + return SstoreResetGas(stateSize, contractSize) - ColdSloadCost(stateSize, contractSize) + TxAccessListStorageKeyGas // Once per SSTORE operation for clearing an originally existing storage slot +} + +func CalculateGasWithStateScaling(stateSize, contractSize *big.Int, baseRate uint64) uint64 { + var stateSizeFloat, contractSizeFloat, scalingFactor float64 + if stateSize.Sign() != 0 { + stateSizeFloat, _ = stateSize.Float64() + scalingFactor += math.Log(stateSizeFloat) + } + if contractSize.Sign() != 0 { + contractSizeFloat, _ = contractSize.Float64() + scalingFactor += math.Log(contractSizeFloat) + } + // If we can assume that the gas price constants is correct for level 7 trie + return (uint64(scalingFactor) * baseRate) / 7 +} diff --git a/quai/api.go b/quai/api.go index c99ab8205d..b73e3fdbde 100644 --- a/quai/api.go +++ b/quai/api.go @@ -260,7 +260,7 @@ func (api *PublicDebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error if block == nil { return state.Dump{}, fmt.Errorf("block #%d not found", blockNr) } - stateDb, err := api.quai.core.StateAt(block.EVMRoot(), block.UTXORoot(), block.EtxSetRoot()) + stateDb, err := api.quai.core.StateAt(block.EVMRoot(), block.UTXORoot(), block.EtxSetRoot(), block.QuaiStateSize()) if err != nil { return state.Dump{}, err } @@ -311,7 +311,7 @@ func (api *PublicDebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, sta if block == nil { return state.IteratorDump{}, fmt.Errorf("block #%d not found", number) } - stateDb, err = api.quai.core.StateAt(block.EVMRoot(), block.UTXORoot(), block.EtxSetRoot()) + stateDb, err = api.quai.core.StateAt(block.EVMRoot(), block.UTXORoot(), block.EtxSetRoot(), block.QuaiStateSize()) if err != nil { return state.IteratorDump{}, err } @@ -321,7 +321,7 @@ func (api *PublicDebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, sta if block == nil { return state.IteratorDump{}, fmt.Errorf("block %s not found", hash.Hex()) } - stateDb, err = api.quai.core.StateAt(block.EVMRoot(), block.UTXORoot(), block.EtxSetRoot()) + stateDb, err = api.quai.core.StateAt(block.EVMRoot(), block.UTXORoot(), block.EtxSetRoot(), block.QuaiStateSize()) if err != nil { return state.IteratorDump{}, err } diff --git a/quai/api_backend.go b/quai/api_backend.go index f10f0a25d1..dc19391bf8 100644 --- a/quai/api_backend.go +++ b/quai/api_backend.go @@ -186,7 +186,7 @@ func (b *QuaiAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc. if header == nil { return nil, nil, errors.New("header not found") } - stateDb, err := b.quai.Core().StateAt(header.EVMRoot(), header.UTXORoot(), header.EtxSetRoot()) + stateDb, err := b.quai.Core().StateAt(header.EVMRoot(), header.UTXORoot(), header.EtxSetRoot(), header.QuaiStateSize()) return stateDb, header, err } @@ -209,7 +209,7 @@ func (b *QuaiAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, block if blockNrOrHash.RequireCanonical && b.quai.core.GetCanonicalHash(header.NumberU64(b.NodeCtx())) != hash { return nil, nil, errors.New("hash is not currently canonical") } - stateDb, err := b.quai.Core().StateAt(header.EVMRoot(), header.UTXORoot(), header.EtxSetRoot()) + stateDb, err := b.quai.Core().StateAt(header.EVMRoot(), header.UTXORoot(), header.EtxSetRoot(), header.QuaiStateSize()) return stateDb, header, err } return nil, nil, errors.New("invalid arguments; neither block nor hash specified") diff --git a/quai/api_test.go b/quai/api_test.go index 711dd61668..b8f889dfd9 100644 --- a/quai/api_test.go +++ b/quai/api_test.go @@ -68,7 +68,7 @@ func TestAccountRange(t *testing.T) { t.Parallel() var ( statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(log.Global), nil) - state, _ = state.New(common.Hash{}, common.Hash{}, common.Hash{}, statedb, nil, nil, nil, common.Location{0, 0}, log.Global) + state, _ = state.New(common.Hash{}, common.Hash{}, common.Hash{}, big.NewInt(0), statedb, nil, nil, nil, common.Location{0, 0}, log.Global) addrs = [AccountRangeMaxResults * 2]common.InternalAddress{} m = map[common.AddressBytes]bool{} ) @@ -140,7 +140,7 @@ func TestEmptyAccountRange(t *testing.T) { t.Parallel() var ( statedb = state.NewDatabase(rawdb.NewMemoryDatabase(log.Global)) - st, _ = state.New(common.Hash{}, common.Hash{}, common.Hash{}, statedb, nil, nil, nil, common.Location{0, 0}, log.Global) + st, _ = state.New(common.Hash{}, common.Hash{}, common.Hash{}, big.NewInt(0), statedb, nil, nil, nil, common.Location{0, 0}, log.Global) ) st.Commit(true) st.IntermediateRoot(true) @@ -163,7 +163,7 @@ func TestStorageRangeAt(t *testing.T) { t.Parallel() // Create a state where account 0x010000... has a few storage entries. var ( - state, _ = state.New(common.Hash{}, common.Hash{}, common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase(log.Global)), nil, nil, nil, common.Location{0, 0}, log.Global) + state, _ = state.New(common.Hash{}, common.Hash{}, common.Hash{}, big.NewInt(0), state.NewDatabase(rawdb.NewMemoryDatabase(log.Global)), nil, nil, nil, common.Location{0, 0}, log.Global) addr = common.InternalAddress{0x01} keys = []common.Hash{ // hashes of Keys of storage common.HexToHash("340dd630ad21bf010b4e676dbfa9ba9a02175262d1fa356232cfde6cb5b47ef2"), diff --git a/quaiclient/ethclient/ethclient.go b/quaiclient/ethclient/ethclient.go index 13f3d2005c..8562cf5c58 100644 --- a/quaiclient/ethclient/ethclient.go +++ b/quaiclient/ethclient/ethclient.go @@ -338,6 +338,12 @@ func (ec *Client) BalanceAt(ctx context.Context, account common.MixedcaseAddress return (*big.Int)(&result), err } +func (ec *Client) ContractSizeAt(ctx context.Context, account common.MixedcaseAddress, blockNumber *big.Int) (*big.Int, error) { + var result hexutil.Big + err := ec.c.CallContext(ctx, &result, "quai_getContractSize", account.Original(), toBlockNumArg(blockNumber)) + return (*big.Int)(&result), err +} + func (ec *Client) GetOutpointsByAddress(ctx context.Context, address common.MixedcaseAddress) (map[string]*types.OutpointAndDenomination, error) { var outpoints map[string]*types.OutpointAndDenomination err := ec.c.CallContext(ctx, &outpoints, "quai_getOutpointsByAddress", address.Original())