aboutsummaryrefslogtreecommitdiffstats
path: root/ethchain/state_transition.go
diff options
context:
space:
mode:
Diffstat (limited to 'ethchain/state_transition.go')
-rw-r--r--ethchain/state_transition.go206
1 files changed, 206 insertions, 0 deletions
diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go
new file mode 100644
index 000000000..6ec9205e9
--- /dev/null
+++ b/ethchain/state_transition.go
@@ -0,0 +1,206 @@
+package ethchain
+
+import (
+ "fmt"
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+)
+
+type StateTransition struct {
+ coinbase []byte
+ tx *Transaction
+ gas *big.Int
+ state *State
+ block *Block
+
+ cb, rec, sen *StateObject
+}
+
+func NewStateTransition(coinbase []byte, tx *Transaction, state *State, block *Block) *StateTransition {
+ return &StateTransition{coinbase, tx, new(big.Int), state, block, nil, nil, nil}
+}
+
+func (self *StateTransition) Coinbase() *StateObject {
+ if self.cb != nil {
+ return self.cb
+ }
+
+ self.cb = self.state.GetAccount(self.coinbase)
+ return self.cb
+}
+func (self *StateTransition) Sender() *StateObject {
+ if self.sen != nil {
+ return self.sen
+ }
+
+ self.sen = self.state.GetAccount(self.tx.Sender())
+ return self.sen
+}
+func (self *StateTransition) Receiver() *StateObject {
+ if self.tx.CreatesContract() {
+ return nil
+ }
+
+ if self.rec != nil {
+ return self.rec
+ }
+
+ self.rec = self.state.GetAccount(self.tx.Recipient)
+ return self.rec
+}
+
+func (self *StateTransition) MakeStateObject(state *State, tx *Transaction) *StateObject {
+ contract := MakeContract(tx, state)
+ if contract != nil {
+ state.states[string(tx.CreationAddress())] = contract.state
+
+ return contract
+ }
+
+ return nil
+}
+
+func (self *StateTransition) UseGas(amount *big.Int) error {
+ if self.gas.Cmp(amount) < 0 {
+ return OutOfGasError()
+ }
+ self.gas.Sub(self.gas, amount)
+
+ return nil
+}
+
+func (self *StateTransition) AddGas(amount *big.Int) {
+ self.gas.Add(self.gas, amount)
+}
+
+func (self *StateTransition) BuyGas() error {
+ var err error
+
+ sender := self.Sender()
+ if sender.Amount.Cmp(self.tx.GasValue()) < 0 {
+ return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), self.tx.Value)
+ }
+
+ coinbase := self.Coinbase()
+ err = coinbase.BuyGas(self.tx.Gas, self.tx.GasPrice)
+ if err != nil {
+ return err
+ }
+ self.state.UpdateStateObject(coinbase)
+
+ self.AddGas(self.tx.Gas)
+ sender.SubAmount(self.tx.GasValue())
+
+ return nil
+}
+
+func (self *StateTransition) TransitionState() (err error) {
+ //snapshot := st.state.Snapshot()
+
+ defer func() {
+ if r := recover(); r != nil {
+ ethutil.Config.Log.Infoln(r)
+ err = fmt.Errorf("%v", r)
+ }
+ }()
+
+ var (
+ tx = self.tx
+ sender = self.Sender()
+ receiver *StateObject
+ )
+
+ if sender.Nonce != tx.Nonce {
+ return NonceError(tx.Nonce, sender.Nonce)
+ }
+
+ sender.Nonce += 1
+ defer func() {
+ // Notify all subscribers
+ //self.Ethereum.Reactor().Post("newTx:post", tx)
+ }()
+
+ if err = self.BuyGas(); err != nil {
+ return err
+ }
+
+ receiver = self.Receiver()
+
+ if err = self.UseGas(GasTx); err != nil {
+ return err
+ }
+
+ dataPrice := big.NewInt(int64(len(tx.Data)))
+ dataPrice.Mul(dataPrice, GasData)
+ if err = self.UseGas(dataPrice); err != nil {
+ return err
+ }
+
+ if receiver == nil { // Contract
+ receiver = self.MakeStateObject(self.state, tx)
+ if receiver == nil {
+ return fmt.Errorf("ERR. Unable to create contract with transaction %v", tx)
+ }
+ }
+
+ if err = self.transferValue(sender, receiver); err != nil {
+ return err
+ }
+
+ if tx.CreatesContract() {
+ fmt.Println(Disassemble(receiver.Init()))
+ // Evaluate the initialization script
+ // and use the return value as the
+ // script section for the state object.
+ //script, gas, err = sm.Eval(state, contract.Init(), contract, tx, block)
+ code, err := self.Eval(receiver.Init(), receiver)
+ if err != nil {
+ return fmt.Errorf("Error during init script run %v", err)
+ }
+
+ receiver.script = code
+ }
+
+ self.state.UpdateStateObject(sender)
+ self.state.UpdateStateObject(receiver)
+
+ return nil
+}
+
+func (self *StateTransition) transferValue(sender, receiver *StateObject) error {
+ if sender.Amount.Cmp(self.tx.Value) < 0 {
+ return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.tx.Value, sender.Amount)
+ }
+
+ // Subtract the amount from the senders account
+ sender.SubAmount(self.tx.Value)
+ // Add the amount to receivers account which should conclude this transaction
+ receiver.AddAmount(self.tx.Value)
+
+ ethutil.Config.Log.Debugf("%x => %x (%v) %x\n", sender.Address()[:4], receiver.Address()[:4], self.tx.Value, self.tx.Hash())
+
+ return nil
+}
+
+func (self *StateTransition) Eval(script []byte, context *StateObject) (ret []byte, err error) {
+ var (
+ tx = self.tx
+ block = self.block
+ initiator = self.Sender()
+ state = self.state
+ )
+
+ closure := NewClosure(initiator, context, script, state, self.gas, tx.GasPrice)
+ vm := NewVm(state, nil, RuntimeVars{
+ Origin: initiator.Address(),
+ BlockNumber: block.BlockInfo().Number,
+ PrevHash: block.PrevHash,
+ Coinbase: block.Coinbase,
+ Time: block.Time,
+ Diff: block.Difficulty,
+ Value: tx.Value,
+ })
+ ret, _, err = closure.Call(vm, tx.Data, nil)
+
+ return
+}