From b7553e2bfe9bd61d855bd3d41330e4a657319a88 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Fri, 20 Mar 2015 19:59:42 -0700 Subject: [PATCH] Address is generated with VMAppState, and it increments the nonce too. --- state/state.go | 41 ++++++++++++++++++++++++++++----------- state/vm_app_state.go | 17 ++++++++++++++-- vm/test/fake_app_state.go | 24 ++++++++++++++++------- vm/types.go | 2 +- vm/vm.go | 16 ++------------- 5 files changed, 65 insertions(+), 35 deletions(-) diff --git a/state/state.go b/state/state.go index 3340840ce..1f44c4c62 100644 --- a/state/state.go +++ b/state/state.go @@ -277,8 +277,10 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error { return nil case *blk.CallTx: + var inAcc, outAcc *account.Account + // Validate input - inAcc := s.GetAccount(tx.Input.Address) + inAcc = s.GetAccount(tx.Input.Address) if inAcc == nil { return blk.ErrTxInvalidAddress } @@ -295,13 +297,16 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error { return blk.ErrTxInsufficientFunds } - // Validate output - if len(tx.Address) != 20 { - return blk.ErrTxInvalidAddress - } - outAcc := s.GetAccount(tx.Address) - if outAcc == nil { - return blk.ErrTxInvalidAddress + createAccount := len(tx.Address) == 0 + if !createAccount { + // Validate output + if len(tx.Address) != 20 { + return blk.ErrTxInvalidAddress + } + outAcc = s.GetAccount(tx.Address) + if outAcc == nil { + return blk.ErrTxInvalidAddress + } } // Good! @@ -316,9 +321,18 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error { BlockTime: s.LastBlockTime.Unix(), GasLimit: 10000000, } - caller := toVMAccount(inAcc) - callee := toVMAccount(outAcc) - appState.AddAccount(caller) // because we adjusted by input above. + var caller, callee *vm.Account + var err error + caller = toVMAccount(inAcc) + if outAcc == nil { + callee = toVMAccount(outAcc) + } else { + callee, err = appState.CreateAccount(caller) + if err != nil { + return err + } + } + appState.AddAccount(caller) // because we adjusted by input above, and bumped nonce maybe. appState.AddAccount(callee) // because we adjusted by input above. vmach := vm.NewVM(appState, params, caller.Address) gas := tx.GasLimit @@ -330,6 +344,9 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error { // Throw away 'appState' which holds incomplete updates. } else { // Success + if createAccount { + callee.Code = ret + } appState.Sync() } // Create a receipt from the ret and whether errored. @@ -339,6 +356,8 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error { // the proposer determines the order of txs. // So mempool will skip the actual .Call(), // and only deduct from the caller's balance. + inAcc.Balance -= value + s.UpdateAccount(inAcc) } return nil diff --git a/state/vm_app_state.go b/state/vm_app_state.go index 247e42c4f..75945640d 100644 --- a/state/vm_app_state.go +++ b/state/vm_app_state.go @@ -8,6 +8,7 @@ import ( . "github.com/tendermint/tendermint/common" "github.com/tendermint/tendermint/merkle" "github.com/tendermint/tendermint/vm" + "github.com/tendermint/tendermint/vm/sha3" ) // Converts state.Account to vm.Account struct. @@ -115,7 +116,18 @@ func (vas *VMAppState) DeleteAccount(account *vm.Account) error { } } -func (vas *VMAppState) CreateAccount(addr vm.Word) (*vm.Account, error) { +// Creates a 20 byte address and bumps the creator's nonce. +func (vas *VMAppState) CreateAccount(creator *vm.Account) (*vm.Account, error) { + + // Generate an address + nonce := creator.Nonce + creator.Nonce += 1 + temp := make([]byte, 32+8) + copy(temp, creator.Address[:]) + vm.PutUint64(temp[32:], nonce) + addr := vm.RightPadWord(sha3.Sha3(temp)[:20]) + + // Create account from address. account, deleted := unpack(vas.accounts[addr.String()]) if deleted || account == nil { account = &vm.Account{ @@ -128,7 +140,8 @@ func (vas *VMAppState) CreateAccount(addr vm.Word) (*vm.Account, error) { vas.accounts[addr.String()] = AccountInfo{account, false} return account, nil } else { - return nil, Errorf("Account already exists: %X", addr) + panic(Fmt("Could not create account, address already exists: %X", addr)) + // return nil, Errorf("Account already exists: %X", addr) } } diff --git a/vm/test/fake_app_state.go b/vm/test/fake_app_state.go index 16d69572e..cc3b71a43 100644 --- a/vm/test/fake_app_state.go +++ b/vm/test/fake_app_state.go @@ -5,6 +5,7 @@ import ( . "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/vm" + "github.com/tendermint/tendermint/vm/sha3" ) type FakeAppState struct { @@ -43,7 +44,8 @@ func (fas *FakeAppState) DeleteAccount(account *Account) error { } } -func (fas *FakeAppState) CreateAccount(addr Word) (*Account, error) { +func (fas *FakeAppState) CreateAccount(creator *Account) (*Account, error) { + addr := createAddress(creator) account := fas.accounts[addr.String()] if account == nil { return &Account{ @@ -102,16 +104,24 @@ func main() { ourVm := NewVM(appState, params, Zero) // Create accounts - account1, err := appState.CreateAccount(Uint64ToWord(100)) - if err != nil { - panic(err) + account1 := &Account{ + Address: Uint64ToWord(100), } - account2, err := appState.CreateAccount(Uint64ToWord(101)) - if err != nil { - panic(err) + account2 := &Account{ + Address: Uint64ToWord(101), } var gas uint64 = 1000 output, err := ourVm.Call(account1, account2, []byte{0x60, 0x01, 0x60, 0x01}, []byte{}, 0, &gas) fmt.Printf("Output: %v Error: %v\n", output, err) } + +// Creates a 20 byte address and bumps the nonce. +func createAddress(creator *Account) Word { + nonce := creator.Nonce + creator.Nonce += 1 + temp := make([]byte, 32+8) + copy(temp, creator.Address[:]) + PutUint64(temp[32:], nonce) + return RightPadWord(sha3.Sha3(temp)[:20]) +} diff --git a/vm/types.go b/vm/types.go index 49a23b98f..e4cf0a371 100644 --- a/vm/types.go +++ b/vm/types.go @@ -48,7 +48,7 @@ type AppState interface { GetAccount(addr Word) (*Account, error) UpdateAccount(*Account) error DeleteAccount(*Account) error - CreateAccount(addr Word) (*Account, error) + CreateAccount(*Account) (*Account, error) // Storage GetStorage(Word, Word) (Word, error) diff --git a/vm/vm.go b/vm/vm.go index 3b0f6c53b..d81e827fd 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -46,6 +46,7 @@ func NewVM(appState AppState, params Params, origin Word) *VM { } } +// CONTRACT appState is aware of caller and callee, so we can just mutate them. // value: To be transferred from caller to callee. Refunded upon error. // gas: Available gas. No refunds for gas. func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, gas *uint64) (output []byte, err error) { @@ -541,14 +542,9 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga return nil, firstErr(err, ErrInsufficientBalance) } - // Create a new address - nonce := caller.Nonce - addr := createAddress(caller.Address, nonce) - caller.Nonce += 1 - // TODO charge for gas to create account _ the code length * GasCreateByte - newAccount, err := vm.appState.CreateAccount(addr) + newAccount, err := vm.appState.CreateAccount(caller) if err != nil { stack.Push(Zero) fmt.Printf(" (*) 0x0 %v\n", err) @@ -708,14 +704,6 @@ func useGas(gas *uint64, gasToUse uint64) bool { } } -// Creates a 20 byte address from the creatorAddr and nonce. -func createAddress(creatorAddr Word, nonce uint64) Word { - temp := make([]byte, 32+8) - copy(temp, creatorAddr[:]) - PutUint64(temp[32:], nonce) - return RightPadWord(sha3.Sha3(temp)[:20]) -} - func transfer(from, to *Account, amount uint64) error { if from.Balance < amount { return ErrInsufficientBalance