diff --git a/state/block_cache.go b/state/block_cache.go index e30e4121..0bbab6fa 100644 --- a/state/block_cache.go +++ b/state/block_cache.go @@ -5,11 +5,11 @@ import ( "sort" acm "github.com/tendermint/tendermint/account" - "github.com/tendermint/tendermint/wire" . "github.com/tendermint/tendermint/common" dbm "github.com/tendermint/tendermint/db" "github.com/tendermint/tendermint/merkle" "github.com/tendermint/tendermint/types" + "github.com/tendermint/tendermint/wire" ) func makeStorage(db dbm.DB, root []byte) merkle.Tree { @@ -177,7 +177,7 @@ func (cache *BlockCache) Sync() { addr, key := Tuple256Split(storageKey) if addr != curAddr || curAcc == nil { acc, storage, removed, _ := cache.accounts[string(addr.Postfix(20))].unpack() - if storage == nil { + if !removed && storage == nil { storage = makeStorage(cache.db, acc.StorageRoot) } curAddr = addr @@ -211,7 +211,7 @@ func (cache *BlockCache) Sync() { for _, addrStr := range addrStrs { acc, storage, removed, dirty := cache.accounts[addrStr].unpack() if removed { - removed := cache.backend.RemoveAccount(acc.Address) + removed := cache.backend.RemoveAccount([]byte(addrStr)) if !removed { PanicCrisis(Fmt("Could not remove account to be removed: %X", acc.Address)) } diff --git a/state/state_test.go b/state/state_test.go index 8eaebe37..9ec9b212 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -1,19 +1,18 @@ package state import ( - "github.com/tendermint/tendermint/account" - _ "github.com/tendermint/tendermint/config/tendermint_test" - "github.com/tendermint/tendermint/types" - "bytes" "testing" "time" + + "github.com/tendermint/tendermint/account" + _ "github.com/tendermint/tendermint/config/tendermint_test" + "github.com/tendermint/tendermint/types" ) func execTxWithState(state *State, tx types.Tx, runCall bool) error { cache := NewBlockCache(state) - err := ExecTx(cache, tx, runCall, nil) - if err != nil { + if err := ExecTx(cache, tx, runCall, nil); err != nil { return err } else { cache.Sync() @@ -570,6 +569,60 @@ proof-of-work chain as proof of what happened while they were gone ` } +func TestSuicide(t *testing.T) { + + state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000) + + acc0 := state.GetAccount(privAccounts[0].PubKey.Address()) + acc0PubKey := privAccounts[0].PubKey + acc1 := state.GetAccount(privAccounts[1].PubKey.Address()) + acc2 := state.GetAccount(privAccounts[2].Address) + sendingAmount, refundedBalance, oldBalance := int64(1), acc1.Balance, acc2.Balance + + newAcc1 := state.GetAccount(acc1.Address) + + // store 0x1 at 0x1, push an address, then suicide :) + contractCode := []byte{0x60, 0x01, 0x60, 0x01, 0x55, 0x73} + contractCode = append(contractCode, acc2.Address...) + contractCode = append(contractCode, 0xff) + newAcc1.Code = contractCode + state.UpdateAccount(newAcc1) + + // send call tx with no data, cause suicide + tx := types.NewCallTxWithNonce(acc0PubKey, acc1.Address, nil, sendingAmount, 1000, 0, acc0.Sequence+1) + tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx) + + // we use cache instead of execTxWithState so we can run the tx twice + cache := NewBlockCache(state) + if err := ExecTx(cache, tx, true, nil); err != nil { + t.Errorf("Got error in executing call transaction, %v", err) + } + + // if we do it again, we won't get an error, but the suicide + // shouldn't happen twice and the caller should lose fee + tx.Input.Sequence += 1 + tx.Input.Signature = privAccounts[0].Sign(state.ChainID, tx) + if err := ExecTx(cache, tx, true, nil); err != nil { + t.Errorf("Got error in executing call transaction, %v", err) + } + + // commit the block + cache.Sync() + + // acc2 should receive the sent funds and the contracts balance + newAcc2 := state.GetAccount(acc2.Address) + newBalance := sendingAmount + refundedBalance + oldBalance + if newAcc2.Balance != newBalance { + t.Errorf("Unexpected newAcc2 balance. Expected %v, got %v", + newAcc2.Balance, newBalance) + } + newAcc1 = state.GetAccount(acc1.Address) + if newAcc1 != nil { + t.Errorf("Expected account to be removed") + } + +} + func TestAddValidator(t *testing.T) { // Generate a state, save & load it. diff --git a/vm/vm.go b/vm/vm.go index ea06825b..d0a0906e 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -174,9 +174,6 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas switch op { - case STOP: // 0x00 - return nil, nil - case ADD: // 0x01 x, y := stack.Pop(), stack.Pop() xb := new(big.Int).SetBytes(x[:]) @@ -837,7 +834,8 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas if useGasNegative(gas, GasGetAccount, &err) { return nil, err } - // TODO if the receiver is , then make it the fee. + // TODO if the receiver is , then make it the fee. (?) + // TODO: create account if doesn't exist (no reason not to) receiver := vm.appState.GetAccount(addr) if receiver == nil { return nil, firstErr(err, ErrUnknownAddress) @@ -849,6 +847,9 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas dbg.Printf(" => (%X) %v\n", addr[:4], balance) fallthrough + case STOP: // 0x00 + return nil, nil + default: dbg.Printf("(pc) %-3v Invalid opcode %X\n", pc, op) return nil, fmt.Errorf("Invalid opcode %X", op)