package state import ( "errors" "fmt" "math/big" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/metrics" ) type proofList [][]byte func (n *proofList) Put(key []byte, value []byte) error { *n = append(*n, value) return nil } func (n *proofList) Delete(key []byte) error { panic("not supported") } // StateDB structs within the ethereum protocol are used to store anything // within the merkle trie. StateDBs take care of caching and storing // nested states. It's the general query interface to retrieve: // * Contracts // * Accounts // // This implementation is read-only and performs no journaling, prefetching, or metrics tracking. type StateDB struct { db Database trie Trie hasher crypto.KeccakState // This map holds 'live' objects, which will get modified while processing a state transition. stateObjects map[common.Address]*stateObject // DB error. // State objects are used by the consensus core and VM which are // unable to deal with database-level errors. Any error that occurs // during a database read is memoized here and will eventually be returned // by StateDB.Commit. dbErr error preimages map[common.Hash][]byte // Measurements gathered during execution for debugging purposes AccountReads time.Duration StorageReads time.Duration } // New creates a new state from a given trie. func New(root common.Hash, db Database) (*StateDB, error) { tr, err := db.OpenTrie(root) if err != nil { return nil, err } sdb := &StateDB{ db: db, trie: tr, stateObjects: make(map[common.Address]*stateObject), preimages: make(map[common.Hash][]byte), hasher: crypto.NewKeccakState(), } return sdb, nil } // setError remembers the first non-nil error it is called with. func (s *StateDB) setError(err error) { if s.dbErr == nil { s.dbErr = err } } func (s *StateDB) Error() error { return s.dbErr } func (s *StateDB) AddLog(log *types.Log) { panic("unsupported") } // AddPreimage records a SHA3 preimage seen by the VM. func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) { if _, ok := s.preimages[hash]; !ok { pi := make([]byte, len(preimage)) copy(pi, preimage) s.preimages[hash] = pi } } // Preimages returns a list of SHA3 preimages that have been submitted. func (s *StateDB) Preimages() map[common.Hash][]byte { return s.preimages } // AddRefund adds gas to the refund counter func (s *StateDB) AddRefund(gas uint64) { panic("unsupported") } // SubRefund removes gas from the refund counter. // This method will panic if the refund counter goes below zero func (s *StateDB) SubRefund(gas uint64) { panic("unsupported") } // 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.Address) bool { return s.getStateObject(addr) != nil } // Empty returns whether the state object is either non-existent // or empty according to the EIP161 specification (balance = nonce = code = 0) func (s *StateDB) Empty(addr common.Address) bool { so := s.getStateObject(addr) return so == nil || so.empty() } // GetBalance retrieves the balance from the given address or 0 if object not found func (s *StateDB) GetBalance(addr common.Address) *big.Int { stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.Balance() } return common.Big0 } func (s *StateDB) GetNonce(addr common.Address) uint64 { stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.Nonce() } return 0 } func (s *StateDB) GetCode(addr common.Address) []byte { stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.Code(s.db) } return nil } func (s *StateDB) GetCodeSize(addr common.Address) int { stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.CodeSize(s.db) } return 0 } func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { stateObject := s.getStateObject(addr) if stateObject == nil { return common.Hash{} } return common.BytesToHash(stateObject.CodeHash()) } // GetState retrieves a value from the given account's storage trie. func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.GetState(s.db, hash) } return common.Hash{} } // GetProof returns the Merkle proof for a given account. func (s *StateDB) GetProof(addr common.Address) ([][]byte, error) { return s.GetProofByHash(crypto.Keccak256Hash(addr.Bytes())) } // GetProofByHash returns the Merkle proof for a given account. func (s *StateDB) GetProofByHash(addrHash common.Hash) ([][]byte, error) { var proof proofList err := s.trie.Prove(addrHash[:], 0, &proof) return proof, err } // GetStorageProof returns the Merkle proof for given storage slot. func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) { var proof proofList trie := s.StorageTrie(a) if trie == nil { return proof, errors.New("storage trie for requested address does not exist") } err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof) return proof, err } // GetCommittedState retrieves a value from the given account's committed storage trie. func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { return s.GetState(addr, hash) } // Database retrieves the low level database supporting the lower level trie ops. func (s *StateDB) Database() Database { return s.db } // StorageTrie returns the storage trie of an account. // The return value is a copy and is nil for non-existent accounts. func (s *StateDB) StorageTrie(addr common.Address) Trie { stateObject := s.getStateObject(addr) if stateObject == nil { return nil } return stateObject.getTrie(s.db) } func (s *StateDB) HasSuicided(addr common.Address) bool { return false } /* * SETTERS */ // AddBalance adds amount to the account associated with addr. func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) { panic("unsupported") } // SubBalance subtracts amount from the account associated with addr. func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) { panic("unsupported") } func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) { panic("unsupported") } func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { panic("unsupported") } func (s *StateDB) SetCode(addr common.Address, code []byte) { panic("unsupported") } func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { panic("unsupported") } // SetStorage replaces the entire storage for the specified account with given // storage. This function should only be used for debugging. func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) { panic("unsupported") } // Suicide marks the given account as suicided. // This clears the account balance. // // The account's state object is still available until the state is committed, // getStateObject will return a non-nil account after Suicide. func (s *StateDB) Suicide(addr common.Address) bool { panic("unsupported") return false } // // Setting, updating & deleting state object methods. // // getStateObject retrieves a state object given by the address, returning nil if // the object is not found or was deleted in this execution context. func (s *StateDB) getStateObject(addr common.Address) *stateObject { // Prefer live objects if any is available if obj := s.stateObjects[addr]; obj != nil { return obj } // If no live objects are available, load from the database start := time.Now() var err error data, err := s.trie.TryGetAccount(addr.Bytes()) if metrics.EnabledExpensive { s.AccountReads += time.Since(start) } if err != nil { s.setError(fmt.Errorf("getStateObject (%x) error: %w", addr.Bytes(), err)) return nil } if data == nil { return nil } // Insert into the live set obj := newObject(s, addr, *data) s.setStateObject(obj) return obj } func (s *StateDB) setStateObject(object *stateObject) { s.stateObjects[object.Address()] = object } // CreateAccount explicitly creates a state object. If a state object with the address // already exists the balance is carried over to the new account. // // CreateAccount is called during the EVM CREATE operation. The situation might arise that // a contract does the following: // // 1. sends funds to sha(account ++ (nonce + 1)) // 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) // // Carrying over the balance ensures that Ether doesn't disappear. func (s *StateDB) CreateAccount(addr common.Address) { panic("unsupported") } func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error { return nil } // Snapshot returns an identifier for the current revision of the state. func (s *StateDB) Snapshot() int { return 0 } // RevertToSnapshot reverts all state changes made since the given revision. func (s *StateDB) RevertToSnapshot(revid int) { panic("unsupported") } // GetRefund returns the current value of the refund counter. func (s *StateDB) GetRefund() uint64 { panic("unsupported") return 0 } // PrepareAccessList handles the preparatory steps for executing a state transition with // regards to both EIP-2929 and EIP-2930: // // - Add sender to access list (2929) // - Add destination to access list (2929) // - Add precompiles to access list (2929) // - Add the contents of the optional tx access list (2930) // // This method should only be called if Berlin/2929+2930 is applicable at the current number. func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { panic("unsupported") } // AddAddressToAccessList adds the given address to the access list func (s *StateDB) AddAddressToAccessList(addr common.Address) { panic("unsupported") } // AddSlotToAccessList adds the given (address, slot)-tuple to the access list func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { panic("unsupported") } // AddressInAccessList returns true if the given address is in the access list. func (s *StateDB) AddressInAccessList(addr common.Address) bool { return false } // SlotInAccessList returns true if the given (address, slot)-tuple is in the access list. func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { return }