github.com/onflow/flow-go@v0.33.17/fvm/evm/emulator/state/base.go (about) 1 package state 2 3 import ( 4 "fmt" 5 "math/big" 6 7 gethCommon "github.com/ethereum/go-ethereum/common" 8 gethTypes "github.com/ethereum/go-ethereum/core/types" 9 "github.com/onflow/atree" 10 11 "github.com/onflow/flow-go/fvm/evm/types" 12 "github.com/onflow/flow-go/model/flow" 13 ) 14 15 const ( 16 // AccountsStorageIDKey is the path where we store the collection ID for accounts 17 AccountsStorageIDKey = "AccountsStorageIDKey" 18 // CodesStorageIDKey is the path where we store the collection ID for codes 19 CodesStorageIDKey = "CodesStorageIDKey" 20 ) 21 22 // BaseView implements a types.BaseView 23 // it acts as the base layer of state queries for the stateDB 24 // it stores accounts, codes and storage slots. 25 // 26 // under the hood it uses a set of collections, 27 // one for account's meta data, one for codes 28 // and one for each of account storage space. 29 type BaseView struct { 30 rootAddress flow.Address 31 ledger atree.Ledger 32 collectionProvider *CollectionProvider 33 34 // collections 35 accounts *Collection 36 codes *Collection 37 slots map[gethCommon.Address]*Collection 38 39 // cached values 40 cachedAccounts map[gethCommon.Address]*Account 41 cachedCodes map[gethCommon.Address][]byte 42 cachedSlots map[types.SlotAddress]gethCommon.Hash 43 44 // flags 45 accountSetupOnCommit bool 46 codeSetupOnCommit bool 47 } 48 49 var _ types.BaseView = &BaseView{} 50 51 // NewBaseView constructs a new base view 52 func NewBaseView(ledger atree.Ledger, rootAddress flow.Address) (*BaseView, error) { 53 cp, err := NewCollectionProvider(atree.Address(rootAddress), ledger) 54 if err != nil { 55 return nil, err 56 } 57 58 view := &BaseView{ 59 ledger: ledger, 60 rootAddress: rootAddress, 61 collectionProvider: cp, 62 63 slots: make(map[gethCommon.Address]*Collection), 64 65 cachedAccounts: make(map[gethCommon.Address]*Account), 66 cachedCodes: make(map[gethCommon.Address][]byte), 67 cachedSlots: make(map[types.SlotAddress]gethCommon.Hash), 68 } 69 70 // fetch the account collection, if not exist, create one 71 view.accounts, view.accountSetupOnCommit, err = view.fetchOrCreateCollection(AccountsStorageIDKey) 72 if err != nil { 73 return nil, err 74 } 75 76 // fetch the code collection, if not exist, create one 77 view.codes, view.codeSetupOnCommit, err = view.fetchOrCreateCollection(CodesStorageIDKey) 78 if err != nil { 79 return nil, err 80 } 81 82 return view, nil 83 } 84 85 // Exist returns true if the address exist in the state 86 func (v *BaseView) Exist(addr gethCommon.Address) (bool, error) { 87 acc, err := v.getAccount(addr) 88 return acc != nil, err 89 } 90 91 // IsCreated returns true if the address has been created in the context of this transaction 92 func (v *BaseView) IsCreated(gethCommon.Address) bool { 93 return false 94 } 95 96 // HasSelfDestructed returns true if an address is flagged for destruction at the end of transaction 97 func (v *BaseView) HasSelfDestructed(gethCommon.Address) (bool, *big.Int) { 98 return false, new(big.Int) 99 } 100 101 // GetBalance returns the balance of an address 102 // 103 // for non-existent accounts it returns a balance of zero 104 func (v *BaseView) GetBalance(addr gethCommon.Address) (*big.Int, error) { 105 acc, err := v.getAccount(addr) 106 bal := big.NewInt(0) 107 if acc != nil { 108 bal = acc.Balance 109 } 110 return bal, err 111 } 112 113 // GetNonce returns the nonce of an address 114 // 115 // for non-existent accounts it returns zero 116 func (v *BaseView) GetNonce(addr gethCommon.Address) (uint64, error) { 117 acc, err := v.getAccount(addr) 118 nonce := uint64(0) 119 if acc != nil { 120 nonce = acc.Nonce 121 } 122 return nonce, err 123 } 124 125 // GetCode returns the code of an address 126 // 127 // for non-existent accounts or accounts without a code (e.g. EOAs) it returns nil 128 func (v *BaseView) GetCode(addr gethCommon.Address) ([]byte, error) { 129 return v.getCode(addr) 130 } 131 132 // GetCodeHash returns the code hash of an address 133 // 134 // for non-existent accounts or accounts without a code (e.g. EOAs) it returns default empty 135 // hash value (gethTypes.EmptyCodeHash) 136 func (v *BaseView) GetCodeHash(addr gethCommon.Address) (gethCommon.Hash, error) { 137 acc, err := v.getAccount(addr) 138 codeHash := gethTypes.EmptyCodeHash 139 if acc != nil { 140 codeHash = acc.CodeHash 141 } 142 return codeHash, err 143 } 144 145 // GetCodeSize returns the code size of an address 146 // 147 // for non-existent accounts or accounts without a code (e.g. EOAs) it returns zero 148 func (v *BaseView) GetCodeSize(addr gethCommon.Address) (int, error) { 149 code, err := v.GetCode(addr) 150 return len(code), err 151 } 152 153 // GetState returns values for a slot in the main storage 154 // 155 // for non-existent slots it returns the default empty hash value (gethTypes.EmptyCodeHash) 156 func (v *BaseView) GetState(sk types.SlotAddress) (gethCommon.Hash, error) { 157 return v.getSlot(sk) 158 } 159 160 // UpdateSlot updates the value for a slot 161 func (v *BaseView) UpdateSlot(sk types.SlotAddress, value gethCommon.Hash) error { 162 return v.storeSlot(sk, value) 163 } 164 165 // GetRefund returns the total amount of (gas) refund 166 // 167 // this method returns the value of zero 168 func (v *BaseView) GetRefund() uint64 { 169 return 0 170 } 171 172 // GetTransientState returns values for an slot transient storage 173 // 174 // transient storage is not a functionality for the base view so it always 175 // returns the default value for non-existent slots 176 func (v *BaseView) GetTransientState(types.SlotAddress) gethCommon.Hash { 177 return gethCommon.Hash{} 178 } 179 180 // AddressInAccessList checks if an address is in the access list 181 // 182 // access list control is not a functionality of the base view 183 // it always returns false 184 func (v *BaseView) AddressInAccessList(gethCommon.Address) bool { 185 return false 186 } 187 188 // SlotInAccessList checks if a slot is in the access list 189 // 190 // access list control is not a functionality of the base view 191 // it always returns false 192 func (v *BaseView) SlotInAccessList(types.SlotAddress) (addressOk bool, slotOk bool) { 193 return false, false 194 } 195 196 // CreateAccount creates a new account 197 func (v *BaseView) CreateAccount( 198 addr gethCommon.Address, 199 balance *big.Int, 200 nonce uint64, 201 code []byte, 202 codeHash gethCommon.Hash, 203 ) error { 204 var colID []byte 205 // if is an smart contract account 206 if len(code) > 0 { 207 err := v.storeCode(addr, code) 208 if err != nil { 209 return err 210 } 211 } 212 213 // create a new account and store it 214 acc := NewAccount(addr, balance, nonce, codeHash, colID) 215 216 // no need to update the cache , storeAccount would update the cache 217 return v.storeAccount(acc) 218 } 219 220 // UpdateAccount updates an account's meta data 221 func (v *BaseView) UpdateAccount( 222 addr gethCommon.Address, 223 balance *big.Int, 224 nonce uint64, 225 code []byte, 226 codeHash gethCommon.Hash, 227 ) error { 228 acc, err := v.getAccount(addr) 229 if err != nil { 230 return err 231 } 232 // if update is called on a non existing account 233 // we gracefully call the create account 234 // TODO: but we might need to revisit this action in the future 235 if acc == nil { 236 return v.CreateAccount(addr, balance, nonce, code, codeHash) 237 } 238 // if it has a code change 239 if codeHash != acc.CodeHash { 240 err := v.storeCode(addr, code) 241 if err != nil { 242 return err 243 } 244 // TODO: maybe purge the state in the future as well 245 // currently the behaviour of stateDB doesn't purge the data 246 // We don't need to check if the code is empty and we purge the state 247 // this is not possible right now. 248 } 249 newAcc := NewAccount(addr, balance, nonce, codeHash, acc.CollectionID) 250 // no need to update the cache , storeAccount would update the cache 251 return v.storeAccount(newAcc) 252 } 253 254 // DeleteAccount deletes an account's meta data, code, and 255 // storage slots associated with that address 256 func (v *BaseView) DeleteAccount(addr gethCommon.Address) error { 257 // 1. check account exists 258 acc, err := v.getAccount(addr) 259 if err != nil { 260 return err 261 } 262 if acc == nil { 263 return fmt.Errorf("account doesn't exist to be deleted") 264 } 265 266 // 2. update the cache 267 delete(v.cachedAccounts, addr) 268 269 // 3. collections 270 err = v.accounts.Remove(addr.Bytes()) 271 if err != nil { 272 return err 273 } 274 275 // 4. remove the code 276 if acc.HasCode() { 277 err = v.storeCode(addr, nil) 278 if err != nil { 279 return err 280 } 281 } 282 283 // 5. remove storage slots 284 if len(acc.CollectionID) > 0 { 285 col, found := v.slots[addr] 286 if !found { 287 col, err = v.collectionProvider.CollectionByID(acc.CollectionID) 288 if err != nil { 289 return err 290 } 291 } 292 // delete all slots related to this account (eip-6780) 293 keys, err := col.Destroy() 294 if err != nil { 295 return err 296 } 297 298 delete(v.slots, addr) 299 300 for _, key := range keys { 301 delete(v.cachedSlots, types.SlotAddress{ 302 Address: addr, 303 Key: gethCommon.BytesToHash(key), 304 }) 305 } 306 } 307 return nil 308 } 309 310 // Commit commits the changes to the underlying storage layers 311 func (v *BaseView) Commit() error { 312 // commit collection changes 313 err := v.collectionProvider.Commit() 314 if err != nil { 315 return err 316 } 317 318 // if this is the first time we are setting up an 319 // account collection, store its collection id. 320 if v.accountSetupOnCommit { 321 err = v.ledger.SetValue(v.rootAddress[:], []byte(AccountsStorageIDKey), v.accounts.CollectionID()) 322 if err != nil { 323 return err 324 } 325 v.accountSetupOnCommit = false 326 327 } 328 329 // if this is the first time we are setting up an 330 // code collection, store its collection id. 331 if v.codeSetupOnCommit { 332 err = v.ledger.SetValue(v.rootAddress[:], []byte(CodesStorageIDKey), v.codes.CollectionID()) 333 if err != nil { 334 return err 335 } 336 v.codeSetupOnCommit = false 337 } 338 return nil 339 } 340 341 func (v *BaseView) fetchOrCreateCollection(path string) (collection *Collection, created bool, error error) { 342 collectionID, err := v.ledger.GetValue(v.rootAddress[:], []byte(path)) 343 if err != nil { 344 return nil, false, err 345 } 346 if len(collectionID) == 0 { 347 collection, err = v.collectionProvider.NewCollection() 348 return collection, true, err 349 } 350 collection, err = v.collectionProvider.CollectionByID(collectionID) 351 return collection, false, err 352 } 353 354 func (v *BaseView) getAccount(addr gethCommon.Address) (*Account, error) { 355 // check cached accounts first 356 acc, found := v.cachedAccounts[addr] 357 if found { 358 return acc, nil 359 } 360 361 // then collect it from the account collection 362 data, err := v.accounts.Get(addr.Bytes()) 363 if err != nil { 364 return nil, err 365 } 366 // decode it 367 acc, err = DecodeAccount(data) 368 if err != nil { 369 return nil, err 370 } 371 // cache it 372 if acc != nil { 373 v.cachedAccounts[addr] = acc 374 } 375 return acc, nil 376 } 377 378 func (v *BaseView) storeAccount(acc *Account) error { 379 data, err := acc.Encode() 380 if err != nil { 381 return err 382 } 383 // update the cache 384 v.cachedAccounts[acc.Address] = acc 385 return v.accounts.Set(acc.Address.Bytes(), data) 386 } 387 388 func (v *BaseView) getCode(addr gethCommon.Address) ([]byte, error) { 389 // check the cache first 390 code, found := v.cachedCodes[addr] 391 if found { 392 return code, nil 393 } 394 // check if account exist in cache and has codeHash 395 acc, found := v.cachedAccounts[addr] 396 if found && !acc.HasCode() { 397 return nil, nil 398 } 399 // then collect it from the code collection 400 code, err := v.codes.Get(addr.Bytes()) 401 if err != nil { 402 return nil, err 403 } 404 if code != nil { 405 v.cachedCodes[addr] = code 406 } 407 return code, nil 408 } 409 410 func (v *BaseView) storeCode(addr gethCommon.Address, code []byte) error { 411 if len(code) == 0 { 412 delete(v.cachedCodes, addr) 413 return v.codes.Remove(addr.Bytes()) 414 } 415 v.cachedCodes[addr] = code 416 return v.codes.Set(addr.Bytes(), code) 417 } 418 419 func (v *BaseView) getSlot(sk types.SlotAddress) (gethCommon.Hash, error) { 420 value, found := v.cachedSlots[sk] 421 if found { 422 return value, nil 423 } 424 425 acc, err := v.getAccount(sk.Address) 426 if err != nil { 427 return gethCommon.Hash{}, err 428 } 429 if acc == nil || len(acc.CollectionID) == 0 { 430 return gethCommon.Hash{}, nil 431 } 432 433 col, err := v.getSlotCollection(acc) 434 if err != nil { 435 return gethCommon.Hash{}, err 436 } 437 438 val, err := col.Get(sk.Key.Bytes()) 439 if err != nil { 440 return gethCommon.Hash{}, err 441 } 442 value = gethCommon.BytesToHash(val) 443 v.cachedSlots[sk] = value 444 return value, nil 445 } 446 447 func (v *BaseView) storeSlot(sk types.SlotAddress, data gethCommon.Hash) error { 448 acc, err := v.getAccount(sk.Address) 449 if err != nil { 450 return err 451 } 452 if acc == nil { 453 return fmt.Errorf("slot belongs to a non-existing account") 454 } 455 if !acc.HasCode() { 456 return fmt.Errorf("slot belongs to a non-smart contract account") 457 } 458 col, err := v.getSlotCollection(acc) 459 if err != nil { 460 return err 461 } 462 463 emptyValue := gethCommon.Hash{} 464 if data == emptyValue { 465 delete(v.cachedSlots, sk) 466 return col.Remove(sk.Key.Bytes()) 467 } 468 v.cachedSlots[sk] = data 469 return col.Set(sk.Key.Bytes(), data.Bytes()) 470 } 471 472 func (v *BaseView) getSlotCollection(acc *Account) (*Collection, error) { 473 var err error 474 475 if len(acc.CollectionID) == 0 { 476 // create a new collection for slots 477 col, err := v.collectionProvider.NewCollection() 478 if err != nil { 479 return nil, err 480 } 481 // cache collection 482 v.slots[acc.Address] = col 483 // update account's collection ID 484 acc.CollectionID = col.CollectionID() 485 err = v.storeAccount(acc) 486 if err != nil { 487 return nil, err 488 } 489 return col, nil 490 } 491 492 col, found := v.slots[acc.Address] 493 if !found { 494 col, err = v.collectionProvider.CollectionByID(acc.CollectionID) 495 if err != nil { 496 return nil, err 497 } 498 v.slots[acc.Address] = col 499 } 500 return col, nil 501 }