state_transition.go 6.33 KB
Newer Older
obscuren's avatar
obscuren committed
1
package core
2 3 4

import (
	"fmt"
5
	"math/big"
6

obscuren's avatar
obscuren committed
7
	"github.com/ethereum/go-ethereum/common"
obscuren's avatar
obscuren committed
8 9
	"github.com/ethereum/go-ethereum/core/state"
	"github.com/ethereum/go-ethereum/core/vm"
obscuren's avatar
obscuren committed
10
	"github.com/ethereum/go-ethereum/crypto"
obscuren's avatar
obscuren committed
11 12
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
13
	"github.com/ethereum/go-ethereum/params"
14 15
)

16 17
const tryJit = false

obscuren's avatar
obscuren committed
18
var ()
obscuren's avatar
obscuren committed
19

obscuren's avatar
obscuren committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*
 * The State transitioning model
 *
 * A state transition is a change made when a transaction is applied to the current world state
 * The state transitioning model does all all the necessary work to work out a valid new state root.
 * 1) Nonce handling
 * 2) Pre pay / buy gas of the coinbase (miner)
 * 3) Create a new state object if the recipient is \0*32
 * 4) Value transfer
 * == If contract creation ==
 * 4a) Attempt to run transaction data
 * 4b) If valid, use result as code for the new state object
 * == end ==
 * 5) Run Script section
 * 6) Derive new state root
 */
36
type StateTransition struct {
obscuren's avatar
obscuren committed
37
	coinbase      common.Address
38 39 40 41 42 43
	msg           Message
	gas, gasPrice *big.Int
	initialGas    *big.Int
	value         *big.Int
	data          []byte
	state         *state.StateDB
44

obscuren's avatar
obscuren committed
45
	cb, rec, sen *state.StateObject
46

47
	env vm.Environment
48 49
}

50
// Message represents a message sent to a contract.
51
type Message interface {
52 53
	From() (common.Address, error)
	To() *common.Address
54 55 56 57 58 59 60

	GasPrice() *big.Int
	Gas() *big.Int
	Value() *big.Int

	Nonce() uint64
	Data() []byte
61
}
obscuren's avatar
obscuren committed
62 63 64 65 66 67

func AddressFromMessage(msg Message) common.Address {
	from, _ := msg.From()

	return crypto.CreateAddress(from, msg.Nonce())
}
68

obscuren's avatar
obscuren committed
69
func MessageCreatesContract(msg Message) bool {
70
	return msg.To() == nil
obscuren's avatar
obscuren committed
71 72
}

obscuren's avatar
obscuren committed
73 74 75 76
func MessageGasValue(msg Message) *big.Int {
	return new(big.Int).Mul(msg.Gas(), msg.GasPrice())
}

obscuren's avatar
obscuren committed
77 78 79 80 81 82 83 84 85 86 87 88 89
func IntrinsicGas(msg Message) *big.Int {
	igas := new(big.Int).Set(params.TxGas)
	for _, byt := range msg.Data() {
		if byt != 0 {
			igas.Add(igas, params.TxDataNonZeroGas)
		} else {
			igas.Add(igas, params.TxDataZeroGas)
		}
	}

	return igas
}

90 91 92 93
func ApplyMessage(env vm.Environment, msg Message, coinbase *state.StateObject) ([]byte, *big.Int, error) {
	return NewStateTransition(env, msg, coinbase).transitionState()
}

94 95 96 97 98 99 100 101 102 103 104 105
func NewStateTransition(env vm.Environment, msg Message, coinbase *state.StateObject) *StateTransition {
	return &StateTransition{
		coinbase:   coinbase.Address(),
		env:        env,
		msg:        msg,
		gas:        new(big.Int),
		gasPrice:   new(big.Int).Set(msg.GasPrice()),
		initialGas: new(big.Int),
		value:      msg.Value(),
		data:       msg.Data(),
		state:      env.State(),
		cb:         coinbase,
106
	}
107 108
}

obscuren's avatar
obscuren committed
109
func (self *StateTransition) Coinbase() *state.StateObject {
obscuren's avatar
obscuren committed
110
	return self.state.GetOrNewStateObject(self.coinbase)
111
}
112
func (self *StateTransition) From() *state.StateObject {
113 114
	f, _ := self.msg.From()
	return self.state.GetOrNewStateObject(f)
115
}
116
func (self *StateTransition) To() *state.StateObject {
117
	if self.msg == nil {
118 119
		return nil
	}
120 121 122 123 124
	to := self.msg.To()
	if to == nil {
		return nil // contract creation
	}
	return self.state.GetOrNewStateObject(*to)
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
}

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

143
	sender := self.From()
obscuren's avatar
obscuren committed
144
	if sender.Balance().Cmp(MessageGasValue(self.msg)) < 0 {
obscuren's avatar
obscuren committed
145
		return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address().Bytes()[:4], MessageGasValue(self.msg), sender.Balance())
146 147 148
	}

	coinbase := self.Coinbase()
149
	err = coinbase.BuyGas(self.msg.Gas(), self.msg.GasPrice())
150 151 152 153
	if err != nil {
		return err
	}

154
	self.AddGas(self.msg.Gas())
obscuren's avatar
obscuren committed
155
	self.initialGas.Set(self.msg.Gas())
156
	sender.SubBalance(MessageGasValue(self.msg))
157 158 159 160

	return nil
}

161 162
func (self *StateTransition) preCheck() (err error) {
	var (
163 164
		msg    = self.msg
		sender = self.From()
165 166 167
	)

	// Make sure this transaction's nonce is correct
168 169
	if sender.Nonce() != msg.Nonce() {
		return NonceError(msg.Nonce(), sender.Nonce())
170 171 172 173
	}

	// Pre-pay gas / Buy gas of the coinbase account
	if err = self.BuyGas(); err != nil {
obscuren's avatar
obscuren committed
174 175 176
		if state.IsGasLimitErr(err) {
			return err
		}
177
		return InvalidTxError(err)
178 179 180 181 182
	}

	return nil
}

183
func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, err error) {
184 185 186 187
	if err = self.preCheck(); err != nil {
		return
	}

188
	var (
189 190
		msg    = self.msg
		sender = self.From()
191 192
	)

obscuren's avatar
obscuren committed
193 194
	// Pay intrinsic gas
	if err = self.UseGas(IntrinsicGas(self.msg)); err != nil {
195
		return nil, nil, InvalidTxError(err)
196 197
	}

198
	vmenv := self.env
obscuren's avatar
obscuren committed
199
	var ref vm.ContextRef
obscuren's avatar
obscuren committed
200
	if MessageCreatesContract(msg) {
obscuren's avatar
obscuren committed
201
		ret, err, ref = vmenv.Create(sender, self.msg.Data(), self.gas, self.gasPrice, self.value)
202 203
		if err == nil {
			dataGas := big.NewInt(int64(len(ret)))
204
			dataGas.Mul(dataGas, params.CreateDataGas)
obscuren's avatar
obscuren committed
205
			if err := self.UseGas(dataGas); err == nil {
206
				ref.SetCode(ret)
207
			} else {
obscuren's avatar
obscuren committed
208
				glog.V(logger.Core).Infoln("Insufficient gas for creating code. Require", dataGas, "and have", self.gas)
209 210
			}
		}
obscuren's avatar
obscuren committed
211
	} else {
obscuren's avatar
obscuren committed
212
		// Increment the nonce for the next transaction
213
		self.state.SetNonce(sender.Address(), sender.Nonce()+1)
214
		ret, err = vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
obscuren's avatar
obscuren committed
215
	}
216

obscuren's avatar
obscuren committed
217
	if err != nil && IsValueTransferErr(err) {
218
		return nil, nil, InvalidTxError(err)
219 220
	}

221 222
	self.refundGas()
	self.state.AddBalance(self.coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice))
223

224
	return ret, self.gasUsed(), err
225
}
obscuren's avatar
obscuren committed
226

227
func (self *StateTransition) refundGas() {
228 229 230
	coinbase, sender := self.Coinbase(), self.From()
	// Return remaining gas
	remaining := new(big.Int).Mul(self.gas, self.msg.GasPrice())
231
	sender.AddBalance(remaining)
232

obscuren's avatar
obscuren committed
233
	uhalf := new(big.Int).Div(self.gasUsed(), common.Big2)
obscuren's avatar
obscuren committed
234
	for addr, ref := range self.state.Refunds() {
obscuren's avatar
obscuren committed
235
		refund := common.BigMin(uhalf, ref)
236
		self.gas.Add(self.gas, refund)
obscuren's avatar
obscuren committed
237
		self.state.AddBalance(common.StringToAddress(addr), refund.Mul(refund, self.msg.GasPrice()))
obscuren's avatar
obscuren committed
238 239
	}

240
	coinbase.RefundGas(self.gas, self.msg.GasPrice())
obscuren's avatar
obscuren committed
241 242
}

243
func (self *StateTransition) gasUsed() *big.Int {
obscuren's avatar
obscuren committed
244 245
	return new(big.Int).Sub(self.initialGas, self.gas)
}
246 247 248

// Converts an message in to a state object
func makeContract(msg Message, state *state.StateDB) *state.StateObject {
249 250
	faddr, _ := msg.From()
	addr := crypto.CreateAddress(faddr, msg.Nonce())
251

252 253
	contract := state.GetOrNewStateObject(addr)
	contract.SetInitCode(msg.Data())
254

255
	return contract
256
}