Refactored to new state and vm
This commit is contained in:
parent
958b482ada
commit
32d125131f
@ -6,7 +6,7 @@ Ethereum
|
||||
Ethereum Go Development package (C) Jeffrey Wilcke
|
||||
|
||||
Ethereum is currently in its testing phase. The current state is "Proof
|
||||
of Concept 0.5.20". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)).
|
||||
of Concept 0.6.0". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)).
|
||||
|
||||
Ethereum Go is split up in several sub packages Please refer to each
|
||||
individual package for more information.
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethcrypto"
|
||||
"github.com/ethereum/eth-go/ethstate"
|
||||
"github.com/ethereum/eth-go/ethtrie"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
@ -39,7 +40,7 @@ type Block struct {
|
||||
Coinbase []byte
|
||||
// Block Trie state
|
||||
//state *ethutil.Trie
|
||||
state *State
|
||||
state *ethstate.State
|
||||
// Difficulty for the current block
|
||||
Difficulty *big.Int
|
||||
// Creation time
|
||||
@ -104,7 +105,7 @@ func CreateBlock(root interface{},
|
||||
}
|
||||
block.SetUncles([]*Block{})
|
||||
|
||||
block.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, root))
|
||||
block.state = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, root))
|
||||
|
||||
return block
|
||||
}
|
||||
@ -116,12 +117,12 @@ func (block *Block) Hash() []byte {
|
||||
|
||||
func (block *Block) HashNoNonce() []byte {
|
||||
return ethcrypto.Sha3Bin(ethutil.Encode([]interface{}{block.PrevHash,
|
||||
block.UncleSha, block.Coinbase, block.state.trie.Root,
|
||||
block.UncleSha, block.Coinbase, block.state.Trie.Root,
|
||||
block.TxSha, block.Difficulty, block.Number, block.MinGasPrice,
|
||||
block.GasLimit, block.GasUsed, block.Time, block.Extra}))
|
||||
}
|
||||
|
||||
func (block *Block) State() *State {
|
||||
func (block *Block) State() *ethstate.State {
|
||||
return block.state
|
||||
}
|
||||
|
||||
@ -140,17 +141,17 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool {
|
||||
|
||||
base := new(big.Int)
|
||||
contract.Amount = base.Sub(contract.Amount, fee)
|
||||
block.state.trie.Update(string(addr), string(contract.RlpEncode()))
|
||||
block.state.Trie.Update(string(addr), string(contract.RlpEncode()))
|
||||
|
||||
data := block.state.trie.Get(string(block.Coinbase))
|
||||
data := block.state.Trie.Get(string(block.Coinbase))
|
||||
|
||||
// Get the ether (Coinbase) and add the fee (gief fee to miner)
|
||||
account := NewStateObjectFromBytes(block.Coinbase, []byte(data))
|
||||
account := ethstate.NewStateObjectFromBytes(block.Coinbase, []byte(data))
|
||||
|
||||
base = new(big.Int)
|
||||
account.Amount = base.Add(account.Amount, fee)
|
||||
|
||||
//block.state.trie.Update(string(block.Coinbase), string(ether.RlpEncode()))
|
||||
//block.state.Trie.Update(string(block.Coinbase), string(ether.RlpEncode()))
|
||||
block.state.UpdateStateObject(account)
|
||||
|
||||
return true
|
||||
@ -312,7 +313,7 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
|
||||
block.PrevHash = header.Get(0).Bytes()
|
||||
block.UncleSha = header.Get(1).Bytes()
|
||||
block.Coinbase = header.Get(2).Bytes()
|
||||
block.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, header.Get(3).Val))
|
||||
block.state = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, header.Get(3).Val))
|
||||
block.TxSha = header.Get(4).Bytes()
|
||||
block.Difficulty = header.Get(5).BigInt()
|
||||
block.Number = header.Get(6).BigInt()
|
||||
@ -354,7 +355,7 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block {
|
||||
block.PrevHash = header.Get(0).Bytes()
|
||||
block.UncleSha = header.Get(1).Bytes()
|
||||
block.Coinbase = header.Get(2).Bytes()
|
||||
block.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, header.Get(3).Val))
|
||||
block.state = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, header.Get(3).Val))
|
||||
block.TxSha = header.Get(4).Bytes()
|
||||
block.Difficulty = header.Get(5).BigInt()
|
||||
block.Number = header.Get(6).BigInt()
|
||||
@ -369,7 +370,7 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block {
|
||||
}
|
||||
|
||||
func (block *Block) GetRoot() interface{} {
|
||||
return block.state.trie.Root
|
||||
return block.state.Trie.Root
|
||||
}
|
||||
|
||||
func (self *Block) Receipts() []*Receipt {
|
||||
@ -385,7 +386,7 @@ func (block *Block) header() []interface{} {
|
||||
// Coinbase address
|
||||
block.Coinbase,
|
||||
// root state
|
||||
block.state.trie.Root,
|
||||
block.state.Trie.Root,
|
||||
// Sha of tx
|
||||
block.TxSha,
|
||||
// Current block Difficulty
|
||||
@ -429,7 +430,7 @@ func (block *Block) String() string {
|
||||
block.PrevHash,
|
||||
block.UncleSha,
|
||||
block.Coinbase,
|
||||
block.state.trie.Root,
|
||||
block.state.Trie.Root,
|
||||
block.TxSha,
|
||||
block.Difficulty,
|
||||
block.Number,
|
||||
|
@ -44,7 +44,7 @@ func (bc *BlockChain) NewBlock(coinbase []byte) *Block {
|
||||
hash := ZeroHash256
|
||||
|
||||
if bc.CurrentBlock != nil {
|
||||
root = bc.CurrentBlock.state.trie.Root
|
||||
root = bc.CurrentBlock.state.Trie.Root
|
||||
hash = bc.LastBlockHash
|
||||
lastBlockTime = bc.CurrentBlock.Time
|
||||
}
|
||||
@ -297,7 +297,7 @@ func (bc *BlockChain) setLastBlock() {
|
||||
} else {
|
||||
AddTestNetFunds(bc.genesisBlock)
|
||||
|
||||
bc.genesisBlock.state.trie.Sync()
|
||||
bc.genesisBlock.state.Trie.Sync()
|
||||
// Prepare the genesis block
|
||||
bc.Add(bc.genesisBlock)
|
||||
|
||||
|
@ -1,121 +0,0 @@
|
||||
package ethchain
|
||||
|
||||
// TODO Re write VM to use values instead of big integers?
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type ClosureRef interface {
|
||||
ReturnGas(*big.Int, *big.Int, *State)
|
||||
Address() []byte
|
||||
GetStorage(*big.Int) *ethutil.Value
|
||||
SetStorage(*big.Int, *ethutil.Value)
|
||||
N() *big.Int
|
||||
}
|
||||
|
||||
// Basic inline closure object which implement the 'closure' interface
|
||||
type Closure struct {
|
||||
caller ClosureRef
|
||||
object *StateObject
|
||||
Script []byte
|
||||
State *State
|
||||
|
||||
Gas, UsedGas, Price *big.Int
|
||||
|
||||
Args []byte
|
||||
}
|
||||
|
||||
// Create a new closure for the given data items
|
||||
func NewClosure(caller ClosureRef, object *StateObject, script []byte, state *State, gas, price *big.Int) *Closure {
|
||||
c := &Closure{caller: caller, object: object, Script: script, State: state, Args: nil}
|
||||
|
||||
// Gas should be a pointer so it can safely be reduced through the run
|
||||
// This pointer will be off the state transition
|
||||
c.Gas = gas //new(big.Int).Set(gas)
|
||||
// In most cases price and value are pointers to transaction objects
|
||||
// and we don't want the transaction's values to change.
|
||||
c.Price = new(big.Int).Set(price)
|
||||
c.UsedGas = new(big.Int)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Retuns the x element in data slice
|
||||
func (c *Closure) GetStorage(x *big.Int) *ethutil.Value {
|
||||
m := c.object.GetStorage(x)
|
||||
if m == nil {
|
||||
return ethutil.EmptyValue()
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (c *Closure) Get(x *big.Int) *ethutil.Value {
|
||||
return c.Gets(x, big.NewInt(1))
|
||||
}
|
||||
|
||||
func (c *Closure) Gets(x, y *big.Int) *ethutil.Value {
|
||||
if x.Int64() >= int64(len(c.Script)) || y.Int64() >= int64(len(c.Script)) {
|
||||
return ethutil.NewValue(0)
|
||||
}
|
||||
|
||||
partial := c.Script[x.Int64() : x.Int64()+y.Int64()]
|
||||
|
||||
return ethutil.NewValue(partial)
|
||||
}
|
||||
|
||||
func (c *Closure) SetStorage(x *big.Int, val *ethutil.Value) {
|
||||
c.object.SetStorage(x, val)
|
||||
}
|
||||
|
||||
func (c *Closure) Address() []byte {
|
||||
return c.object.Address()
|
||||
}
|
||||
|
||||
func (c *Closure) Call(vm *Vm, args []byte) ([]byte, *big.Int, error) {
|
||||
c.Args = args
|
||||
|
||||
ret, err := vm.RunClosure(c)
|
||||
|
||||
return ret, c.UsedGas, err
|
||||
}
|
||||
|
||||
func (c *Closure) Return(ret []byte) []byte {
|
||||
// Return the remaining gas to the caller
|
||||
c.caller.ReturnGas(c.Gas, c.Price, c.State)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *Closure) UseGas(gas *big.Int) bool {
|
||||
if c.Gas.Cmp(gas) < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Sub the amount of gas from the remaining
|
||||
c.Gas.Sub(c.Gas, gas)
|
||||
c.UsedGas.Add(c.UsedGas, gas)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Implement the caller interface
|
||||
func (c *Closure) ReturnGas(gas, price *big.Int, state *State) {
|
||||
// Return the gas to the closure
|
||||
c.Gas.Add(c.Gas, gas)
|
||||
c.UsedGas.Sub(c.UsedGas, gas)
|
||||
}
|
||||
|
||||
func (c *Closure) Object() *StateObject {
|
||||
return c.object
|
||||
}
|
||||
|
||||
func (c *Closure) Caller() ClosureRef {
|
||||
return c.caller
|
||||
}
|
||||
|
||||
func (c *Closure) N() *big.Int {
|
||||
return c.object.N()
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type OpType int
|
||||
|
||||
const (
|
||||
tNorm = iota
|
||||
tData
|
||||
tExtro
|
||||
tCrypto
|
||||
)
|
||||
|
||||
type TxCallback func(opType OpType) bool
|
||||
|
||||
// Simple push/pop stack mechanism
|
||||
type Stack struct {
|
||||
data []*big.Int
|
||||
}
|
||||
|
||||
func NewStack() *Stack {
|
||||
return &Stack{}
|
||||
}
|
||||
|
||||
func (st *Stack) Data() []*big.Int {
|
||||
return st.data
|
||||
}
|
||||
|
||||
func (st *Stack) Len() int {
|
||||
return len(st.data)
|
||||
}
|
||||
|
||||
func (st *Stack) Pop() *big.Int {
|
||||
str := st.data[len(st.data)-1]
|
||||
|
||||
copy(st.data[:len(st.data)-1], st.data[:len(st.data)-1])
|
||||
st.data = st.data[:len(st.data)-1]
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func (st *Stack) Popn() (*big.Int, *big.Int) {
|
||||
ints := st.data[len(st.data)-2:]
|
||||
|
||||
copy(st.data[:len(st.data)-2], st.data[:len(st.data)-2])
|
||||
st.data = st.data[:len(st.data)-2]
|
||||
|
||||
return ints[0], ints[1]
|
||||
}
|
||||
|
||||
func (st *Stack) Peek() *big.Int {
|
||||
str := st.data[len(st.data)-1]
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func (st *Stack) Peekn() (*big.Int, *big.Int) {
|
||||
ints := st.data[:2]
|
||||
|
||||
return ints[0], ints[1]
|
||||
}
|
||||
|
||||
func (st *Stack) Push(d *big.Int) {
|
||||
st.data = append(st.data, new(big.Int).Set(d))
|
||||
}
|
||||
|
||||
func (st *Stack) Get(amount *big.Int) []*big.Int {
|
||||
// offset + size <= len(data)
|
||||
length := big.NewInt(int64(len(st.data)))
|
||||
if amount.Cmp(length) <= 0 {
|
||||
start := new(big.Int).Sub(length, amount)
|
||||
return st.data[start.Int64():length.Int64()]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *Stack) Print() {
|
||||
fmt.Println("### stack ###")
|
||||
if len(st.data) > 0 {
|
||||
for i, val := range st.data {
|
||||
fmt.Printf("%-3d %v\n", i, val)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("-- empty --")
|
||||
}
|
||||
fmt.Println("#############")
|
||||
}
|
||||
|
||||
type Memory struct {
|
||||
store []byte
|
||||
}
|
||||
|
||||
func (m *Memory) Set(offset, size int64, value []byte) {
|
||||
totSize := offset + size
|
||||
lenSize := int64(len(m.store) - 1)
|
||||
if totSize > lenSize {
|
||||
// Calculate the diff between the sizes
|
||||
diff := totSize - lenSize
|
||||
if diff > 0 {
|
||||
// Create a new empty slice and append it
|
||||
newSlice := make([]byte, diff-1)
|
||||
// Resize slice
|
||||
m.store = append(m.store, newSlice...)
|
||||
}
|
||||
}
|
||||
copy(m.store[offset:offset+size], value)
|
||||
}
|
||||
|
||||
func (m *Memory) Resize(size uint64) {
|
||||
if uint64(m.Len()) < size {
|
||||
m.store = append(m.store, make([]byte, size-uint64(m.Len()))...)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Memory) Get(offset, size int64) []byte {
|
||||
if len(m.store) > int(offset) {
|
||||
end := int(math.Min(float64(len(m.store)), float64(offset+size)))
|
||||
|
||||
return m.store[offset:end]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Memory) Len() int {
|
||||
return len(m.store)
|
||||
}
|
||||
|
||||
func (m *Memory) Data() []byte {
|
||||
return m.store
|
||||
}
|
||||
|
||||
func (m *Memory) Print() {
|
||||
fmt.Printf("### mem %d bytes ###\n", len(m.store))
|
||||
if len(m.store) > 0 {
|
||||
addr := 0
|
||||
for i := 0; i+32 <= len(m.store); i += 32 {
|
||||
fmt.Printf("%03d: % x\n", addr, m.store[i:i+32])
|
||||
addr++
|
||||
}
|
||||
} else {
|
||||
fmt.Println("-- empty --")
|
||||
}
|
||||
fmt.Println("####################")
|
||||
}
|
@ -1,250 +0,0 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethcrypto"
|
||||
"github.com/ethereum/eth-go/ethtrie"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// States within the ethereum protocol are used to store anything
|
||||
// within the merkle trie. States take care of caching and storing
|
||||
// nested states. It's the general query interface to retrieve:
|
||||
// * Contracts
|
||||
// * Accounts
|
||||
type State struct {
|
||||
// The trie for this structure
|
||||
trie *ethtrie.Trie
|
||||
|
||||
stateObjects map[string]*StateObject
|
||||
|
||||
manifest *Manifest
|
||||
}
|
||||
|
||||
// Create a new state from a given trie
|
||||
func NewState(trie *ethtrie.Trie) *State {
|
||||
return &State{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()}
|
||||
}
|
||||
|
||||
// Retrieve the balance from the given address or 0 if object not found
|
||||
func (self *State) GetBalance(addr []byte) *big.Int {
|
||||
stateObject := self.GetStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.Amount
|
||||
}
|
||||
|
||||
return ethutil.Big0
|
||||
}
|
||||
|
||||
func (self *State) GetNonce(addr []byte) uint64 {
|
||||
stateObject := self.GetStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.Nonce
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
//
|
||||
// Setting, updating & deleting state object methods
|
||||
//
|
||||
|
||||
// Update the given state object and apply it to state trie
|
||||
func (self *State) UpdateStateObject(stateObject *StateObject) {
|
||||
addr := stateObject.Address()
|
||||
|
||||
ethutil.Config.Db.Put(ethcrypto.Sha3Bin(stateObject.Script()), stateObject.Script())
|
||||
|
||||
self.trie.Update(string(addr), string(stateObject.RlpEncode()))
|
||||
|
||||
self.manifest.AddObjectChange(stateObject)
|
||||
}
|
||||
|
||||
// Delete the given state object and delete it from the state trie
|
||||
func (self *State) DeleteStateObject(stateObject *StateObject) {
|
||||
self.trie.Delete(string(stateObject.Address()))
|
||||
|
||||
delete(self.stateObjects, string(stateObject.Address()))
|
||||
}
|
||||
|
||||
// Retrieve a state object given my the address. Nil if not found
|
||||
func (self *State) GetStateObject(addr []byte) *StateObject {
|
||||
addr = ethutil.Address(addr)
|
||||
|
||||
stateObject := self.stateObjects[string(addr)]
|
||||
if stateObject != nil {
|
||||
return stateObject
|
||||
}
|
||||
|
||||
data := self.trie.Get(string(addr))
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
stateObject = NewStateObjectFromBytes(addr, []byte(data))
|
||||
self.stateObjects[string(addr)] = stateObject
|
||||
|
||||
return stateObject
|
||||
}
|
||||
|
||||
// Retrieve a state object or create a new state object if nil
|
||||
func (self *State) GetOrNewStateObject(addr []byte) *StateObject {
|
||||
stateObject := self.GetStateObject(addr)
|
||||
if stateObject == nil {
|
||||
stateObject = self.NewStateObject(addr)
|
||||
}
|
||||
|
||||
return stateObject
|
||||
}
|
||||
|
||||
// Create a state object whether it exist in the trie or not
|
||||
func (self *State) NewStateObject(addr []byte) *StateObject {
|
||||
statelogger.Infof("(+) %x\n", addr)
|
||||
|
||||
stateObject := NewStateObject(addr)
|
||||
self.stateObjects[string(addr)] = stateObject
|
||||
|
||||
return stateObject
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
func (self *State) GetAccount(addr []byte) *StateObject {
|
||||
return self.GetOrNewStateObject(addr)
|
||||
}
|
||||
|
||||
//
|
||||
// Setting, copying of the state methods
|
||||
//
|
||||
|
||||
func (s *State) Cmp(other *State) bool {
|
||||
return s.trie.Cmp(other.trie)
|
||||
}
|
||||
|
||||
func (self *State) Copy() *State {
|
||||
if self.trie != nil {
|
||||
state := NewState(self.trie.Copy())
|
||||
for k, stateObject := range self.stateObjects {
|
||||
state.stateObjects[k] = stateObject.Copy()
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *State) Set(state *State) {
|
||||
if state == nil {
|
||||
panic("Tried setting 'state' to nil through 'Set'")
|
||||
}
|
||||
|
||||
self.trie = state.trie
|
||||
self.stateObjects = state.stateObjects
|
||||
}
|
||||
|
||||
func (s *State) Root() interface{} {
|
||||
return s.trie.Root
|
||||
}
|
||||
|
||||
// Resets the trie and all siblings
|
||||
func (s *State) Reset() {
|
||||
s.trie.Undo()
|
||||
|
||||
// Reset all nested states
|
||||
for _, stateObject := range s.stateObjects {
|
||||
if stateObject.state == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
//stateObject.state.Reset()
|
||||
stateObject.Reset()
|
||||
}
|
||||
|
||||
s.Empty()
|
||||
}
|
||||
|
||||
// Syncs the trie and all siblings
|
||||
func (s *State) Sync() {
|
||||
// Sync all nested states
|
||||
for _, stateObject := range s.stateObjects {
|
||||
//s.UpdateStateObject(stateObject)
|
||||
|
||||
if stateObject.state == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
stateObject.state.Sync()
|
||||
}
|
||||
|
||||
s.trie.Sync()
|
||||
|
||||
s.Empty()
|
||||
}
|
||||
|
||||
func (self *State) Empty() {
|
||||
self.stateObjects = make(map[string]*StateObject)
|
||||
}
|
||||
|
||||
func (self *State) Update() {
|
||||
for _, stateObject := range self.stateObjects {
|
||||
if stateObject.remove {
|
||||
self.DeleteStateObject(stateObject)
|
||||
} else {
|
||||
stateObject.Sync()
|
||||
|
||||
self.UpdateStateObject(stateObject)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME trie delete is broken
|
||||
valid, t2 := ethtrie.ParanoiaCheck(self.trie)
|
||||
if !valid {
|
||||
statelogger.Infof("Warn: PARANOIA: Different state root during copy %x vs %x\n", self.trie.Root, t2.Root)
|
||||
|
||||
self.trie = t2
|
||||
}
|
||||
}
|
||||
|
||||
// Debug stuff
|
||||
func (self *State) CreateOutputForDiff() {
|
||||
for _, stateObject := range self.stateObjects {
|
||||
stateObject.CreateOutputForDiff()
|
||||
}
|
||||
}
|
||||
|
||||
// Object manifest
|
||||
//
|
||||
// The object manifest is used to keep changes to the state so we can keep track of the changes
|
||||
// that occurred during a state transitioning phase.
|
||||
type Manifest struct {
|
||||
// XXX These will be handy in the future. Not important for now.
|
||||
objectAddresses map[string]bool
|
||||
storageAddresses map[string]map[string]bool
|
||||
|
||||
objectChanges map[string]*StateObject
|
||||
storageChanges map[string]map[string]*big.Int
|
||||
}
|
||||
|
||||
func NewManifest() *Manifest {
|
||||
m := &Manifest{objectAddresses: make(map[string]bool), storageAddresses: make(map[string]map[string]bool)}
|
||||
m.Reset()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Manifest) Reset() {
|
||||
m.objectChanges = make(map[string]*StateObject)
|
||||
m.storageChanges = make(map[string]map[string]*big.Int)
|
||||
}
|
||||
|
||||
func (m *Manifest) AddObjectChange(stateObject *StateObject) {
|
||||
m.objectChanges[string(stateObject.Address())] = stateObject
|
||||
}
|
||||
|
||||
func (m *Manifest) AddStorageChange(stateObject *StateObject, storageAddr []byte, storage *big.Int) {
|
||||
if m.storageChanges[string(stateObject.Address())] == nil {
|
||||
m.storageChanges[string(stateObject.Address())] = make(map[string]*big.Int)
|
||||
}
|
||||
|
||||
m.storageChanges[string(stateObject.Address())][string(storageAddr)] = storage
|
||||
}
|
@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethcrypto"
|
||||
"github.com/ethereum/eth-go/ethlog"
|
||||
_ "github.com/ethereum/eth-go/ethtrie"
|
||||
"github.com/ethereum/eth-go/ethstate"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/eth-go/ethwire"
|
||||
"math/big"
|
||||
@ -50,8 +50,6 @@ type StateManager struct {
|
||||
mutex sync.Mutex
|
||||
// Canonical block chain
|
||||
bc *BlockChain
|
||||
// Stack for processing contracts
|
||||
stack *Stack
|
||||
// non-persistent key/value memory storage
|
||||
mem map[string]*big.Int
|
||||
// Proof of work used for validating
|
||||
@ -62,10 +60,10 @@ type StateManager struct {
|
||||
// Transiently state. The trans state isn't ever saved, validated and
|
||||
// it could be used for setting account nonces without effecting
|
||||
// the main states.
|
||||
transState *State
|
||||
transState *ethstate.State
|
||||
// Mining state. The mining state is used purely and solely by the mining
|
||||
// operation.
|
||||
miningState *State
|
||||
miningState *ethstate.State
|
||||
|
||||
// The last attempted block is mainly used for debugging purposes
|
||||
// This does not have to be a valid block and will be set during
|
||||
@ -75,7 +73,6 @@ type StateManager struct {
|
||||
|
||||
func NewStateManager(ethereum EthManager) *StateManager {
|
||||
sm := &StateManager{
|
||||
stack: NewStack(),
|
||||
mem: make(map[string]*big.Int),
|
||||
Pow: &EasyPow{},
|
||||
Ethereum: ethereum,
|
||||
@ -87,19 +84,19 @@ func NewStateManager(ethereum EthManager) *StateManager {
|
||||
return sm
|
||||
}
|
||||
|
||||
func (sm *StateManager) CurrentState() *State {
|
||||
func (sm *StateManager) CurrentState() *ethstate.State {
|
||||
return sm.Ethereum.BlockChain().CurrentBlock.State()
|
||||
}
|
||||
|
||||
func (sm *StateManager) TransState() *State {
|
||||
func (sm *StateManager) TransState() *ethstate.State {
|
||||
return sm.transState
|
||||
}
|
||||
|
||||
func (sm *StateManager) MiningState() *State {
|
||||
func (sm *StateManager) MiningState() *ethstate.State {
|
||||
return sm.miningState
|
||||
}
|
||||
|
||||
func (sm *StateManager) NewMiningState() *State {
|
||||
func (sm *StateManager) NewMiningState() *ethstate.State {
|
||||
sm.miningState = sm.Ethereum.BlockChain().CurrentBlock.State().Copy()
|
||||
|
||||
return sm.miningState
|
||||
@ -109,7 +106,7 @@ func (sm *StateManager) BlockChain() *BlockChain {
|
||||
return sm.bc
|
||||
}
|
||||
|
||||
func (self *StateManager) ProcessTransactions(coinbase *StateObject, state *State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) {
|
||||
func (self *StateManager) ProcessTransactions(coinbase *ethstate.StateObject, state *ethstate.State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) {
|
||||
var (
|
||||
receipts Receipts
|
||||
handled, unhandled Transactions
|
||||
@ -225,7 +222,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
|
||||
}
|
||||
|
||||
if !block.State().Cmp(state) {
|
||||
err = fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().trie.Root, state.trie.Root)
|
||||
err = fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().Trie.Root, state.Trie.Root)
|
||||
return
|
||||
}
|
||||
|
||||
@ -242,7 +239,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
|
||||
if dontReact == false {
|
||||
sm.Ethereum.Reactor().Post("newBlock", block)
|
||||
|
||||
state.manifest.Reset()
|
||||
state.Manifest().Reset()
|
||||
}
|
||||
|
||||
sm.Ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val})
|
||||
@ -255,7 +252,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) ApplyDiff(state *State, parent, block *Block) (receipts Receipts, err error) {
|
||||
func (sm *StateManager) ApplyDiff(state *ethstate.State, parent, block *Block) (receipts Receipts, err error) {
|
||||
coinbase := state.GetOrNewStateObject(block.Coinbase)
|
||||
coinbase.SetGasPool(block.CalcGasLimit(parent))
|
||||
|
||||
@ -340,7 +337,7 @@ func CalculateUncleReward(block *Block) *big.Int {
|
||||
return UncleReward
|
||||
}
|
||||
|
||||
func (sm *StateManager) AccumelateRewards(state *State, block *Block) error {
|
||||
func (sm *StateManager) AccumelateRewards(state *ethstate.State, block *Block) error {
|
||||
// Get the account associated with the coinbase
|
||||
account := state.GetAccount(block.Coinbase)
|
||||
// Reward amount of ether to the coinbase address
|
||||
@ -364,14 +361,14 @@ func (sm *StateManager) Stop() {
|
||||
sm.bc.Stop()
|
||||
}
|
||||
|
||||
func (sm *StateManager) notifyChanges(state *State) {
|
||||
for addr, stateObject := range state.manifest.objectChanges {
|
||||
func (sm *StateManager) notifyChanges(state *ethstate.State) {
|
||||
for addr, stateObject := range state.Manifest().ObjectChanges {
|
||||
sm.Ethereum.Reactor().Post("object:"+addr, stateObject)
|
||||
}
|
||||
|
||||
for stateObjectAddr, mappedObjects := range state.manifest.storageChanges {
|
||||
for stateObjectAddr, mappedObjects := range state.Manifest().StorageChanges {
|
||||
for addr, value := range mappedObjects {
|
||||
sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, &StorageState{[]byte(stateObjectAddr), []byte(addr), value})
|
||||
sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, ðstate.StorageState{[]byte(stateObjectAddr), []byte(addr), value})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,368 +0,0 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethcrypto"
|
||||
"github.com/ethereum/eth-go/ethtrie"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Code []byte
|
||||
|
||||
func (self Code) String() string {
|
||||
return strings.Join(Disassemble(self), " ")
|
||||
}
|
||||
|
||||
type Storage map[string]*ethutil.Value
|
||||
|
||||
func (self Storage) Copy() Storage {
|
||||
cpy := make(Storage)
|
||||
for key, value := range self {
|
||||
// XXX Do we need a 'value' copy or is this sufficient?
|
||||
cpy[key] = value
|
||||
}
|
||||
|
||||
return cpy
|
||||
}
|
||||
|
||||
type StateObject struct {
|
||||
// Address of the object
|
||||
address []byte
|
||||
// Shared attributes
|
||||
Amount *big.Int
|
||||
ScriptHash []byte
|
||||
Nonce uint64
|
||||
// Contract related attributes
|
||||
state *State
|
||||
script Code
|
||||
initScript Code
|
||||
|
||||
storage Storage
|
||||
|
||||
// Total gas pool is the total amount of gas currently
|
||||
// left if this object is the coinbase. Gas is directly
|
||||
// purchased of the coinbase.
|
||||
gasPool *big.Int
|
||||
|
||||
// Mark for deletion
|
||||
// When an object is marked for deletion it will be delete from the trie
|
||||
// during the "update" phase of the state transition
|
||||
remove bool
|
||||
}
|
||||
|
||||
func (self *StateObject) Reset() {
|
||||
self.storage = make(Storage)
|
||||
self.state.Reset()
|
||||
}
|
||||
|
||||
// Converts an transaction in to a state object
|
||||
func MakeContract(tx *Transaction, state *State) *StateObject {
|
||||
// Create contract if there's no recipient
|
||||
if tx.IsContract() {
|
||||
addr := tx.CreationAddress()
|
||||
|
||||
contract := state.NewStateObject(addr)
|
||||
contract.initScript = tx.Data
|
||||
contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewStateObject(addr []byte) *StateObject {
|
||||
// This to ensure that it has 20 bytes (and not 0 bytes), thus left or right pad doesn't matter.
|
||||
address := ethutil.Address(addr)
|
||||
|
||||
object := &StateObject{address: address, Amount: new(big.Int), gasPool: new(big.Int)}
|
||||
object.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
|
||||
object.storage = make(Storage)
|
||||
object.gasPool = new(big.Int)
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject {
|
||||
contract := NewStateObject(address)
|
||||
contract.Amount = Amount
|
||||
contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root)))
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
func NewStateObjectFromBytes(address, data []byte) *StateObject {
|
||||
object := &StateObject{address: address}
|
||||
object.RlpDecode(data)
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (self *StateObject) MarkForDeletion() {
|
||||
self.remove = true
|
||||
statelogger.DebugDetailf("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.Amount)
|
||||
}
|
||||
|
||||
func (c *StateObject) GetAddr(addr []byte) *ethutil.Value {
|
||||
return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr))))
|
||||
}
|
||||
|
||||
func (c *StateObject) SetAddr(addr []byte, value interface{}) {
|
||||
c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode()))
|
||||
}
|
||||
|
||||
func (self *StateObject) GetStorage(key *big.Int) *ethutil.Value {
|
||||
return self.getStorage(key.Bytes())
|
||||
}
|
||||
func (self *StateObject) SetStorage(key *big.Int, value *ethutil.Value) {
|
||||
self.setStorage(key.Bytes(), value)
|
||||
}
|
||||
|
||||
func (self *StateObject) getStorage(k []byte) *ethutil.Value {
|
||||
key := ethutil.LeftPadBytes(k, 32)
|
||||
|
||||
value := self.storage[string(key)]
|
||||
if value == nil {
|
||||
value = self.GetAddr(key)
|
||||
|
||||
if !value.IsNil() {
|
||||
self.storage[string(key)] = value
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
|
||||
//return self.GetAddr(key)
|
||||
}
|
||||
|
||||
func (self *StateObject) setStorage(k []byte, value *ethutil.Value) {
|
||||
key := ethutil.LeftPadBytes(k, 32)
|
||||
self.storage[string(key)] = value.Copy()
|
||||
|
||||
/*
|
||||
if value.BigInt().Cmp(ethutil.Big0) == 0 {
|
||||
self.state.trie.Delete(string(key))
|
||||
return
|
||||
}
|
||||
|
||||
self.SetAddr(key, value)
|
||||
*/
|
||||
}
|
||||
|
||||
// Iterate over each storage address and yield callback
|
||||
func (self *StateObject) EachStorage(cb ethtrie.EachCallback) {
|
||||
// First loop over the uncommit/cached values in storage
|
||||
for key, value := range self.storage {
|
||||
// XXX Most iterators Fns as it stands require encoded values
|
||||
encoded := ethutil.NewValue(value.Encode())
|
||||
cb(key, encoded)
|
||||
}
|
||||
|
||||
it := self.state.trie.NewIterator()
|
||||
it.Each(func(key string, value *ethutil.Value) {
|
||||
// If it's cached don't call the callback.
|
||||
if self.storage[key] == nil {
|
||||
cb(key, value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (self *StateObject) Sync() {
|
||||
/*
|
||||
fmt.Println("############# BEFORE ################")
|
||||
self.state.EachStorage(func(key string, value *ethutil.Value) {
|
||||
fmt.Printf("%x %x %x\n", self.Address(), []byte(key), value.Bytes())
|
||||
})
|
||||
fmt.Printf("%x @:%x\n", self.Address(), self.state.Root())
|
||||
fmt.Println("#####################################")
|
||||
*/
|
||||
for key, value := range self.storage {
|
||||
if value.Len() == 0 { // value.BigInt().Cmp(ethutil.Big0) == 0 {
|
||||
//data := self.getStorage([]byte(key))
|
||||
//fmt.Printf("deleting %x %x 0x%x\n", self.Address(), []byte(key), data)
|
||||
self.state.trie.Delete(string(key))
|
||||
continue
|
||||
}
|
||||
|
||||
self.SetAddr([]byte(key), value)
|
||||
}
|
||||
|
||||
valid, t2 := ethtrie.ParanoiaCheck(self.state.trie)
|
||||
if !valid {
|
||||
statelogger.Infof("Warn: PARANOIA: Different state storage root during copy %x vs %x\n", self.state.trie.Root, t2.Root)
|
||||
|
||||
self.state.trie = t2
|
||||
}
|
||||
|
||||
/*
|
||||
fmt.Println("############# AFTER ################")
|
||||
self.state.EachStorage(func(key string, value *ethutil.Value) {
|
||||
fmt.Printf("%x %x %x\n", self.Address(), []byte(key), value.Bytes())
|
||||
})
|
||||
*/
|
||||
//fmt.Printf("%x @:%x\n", self.Address(), self.state.Root())
|
||||
}
|
||||
|
||||
func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value {
|
||||
if int64(len(c.script)-1) < pc.Int64() {
|
||||
return ethutil.NewValue(0)
|
||||
}
|
||||
|
||||
return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]})
|
||||
}
|
||||
|
||||
func (c *StateObject) AddAmount(amount *big.Int) {
|
||||
c.SetAmount(new(big.Int).Add(c.Amount, amount))
|
||||
|
||||
statelogger.Debugf("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount)
|
||||
}
|
||||
|
||||
func (c *StateObject) SubAmount(amount *big.Int) {
|
||||
c.SetAmount(new(big.Int).Sub(c.Amount, amount))
|
||||
|
||||
statelogger.Debugf("%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount)
|
||||
}
|
||||
|
||||
func (c *StateObject) SetAmount(amount *big.Int) {
|
||||
c.Amount = amount
|
||||
}
|
||||
|
||||
//
|
||||
// Gas setters and getters
|
||||
//
|
||||
|
||||
// Return the gas back to the origin. Used by the Virtual machine or Closures
|
||||
func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) {}
|
||||
func (c *StateObject) ConvertGas(gas, price *big.Int) error {
|
||||
total := new(big.Int).Mul(gas, price)
|
||||
if total.Cmp(c.Amount) > 0 {
|
||||
return fmt.Errorf("insufficient amount: %v, %v", c.Amount, total)
|
||||
}
|
||||
|
||||
c.SubAmount(total)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *StateObject) SetGasPool(gasLimit *big.Int) {
|
||||
self.gasPool = new(big.Int).Set(gasLimit)
|
||||
|
||||
statelogger.DebugDetailf("%x: fuel (+ %v)", self.Address(), self.gasPool)
|
||||
}
|
||||
|
||||
func (self *StateObject) BuyGas(gas, price *big.Int) error {
|
||||
if self.gasPool.Cmp(gas) < 0 {
|
||||
return GasLimitError(self.gasPool, gas)
|
||||
}
|
||||
|
||||
rGas := new(big.Int).Set(gas)
|
||||
rGas.Mul(rGas, price)
|
||||
|
||||
self.AddAmount(rGas)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *StateObject) RefundGas(gas, price *big.Int) {
|
||||
self.gasPool.Add(self.gasPool, gas)
|
||||
|
||||
rGas := new(big.Int).Set(gas)
|
||||
rGas.Mul(rGas, price)
|
||||
|
||||
self.Amount.Sub(self.Amount, rGas)
|
||||
}
|
||||
|
||||
func (self *StateObject) Copy() *StateObject {
|
||||
stateObject := NewStateObject(self.Address())
|
||||
stateObject.Amount.Set(self.Amount)
|
||||
stateObject.ScriptHash = ethutil.CopyBytes(self.ScriptHash)
|
||||
stateObject.Nonce = self.Nonce
|
||||
if self.state != nil {
|
||||
stateObject.state = self.state.Copy()
|
||||
}
|
||||
stateObject.script = ethutil.CopyBytes(self.script)
|
||||
stateObject.initScript = ethutil.CopyBytes(self.initScript)
|
||||
stateObject.storage = self.storage.Copy()
|
||||
stateObject.gasPool.Set(self.gasPool)
|
||||
|
||||
return stateObject
|
||||
}
|
||||
|
||||
func (self *StateObject) Set(stateObject *StateObject) {
|
||||
*self = *stateObject
|
||||
}
|
||||
|
||||
//
|
||||
// Attribute accessors
|
||||
//
|
||||
|
||||
func (c *StateObject) State() *State {
|
||||
return c.state
|
||||
}
|
||||
|
||||
func (c *StateObject) N() *big.Int {
|
||||
return big.NewInt(int64(c.Nonce))
|
||||
}
|
||||
|
||||
// Returns the address of the contract/account
|
||||
func (c *StateObject) Address() []byte {
|
||||
return c.address
|
||||
}
|
||||
|
||||
// Returns the main script body
|
||||
func (c *StateObject) Script() Code {
|
||||
return c.script
|
||||
}
|
||||
|
||||
// Returns the initialization script
|
||||
func (c *StateObject) Init() Code {
|
||||
return c.initScript
|
||||
}
|
||||
|
||||
// Debug stuff
|
||||
func (self *StateObject) CreateOutputForDiff() {
|
||||
fmt.Printf("%x %x %x %x\n", self.Address(), self.state.Root(), self.Amount.Bytes(), self.Nonce)
|
||||
self.EachStorage(func(addr string, value *ethutil.Value) {
|
||||
fmt.Printf("%x %x\n", addr, value.Bytes())
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
// Encoding
|
||||
//
|
||||
|
||||
// State object encoding methods
|
||||
func (c *StateObject) RlpEncode() []byte {
|
||||
var root interface{}
|
||||
if c.state != nil {
|
||||
root = c.state.trie.Root
|
||||
} else {
|
||||
root = ""
|
||||
}
|
||||
|
||||
return ethutil.Encode([]interface{}{c.Nonce, c.Amount, root, ethcrypto.Sha3Bin(c.script)})
|
||||
}
|
||||
|
||||
func (c *StateObject) RlpDecode(data []byte) {
|
||||
decoder := ethutil.NewValueFromBytes(data)
|
||||
|
||||
c.Nonce = decoder.Get(0).Uint()
|
||||
c.Amount = decoder.Get(1).BigInt()
|
||||
c.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
|
||||
c.storage = make(map[string]*ethutil.Value)
|
||||
c.gasPool = new(big.Int)
|
||||
|
||||
c.ScriptHash = decoder.Get(3).Bytes()
|
||||
|
||||
c.script, _ = ethutil.Config.Db.Get(c.ScriptHash)
|
||||
}
|
||||
|
||||
// Storage change object. Used by the manifest for notifying changes to
|
||||
// the sub channels.
|
||||
type StorageState struct {
|
||||
StateAddress []byte
|
||||
Address []byte
|
||||
Value *big.Int
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethdb"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSync(t *testing.T) {
|
||||
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state := NewState(ethutil.NewTrie(db, ""))
|
||||
|
||||
contract := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256)
|
||||
|
||||
contract.script = []byte{42}
|
||||
|
||||
state.UpdateStateObject(contract)
|
||||
state.Sync()
|
||||
|
||||
object := state.GetStateObject([]byte("aa"))
|
||||
if len(object.Script()) == 0 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestObjectGet(t *testing.T) {
|
||||
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
ethutil.Config.Db = db
|
||||
|
||||
state := NewState(ethutil.NewTrie(db, ""))
|
||||
|
||||
contract := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256)
|
||||
state.UpdateStateObject(contract)
|
||||
|
||||
contract = state.GetStateObject([]byte("aa"))
|
||||
contract.SetStorage(big.NewInt(0), ethutil.NewValue("hello"))
|
||||
o := contract.GetMem(big.NewInt(0))
|
||||
fmt.Println(o)
|
||||
|
||||
state.UpdateStateObject(contract)
|
||||
contract.SetStorage(big.NewInt(0), ethutil.NewValue("hello00"))
|
||||
|
||||
contract = state.GetStateObject([]byte("aa"))
|
||||
o = contract.GetMem(big.NewInt(0))
|
||||
fmt.Println("after", o)
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethdb"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSnapshot(t *testing.T) {
|
||||
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state := NewState(ethutil.NewTrie(db, ""))
|
||||
|
||||
stateObject := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256)
|
||||
state.UpdateStateObject(stateObject)
|
||||
stateObject.SetStorage(ethutil.Big("0"), ethutil.NewValue(42))
|
||||
|
||||
snapshot := state.Copy()
|
||||
|
||||
stateObject = state.GetStateObject([]byte("aa"))
|
||||
stateObject.SetStorage(ethutil.Big("0"), ethutil.NewValue(43))
|
||||
|
||||
state.Set(snapshot)
|
||||
|
||||
stateObject = state.GetStateObject([]byte("aa"))
|
||||
if !stateObject.GetStorage(ethutil.Big("0")).Cmp(ethutil.NewValue(42)) {
|
||||
t.Error("Expected storage 0 to be 42")
|
||||
}
|
||||
}
|
@ -2,6 +2,10 @@ package ethchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethstate"
|
||||
"github.com/ethereum/eth-go/ethtrie"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/eth-go/ethvm"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
@ -27,17 +31,17 @@ type StateTransition struct {
|
||||
gas, gasPrice *big.Int
|
||||
value *big.Int
|
||||
data []byte
|
||||
state *State
|
||||
state *ethstate.State
|
||||
block *Block
|
||||
|
||||
cb, rec, sen *StateObject
|
||||
cb, rec, sen *ethstate.StateObject
|
||||
}
|
||||
|
||||
func NewStateTransition(coinbase *StateObject, tx *Transaction, state *State, block *Block) *StateTransition {
|
||||
func NewStateTransition(coinbase *ethstate.StateObject, tx *Transaction, state *ethstate.State, block *Block) *StateTransition {
|
||||
return &StateTransition{coinbase.Address(), tx.Recipient, tx, new(big.Int), new(big.Int).Set(tx.GasPrice), tx.Value, tx.Data, state, block, coinbase, nil, nil}
|
||||
}
|
||||
|
||||
func (self *StateTransition) Coinbase() *StateObject {
|
||||
func (self *StateTransition) Coinbase() *ethstate.StateObject {
|
||||
if self.cb != nil {
|
||||
return self.cb
|
||||
}
|
||||
@ -45,7 +49,7 @@ func (self *StateTransition) Coinbase() *StateObject {
|
||||
self.cb = self.state.GetOrNewStateObject(self.coinbase)
|
||||
return self.cb
|
||||
}
|
||||
func (self *StateTransition) Sender() *StateObject {
|
||||
func (self *StateTransition) Sender() *ethstate.StateObject {
|
||||
if self.sen != nil {
|
||||
return self.sen
|
||||
}
|
||||
@ -54,7 +58,7 @@ func (self *StateTransition) Sender() *StateObject {
|
||||
|
||||
return self.sen
|
||||
}
|
||||
func (self *StateTransition) Receiver() *StateObject {
|
||||
func (self *StateTransition) Receiver() *ethstate.StateObject {
|
||||
if self.tx != nil && self.tx.CreatesContract() {
|
||||
return nil
|
||||
}
|
||||
@ -67,7 +71,7 @@ func (self *StateTransition) Receiver() *StateObject {
|
||||
return self.rec
|
||||
}
|
||||
|
||||
func (self *StateTransition) MakeStateObject(state *State, tx *Transaction) *StateObject {
|
||||
func (self *StateTransition) MakeStateObject(state *ethstate.State, tx *Transaction) *ethstate.StateObject {
|
||||
contract := MakeContract(tx, state)
|
||||
|
||||
return contract
|
||||
@ -154,7 +158,7 @@ func (self *StateTransition) TransitionState() (err error) {
|
||||
var (
|
||||
tx = self.tx
|
||||
sender = self.Sender()
|
||||
receiver *StateObject
|
||||
receiver *ethstate.StateObject
|
||||
)
|
||||
|
||||
defer self.RefundGas()
|
||||
@ -163,13 +167,13 @@ func (self *StateTransition) TransitionState() (err error) {
|
||||
sender.Nonce += 1
|
||||
|
||||
// Transaction gas
|
||||
if err = self.UseGas(GasTx); err != nil {
|
||||
if err = self.UseGas(ethvm.GasTx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Pay data gas
|
||||
dataPrice := big.NewInt(int64(len(self.data)))
|
||||
dataPrice.Mul(dataPrice, GasData)
|
||||
dataPrice.Mul(dataPrice, ethvm.GasData)
|
||||
if err = self.UseGas(dataPrice); err != nil {
|
||||
return
|
||||
}
|
||||
@ -178,7 +182,7 @@ func (self *StateTransition) TransitionState() (err error) {
|
||||
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount)
|
||||
}
|
||||
|
||||
var snapshot *State
|
||||
var snapshot *ethstate.State
|
||||
// If the receiver is nil it's a contract (\0*32).
|
||||
if tx.CreatesContract() {
|
||||
// Subtract the (irreversible) amount from the senders account
|
||||
@ -220,10 +224,10 @@ func (self *StateTransition) TransitionState() (err error) {
|
||||
return fmt.Errorf("Error during init execution %v", err)
|
||||
}
|
||||
|
||||
receiver.script = code
|
||||
receiver.Code = code
|
||||
} else {
|
||||
if len(receiver.Script()) > 0 {
|
||||
_, err = self.Eval(receiver.Script(), receiver, "code")
|
||||
if len(receiver.Code) > 0 {
|
||||
_, err = self.Eval(receiver.Code, receiver, "code")
|
||||
if err != nil {
|
||||
self.state.Set(snapshot)
|
||||
|
||||
@ -235,7 +239,7 @@ func (self *StateTransition) TransitionState() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (self *StateTransition) transferValue(sender, receiver *StateObject) error {
|
||||
func (self *StateTransition) transferValue(sender, receiver *ethstate.StateObject) error {
|
||||
if sender.Amount.Cmp(self.value) < 0 {
|
||||
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount)
|
||||
}
|
||||
@ -248,34 +252,61 @@ func (self *StateTransition) transferValue(sender, receiver *StateObject) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *StateTransition) Eval(script []byte, context *StateObject, typ string) (ret []byte, err error) {
|
||||
func (self *StateTransition) Eval(script []byte, context *ethstate.StateObject, typ string) (ret []byte, err error) {
|
||||
var (
|
||||
block = self.block
|
||||
initiator = self.Sender()
|
||||
state = self.state
|
||||
transactor = self.Sender()
|
||||
state = self.state
|
||||
env = NewEnv(state, self.tx, self.block)
|
||||
callerClosure = ethvm.NewClosure(transactor, context, script, self.gas, self.gasPrice)
|
||||
)
|
||||
|
||||
closure := NewClosure(initiator, context, script, state, self.gas, self.gasPrice)
|
||||
vm := NewVm(state, nil, RuntimeVars{
|
||||
Origin: initiator.Address(),
|
||||
Block: block,
|
||||
BlockNumber: block.Number,
|
||||
PrevHash: block.PrevHash,
|
||||
Coinbase: block.Coinbase,
|
||||
Time: block.Time,
|
||||
Diff: block.Difficulty,
|
||||
Value: self.value,
|
||||
})
|
||||
vm := ethvm.New(env)
|
||||
vm.Verbose = true
|
||||
vm.Fn = typ
|
||||
|
||||
ret, err = Call(vm, closure, self.data)
|
||||
ret, _, err = callerClosure.Call(vm, self.tx.Data)
|
||||
|
||||
/*
|
||||
closure := NewClosure(initiator, context, script, state, self.gas, self.gasPrice)
|
||||
vm := NewVm(state, nil, RuntimeVars{
|
||||
Origin: initiator.Address(),
|
||||
Block: block,
|
||||
BlockNumber: block.Number,
|
||||
PrevHash: block.PrevHash,
|
||||
Coinbase: block.Coinbase,
|
||||
Time: block.Time,
|
||||
Diff: block.Difficulty,
|
||||
Value: self.value,
|
||||
})
|
||||
vm.Verbose = true
|
||||
vm.Fn = typ
|
||||
|
||||
ret, err = Call(vm, closure, self.data)
|
||||
*/
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func Call(vm *Vm, closure *Closure, data []byte) (ret []byte, err error) {
|
||||
/*
|
||||
func Call(vm *eth.Vm, closure *Closure, data []byte) (ret []byte, err error) {
|
||||
ret, _, err = closure.Call(vm, data)
|
||||
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
// Converts an transaction in to a state object
|
||||
func MakeContract(tx *Transaction, state *ethstate.State) *ethstate.StateObject {
|
||||
// Create contract if there's no recipient
|
||||
if tx.IsContract() {
|
||||
addr := tx.CreationAddress()
|
||||
|
||||
contract := state.NewStateObject(addr)
|
||||
contract.InitCode = tx.Data
|
||||
contract.State = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethlog"
|
||||
"github.com/ethereum/eth-go/ethstate"
|
||||
"github.com/ethereum/eth-go/ethwire"
|
||||
"math/big"
|
||||
"sync"
|
||||
@ -252,7 +253,7 @@ func (pool *TxPool) CurrentTransactions() []*Transaction {
|
||||
return txList
|
||||
}
|
||||
|
||||
func (pool *TxPool) RemoveInvalid(state *State) {
|
||||
func (pool *TxPool) RemoveInvalid(state *ethstate.State) {
|
||||
for e := pool.pool.Front(); e != nil; e = e.Next() {
|
||||
tx := e.Value.(*Transaction)
|
||||
sender := state.GetAccount(tx.Sender())
|
||||
|
843
ethchain/vm.go
843
ethchain/vm.go
@ -1,843 +0,0 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethcrypto"
|
||||
"github.com/ethereum/eth-go/ethlog"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var vmlogger = ethlog.NewLogger("VM")
|
||||
|
||||
var (
|
||||
GasStep = big.NewInt(1)
|
||||
GasSha = big.NewInt(20)
|
||||
GasSLoad = big.NewInt(20)
|
||||
GasSStore = big.NewInt(100)
|
||||
GasBalance = big.NewInt(20)
|
||||
GasCreate = big.NewInt(100)
|
||||
GasCall = big.NewInt(20)
|
||||
GasMemory = big.NewInt(1)
|
||||
GasData = big.NewInt(5)
|
||||
GasTx = big.NewInt(500)
|
||||
|
||||
LogTyPretty byte = 0x1
|
||||
LogTyDiff byte = 0x2
|
||||
)
|
||||
|
||||
type Debugger interface {
|
||||
BreakHook(step int, op OpCode, mem *Memory, stack *Stack, stateObject *StateObject) bool
|
||||
StepHook(step int, op OpCode, mem *Memory, stack *Stack, stateObject *StateObject) bool
|
||||
BreakPoints() []int64
|
||||
SetCode(byteCode []byte)
|
||||
}
|
||||
|
||||
type Vm struct {
|
||||
// Stack for processing contracts
|
||||
stack *Stack
|
||||
// non-persistent key/value memory storage
|
||||
mem map[string]*big.Int
|
||||
|
||||
vars RuntimeVars
|
||||
|
||||
state *State
|
||||
|
||||
stateManager *StateManager
|
||||
|
||||
Verbose bool
|
||||
|
||||
logTy byte
|
||||
logStr string
|
||||
|
||||
err error
|
||||
|
||||
// Debugging
|
||||
Dbg Debugger
|
||||
|
||||
BreakPoints []int64
|
||||
Stepping bool
|
||||
Fn string
|
||||
}
|
||||
|
||||
type RuntimeVars struct {
|
||||
Origin []byte
|
||||
Block *Block
|
||||
BlockNumber *big.Int
|
||||
PrevHash []byte
|
||||
Coinbase []byte
|
||||
Time int64
|
||||
Diff *big.Int
|
||||
TxData []string
|
||||
Value *big.Int
|
||||
}
|
||||
|
||||
func (self *Vm) Printf(format string, v ...interface{}) *Vm {
|
||||
if self.Verbose && self.logTy == LogTyPretty {
|
||||
self.logStr += fmt.Sprintf(format, v...)
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *Vm) Endl() *Vm {
|
||||
if self.Verbose && self.logTy == LogTyPretty {
|
||||
vmlogger.Debugln(self.logStr)
|
||||
self.logStr = ""
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm {
|
||||
lt := LogTyPretty
|
||||
if ethutil.Config.Diff {
|
||||
lt = LogTyDiff
|
||||
}
|
||||
|
||||
return &Vm{vars: vars, state: state, stateManager: stateManager, logTy: lt}
|
||||
}
|
||||
|
||||
var Pow256 = ethutil.BigPow(2, 256)
|
||||
|
||||
var isRequireError = false
|
||||
|
||||
func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
||||
// Recover from any require exception
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = closure.Return(nil)
|
||||
err = fmt.Errorf("%v", r)
|
||||
vmlogger.Errorln("vm err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Debug hook
|
||||
if vm.Dbg != nil {
|
||||
vm.Dbg.SetCode(closure.Script)
|
||||
}
|
||||
|
||||
// Don't bother with the execution if there's no code.
|
||||
if len(closure.Script) == 0 {
|
||||
return closure.Return(nil), nil
|
||||
}
|
||||
|
||||
vmlogger.Debugf("(%s) %x gas: %v (d) %x\n", vm.Fn, closure.Address(), closure.Gas, closure.Args)
|
||||
|
||||
var (
|
||||
op OpCode
|
||||
|
||||
mem = &Memory{}
|
||||
stack = NewStack()
|
||||
pc = big.NewInt(0)
|
||||
step = 0
|
||||
prevStep = 0
|
||||
require = func(m int) {
|
||||
if stack.Len() < m {
|
||||
isRequireError = true
|
||||
panic(fmt.Sprintf("%04v (%v) stack err size = %d, required = %d", pc, op, stack.Len(), m))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
for {
|
||||
prevStep = step
|
||||
// The base for all big integer arithmetic
|
||||
base := new(big.Int)
|
||||
|
||||
step++
|
||||
// Get the memory location of pc
|
||||
val := closure.Get(pc)
|
||||
// Get the opcode (it must be an opcode!)
|
||||
op = OpCode(val.Uint())
|
||||
|
||||
// XXX Leave this Println intact. Don't change this to the log system.
|
||||
// Used for creating diffs between implementations
|
||||
if vm.logTy == LogTyDiff {
|
||||
switch op {
|
||||
case STOP, RETURN, SUICIDE:
|
||||
closure.object.Sync()
|
||||
closure.object.EachStorage(func(key string, value *ethutil.Value) {
|
||||
value.Decode()
|
||||
fmt.Printf("%x %x\n", new(big.Int).SetBytes([]byte(key)).Bytes(), value.Bytes())
|
||||
})
|
||||
}
|
||||
|
||||
b := pc.Bytes()
|
||||
if len(b) == 0 {
|
||||
b = []byte{0}
|
||||
}
|
||||
|
||||
fmt.Printf("%x %x %x %x\n", closure.Address(), b, []byte{byte(op)}, closure.Gas.Bytes())
|
||||
}
|
||||
|
||||
gas := new(big.Int)
|
||||
addStepGasUsage := func(amount *big.Int) {
|
||||
if amount.Cmp(ethutil.Big0) >= 0 {
|
||||
gas.Add(gas, amount)
|
||||
}
|
||||
}
|
||||
|
||||
addStepGasUsage(GasStep)
|
||||
|
||||
var newMemSize uint64 = 0
|
||||
switch op {
|
||||
case STOP:
|
||||
gas.Set(ethutil.Big0)
|
||||
case SUICIDE:
|
||||
gas.Set(ethutil.Big0)
|
||||
case SLOAD:
|
||||
gas.Set(GasSLoad)
|
||||
case SSTORE:
|
||||
var mult *big.Int
|
||||
y, x := stack.Peekn()
|
||||
val := closure.GetStorage(x)
|
||||
if val.BigInt().Cmp(ethutil.Big0) == 0 && len(y.Bytes()) > 0 {
|
||||
mult = ethutil.Big2
|
||||
} else if val.BigInt().Cmp(ethutil.Big0) != 0 && len(y.Bytes()) == 0 {
|
||||
mult = ethutil.Big0
|
||||
} else {
|
||||
mult = ethutil.Big1
|
||||
}
|
||||
gas = new(big.Int).Mul(mult, GasSStore)
|
||||
case BALANCE:
|
||||
gas.Set(GasBalance)
|
||||
case MSTORE:
|
||||
require(2)
|
||||
newMemSize = stack.Peek().Uint64() + 32
|
||||
case MLOAD:
|
||||
require(1)
|
||||
|
||||
newMemSize = stack.Peek().Uint64() + 32
|
||||
case MSTORE8:
|
||||
require(2)
|
||||
newMemSize = stack.Peek().Uint64() + 1
|
||||
case RETURN:
|
||||
require(2)
|
||||
|
||||
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64()
|
||||
case SHA3:
|
||||
require(2)
|
||||
|
||||
gas.Set(GasSha)
|
||||
|
||||
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64()
|
||||
case CALLDATACOPY:
|
||||
require(3)
|
||||
|
||||
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
|
||||
case CODECOPY:
|
||||
require(3)
|
||||
|
||||
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
|
||||
case CALL:
|
||||
require(7)
|
||||
gas.Set(GasCall)
|
||||
addStepGasUsage(stack.data[stack.Len()-1])
|
||||
|
||||
x := stack.data[stack.Len()-6].Uint64() + stack.data[stack.Len()-7].Uint64()
|
||||
y := stack.data[stack.Len()-4].Uint64() + stack.data[stack.Len()-5].Uint64()
|
||||
|
||||
newMemSize = uint64(math.Max(float64(x), float64(y)))
|
||||
case CREATE:
|
||||
require(3)
|
||||
gas.Set(GasCreate)
|
||||
|
||||
newMemSize = stack.data[stack.Len()-2].Uint64() + stack.data[stack.Len()-3].Uint64()
|
||||
}
|
||||
|
||||
newMemSize = (newMemSize + 31) / 32 * 32
|
||||
if newMemSize > uint64(mem.Len()) {
|
||||
m := GasMemory.Uint64() * (newMemSize - uint64(mem.Len())) / 32
|
||||
addStepGasUsage(big.NewInt(int64(m)))
|
||||
}
|
||||
|
||||
if !closure.UseGas(gas) {
|
||||
err := fmt.Errorf("Insufficient gas for %v. req %v has %v", op, gas, closure.Gas)
|
||||
|
||||
closure.UseGas(closure.Gas)
|
||||
|
||||
return closure.Return(nil), err
|
||||
}
|
||||
|
||||
vm.Printf("(pc) %-3d -o- %-14s", pc, op.String())
|
||||
vm.Printf(" (g) %-3v (%v)", gas, closure.Gas)
|
||||
|
||||
mem.Resize(newMemSize)
|
||||
|
||||
switch op {
|
||||
case LOG:
|
||||
stack.Print()
|
||||
mem.Print()
|
||||
// 0x20 range
|
||||
case ADD:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
vm.Printf(" %v + %v", y, x)
|
||||
|
||||
base.Add(y, x)
|
||||
|
||||
vm.Printf(" = %v", base)
|
||||
// Pop result back on the stack
|
||||
stack.Push(base)
|
||||
case SUB:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
vm.Printf(" %v - %v", y, x)
|
||||
|
||||
base.Sub(y, x)
|
||||
|
||||
vm.Printf(" = %v", base)
|
||||
// Pop result back on the stack
|
||||
stack.Push(base)
|
||||
case MUL:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
vm.Printf(" %v * %v", y, x)
|
||||
|
||||
base.Mul(y, x)
|
||||
|
||||
vm.Printf(" = %v", base)
|
||||
// Pop result back on the stack
|
||||
stack.Push(base)
|
||||
case DIV:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
vm.Printf(" %v / %v", y, x)
|
||||
|
||||
base.Div(y, x)
|
||||
|
||||
vm.Printf(" = %v", base)
|
||||
// Pop result back on the stack
|
||||
stack.Push(base)
|
||||
case SDIV:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
// n > 2**255
|
||||
if x.Cmp(Pow256) > 0 {
|
||||
x.Sub(Pow256, x)
|
||||
}
|
||||
if y.Cmp(Pow256) > 0 {
|
||||
y.Sub(Pow256, y)
|
||||
}
|
||||
z := new(big.Int)
|
||||
z.Div(x, y)
|
||||
if z.Cmp(Pow256) > 0 {
|
||||
z.Sub(Pow256, z)
|
||||
}
|
||||
// Push result on to the stack
|
||||
stack.Push(z)
|
||||
case MOD:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
|
||||
vm.Printf(" %v %% %v", y, x)
|
||||
|
||||
base.Mod(y, x)
|
||||
|
||||
vm.Printf(" = %v", base)
|
||||
stack.Push(base)
|
||||
case SMOD:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
// n > 2**255
|
||||
if x.Cmp(Pow256) > 0 {
|
||||
x.Sub(Pow256, x)
|
||||
}
|
||||
if y.Cmp(Pow256) > 0 {
|
||||
y.Sub(Pow256, y)
|
||||
}
|
||||
z := new(big.Int)
|
||||
z.Mod(x, y)
|
||||
if z.Cmp(Pow256) > 0 {
|
||||
z.Sub(Pow256, z)
|
||||
}
|
||||
// Push result on to the stack
|
||||
stack.Push(z)
|
||||
case EXP:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
|
||||
vm.Printf(" %v ** %v", y, x)
|
||||
|
||||
base.Exp(y, x, Pow256)
|
||||
|
||||
vm.Printf(" = %v", base)
|
||||
|
||||
stack.Push(base)
|
||||
case NEG:
|
||||
require(1)
|
||||
base.Sub(Pow256, stack.Pop())
|
||||
stack.Push(base)
|
||||
case LT:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
vm.Printf(" %v < %v", y, x)
|
||||
// x < y
|
||||
if y.Cmp(x) < 0 {
|
||||
stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case GT:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
vm.Printf(" %v > %v", y, x)
|
||||
|
||||
// x > y
|
||||
if y.Cmp(x) > 0 {
|
||||
stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
|
||||
case SLT:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
vm.Printf(" %v < %v", y, x)
|
||||
// x < y
|
||||
if y.Cmp(x) < 0 {
|
||||
stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case SGT:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
vm.Printf(" %v > %v", y, x)
|
||||
|
||||
// x > y
|
||||
if y.Cmp(x) > 0 {
|
||||
stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
|
||||
case EQ:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
vm.Printf(" %v == %v", y, x)
|
||||
|
||||
// x == y
|
||||
if x.Cmp(y) == 0 {
|
||||
stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case NOT:
|
||||
require(1)
|
||||
x := stack.Pop()
|
||||
if x.Cmp(ethutil.BigFalse) > 0 {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
} else {
|
||||
stack.Push(ethutil.BigTrue)
|
||||
}
|
||||
|
||||
// 0x10 range
|
||||
case AND:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
vm.Printf(" %v & %v", y, x)
|
||||
|
||||
stack.Push(base.And(y, x))
|
||||
case OR:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
vm.Printf(" %v | %v", y, x)
|
||||
|
||||
stack.Push(base.Or(y, x))
|
||||
case XOR:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
vm.Printf(" %v ^ %v", y, x)
|
||||
|
||||
stack.Push(base.Xor(y, x))
|
||||
case BYTE:
|
||||
require(2)
|
||||
val, th := stack.Popn()
|
||||
if th.Cmp(big.NewInt(32)) < 0 && th.Cmp(big.NewInt(int64(len(val.Bytes())))) < 0 {
|
||||
byt := big.NewInt(int64(val.Bytes()[th.Int64()]))
|
||||
stack.Push(byt)
|
||||
|
||||
vm.Printf(" => 0x%x", byt.Bytes())
|
||||
} else {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
|
||||
// 0x20 range
|
||||
case SHA3:
|
||||
require(2)
|
||||
size, offset := stack.Popn()
|
||||
data := ethcrypto.Sha3Bin(mem.Get(offset.Int64(), size.Int64()))
|
||||
|
||||
stack.Push(ethutil.BigD(data))
|
||||
|
||||
vm.Printf(" => %x", data)
|
||||
// 0x30 range
|
||||
case ADDRESS:
|
||||
stack.Push(ethutil.BigD(closure.Address()))
|
||||
|
||||
vm.Printf(" => %x", closure.Address())
|
||||
case BALANCE:
|
||||
require(1)
|
||||
|
||||
addr := stack.Pop().Bytes()
|
||||
balance := vm.state.GetBalance(addr)
|
||||
|
||||
stack.Push(balance)
|
||||
|
||||
vm.Printf(" => %v (%x)", balance, addr)
|
||||
case ORIGIN:
|
||||
stack.Push(ethutil.BigD(vm.vars.Origin))
|
||||
|
||||
vm.Printf(" => %x", vm.vars.Origin)
|
||||
case CALLER:
|
||||
caller := closure.caller.Address()
|
||||
stack.Push(ethutil.BigD(caller))
|
||||
|
||||
vm.Printf(" => %x", caller)
|
||||
case CALLVALUE:
|
||||
stack.Push(vm.vars.Value)
|
||||
|
||||
vm.Printf(" => %v", vm.vars.Value)
|
||||
case CALLDATALOAD:
|
||||
require(1)
|
||||
var (
|
||||
offset = stack.Pop()
|
||||
data = make([]byte, 32)
|
||||
lenData = big.NewInt(int64(len(closure.Args)))
|
||||
)
|
||||
|
||||
if lenData.Cmp(offset) >= 0 {
|
||||
length := new(big.Int).Add(offset, ethutil.Big32)
|
||||
length = ethutil.BigMin(length, lenData)
|
||||
|
||||
copy(data, closure.Args[offset.Int64():length.Int64()])
|
||||
}
|
||||
|
||||
vm.Printf(" => 0x%x", data)
|
||||
|
||||
stack.Push(ethutil.BigD(data))
|
||||
case CALLDATASIZE:
|
||||
l := int64(len(closure.Args))
|
||||
stack.Push(big.NewInt(l))
|
||||
|
||||
vm.Printf(" => %d", l)
|
||||
case CALLDATACOPY:
|
||||
var (
|
||||
size = int64(len(closure.Args))
|
||||
mOff = stack.Pop().Int64()
|
||||
cOff = stack.Pop().Int64()
|
||||
l = stack.Pop().Int64()
|
||||
)
|
||||
|
||||
if cOff > size {
|
||||
cOff = 0
|
||||
l = 0
|
||||
} else if cOff+l > size {
|
||||
l = 0
|
||||
}
|
||||
|
||||
code := closure.Args[cOff : cOff+l]
|
||||
|
||||
mem.Set(mOff, l, code)
|
||||
case CODESIZE:
|
||||
l := big.NewInt(int64(len(closure.Script)))
|
||||
stack.Push(l)
|
||||
|
||||
vm.Printf(" => %d", l)
|
||||
case CODECOPY:
|
||||
var (
|
||||
size = int64(len(closure.Script))
|
||||
mOff = stack.Pop().Int64()
|
||||
cOff = stack.Pop().Int64()
|
||||
l = stack.Pop().Int64()
|
||||
)
|
||||
|
||||
if cOff > size {
|
||||
cOff = 0
|
||||
l = 0
|
||||
} else if cOff+l > size {
|
||||
l = 0
|
||||
}
|
||||
|
||||
code := closure.Script[cOff : cOff+l]
|
||||
fmt.Println("len:", l, "code off:", cOff, "mem off:", mOff)
|
||||
|
||||
mem.Set(mOff, l, code)
|
||||
case GASPRICE:
|
||||
stack.Push(closure.Price)
|
||||
|
||||
// 0x40 range
|
||||
case PREVHASH:
|
||||
stack.Push(ethutil.BigD(vm.vars.PrevHash))
|
||||
case COINBASE:
|
||||
stack.Push(ethutil.BigD(vm.vars.Coinbase))
|
||||
case TIMESTAMP:
|
||||
stack.Push(big.NewInt(vm.vars.Time))
|
||||
case NUMBER:
|
||||
stack.Push(vm.vars.BlockNumber)
|
||||
case DIFFICULTY:
|
||||
stack.Push(vm.vars.Diff)
|
||||
case GASLIMIT:
|
||||
// TODO
|
||||
stack.Push(big.NewInt(0))
|
||||
|
||||
// 0x50 range
|
||||
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
|
||||
a := big.NewInt(int64(op) - int64(PUSH1) + 1)
|
||||
pc.Add(pc, ethutil.Big1)
|
||||
data := closure.Gets(pc, a)
|
||||
val := ethutil.BigD(data.Bytes())
|
||||
// Push value to stack
|
||||
stack.Push(val)
|
||||
pc.Add(pc, a.Sub(a, big.NewInt(1)))
|
||||
|
||||
step += int(op) - int(PUSH1) + 1
|
||||
|
||||
vm.Printf(" => 0x%x", data.Bytes())
|
||||
case POP:
|
||||
require(1)
|
||||
stack.Pop()
|
||||
case DUP:
|
||||
require(1)
|
||||
stack.Push(stack.Peek())
|
||||
|
||||
vm.Printf(" => 0x%x", stack.Peek().Bytes())
|
||||
case SWAP:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
stack.Push(y)
|
||||
stack.Push(x)
|
||||
case MLOAD:
|
||||
require(1)
|
||||
offset := stack.Pop()
|
||||
val := ethutil.BigD(mem.Get(offset.Int64(), 32))
|
||||
stack.Push(val)
|
||||
|
||||
vm.Printf(" => 0x%x", val.Bytes())
|
||||
case MSTORE: // Store the value at stack top-1 in to memory at location stack top
|
||||
require(2)
|
||||
// Pop value of the stack
|
||||
val, mStart := stack.Popn()
|
||||
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256))
|
||||
|
||||
vm.Printf(" => 0x%x", val)
|
||||
case MSTORE8:
|
||||
require(2)
|
||||
val, mStart := stack.Popn()
|
||||
//base.And(val, new(big.Int).SetInt64(0xff))
|
||||
//mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256))
|
||||
mem.store[mStart.Int64()] = byte(val.Int64() & 0xff)
|
||||
|
||||
vm.Printf(" => 0x%x", val)
|
||||
case SLOAD:
|
||||
require(1)
|
||||
loc := stack.Pop()
|
||||
val := closure.GetStorage(loc)
|
||||
|
||||
stack.Push(val.BigInt())
|
||||
|
||||
vm.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
|
||||
case SSTORE:
|
||||
require(2)
|
||||
val, loc := stack.Popn()
|
||||
closure.SetStorage(loc, ethutil.NewValue(val))
|
||||
|
||||
// Add the change to manifest
|
||||
vm.state.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val)
|
||||
|
||||
vm.Printf(" {0x%x : 0x%x}", loc, val)
|
||||
case JUMP:
|
||||
require(1)
|
||||
pc = stack.Pop()
|
||||
// Reduce pc by one because of the increment that's at the end of this for loop
|
||||
vm.Printf(" ~> %v", pc).Endl()
|
||||
|
||||
continue
|
||||
case JUMPI:
|
||||
require(2)
|
||||
cond, pos := stack.Popn()
|
||||
if cond.Cmp(ethutil.BigTrue) >= 0 {
|
||||
pc = pos
|
||||
|
||||
vm.Printf(" ~> %v (t)", pc).Endl()
|
||||
|
||||
continue
|
||||
} else {
|
||||
vm.Printf(" (f)")
|
||||
}
|
||||
case PC:
|
||||
stack.Push(pc)
|
||||
case MSIZE:
|
||||
stack.Push(big.NewInt(int64(mem.Len())))
|
||||
|
||||
vm.Printf(" => %v", mem.Len()).Endl()
|
||||
case GAS:
|
||||
stack.Push(closure.Gas)
|
||||
// 0x60 range
|
||||
case CREATE:
|
||||
require(3)
|
||||
|
||||
var (
|
||||
err error
|
||||
value = stack.Pop()
|
||||
size, offset = stack.Popn()
|
||||
|
||||
// Snapshot the current stack so we are able to
|
||||
// revert back to it later.
|
||||
snapshot = vm.state.Copy()
|
||||
)
|
||||
|
||||
// Generate a new address
|
||||
addr := ethcrypto.CreateAddress(closure.Address(), closure.N().Uint64())
|
||||
for i := uint64(0); vm.state.GetStateObject(addr) != nil; i++ {
|
||||
ethcrypto.CreateAddress(closure.Address(), closure.N().Uint64()+i)
|
||||
}
|
||||
closure.object.Nonce++
|
||||
|
||||
vm.Printf(" (*) %x", addr).Endl()
|
||||
|
||||
// Create a new contract
|
||||
contract := vm.state.NewStateObject(addr)
|
||||
if contract.Amount.Cmp(value) >= 0 {
|
||||
closure.object.SubAmount(value)
|
||||
contract.AddAmount(value)
|
||||
|
||||
// Set the init script
|
||||
initCode := mem.Get(offset.Int64(), size.Int64())
|
||||
//fmt.Printf("%x\n", initCode)
|
||||
// Transfer all remaining gas to the new
|
||||
// contract so it may run the init script
|
||||
gas := new(big.Int).Set(closure.Gas)
|
||||
closure.UseGas(closure.Gas)
|
||||
|
||||
// Create the closure
|
||||
c := NewClosure(closure, contract, initCode, vm.state, gas, closure.Price)
|
||||
// Call the closure and set the return value as
|
||||
// main script.
|
||||
contract.script, err = Call(vm, c, nil)
|
||||
} else {
|
||||
err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
|
||||
// Revert the state as it was before.
|
||||
vm.state.Set(snapshot)
|
||||
|
||||
vm.Printf("CREATE err %v", err)
|
||||
} else {
|
||||
stack.Push(ethutil.BigD(addr))
|
||||
vm.Printf("CREATE success")
|
||||
}
|
||||
vm.Endl()
|
||||
|
||||
// Debug hook
|
||||
if vm.Dbg != nil {
|
||||
vm.Dbg.SetCode(closure.Script)
|
||||
}
|
||||
case CALL:
|
||||
require(7)
|
||||
|
||||
vm.Endl()
|
||||
|
||||
gas := stack.Pop()
|
||||
// Pop gas and value of the stack.
|
||||
value, addr := stack.Popn()
|
||||
// Pop input size and offset
|
||||
inSize, inOffset := stack.Popn()
|
||||
// Pop return size and offset
|
||||
retSize, retOffset := stack.Popn()
|
||||
|
||||
// Get the arguments from the memory
|
||||
args := mem.Get(inOffset.Int64(), inSize.Int64())
|
||||
|
||||
if closure.object.Amount.Cmp(value) < 0 {
|
||||
vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount)
|
||||
|
||||
closure.ReturnGas(gas, nil, nil)
|
||||
|
||||
stack.Push(ethutil.BigFalse)
|
||||
} else {
|
||||
snapshot := vm.state.Copy()
|
||||
|
||||
stateObject := vm.state.GetOrNewStateObject(addr.Bytes())
|
||||
|
||||
closure.object.SubAmount(value)
|
||||
stateObject.AddAmount(value)
|
||||
|
||||
// Create a new callable closure
|
||||
c := NewClosure(closure, stateObject, stateObject.script, vm.state, gas, closure.Price)
|
||||
// Executer the closure and get the return value (if any)
|
||||
ret, err := Call(vm, c, args)
|
||||
if err != nil {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
|
||||
vmlogger.Debugf("Closure execution failed. %v\n", err)
|
||||
|
||||
vm.state.Set(snapshot)
|
||||
} else {
|
||||
stack.Push(ethutil.BigTrue)
|
||||
|
||||
mem.Set(retOffset.Int64(), retSize.Int64(), ret)
|
||||
}
|
||||
|
||||
// Debug hook
|
||||
if vm.Dbg != nil {
|
||||
vm.Dbg.SetCode(closure.Script)
|
||||
}
|
||||
}
|
||||
case RETURN:
|
||||
require(2)
|
||||
size, offset := stack.Popn()
|
||||
ret := mem.Get(offset.Int64(), size.Int64())
|
||||
|
||||
vm.Printf(" => (%d) 0x%x", len(ret), ret).Endl()
|
||||
|
||||
return closure.Return(ret), nil
|
||||
case SUICIDE:
|
||||
require(1)
|
||||
|
||||
receiver := vm.state.GetOrNewStateObject(stack.Pop().Bytes())
|
||||
|
||||
receiver.AddAmount(closure.object.Amount)
|
||||
|
||||
closure.object.MarkForDeletion()
|
||||
|
||||
fallthrough
|
||||
case STOP: // Stop the closure
|
||||
vm.Endl()
|
||||
|
||||
return closure.Return(nil), nil
|
||||
default:
|
||||
vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op)
|
||||
fmt.Println(Code(closure.Script))
|
||||
|
||||
return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op)
|
||||
}
|
||||
|
||||
pc.Add(pc, ethutil.Big1)
|
||||
|
||||
vm.Endl()
|
||||
|
||||
if vm.Dbg != nil {
|
||||
for _, instrNo := range vm.Dbg.BreakPoints() {
|
||||
if pc.Cmp(big.NewInt(instrNo)) == 0 {
|
||||
vm.Stepping = true
|
||||
|
||||
if !vm.Dbg.BreakHook(prevStep, op, mem, stack, closure.Object()) {
|
||||
return nil, nil
|
||||
}
|
||||
} else if vm.Stepping {
|
||||
if !vm.Dbg.StepHook(prevStep, op, mem, stack, closure.Object()) {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
28
ethchain/vm_env.go
Normal file
28
ethchain/vm_env.go
Normal file
@ -0,0 +1,28 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethstate"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type VMEnv struct {
|
||||
state *ethstate.State
|
||||
block *Block
|
||||
tx *Transaction
|
||||
}
|
||||
|
||||
func NewEnv(state *ethstate.State, tx *Transaction, block *Block) *VMEnv {
|
||||
return &VMEnv{
|
||||
state: state,
|
||||
block: block,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *VMEnv) Origin() []byte { return self.tx.Sender() }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
|
||||
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
|
||||
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
|
||||
func (self *VMEnv) Time() int64 { return self.block.Time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
|
||||
func (self *VMEnv) Value() *big.Int { return self.tx.Value }
|
||||
func (self *VMEnv) State() *ethstate.State { return self.state }
|
@ -1,68 +0,0 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
_ "bytes"
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethdb"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRun4(t *testing.T) {
|
||||
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state := NewState(ethutil.NewTrie(db, ""))
|
||||
|
||||
callerScript, err := ethutil.Compile(`
|
||||
this.store[this.origin()] = 10**20
|
||||
hello := "world"
|
||||
|
||||
return lambda {
|
||||
big to = this.data[0]
|
||||
big from = this.origin()
|
||||
big value = this.data[1]
|
||||
|
||||
if this.store[from] >= value {
|
||||
this.store[from] = this.store[from] - value
|
||||
this.store[to] = this.store[to] + value
|
||||
}
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(Disassemble(callerScript))
|
||||
|
||||
callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), callerScript)
|
||||
callerTx.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
|
||||
|
||||
// Contract addr as test address
|
||||
gas := big.NewInt(1000)
|
||||
gasPrice := big.NewInt(10)
|
||||
account := NewAccount(ContractAddr, big.NewInt(10000000))
|
||||
fmt.Println("account.Amount =", account.Amount)
|
||||
c := MakeContract(callerTx, state)
|
||||
e := account.ConvertGas(gas, gasPrice)
|
||||
if e != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println("account.Amount =", account.Amount)
|
||||
callerClosure := NewClosure(account, c, callerScript, state, gas, gasPrice)
|
||||
|
||||
vm := NewVm(state, nil, RuntimeVars{
|
||||
Origin: account.Address(),
|
||||
BlockNumber: big.NewInt(1),
|
||||
PrevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
|
||||
Coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
|
||||
Time: 1,
|
||||
Diff: big.NewInt(256),
|
||||
})
|
||||
var ret []byte
|
||||
ret, _, e = callerClosure.Call(vm, nil, nil)
|
||||
if e != nil {
|
||||
fmt.Println("error", e)
|
||||
}
|
||||
fmt.Println(ret)
|
||||
}
|
@ -6,6 +6,7 @@ import (
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethcrypto"
|
||||
"github.com/ethereum/eth-go/ethlog"
|
||||
"github.com/ethereum/eth-go/ethstate"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
"strings"
|
||||
@ -24,11 +25,11 @@ type helper struct {
|
||||
func EthereumConfig(stateManager *ethchain.StateManager) helper {
|
||||
return helper{stateManager}
|
||||
}
|
||||
func (self helper) obj() *ethchain.StateObject {
|
||||
func (self helper) obj() *ethstate.StateObject {
|
||||
return self.sm.CurrentState().GetStateObject(cnfCtr)
|
||||
}
|
||||
|
||||
func (self helper) NameReg() *ethchain.StateObject {
|
||||
func (self helper) NameReg() *ethstate.StateObject {
|
||||
if self.obj() != nil {
|
||||
addr := self.obj().GetStorage(big.NewInt(0))
|
||||
if len(addr.Bytes()) > 0 {
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethcrypto"
|
||||
"github.com/ethereum/eth-go/ethstate"
|
||||
"github.com/ethereum/eth-go/ethtrie"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"strings"
|
||||
@ -154,10 +155,10 @@ func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *
|
||||
}
|
||||
|
||||
type PStateObject struct {
|
||||
object *ethchain.StateObject
|
||||
object *ethstate.StateObject
|
||||
}
|
||||
|
||||
func NewPStateObject(object *ethchain.StateObject) *PStateObject {
|
||||
func NewPStateObject(object *ethstate.StateObject) *PStateObject {
|
||||
return &PStateObject{object: object}
|
||||
}
|
||||
|
||||
@ -200,7 +201,7 @@ func (c *PStateObject) Nonce() int {
|
||||
|
||||
func (c *PStateObject) Root() string {
|
||||
if c.object != nil {
|
||||
return ethutil.Bytes2Hex(ethutil.NewValue(c.object.State().Root()).Bytes())
|
||||
return ethutil.Bytes2Hex(ethutil.NewValue(c.object.State.Root()).Bytes())
|
||||
}
|
||||
|
||||
return "<err>"
|
||||
@ -208,7 +209,7 @@ func (c *PStateObject) Root() string {
|
||||
|
||||
func (c *PStateObject) IsContract() bool {
|
||||
if c.object != nil {
|
||||
return len(c.object.Script()) > 0
|
||||
return len(c.object.Code) > 0
|
||||
}
|
||||
|
||||
return false
|
||||
@ -245,7 +246,7 @@ func (c *PStateObject) StateKeyVal(asJson bool) interface{} {
|
||||
|
||||
func (c *PStateObject) Script() string {
|
||||
if c.object != nil {
|
||||
return strings.Join(ethchain.Disassemble(c.object.Script()), " ")
|
||||
return strings.Join(ethchain.Disassemble(c.object.Code), " ")
|
||||
}
|
||||
|
||||
return ""
|
||||
@ -253,7 +254,7 @@ func (c *PStateObject) Script() string {
|
||||
|
||||
func (c *PStateObject) HexScript() string {
|
||||
if c.object != nil {
|
||||
return ethutil.Bytes2Hex(c.object.Script())
|
||||
return ethutil.Bytes2Hex(c.object.Code)
|
||||
}
|
||||
|
||||
return ""
|
||||
@ -265,6 +266,6 @@ type PStorageState struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
func NewPStorageState(storageObject *ethchain.StorageState) *PStorageState {
|
||||
func NewPStorageState(storageObject *ethstate.StorageState) *PStorageState {
|
||||
return &PStorageState{ethutil.Bytes2Hex(storageObject.StateAddress), ethutil.Bytes2Hex(storageObject.Address), storageObject.Value.String()}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ var statelogger = ethlog.NewLogger("STATE")
|
||||
// * Accounts
|
||||
type State struct {
|
||||
// The trie for this structure
|
||||
trie *ethtrie.Trie
|
||||
Trie *ethtrie.Trie
|
||||
|
||||
stateObjects map[string]*StateObject
|
||||
|
||||
@ -26,7 +26,7 @@ type State struct {
|
||||
|
||||
// Create a new state from a given trie
|
||||
func NewState(trie *ethtrie.Trie) *State {
|
||||
return &State{trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()}
|
||||
return &State{Trie: trie, stateObjects: make(map[string]*StateObject), manifest: NewManifest()}
|
||||
}
|
||||
|
||||
// Retrieve the balance from the given address or 0 if object not found
|
||||
@ -58,14 +58,14 @@ func (self *State) UpdateStateObject(stateObject *StateObject) {
|
||||
|
||||
ethutil.Config.Db.Put(ethcrypto.Sha3Bin(stateObject.Code), stateObject.Code)
|
||||
|
||||
self.trie.Update(string(addr), string(stateObject.RlpEncode()))
|
||||
self.Trie.Update(string(addr), string(stateObject.RlpEncode()))
|
||||
|
||||
self.manifest.AddObjectChange(stateObject)
|
||||
}
|
||||
|
||||
// Delete the given state object and delete it from the state trie
|
||||
func (self *State) DeleteStateObject(stateObject *StateObject) {
|
||||
self.trie.Delete(string(stateObject.Address()))
|
||||
self.Trie.Delete(string(stateObject.Address()))
|
||||
|
||||
delete(self.stateObjects, string(stateObject.Address()))
|
||||
}
|
||||
@ -79,7 +79,7 @@ func (self *State) GetStateObject(addr []byte) *StateObject {
|
||||
return stateObject
|
||||
}
|
||||
|
||||
data := self.trie.Get(string(addr))
|
||||
data := self.Trie.Get(string(addr))
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
@ -122,12 +122,12 @@ func (self *State) GetAccount(addr []byte) *StateObject {
|
||||
//
|
||||
|
||||
func (s *State) Cmp(other *State) bool {
|
||||
return s.trie.Cmp(other.trie)
|
||||
return s.Trie.Cmp(other.Trie)
|
||||
}
|
||||
|
||||
func (self *State) Copy() *State {
|
||||
if self.trie != nil {
|
||||
state := NewState(self.trie.Copy())
|
||||
if self.Trie != nil {
|
||||
state := NewState(self.Trie.Copy())
|
||||
for k, stateObject := range self.stateObjects {
|
||||
state.stateObjects[k] = stateObject.Copy()
|
||||
}
|
||||
@ -143,21 +143,21 @@ func (self *State) Set(state *State) {
|
||||
panic("Tried setting 'state' to nil through 'Set'")
|
||||
}
|
||||
|
||||
self.trie = state.trie
|
||||
self.Trie = state.Trie
|
||||
self.stateObjects = state.stateObjects
|
||||
}
|
||||
|
||||
func (s *State) Root() interface{} {
|
||||
return s.trie.Root
|
||||
return s.Trie.Root
|
||||
}
|
||||
|
||||
// Resets the trie and all siblings
|
||||
func (s *State) Reset() {
|
||||
s.trie.Undo()
|
||||
s.Trie.Undo()
|
||||
|
||||
// Reset all nested states
|
||||
for _, stateObject := range s.stateObjects {
|
||||
if stateObject.state == nil {
|
||||
if stateObject.State == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -174,14 +174,14 @@ func (s *State) Sync() {
|
||||
for _, stateObject := range s.stateObjects {
|
||||
//s.UpdateStateObject(stateObject)
|
||||
|
||||
if stateObject.state == nil {
|
||||
if stateObject.State == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
stateObject.state.Sync()
|
||||
stateObject.State.Sync()
|
||||
}
|
||||
|
||||
s.trie.Sync()
|
||||
s.Trie.Sync()
|
||||
|
||||
s.Empty()
|
||||
}
|
||||
@ -202,11 +202,11 @@ func (self *State) Update() {
|
||||
}
|
||||
|
||||
// FIXME trie delete is broken
|
||||
valid, t2 := ethtrie.ParanoiaCheck(self.trie)
|
||||
valid, t2 := ethtrie.ParanoiaCheck(self.Trie)
|
||||
if !valid {
|
||||
statelogger.Infof("Warn: PARANOIA: Different state root during copy %x vs %x\n", self.trie.Root, t2.Root)
|
||||
statelogger.Infof("Warn: PARANOIA: Different state root during copy %x vs %x\n", self.Trie.Root, t2.Root)
|
||||
|
||||
self.trie = t2
|
||||
self.Trie = t2
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,8 +230,8 @@ type Manifest struct {
|
||||
objectAddresses map[string]bool
|
||||
storageAddresses map[string]map[string]bool
|
||||
|
||||
objectChanges map[string]*StateObject
|
||||
storageChanges map[string]map[string]*big.Int
|
||||
ObjectChanges map[string]*StateObject
|
||||
StorageChanges map[string]map[string]*big.Int
|
||||
}
|
||||
|
||||
func NewManifest() *Manifest {
|
||||
@ -242,18 +242,18 @@ func NewManifest() *Manifest {
|
||||
}
|
||||
|
||||
func (m *Manifest) Reset() {
|
||||
m.objectChanges = make(map[string]*StateObject)
|
||||
m.storageChanges = make(map[string]map[string]*big.Int)
|
||||
m.ObjectChanges = make(map[string]*StateObject)
|
||||
m.StorageChanges = make(map[string]map[string]*big.Int)
|
||||
}
|
||||
|
||||
func (m *Manifest) AddObjectChange(stateObject *StateObject) {
|
||||
m.objectChanges[string(stateObject.Address())] = stateObject
|
||||
m.ObjectChanges[string(stateObject.Address())] = stateObject
|
||||
}
|
||||
|
||||
func (m *Manifest) AddStorageChange(stateObject *StateObject, storageAddr []byte, storage *big.Int) {
|
||||
if m.storageChanges[string(stateObject.Address())] == nil {
|
||||
m.storageChanges[string(stateObject.Address())] = make(map[string]*big.Int)
|
||||
if m.StorageChanges[string(stateObject.Address())] == nil {
|
||||
m.StorageChanges[string(stateObject.Address())] = make(map[string]*big.Int)
|
||||
}
|
||||
|
||||
m.storageChanges[string(stateObject.Address())][string(storageAddr)] = storage
|
||||
m.StorageChanges[string(stateObject.Address())][string(storageAddr)] = storage
|
||||
}
|
||||
|
@ -34,9 +34,9 @@ type StateObject struct {
|
||||
CodeHash []byte
|
||||
Nonce uint64
|
||||
// Contract related attributes
|
||||
state *State
|
||||
State *State
|
||||
Code Code
|
||||
initCode Code
|
||||
InitCode Code
|
||||
|
||||
storage Storage
|
||||
|
||||
@ -53,7 +53,7 @@ type StateObject struct {
|
||||
|
||||
func (self *StateObject) Reset() {
|
||||
self.storage = make(Storage)
|
||||
self.state.Reset()
|
||||
self.State.Reset()
|
||||
}
|
||||
|
||||
/*
|
||||
@ -79,7 +79,7 @@ func NewStateObject(addr []byte) *StateObject {
|
||||
address := ethutil.Address(addr)
|
||||
|
||||
object := &StateObject{address: address, Amount: new(big.Int), gasPool: new(big.Int)}
|
||||
object.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
|
||||
object.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
|
||||
object.storage = make(Storage)
|
||||
object.gasPool = new(big.Int)
|
||||
|
||||
@ -89,7 +89,7 @@ func NewStateObject(addr []byte) *StateObject {
|
||||
func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject {
|
||||
contract := NewStateObject(address)
|
||||
contract.Amount = Amount
|
||||
contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root)))
|
||||
contract.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root)))
|
||||
|
||||
return contract
|
||||
}
|
||||
@ -107,11 +107,11 @@ func (self *StateObject) MarkForDeletion() {
|
||||
}
|
||||
|
||||
func (c *StateObject) GetAddr(addr []byte) *ethutil.Value {
|
||||
return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr))))
|
||||
return ethutil.NewValueFromBytes([]byte(c.State.Trie.Get(string(addr))))
|
||||
}
|
||||
|
||||
func (c *StateObject) SetAddr(addr []byte, value interface{}) {
|
||||
c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode()))
|
||||
c.State.Trie.Update(string(addr), string(ethutil.NewValue(value).Encode()))
|
||||
}
|
||||
|
||||
func (self *StateObject) GetStorage(key *big.Int) *ethutil.Value {
|
||||
@ -152,7 +152,7 @@ func (self *StateObject) EachStorage(cb ethtrie.EachCallback) {
|
||||
cb(key, encoded)
|
||||
}
|
||||
|
||||
it := self.state.trie.NewIterator()
|
||||
it := self.State.Trie.NewIterator()
|
||||
it.Each(func(key string, value *ethutil.Value) {
|
||||
// If it's cached don't call the callback.
|
||||
if self.storage[key] == nil {
|
||||
@ -166,18 +166,18 @@ func (self *StateObject) Sync() {
|
||||
if value.Len() == 0 { // value.BigInt().Cmp(ethutil.Big0) == 0 {
|
||||
//data := self.getStorage([]byte(key))
|
||||
//fmt.Printf("deleting %x %x 0x%x\n", self.Address(), []byte(key), data)
|
||||
self.state.trie.Delete(string(key))
|
||||
self.State.Trie.Delete(string(key))
|
||||
continue
|
||||
}
|
||||
|
||||
self.SetAddr([]byte(key), value)
|
||||
}
|
||||
|
||||
valid, t2 := ethtrie.ParanoiaCheck(self.state.trie)
|
||||
valid, t2 := ethtrie.ParanoiaCheck(self.State.Trie)
|
||||
if !valid {
|
||||
statelogger.Infof("Warn: PARANOIA: Different state storage root during copy %x vs %x\n", self.state.trie.Root, t2.Root)
|
||||
statelogger.Infof("Warn: PARANOIA: Different state storage root during copy %x vs %x\n", self.State.Trie.Root, t2.Root)
|
||||
|
||||
self.state.trie = t2
|
||||
self.State.Trie = t2
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,11 +255,11 @@ func (self *StateObject) Copy() *StateObject {
|
||||
stateObject.Amount.Set(self.Amount)
|
||||
stateObject.CodeHash = ethutil.CopyBytes(self.CodeHash)
|
||||
stateObject.Nonce = self.Nonce
|
||||
if self.state != nil {
|
||||
stateObject.state = self.state.Copy()
|
||||
if self.State != nil {
|
||||
stateObject.State = self.State.Copy()
|
||||
}
|
||||
stateObject.Code = ethutil.CopyBytes(self.Code)
|
||||
stateObject.initCode = ethutil.CopyBytes(self.initCode)
|
||||
stateObject.InitCode = ethutil.CopyBytes(self.InitCode)
|
||||
stateObject.storage = self.storage.Copy()
|
||||
stateObject.gasPool.Set(self.gasPool)
|
||||
|
||||
@ -274,10 +274,6 @@ func (self *StateObject) Set(stateObject *StateObject) {
|
||||
// Attribute accessors
|
||||
//
|
||||
|
||||
func (c *StateObject) State() *State {
|
||||
return c.state
|
||||
}
|
||||
|
||||
func (c *StateObject) N() *big.Int {
|
||||
return big.NewInt(int64(c.Nonce))
|
||||
}
|
||||
@ -289,12 +285,12 @@ func (c *StateObject) Address() []byte {
|
||||
|
||||
// Returns the initialization Code
|
||||
func (c *StateObject) Init() Code {
|
||||
return c.initCode
|
||||
return c.InitCode
|
||||
}
|
||||
|
||||
// Debug stuff
|
||||
func (self *StateObject) CreateOutputForDiff() {
|
||||
fmt.Printf("%x %x %x %x\n", self.Address(), self.state.Root(), self.Amount.Bytes(), self.Nonce)
|
||||
fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.Amount.Bytes(), self.Nonce)
|
||||
self.EachStorage(func(addr string, value *ethutil.Value) {
|
||||
fmt.Printf("%x %x\n", addr, value.Bytes())
|
||||
})
|
||||
@ -307,8 +303,8 @@ func (self *StateObject) CreateOutputForDiff() {
|
||||
// State object encoding methods
|
||||
func (c *StateObject) RlpEncode() []byte {
|
||||
var root interface{}
|
||||
if c.state != nil {
|
||||
root = c.state.trie.Root
|
||||
if c.State != nil {
|
||||
root = c.State.Trie.Root
|
||||
} else {
|
||||
root = ""
|
||||
}
|
||||
@ -321,7 +317,7 @@ func (c *StateObject) RlpDecode(data []byte) {
|
||||
|
||||
c.Nonce = decoder.Get(0).Uint()
|
||||
c.Amount = decoder.Get(1).BigInt()
|
||||
c.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
|
||||
c.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
|
||||
c.storage = make(map[string]*ethutil.Value)
|
||||
c.gasPool = new(big.Int)
|
||||
|
||||
|
29
peer.go
29
peer.go
@ -470,23 +470,20 @@ func (p *Peer) HandleInbound() {
|
||||
p.pushPeers()
|
||||
case ethwire.MsgPeersTy:
|
||||
// Received a list of peers (probably because MsgGetPeersTy was send)
|
||||
// Only act on message if we actually requested for a peers list
|
||||
if p.requestedPeerList {
|
||||
data := msg.Data
|
||||
// Create new list of possible peers for the ethereum to process
|
||||
peers := make([]string, data.Len())
|
||||
// Parse each possible peer
|
||||
for i := 0; i < data.Len(); i++ {
|
||||
value := data.Get(i)
|
||||
peers[i] = unpackAddr(value.Get(0), value.Get(1).Uint())
|
||||
}
|
||||
|
||||
// Connect to the list of peers
|
||||
p.ethereum.ProcessPeerList(peers)
|
||||
// Mark unrequested again
|
||||
p.requestedPeerList = false
|
||||
|
||||
data := msg.Data
|
||||
// Create new list of possible peers for the ethereum to process
|
||||
peers := make([]string, data.Len())
|
||||
// Parse each possible peer
|
||||
for i := 0; i < data.Len(); i++ {
|
||||
value := data.Get(i)
|
||||
peers[i] = unpackAddr(value.Get(0), value.Get(1).Uint())
|
||||
}
|
||||
|
||||
// Connect to the list of peers
|
||||
p.ethereum.ProcessPeerList(peers)
|
||||
// Mark unrequested again
|
||||
p.requestedPeerList = false
|
||||
|
||||
case ethwire.MsgGetChainTy:
|
||||
var parent *ethchain.Block
|
||||
// Length minus one since the very last element in the array is a count
|
||||
|
Loading…
Reference in New Issue
Block a user