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.

204 lines
5.1 KiB

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