Make Call() handle value transfers and reverts.

This commit is contained in:
Jae Kwon
2015-03-20 18:48:47 -07:00
parent 7f12738415
commit 36dca3981b
2 changed files with 57 additions and 59 deletions
+37 -45
View File
@@ -98,12 +98,6 @@ func (s *State) Save() {
s.DB.Set(stateKey, buf.Bytes())
}
// NOTE: the valSets are not Copy()'d.
// See TODOs in Copy() below.
func (s *State) Reset(checkpoint *State) {
*s = *checkpoint
}
func (s *State) Copy() *State {
return &State{
DB: s.DB,
@@ -181,21 +175,9 @@ func (s *State) ValidateInputs(accounts map[string]*account.Account, signBytes [
if acc == nil {
panic("ValidateInputs() expects account in accounts")
}
// Check TxInput basic
if err := in.ValidateBasic(); err != nil {
return 0, err
}
// Check signatures
if !acc.PubKey.VerifyBytes(signBytes, in.Signature) {
return 0, blk.ErrTxInvalidSignature
}
// Check sequences
if acc.Sequence+1 != in.Sequence {
return 0, blk.ErrTxInvalidSequence
}
// Check amount
if acc.Balance < in.Amount {
return 0, blk.ErrTxInsufficientFunds
err = s.ValidateInput(acc, signBytes, in)
if err != nil {
return
}
// Good. Add amount to total
total += in.Amount
@@ -203,6 +185,26 @@ func (s *State) ValidateInputs(accounts map[string]*account.Account, signBytes [
return total, nil
}
func (s *State) ValidateInput(acc *account.Account, signBytes []byte, in *blk.TxInput) (err error) {
// Check TxInput basic
if err := in.ValidateBasic(); err != nil {
return err
}
// Check signatures
if !acc.PubKey.VerifyBytes(signBytes, in.Signature) {
return blk.ErrTxInvalidSignature
}
// Check sequences
if acc.Sequence+1 != in.Sequence {
return blk.ErrTxInvalidSequence
}
// Check amount
if acc.Balance < in.Amount {
return blk.ErrTxInsufficientFunds
}
return nil
}
func (s *State) ValidateOutputs(outs []*blk.TxOutput) (total uint64, err error) {
for _, out := range outs {
// Check TxOutput basic
@@ -275,25 +277,25 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error {
return nil
case *blk.CallTx:
accounts := make(map[string]*account.Account)
// validate input
// Validate input
inAcc := s.GetAccount(tx.Input.Address)
if inAcc == nil {
return blk.ErrTxInvalidAddress
}
// PubKey should be present in either "inAcc" or "tx.Input"
// pubKey should be present in either "inAcc" or "tx.Input"
if err := checkInputPubKey(inAcc, tx.Input); err != nil {
return err
}
accounts[string(tx.Input.Address)] = inAcc
signBytes := account.SignBytes(tx)
inTotal, err := s.ValidateInputs(accounts, signBytes, []*blk.TxInput{tx.Input})
err := s.ValidateInput(inAcc, signBytes, tx.Input)
if err != nil {
return err
}
if tx.Input.Amount < tx.Fee {
return blk.ErrTxInsufficientFunds
}
// validate output
// Validate output
if len(tx.Address) != 20 {
return blk.ErrTxInvalidAddress
}
@@ -301,19 +303,10 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error {
if outAcc == nil {
return blk.ErrTxInvalidAddress
}
if inTotal < tx.Fee {
return blk.ErrTxInsufficientFunds
}
accounts[string(tx.Address)] = outAcc
// Remember unmodified state in case call fails.
checkpoint := s.Copy()
// Good! Adjust accounts and maybe actually run the tx.
value := inTotal - tx.Fee
s.AdjustByInputs(accounts, []*blk.TxInput{tx.Input}) // adjusts inAcc.
outAcc.Balance += value
s.UpdateAccounts(accounts)
// Good!
value := tx.Input.Amount - tx.Fee
inAcc.Sequence += 1
if runCall {
appState := NewVMAppState(s)
@@ -331,11 +324,10 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error {
gas := tx.GasLimit
ret, err_ := vmach.Call(caller, callee, outAcc.Code, tx.Data, value, &gas)
if err_ != nil {
// Failure
// Revert the state while charging gas for the user.
s.Reset(checkpoint)
callee.Balance -= tx.Fee
s.UpdateAccounts(accounts)
// Failure. Charge the gas fee. The 'value' was otherwise not transferred.
inAcc.Balance -= tx.Fee
s.UpdateAccount(inAcc)
// Throw away 'appState' which holds incomplete updates.
} else {
// Success
appState.Sync()