github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/core/state/state_object.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package state 18 19 import ( 20 "bytes" 21 "fmt" 22 "io" 23 "math/big" 24 25 "github.com/intfoundation/intchain/common" 26 "github.com/intfoundation/intchain/crypto" 27 "github.com/intfoundation/intchain/rlp" 28 ) 29 30 var emptyCodeHash = crypto.Keccak256(nil) 31 32 type Code []byte 33 34 func (self Code) String() string { 35 return string(self) //strings.Join(Disassemble(self), " ") 36 } 37 38 type Storage map[common.Hash]common.Hash 39 40 func (self Storage) String() (str string) { 41 for key, value := range self { 42 str += fmt.Sprintf("%X : %X\n", key, value) 43 } 44 45 return 46 } 47 48 func (self Storage) Copy() Storage { 49 cpy := make(Storage) 50 for key, value := range self { 51 cpy[key] = value 52 } 53 54 return cpy 55 } 56 57 // stateObject represents an INT Chain account which is being modified. 58 // 59 // The usage pattern is as follows: 60 // First you need to obtain a state object. 61 // Account values can be accessed and modified through the object. 62 // Finally, call CommitTrie to write the modified storage trie into a database. 63 type stateObject struct { 64 address common.Address 65 addrHash common.Hash // hash of intchain address of the account 66 data Account 67 db *StateDB 68 69 // DB error. 70 // State objects are used by the consensus core and VM which are 71 // unable to deal with database-level errors. Any error that occurs 72 // during a database read is memoized here and will eventually be returned 73 // by StateDB.Commit. 74 dbErr error 75 76 // Write caches. 77 trie Trie // storage trie, which becomes non-nil on first access 78 code Code // contract bytecode, which gets set when code is loaded 79 80 originStorage Storage // Storage cache of original entries to dedup rewrites 81 dirtyStorage Storage // Storage entries that need to be flushed to disk 82 83 // Cross Chain TX trie 84 tx1Trie Trie // tx1 trie, which become non-nil on first access 85 tx3Trie Trie // tx3 trie, which become non-nil on first access 86 87 dirtyTX1 map[common.Hash]struct{} // tx1 entries that need to be flushed to disk 88 dirtyTX3 map[common.Hash]struct{} // tx3 entries that need to be flushed to disk 89 90 // Delegate Trie 91 proxiedTrie Trie // proxied trie, store the proxied balance from other user 92 originProxied Proxied // cache data of proxied trie 93 dirtyProxied Proxied // dirty data of proxied trie, need to be flushed to disk later 94 95 rewardTrie Trie // Reward Trie, store the pending reward balance for this account 96 originReward Reward // cache data of Reward trie 97 dirtyReward Reward // dirty data of Reward trie, need to be flushed to disk later 98 99 // Cache flags. 100 // When an object is marked suicided it will be delete from the trie 101 // during the "update" phase of the state transition. 102 dirtyCode bool // true if the code was updated 103 suicided bool 104 touched bool 105 deleted bool 106 onDirty func(addr common.Address) // Callback method to mark a state object newly dirty 107 } 108 109 // empty returns whether the account is considered empty. 110 func (s *stateObject) empty() bool { 111 return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash) && s.data.DepositBalance.Sign() == 0 && len(s.data.ChildChainDepositBalance) == 0 && s.data.ChainBalance.Sign() == 0 && s.data.DelegateBalance.Sign() == 0 && s.data.ProxiedBalance.Sign() == 0 && s.data.DepositProxiedBalance.Sign() == 0 && s.data.PendingRefundBalance.Sign() == 0 112 } 113 114 // Account is the INT Chain consensus representation of accounts. 115 // These objects are stored in the main account trie. 116 type Account struct { 117 Nonce uint64 118 Balance *big.Int // for normal user 119 DepositBalance *big.Int // for validator, can not be consumed 120 ChildChainDepositBalance []*childChainDepositBalance // only valid in main chain for child chain validator before child chain launch, can not be consumed 121 ChainBalance *big.Int // only valid in main chain for child chain owner, can not be consumed 122 Root common.Hash // merkle root of the storage trie 123 TX1Root common.Hash // merkle root of the TX1 trie 124 TX3Root common.Hash // merkle root of the TX3 trie 125 CodeHash []byte 126 127 // Delegation 128 DelegateBalance *big.Int // the accumulative balance which this account delegate the Balance to other user 129 ProxiedBalance *big.Int // the accumulative balance which other user delegate to this account (this balance can be revoked, can be deposit for validator) 130 DepositProxiedBalance *big.Int // the deposit proxied balance for validator which come from ProxiedBalance (this balance can not be revoked) 131 PendingRefundBalance *big.Int // the accumulative balance which other user try to cancel their delegate balance (this balance will be refund to user's address after epoch end) 132 ProxiedRoot common.Hash // merkle root of the Proxied trie 133 // Candidate 134 Candidate bool // flag for Account, true indicate the account has been applied for the Delegation Candidate 135 Commission uint8 // commission percentage of Delegation Candidate (0-100) 136 //BlockTime *big.Int // number for mined blocks current epoch 137 //ForbiddenTime *big.Int // timestamp for last consensus block 138 //IsForbidden bool // candidate is forbidden or not 139 Pubkey string 140 FAddress common.Address 141 142 // Reward 143 RewardBalance *big.Int // the accumulative reward balance for this account 144 //AvailableRewardBalance *big.Int // the available reward balance for this account 145 RewardRoot common.Hash // merkle root of the Reward trie 146 147 } 148 149 // newObject creates a state object. 150 func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *stateObject { 151 if data.Balance == nil { 152 data.Balance = new(big.Int) 153 } 154 if data.DepositBalance == nil { 155 data.DepositBalance = new(big.Int) 156 } 157 if data.ChainBalance == nil { 158 data.ChainBalance = new(big.Int) 159 } 160 // init delegate balance 161 if data.DelegateBalance == nil { 162 data.DelegateBalance = new(big.Int) 163 } 164 if data.ProxiedBalance == nil { 165 data.ProxiedBalance = new(big.Int) 166 } 167 if data.DepositProxiedBalance == nil { 168 data.DepositProxiedBalance = new(big.Int) 169 } 170 if data.PendingRefundBalance == nil { 171 data.PendingRefundBalance = new(big.Int) 172 } 173 // init reward balance 174 if data.RewardBalance == nil { 175 data.RewardBalance = new(big.Int) 176 } 177 178 //if data.AvailableRewardBalance == nil { 179 // data.AvailableRewardBalance = new(big.Int) 180 //} 181 182 //if data.BlockTime == nil { 183 // data.BlockTime = new(big.Int) 184 //} 185 // 186 //if data.ForbiddenTime == nil { 187 // data.ForbiddenTime = new(big.Int) 188 //} 189 190 if data.CodeHash == nil { 191 data.CodeHash = emptyCodeHash 192 } 193 return &stateObject{ 194 db: db, 195 address: address, 196 addrHash: crypto.Keccak256Hash(address[:]), 197 data: data, 198 originStorage: make(Storage), 199 dirtyStorage: make(Storage), 200 dirtyTX1: make(map[common.Hash]struct{}), 201 dirtyTX3: make(map[common.Hash]struct{}), 202 originProxied: make(Proxied), 203 dirtyProxied: make(Proxied), 204 originReward: make(Reward), 205 dirtyReward: make(Reward), 206 onDirty: onDirty, 207 } 208 } 209 210 // EncodeRLP implements rlp.Encoder. 211 func (c *stateObject) EncodeRLP(w io.Writer) error { 212 return rlp.Encode(w, c.data) 213 } 214 215 // setError remembers the first non-nil error it is called with. 216 func (self *stateObject) setError(err error) { 217 if self.dbErr == nil { 218 self.dbErr = err 219 } 220 } 221 222 func (self *stateObject) markSuicided() { 223 self.suicided = true 224 if self.onDirty != nil { 225 self.onDirty(self.Address()) 226 self.onDirty = nil 227 } 228 } 229 230 func (c *stateObject) touch() { 231 c.db.journal = append(c.db.journal, touchChange{ 232 account: &c.address, 233 prev: c.touched, 234 prevDirty: c.onDirty == nil, 235 }) 236 if c.onDirty != nil { 237 c.onDirty(c.Address()) 238 c.onDirty = nil 239 } 240 c.touched = true 241 } 242 243 func (c *stateObject) getTX1Trie(db Database) Trie { 244 if c.tx1Trie == nil { 245 var err error 246 c.tx1Trie, err = db.OpenTX1Trie(c.addrHash, c.data.TX1Root) 247 if err != nil { 248 c.tx1Trie, _ = db.OpenTX1Trie(c.addrHash, common.Hash{}) 249 c.setError(fmt.Errorf("can't create TX1 trie: %v", err)) 250 } 251 } 252 return c.tx1Trie 253 } 254 255 // HasTX1 returns true if tx1 is in account TX1 trie. 256 func (self *stateObject) HasTX1(db Database, txHash common.Hash) bool { 257 // check the dirtyTX1 firstly. 258 _, ok := self.dirtyTX1[txHash] 259 if ok { 260 return true 261 } 262 263 // Load from DB in case it is missing. 264 enc, err := self.getTX1Trie(db).TryGet(txHash[:]) 265 if err != nil { 266 return false 267 } 268 if len(enc) > 0 { 269 _, content, _, err := rlp.Split(enc) 270 if err != nil { 271 self.setError(err) 272 return false 273 } 274 if !bytes.Equal(content, txHash[:]) { 275 self.setError(fmt.Errorf("content mismatch the tx hash")) 276 return false 277 } 278 279 return true 280 } 281 282 return false 283 } 284 285 // AddTX1 adds a tx1 in account tx1 trie. 286 func (self *stateObject) AddTX1(db Database, txHash common.Hash) { 287 self.db.journal = append(self.db.journal, addTX1Change{ 288 account: &self.address, 289 txHash: txHash, 290 }) 291 self.addTX1(txHash) 292 } 293 294 func (self *stateObject) addTX1(txHash common.Hash) { 295 self.dirtyTX1[txHash] = struct{}{} 296 } 297 298 func (self *stateObject) removeTX1(txHash common.Hash) { 299 delete(self.dirtyTX1, txHash) 300 } 301 302 // updateTX1Trie writes cached tx1 modifications into the object's tx1 trie. 303 func (self *stateObject) updateTX1Trie(db Database) Trie { 304 tr := self.getTX1Trie(db) 305 306 for tx1 := range self.dirtyTX1 { 307 delete(self.dirtyTX1, tx1) 308 309 // Encoding []byte cannot fail, ok to ignore the error. 310 v, _ := rlp.EncodeToBytes(bytes.TrimLeft(tx1[:], "\x00")) 311 self.setError(tr.TryUpdate(tx1[:], v)) 312 } 313 return tr 314 } 315 316 // updateTX1Root sets the trie root to the current root hash 317 func (self *stateObject) updateTX1Root(db Database) { 318 self.updateTX1Trie(db) 319 self.data.TX1Root = self.tx1Trie.Hash() 320 } 321 322 // CommitTX1Trie the tx1 trie of the object to dwb. 323 // This updates the trie root. 324 func (self *stateObject) CommitTX1Trie(db Database) error { 325 self.updateTX1Trie(db) 326 if self.dbErr != nil { 327 return self.dbErr 328 } 329 root, err := self.tx1Trie.Commit(nil) 330 if err == nil { 331 self.data.TX1Root = root 332 } 333 return err 334 } 335 336 func (c *stateObject) getTX3Trie(db Database) Trie { 337 if c.tx3Trie == nil { 338 var err error 339 c.tx3Trie, err = db.OpenTX3Trie(c.addrHash, c.data.TX3Root) 340 if err != nil { 341 c.tx3Trie, _ = db.OpenTX3Trie(c.addrHash, common.Hash{}) 342 c.setError(fmt.Errorf("can't create TX3 trie: %v", err)) 343 } 344 } 345 return c.tx3Trie 346 } 347 348 // HasTX3 returns true if tx3 is in account TX3 trie. 349 func (self *stateObject) HasTX3(db Database, txHash common.Hash) bool { 350 // check the dirtyTX3 firstly. 351 _, ok := self.dirtyTX3[txHash] 352 if ok { 353 return true 354 } 355 356 // Load from DB in case it is missing. 357 enc, err := self.getTX3Trie(db).TryGet(txHash[:]) 358 if err != nil { 359 return false 360 } 361 if len(enc) > 0 { 362 _, content, _, err := rlp.Split(enc) 363 if err != nil { 364 self.setError(err) 365 return false 366 } 367 if !bytes.Equal(content, txHash[:]) { 368 self.setError(fmt.Errorf("content mismatch the tx hash")) 369 return false 370 } 371 372 return true 373 } 374 375 return false 376 } 377 378 // AddTX3 adds a tx3 in account tx3 trie. 379 func (self *stateObject) AddTX3(db Database, txHash common.Hash) { 380 self.db.journal = append(self.db.journal, addTX3Change{ 381 account: &self.address, 382 txHash: txHash, 383 }) 384 self.addTX3(txHash) 385 } 386 387 func (self *stateObject) addTX3(txHash common.Hash) { 388 self.dirtyTX3[txHash] = struct{}{} 389 } 390 391 func (self *stateObject) removeTX3(txHash common.Hash) { 392 delete(self.dirtyTX3, txHash) 393 } 394 395 // updateTX3Trie writes cached tx3 modifications into the object's tx3 trie. 396 func (self *stateObject) updateTX3Trie(db Database) Trie { 397 tr := self.getTX3Trie(db) 398 399 for tx3 := range self.dirtyTX3 { 400 delete(self.dirtyTX3, tx3) 401 402 // Encoding []byte cannot fail, ok to ignore the error. 403 v, _ := rlp.EncodeToBytes(bytes.TrimLeft(tx3[:], "\x00")) 404 self.setError(tr.TryUpdate(tx3[:], v)) 405 } 406 return tr 407 } 408 409 // updateTX3Root sets the trie root to the current root hash 410 func (self *stateObject) updateTX3Root(db Database) { 411 self.updateTX3Trie(db) 412 self.data.TX3Root = self.tx3Trie.Hash() 413 } 414 415 // CommitTX3Trie the tx3 trie of the object to dwb. 416 // This updates the trie root. 417 func (self *stateObject) CommitTX3Trie(db Database) error { 418 self.updateTX3Trie(db) 419 if self.dbErr != nil { 420 return self.dbErr 421 } 422 root, err := self.tx3Trie.Commit(nil) 423 if err == nil { 424 self.data.TX3Root = root 425 } 426 return err 427 } 428 429 func (c *stateObject) getTrie(db Database) Trie { 430 if c.trie == nil { 431 var err error 432 c.trie, err = db.OpenStorageTrie(c.addrHash, c.data.Root) 433 if err != nil { 434 c.trie, _ = db.OpenStorageTrie(c.addrHash, common.Hash{}) 435 c.setError(fmt.Errorf("can't create storage trie: %v", err)) 436 } 437 } 438 return c.trie 439 } 440 441 // GetState returns a value in account storage. 442 func (self *stateObject) GetState(db Database, key common.Hash) common.Hash { 443 // If we have a dirty value for this state entry, return it 444 value, dirty := self.dirtyStorage[key] 445 if dirty { 446 return value 447 } 448 // Otherwise return the entry's original value 449 return self.GetCommittedState(db, key) 450 } 451 452 // GetCommittedState retrieves a value from the committed account storage trie. 453 func (self *stateObject) GetCommittedState(db Database, key common.Hash) common.Hash { 454 // If we have the original value cached, return that 455 value, cached := self.originStorage[key] 456 if cached { 457 return value 458 } 459 // Otherwise load the value from the database 460 enc, err := self.getTrie(db).TryGet(key[:]) 461 if err != nil { 462 self.setError(err) 463 return common.Hash{} 464 } 465 if len(enc) > 0 { 466 _, content, _, err := rlp.Split(enc) 467 if err != nil { 468 self.setError(err) 469 } 470 value.SetBytes(content) 471 } 472 self.originStorage[key] = value 473 return value 474 } 475 476 // SetState updates a value in account storage. 477 func (self *stateObject) SetState(db Database, key, value common.Hash) { 478 self.db.journal = append(self.db.journal, storageChange{ 479 account: &self.address, 480 key: key, 481 prevalue: self.GetState(db, key), 482 }) 483 self.setState(key, value) 484 } 485 486 func (self *stateObject) setState(key, value common.Hash) { 487 self.dirtyStorage[key] = value 488 489 if self.onDirty != nil { 490 self.onDirty(self.Address()) 491 self.onDirty = nil 492 } 493 } 494 495 // updateTrie writes cached storage modifications into the object's storage trie. 496 func (self *stateObject) updateTrie(db Database) Trie { 497 tr := self.getTrie(db) 498 for key, value := range self.dirtyStorage { 499 delete(self.dirtyStorage, key) 500 501 // Skip noop changes, persist actual changes 502 if value == self.originStorage[key] { 503 continue 504 } 505 self.originStorage[key] = value 506 507 if (value == common.Hash{}) { 508 self.setError(tr.TryDelete(key[:])) 509 continue 510 } 511 // Encoding []byte cannot fail, ok to ignore the error. 512 v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) 513 self.setError(tr.TryUpdate(key[:], v)) 514 } 515 return tr 516 } 517 518 // UpdateRoot sets the trie root to the current root hash of 519 func (self *stateObject) updateRoot(db Database) { 520 self.updateTrie(db) 521 self.data.Root = self.trie.Hash() 522 } 523 524 // CommitTrie the storage trie of the object to dwb. 525 // This updates the trie root. 526 func (self *stateObject) CommitTrie(db Database) error { 527 self.updateTrie(db) 528 if self.dbErr != nil { 529 return self.dbErr 530 } 531 root, err := self.trie.Commit(nil) 532 if err == nil { 533 self.data.Root = root 534 } 535 return err 536 } 537 538 // AddBalance removes amount from c's balance. 539 // It is used to add funds to the destination account of a transfer. 540 func (c *stateObject) AddBalance(amount *big.Int) { 541 // EIP158: We must check emptiness for the objects such that the account 542 // clearing (0,0,0 objects) can take effect. 543 if amount.Sign() == 0 { 544 if c.empty() { 545 c.touch() 546 } 547 548 return 549 } 550 c.SetBalance(new(big.Int).Add(c.Balance(), amount)) 551 } 552 553 // SubBalance removes amount from c's balance. 554 // It is used to remove funds from the origin account of a transfer. 555 func (c *stateObject) SubBalance(amount *big.Int) { 556 if amount.Sign() == 0 { 557 return 558 } 559 c.SetBalance(new(big.Int).Sub(c.Balance(), amount)) 560 } 561 562 func (self *stateObject) SetBalance(amount *big.Int) { 563 self.db.journal = append(self.db.journal, balanceChange{ 564 account: &self.address, 565 prev: new(big.Int).Set(self.data.Balance), 566 }) 567 self.setBalance(amount) 568 } 569 570 func (self *stateObject) setBalance(amount *big.Int) { 571 self.data.Balance = amount 572 if self.onDirty != nil { 573 self.onDirty(self.Address()) 574 self.onDirty = nil 575 } 576 } 577 578 // Return the gas back to the origin. Used by the Virtual machine or Closures 579 func (c *stateObject) ReturnGas(gas *big.Int) {} 580 581 func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject { 582 stateObject := newObject(db, self.address, self.data, onDirty) 583 if self.trie != nil { 584 stateObject.trie = db.db.CopyTrie(self.trie) 585 } 586 if self.tx1Trie != nil { 587 stateObject.tx1Trie = db.db.CopyTrie(self.tx1Trie) 588 } 589 if self.tx3Trie != nil { 590 stateObject.tx3Trie = db.db.CopyTrie(self.tx3Trie) 591 } 592 if self.proxiedTrie != nil { 593 stateObject.proxiedTrie = db.db.CopyTrie(self.proxiedTrie) 594 } 595 if self.rewardTrie != nil { 596 stateObject.rewardTrie = db.db.CopyTrie(self.rewardTrie) 597 } 598 stateObject.code = self.code 599 stateObject.dirtyStorage = self.dirtyStorage.Copy() 600 stateObject.originStorage = self.originStorage.Copy() 601 stateObject.suicided = self.suicided 602 stateObject.dirtyCode = self.dirtyCode 603 stateObject.deleted = self.deleted 604 stateObject.dirtyTX1 = make(map[common.Hash]struct{}) 605 for tx1 := range self.dirtyTX1 { 606 stateObject.dirtyTX1[tx1] = struct{}{} 607 } 608 stateObject.dirtyTX3 = make(map[common.Hash]struct{}) 609 for tx3 := range self.dirtyTX3 { 610 stateObject.dirtyTX3[tx3] = struct{}{} 611 } 612 stateObject.dirtyProxied = self.dirtyProxied.Copy() 613 stateObject.originProxied = self.originProxied.Copy() 614 stateObject.dirtyReward = self.dirtyReward.Copy() 615 stateObject.originReward = self.originReward.Copy() 616 return stateObject 617 } 618 619 // 620 // Attribute accessors 621 // 622 623 // Returns the address of the contract/account 624 func (c *stateObject) Address() common.Address { 625 return c.address 626 } 627 628 // Code returns the contract code associated with this object, if any. 629 func (self *stateObject) Code(db Database) []byte { 630 if self.code != nil { 631 return self.code 632 } 633 if bytes.Equal(self.CodeHash(), emptyCodeHash) { 634 return nil 635 } 636 code, err := db.ContractCode(self.addrHash, common.BytesToHash(self.CodeHash())) 637 if err != nil { 638 self.setError(fmt.Errorf("can't load code hash %x: %v", self.CodeHash(), err)) 639 } 640 self.code = code 641 return code 642 } 643 644 func (self *stateObject) SetCode(codeHash common.Hash, code []byte) { 645 prevcode := self.Code(self.db.db) 646 self.db.journal = append(self.db.journal, codeChange{ 647 account: &self.address, 648 prevhash: self.CodeHash(), 649 prevcode: prevcode, 650 }) 651 self.setCode(codeHash, code) 652 } 653 654 func (self *stateObject) setCode(codeHash common.Hash, code []byte) { 655 self.code = code 656 self.data.CodeHash = codeHash[:] 657 self.dirtyCode = true 658 if self.onDirty != nil { 659 self.onDirty(self.Address()) 660 self.onDirty = nil 661 } 662 } 663 664 func (self *stateObject) SetNonce(nonce uint64) { 665 self.db.journal = append(self.db.journal, nonceChange{ 666 account: &self.address, 667 prev: self.data.Nonce, 668 }) 669 self.setNonce(nonce) 670 } 671 672 func (self *stateObject) setNonce(nonce uint64) { 673 self.data.Nonce = nonce 674 if self.onDirty != nil { 675 self.onDirty(self.Address()) 676 self.onDirty = nil 677 } 678 } 679 680 func (self *stateObject) CodeHash() []byte { 681 return self.data.CodeHash 682 } 683 684 func (self *stateObject) Balance() *big.Int { 685 return self.data.Balance 686 } 687 688 func (self *stateObject) Nonce() uint64 { 689 return self.data.Nonce 690 } 691 692 // Never called, but must be present to allow stateObject to be used 693 // as a vm.Account interface that also satisfies the vm.ContractRef 694 // interface. Interfaces are awesome. 695 func (self *stateObject) Value() *big.Int { 696 panic("Value on stateObject should never be called") 697 } 698 699 func (self *stateObject) SetAddress(address common.Address) { 700 self.db.journal = append(self.db.journal, fAddressChange{ 701 account: &self.address, 702 prev: self.data.FAddress, 703 }) 704 705 self.setAddress(address) 706 } 707 708 func (self *stateObject) setAddress(address common.Address) { 709 self.data.FAddress = address 710 if self.onDirty != nil { 711 self.onDirty(self.Address()) 712 self.onDirty = nil 713 } 714 } 715 716 func (self *stateObject) GetAddress() common.Address { 717 return self.data.FAddress 718 }