diff --git a/merkle/iavl_tree.go b/merkle/iavl_tree.go index 7318e0889..da474318b 100644 --- a/merkle/iavl_tree.go +++ b/merkle/iavl_tree.go @@ -120,11 +120,14 @@ func (t *IAVLTree) Save() []byte { return t.root.save(t) } +// Sets the root node by reading from db. +// If the hash is empty, then sets root to nil. func (t *IAVLTree) Load(hash []byte) { if len(hash) == 0 { - panic("IAVLTree.Load() hash was nil") + t.root = nil + } else { + t.root = t.ndb.GetNode(t, hash) } - t.root = t.ndb.GetNode(t, hash) } func (t *IAVLTree) Get(key interface{}) (index uint64, value interface{}) { diff --git a/state/state.go b/state/state.go index 46c92961b..c17c83730 100644 --- a/state/state.go +++ b/state/state.go @@ -320,7 +320,7 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error { err error = nil caller *vm.Account = toVMAccount(inAcc) callee *vm.Account = nil - appState = NewVMAppState(s) + appState = NewVMAppState(s) // TODO: confusing. params = vm.Params{ BlockHeight: uint64(s.LastBlockHeight), BlockHash: vm.BytesToWord(s.LastBlockHash), @@ -340,11 +340,12 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error { } } - appState.AddAccount(caller) // because we adjusted by input above, and bumped nonce maybe. - appState.AddAccount(callee) // because we adjusted by input above. + appState.UpdateAccount(caller) // because we adjusted by input above, and bumped nonce maybe. + appState.UpdateAccount(callee) // because we adjusted by input above. vmach := vm.NewVM(appState, params, caller.Address) - ret, err_ := vmach.Call(caller, callee, outAcc.Code, tx.Data, value, &gas) - if err_ != nil { + // NOTE: Call() transfers the value from caller to callee iff call succeeds. + ret, err := vmach.Call(caller, callee, outAcc.Code, tx.Data, value, &gas) + if err != nil { // Failure. Charge the gas fee. The 'value' was otherwise not transferred. inAcc.Balance -= tx.Fee s.UpdateAccount(inAcc) @@ -357,7 +358,7 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error { appState.Sync() } // Create a receipt from the ret and whether errored. - log.Info("VM call complete", "caller", caller, "callee", callee, "return", ret, "err", err_) + log.Info("VM call complete", "caller", caller, "callee", callee, "return", ret, "err", err) } else { // The mempool does not call txs until // the proposer determines the order of txs. diff --git a/state/vm_app_state.go b/state/vm_app_state.go index 75945640d..ef696c966 100644 --- a/state/vm_app_state.go +++ b/state/vm_app_state.go @@ -1,6 +1,7 @@ package state import ( + "bytes" "sort" ac "github.com/tendermint/tendermint/account" @@ -61,16 +62,6 @@ func unpack(accInfo AccountInfo) (*vm.Account, bool) { return accInfo.account, accInfo.deleted } -// Used to add the origin of the tx to VMAppState. -func (vas *VMAppState) AddAccount(account *vm.Account) error { - if _, ok := vas.accounts[account.Address.String()]; ok { - return Errorf("Account already exists: %X", account.Address) - } else { - vas.accounts[account.Address.String()] = AccountInfo{account, false} - return nil - } -} - func (vas *VMAppState) GetAccount(addr vm.Word) (*vm.Account, error) { account, deleted := unpack(vas.accounts[addr.String()]) if deleted { @@ -175,6 +166,7 @@ func (vas *VMAppState) SetStorage(addr vm.Word, key vm.Word, value vm.Word) (boo return ok, nil } +// CONTRACT the updates are in deterministic order. func (vas *VMAppState) Sync() { // Determine order for accounts @@ -200,18 +192,36 @@ func (vas *VMAppState) Sync() { } } - // Update or delete storage items. + // Determine order for storage updates + // The address comes first so it'll be grouped. + storageKeyStrs := []string{} + for keyStr := range vas.storage { + storageKeyStrs = append(storageKeyStrs, keyStr) + } + sort.Strings(storageKeyStrs) + + // Update storage for all account/key. storage := merkle.NewIAVLTree( binary.BasicCodec, // TODO change binary.BasicCodec, // TODO change 1024, // TODO change. vas.state.DB, ) - - for addrKey, value := range vas.storage { - addrKeyBytes := []byte(addrKey) + var currentAccount *vm.Account + var deleted bool + for _, storageKey := range storageKeyStrs { + value := vas.storage[storageKey] + addrKeyBytes := []byte(storageKey) addr := addrKeyBytes[:32] key := addrKeyBytes[32:] + if currentAccount == nil || !bytes.Equal(currentAccount.Address[:], addr) { + currentAccount, deleted = unpack(vas.accounts[string(addr)]) + if deleted { + continue + } + storageRoot := currentAccount.StorageRoot + storage.Load(storageRoot.Bytes()) + } if value.IsZero() { _, removed := storage.Remove(key) if !removed { diff --git a/vm/vm.go b/vm/vm.go index d81e827fd..a81ddd259 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -530,7 +530,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga fmt.Printf(" => %v\n", log) case CREATE: // 0xF0 - value := stack.Pop64() + contractValue := stack.Pop64() offset, size := stack.Pop64(), stack.Pop64() input, ok := subslice(memory, offset, size) if !ok { @@ -538,19 +538,19 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga } // Check balance - if caller.Balance < value { + if callee.Balance < contractValue { return nil, firstErr(err, ErrInsufficientBalance) } // TODO charge for gas to create account _ the code length * GasCreateByte - newAccount, err := vm.appState.CreateAccount(caller) + newAccount, err := vm.appState.CreateAccount(callee) if err != nil { stack.Push(Zero) fmt.Printf(" (*) 0x0 %v\n", err) } else { // Run the input to get the contract code. - ret, err_ := vm.Call(callee, newAccount, input, input, value, gas) + ret, err_ := vm.Call(callee, newAccount, input, input, contractValue, gas) if err_ != nil { stack.Push(Zero) } else {