From 7a33aba6e5e3a35f998a857580b445584a076ac2 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 18 Mar 2015 01:27:16 -0700 Subject: [PATCH] block/state: add CallTx type --- block/tx.go | 27 ++++++++++++++++++++ state/state.go | 67 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/block/tx.go b/block/tx.go index d4d5fb6fd..0958dcb2e 100644 --- a/block/tx.go +++ b/block/tx.go @@ -25,6 +25,7 @@ Tx (Transaction) is an atomic operation on the ledger state. Account Txs: - SendTx Send coins to address + - CallTx Send a msg to a contract that runs in the vm Validation Txs: - BondTx New validator posts a bond @@ -39,6 +40,7 @@ type Tx interface { const ( // Account transactions TxTypeSend = byte(0x01) + TxTypeCall = byte(0x02) // Validation transactions TxTypeBond = byte(0x11) @@ -51,6 +53,7 @@ const ( var _ = binary.RegisterInterface( struct{ Tx }{}, binary.ConcreteType{&SendTx{}}, + binary.ConcreteType{&CallTx{}}, binary.ConcreteType{&BondTx{}}, binary.ConcreteType{&UnbondTx{}}, binary.ConcreteType{&RebondTx{}}, @@ -139,6 +142,30 @@ func (tx *SendTx) String() string { //----------------------------------------------------------------------------- +type CallTx struct { + Input *TxInput + Address []byte + GasLimit uint64 + FeeLimit uint64 + Data []byte +} + +func (tx *CallTx) TypeByte() byte { return TxTypeCall } + +func (tx *CallTx) WriteSignBytes(w io.Writer, n *int64, err *error) { + tx.Input.WriteSignBytes(w, n, err) + binary.WriteByteSlice(tx.Address, w, n, err) + binary.WriteUint64(tx.GasLimit, w, n, err) + binary.WriteUint64(tx.FeeLimit, w, n, err) + binary.WriteByteSlice(tx.Data, w, n, err) +} + +func (tx *CallTx) String() string { + return Fmt("CallTx{%v -> %x: %x}", tx.Input, tx.Address, tx.Data) +} + +//----------------------------------------------------------------------------- + type BondTx struct { PubKey account.PubKeyEd25519 Inputs []*TxInput diff --git a/state/state.go b/state/state.go index 83fd7b896..a3d98d05c 100644 --- a/state/state.go +++ b/state/state.go @@ -128,16 +128,8 @@ func (s *State) GetOrMakeAccounts(ins []*blk.TxInput, outs []*blk.TxOutput) (map return nil, blk.ErrTxInvalidAddress } // PubKey should be present in either "account" or "in" - if _, isNil := acc.PubKey.(account.PubKeyNil); isNil { - if _, isNil := in.PubKey.(account.PubKeyNil); isNil { - return nil, blk.ErrTxUnknownPubKey - } - if !bytes.Equal(in.PubKey.Address(), acc.Address) { - return nil, blk.ErrTxInvalidPubKey - } - acc.PubKey = in.PubKey - } else { - in.PubKey = account.PubKeyNil{} + if err := checkInputPubKey(acc, in); err != nil { + return nil, err } accounts[string(in.Address)] = acc } @@ -161,6 +153,21 @@ func (s *State) GetOrMakeAccounts(ins []*blk.TxInput, outs []*blk.TxOutput) (map return accounts, nil } +func checkInputPubKey(acc *account.Account, in *blk.TxInput) error { + if _, isNil := acc.PubKey.(account.PubKeyNil); isNil { + if _, isNil := in.PubKey.(account.PubKeyNil); isNil { + return blk.ErrTxUnknownPubKey + } + if !bytes.Equal(in.PubKey.Address(), acc.Address) { + return blk.ErrTxInvalidPubKey + } + acc.PubKey = in.PubKey + } else { + in.PubKey = account.PubKeyNil{} + } + return nil +} + func (s *State) ValidateInputs(accounts map[string]*account.Account, signBytes []byte, ins []*blk.TxInput) (total uint64, err error) { for _, in := range ins { acc := accounts[string(in.Address)] @@ -261,6 +268,46 @@ func (s *State) ExecTx(tx_ blk.Tx) error { s.UpdateAccounts(accounts) return nil + case *blk.CallTx: + tx := tx_.(*blk.CallTx) + accounts := map[string]*account.Account{} + inAcc := s.GetAccount(tx.Input.Address) + if inAcc == nil { + return blk.ErrTxInvalidAddress + } + // 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}) + if err != nil { + return err + } + + // validate output address + if len(tx.Address) != 20 { + return blk.ErrTxInvalidAddress + } + outAcc := s.GetAccount(tx.Address) + if outAcc == nil { + return blk.ErrTxInvalidAddress + } + accounts[string(tx.Address)] = outAcc + + // TODO: fees + // inTotal -= fees + + // Good! Adjust accounts + s.AdjustByInputs(accounts, []*blk.TxInput{tx.Input}) + outAcc.Balance += inTotal + s.UpdateAccounts(accounts) + + // TODO: Run the contract call! + + return nil + case *blk.BondTx: tx := tx_.(*blk.BondTx) valInfo := s.GetValidatorInfo(tx.PubKey.Address())