You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

198 lines
4.8 KiB

  1. package state
  2. import (
  3. ac "github.com/tendermint/tendermint/account"
  4. . "github.com/tendermint/tendermint/common"
  5. "github.com/tendermint/tendermint/vm"
  6. "github.com/tendermint/tendermint/vm/sha3"
  7. )
  8. type TxCache struct {
  9. backend *BlockCache
  10. accounts map[Word256]vmAccountInfo
  11. storages map[Tuple256]Word256
  12. logs []*vm.Log
  13. }
  14. func NewTxCache(backend *BlockCache) *TxCache {
  15. return &TxCache{
  16. backend: backend,
  17. accounts: make(map[Word256]vmAccountInfo),
  18. storages: make(map[Tuple256]Word256),
  19. logs: make([]*vm.Log, 0),
  20. }
  21. }
  22. //-------------------------------------
  23. // TxCache.account
  24. func (cache *TxCache) GetAccount(addr Word256) *vm.Account {
  25. acc, removed := vmUnpack(cache.accounts[addr])
  26. if removed {
  27. return nil
  28. } else if acc == nil {
  29. acc2 := cache.backend.GetAccount(addr.Postfix(20))
  30. if acc2 != nil {
  31. return toVMAccount(acc2)
  32. }
  33. }
  34. return acc
  35. }
  36. func (cache *TxCache) UpdateAccount(acc *vm.Account) {
  37. addr := acc.Address
  38. // SANITY CHECK
  39. _, removed := vmUnpack(cache.accounts[addr])
  40. if removed {
  41. panic("UpdateAccount on a removed account")
  42. }
  43. // SANITY CHECK END
  44. cache.accounts[addr] = vmAccountInfo{acc, false}
  45. }
  46. func (cache *TxCache) RemoveAccount(acc *vm.Account) {
  47. addr := acc.Address
  48. // SANITY CHECK
  49. _, removed := vmUnpack(cache.accounts[addr])
  50. if removed {
  51. panic("RemoveAccount on a removed account")
  52. }
  53. // SANITY CHECK END
  54. cache.accounts[addr] = vmAccountInfo{acc, true}
  55. }
  56. // Creates a 20 byte address and bumps the creator's nonce.
  57. func (cache *TxCache) CreateAccount(creator *vm.Account) *vm.Account {
  58. // Generate an address
  59. nonce := creator.Nonce
  60. creator.Nonce += 1
  61. addr := LeftPadWord256(NewContractAddress(creator.Address.Postfix(20), int(nonce)))
  62. // Create account from address.
  63. account, removed := vmUnpack(cache.accounts[addr])
  64. if removed || account == nil {
  65. account = &vm.Account{
  66. Address: addr,
  67. Balance: 0,
  68. Code: nil,
  69. Nonce: 0,
  70. StorageRoot: Zero256,
  71. }
  72. cache.accounts[addr] = vmAccountInfo{account, false}
  73. return account
  74. } else {
  75. // NONCE HANDLING SANITY CHECK OR SHA3 IS BROKEN
  76. panic(Fmt("Could not create account, address already exists: %X", addr))
  77. }
  78. }
  79. // TxCache.account
  80. //-------------------------------------
  81. // TxCache.storage
  82. func (cache *TxCache) GetStorage(addr Word256, key Word256) Word256 {
  83. // Check cache
  84. value, ok := cache.storages[Tuple256{addr, key}]
  85. if ok {
  86. return value
  87. }
  88. // Load from backend
  89. return cache.backend.GetStorage(addr, key)
  90. }
  91. // NOTE: Set value to zero to removed from the trie.
  92. func (cache *TxCache) SetStorage(addr Word256, key Word256, value Word256) {
  93. // SANITY CHECK
  94. _, removed := vmUnpack(cache.accounts[addr])
  95. if removed {
  96. panic("SetStorage() on a removed account")
  97. }
  98. // SANITY CHECK END
  99. cache.storages[Tuple256{addr, key}] = value
  100. }
  101. // TxCache.storage
  102. //-------------------------------------
  103. // These updates do not have to be in deterministic order,
  104. // the backend is responsible for ordering updates.
  105. func (cache *TxCache) Sync() {
  106. // Remove or update storage
  107. for addrKey, value := range cache.storages {
  108. addr, key := Tuple256Split(addrKey)
  109. cache.backend.SetStorage(addr, key, value)
  110. }
  111. // Remove or update accounts
  112. for addr, accInfo := range cache.accounts {
  113. acc, removed := vmUnpack(accInfo)
  114. if removed {
  115. cache.backend.RemoveAccount(addr.Postfix(20))
  116. } else {
  117. cache.backend.UpdateAccount(toStateAccount(acc))
  118. }
  119. }
  120. // TODO support logs, add them to the cache somehow.
  121. }
  122. func (cache *TxCache) AddLog(log *vm.Log) {
  123. cache.logs = append(cache.logs, log)
  124. }
  125. //-----------------------------------------------------------------------------
  126. // Convenience function to return address of new contract
  127. func NewContractAddress(caller []byte, nonce int) []byte {
  128. temp := make([]byte, 32+8)
  129. copy(temp, caller)
  130. PutInt64BE(temp[32:], int64(nonce))
  131. return sha3.Sha3(temp)[:20]
  132. }
  133. // Converts backend.Account to vm.Account struct.
  134. func toVMAccount(acc *ac.Account) *vm.Account {
  135. return &vm.Account{
  136. Address: LeftPadWord256(acc.Address),
  137. Balance: acc.Balance,
  138. Code: acc.Code, // This is crazy.
  139. Nonce: int64(acc.Sequence),
  140. StorageRoot: LeftPadWord256(acc.StorageRoot),
  141. Other: acc.PubKey,
  142. }
  143. }
  144. // Converts vm.Account to backend.Account struct.
  145. func toStateAccount(acc *vm.Account) *ac.Account {
  146. pubKey, ok := acc.Other.(ac.PubKey)
  147. if !ok {
  148. pubKey = nil
  149. }
  150. var storageRoot []byte
  151. if acc.StorageRoot.IsZero() {
  152. storageRoot = nil
  153. } else {
  154. storageRoot = acc.StorageRoot.Bytes()
  155. }
  156. return &ac.Account{
  157. Address: acc.Address.Postfix(20),
  158. PubKey: pubKey,
  159. Balance: acc.Balance,
  160. Code: acc.Code,
  161. Sequence: int(acc.Nonce),
  162. StorageRoot: storageRoot,
  163. }
  164. }
  165. type vmAccountInfo struct {
  166. account *vm.Account
  167. removed bool
  168. }
  169. func vmUnpack(accInfo vmAccountInfo) (*vm.Account, bool) {
  170. return accInfo.account, accInfo.removed
  171. }