merge upstream
This commit is contained in:
commit
9831619881
@ -6,7 +6,7 @@ Ethereum
|
|||||||
Ethereum Go Development package (C) Jeffrey Wilcke
|
Ethereum Go Development package (C) Jeffrey Wilcke
|
||||||
|
|
||||||
Ethereum is currently in its testing phase. The current state is "Proof
|
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
|
Ethereum Go is split up in several sub packages Please refer to each
|
||||||
individual package for more information.
|
individual package for more information.
|
||||||
|
@ -25,7 +25,7 @@ func Disassemble(script []byte) (asm []string) {
|
|||||||
pc.Add(pc, ethutil.Big1)
|
pc.Add(pc, ethutil.Big1)
|
||||||
a := int64(op) - int64(PUSH1) + 1
|
a := int64(op) - int64(PUSH1) + 1
|
||||||
if int(pc.Int64()+a) > len(script) {
|
if int(pc.Int64()+a) > len(script) {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data := script[pc.Int64() : pc.Int64()+a]
|
data := script[pc.Int64() : pc.Int64()+a]
|
||||||
@ -40,5 +40,5 @@ func Disassemble(script []byte) (asm []string) {
|
|||||||
pc.Add(pc, ethutil.Big1)
|
pc.Add(pc, ethutil.Big1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return asm
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,14 @@ package ethchain
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/eth-go/ethcrypto"
|
|
||||||
"github.com/ethereum/eth-go/ethtrie"
|
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
_ "strconv"
|
_ "strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethcrypto"
|
||||||
|
"github.com/ethereum/eth-go/ethstate"
|
||||||
|
"github.com/ethereum/eth-go/ethtrie"
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BlockInfo struct {
|
type BlockInfo struct {
|
||||||
@ -39,7 +41,7 @@ type Block struct {
|
|||||||
Coinbase []byte
|
Coinbase []byte
|
||||||
// Block Trie state
|
// Block Trie state
|
||||||
//state *ethutil.Trie
|
//state *ethutil.Trie
|
||||||
state *State
|
state *ethstate.State
|
||||||
// Difficulty for the current block
|
// Difficulty for the current block
|
||||||
Difficulty *big.Int
|
Difficulty *big.Int
|
||||||
// Creation time
|
// Creation time
|
||||||
@ -62,12 +64,6 @@ type Block struct {
|
|||||||
TxSha []byte
|
TxSha []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// New block takes a raw encoded string
|
|
||||||
// XXX DEPRICATED
|
|
||||||
func NewBlockFromData(raw []byte) *Block {
|
|
||||||
return NewBlockFromBytes(raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBlockFromBytes(raw []byte) *Block {
|
func NewBlockFromBytes(raw []byte) *Block {
|
||||||
block := &Block{}
|
block := &Block{}
|
||||||
block.RlpDecode(raw)
|
block.RlpDecode(raw)
|
||||||
@ -104,7 +100,7 @@ func CreateBlock(root interface{},
|
|||||||
}
|
}
|
||||||
block.SetUncles([]*Block{})
|
block.SetUncles([]*Block{})
|
||||||
|
|
||||||
block.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, root))
|
block.state = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, root))
|
||||||
|
|
||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
@ -116,12 +112,12 @@ func (block *Block) Hash() []byte {
|
|||||||
|
|
||||||
func (block *Block) HashNoNonce() []byte {
|
func (block *Block) HashNoNonce() []byte {
|
||||||
return ethcrypto.Sha3Bin(ethutil.Encode([]interface{}{block.PrevHash,
|
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.TxSha, block.Difficulty, block.Number, block.MinGasPrice,
|
||||||
block.GasLimit, block.GasUsed, block.Time, block.Extra}))
|
block.GasLimit, block.GasUsed, block.Time, block.Extra}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (block *Block) State() *State {
|
func (block *Block) State() *ethstate.State {
|
||||||
return block.state
|
return block.state
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,33 +125,6 @@ func (block *Block) Transactions() []*Transaction {
|
|||||||
return block.transactions
|
return block.transactions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (block *Block) PayFee(addr []byte, fee *big.Int) bool {
|
|
||||||
contract := block.state.GetStateObject(addr)
|
|
||||||
// If we can't pay the fee return
|
|
||||||
if contract == nil || contract.Amount.Cmp(fee) < 0 /* amount < fee */ {
|
|
||||||
fmt.Println("Contract has insufficient funds", contract.Amount, fee)
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
base := new(big.Int)
|
|
||||||
contract.Amount = base.Sub(contract.Amount, fee)
|
|
||||||
block.state.trie.Update(string(addr), string(contract.RlpEncode()))
|
|
||||||
|
|
||||||
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))
|
|
||||||
|
|
||||||
base = new(big.Int)
|
|
||||||
account.Amount = base.Add(account.Amount, fee)
|
|
||||||
|
|
||||||
//block.state.trie.Update(string(block.Coinbase), string(ether.RlpEncode()))
|
|
||||||
block.state.UpdateStateObject(account)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (block *Block) CalcGasLimit(parent *Block) *big.Int {
|
func (block *Block) CalcGasLimit(parent *Block) *big.Int {
|
||||||
if block.Number.Cmp(big.NewInt(0)) == 0 {
|
if block.Number.Cmp(big.NewInt(0)) == 0 {
|
||||||
return ethutil.BigPow(10, 6)
|
return ethutil.BigPow(10, 6)
|
||||||
@ -312,7 +281,7 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
|
|||||||
block.PrevHash = header.Get(0).Bytes()
|
block.PrevHash = header.Get(0).Bytes()
|
||||||
block.UncleSha = header.Get(1).Bytes()
|
block.UncleSha = header.Get(1).Bytes()
|
||||||
block.Coinbase = header.Get(2).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.TxSha = header.Get(4).Bytes()
|
||||||
block.Difficulty = header.Get(5).BigInt()
|
block.Difficulty = header.Get(5).BigInt()
|
||||||
block.Number = header.Get(6).BigInt()
|
block.Number = header.Get(6).BigInt()
|
||||||
@ -354,7 +323,7 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block {
|
|||||||
block.PrevHash = header.Get(0).Bytes()
|
block.PrevHash = header.Get(0).Bytes()
|
||||||
block.UncleSha = header.Get(1).Bytes()
|
block.UncleSha = header.Get(1).Bytes()
|
||||||
block.Coinbase = header.Get(2).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.TxSha = header.Get(4).Bytes()
|
||||||
block.Difficulty = header.Get(5).BigInt()
|
block.Difficulty = header.Get(5).BigInt()
|
||||||
block.Number = header.Get(6).BigInt()
|
block.Number = header.Get(6).BigInt()
|
||||||
@ -369,7 +338,7 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (block *Block) GetRoot() interface{} {
|
func (block *Block) GetRoot() interface{} {
|
||||||
return block.state.trie.Root
|
return block.state.Trie.Root
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Block) Receipts() []*Receipt {
|
func (self *Block) Receipts() []*Receipt {
|
||||||
@ -385,7 +354,7 @@ func (block *Block) header() []interface{} {
|
|||||||
// Coinbase address
|
// Coinbase address
|
||||||
block.Coinbase,
|
block.Coinbase,
|
||||||
// root state
|
// root state
|
||||||
block.state.trie.Root,
|
block.state.Trie.Root,
|
||||||
// Sha of tx
|
// Sha of tx
|
||||||
block.TxSha,
|
block.TxSha,
|
||||||
// Current block Difficulty
|
// Current block Difficulty
|
||||||
@ -429,7 +398,7 @@ func (block *Block) String() string {
|
|||||||
block.PrevHash,
|
block.PrevHash,
|
||||||
block.UncleSha,
|
block.UncleSha,
|
||||||
block.Coinbase,
|
block.Coinbase,
|
||||||
block.state.trie.Root,
|
block.state.Trie.Root,
|
||||||
block.TxSha,
|
block.TxSha,
|
||||||
block.Difficulty,
|
block.Difficulty,
|
||||||
block.Number,
|
block.Number,
|
||||||
|
@ -2,11 +2,12 @@ package ethchain
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/eth-go/ethlog"
|
"github.com/ethereum/eth-go/ethlog"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"github.com/ethereum/eth-go/ethwire"
|
"github.com/ethereum/eth-go/ethwire"
|
||||||
"math"
|
|
||||||
"math/big"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var chainlogger = ethlog.NewLogger("CHAIN")
|
var chainlogger = ethlog.NewLogger("CHAIN")
|
||||||
@ -44,7 +45,7 @@ func (bc *BlockChain) NewBlock(coinbase []byte) *Block {
|
|||||||
hash := ZeroHash256
|
hash := ZeroHash256
|
||||||
|
|
||||||
if bc.CurrentBlock != nil {
|
if bc.CurrentBlock != nil {
|
||||||
root = bc.CurrentBlock.state.trie.Root
|
root = bc.CurrentBlock.state.Trie.Root
|
||||||
hash = bc.LastBlockHash
|
hash = bc.LastBlockHash
|
||||||
lastBlockTime = bc.CurrentBlock.Time
|
lastBlockTime = bc.CurrentBlock.Time
|
||||||
}
|
}
|
||||||
@ -280,7 +281,7 @@ func AddTestNetFunds(block *Block) {
|
|||||||
} {
|
} {
|
||||||
codedAddr := ethutil.Hex2Bytes(addr)
|
codedAddr := ethutil.Hex2Bytes(addr)
|
||||||
account := block.state.GetAccount(codedAddr)
|
account := block.state.GetAccount(codedAddr)
|
||||||
account.Amount = ethutil.Big("1606938044258990275541962092341162602522202993782792835301376") //ethutil.BigPow(2, 200)
|
account.Balance = ethutil.Big("1606938044258990275541962092341162602522202993782792835301376") //ethutil.BigPow(2, 200)
|
||||||
block.state.UpdateStateObject(account)
|
block.state.UpdateStateObject(account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -297,7 +298,7 @@ func (bc *BlockChain) setLastBlock() {
|
|||||||
} else {
|
} else {
|
||||||
AddTestNetFunds(bc.genesisBlock)
|
AddTestNetFunds(bc.genesisBlock)
|
||||||
|
|
||||||
bc.genesisBlock.state.trie.Sync()
|
bc.genesisBlock.state.Trie.Sync()
|
||||||
// Prepare the genesis block
|
// Prepare the genesis block
|
||||||
bc.Add(bc.genesisBlock)
|
bc.Add(bc.genesisBlock)
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/ethereum/eth-go/ethcrypto"
|
"github.com/ethereum/eth-go/ethcrypto"
|
||||||
"github.com/ethereum/eth-go/ethlog"
|
"github.com/ethereum/eth-go/ethlog"
|
||||||
"github.com/ethereum/eth-go/ethreact"
|
"github.com/ethereum/eth-go/ethreact"
|
||||||
|
"github.com/ethereum/eth-go/ethstate"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"github.com/ethereum/eth-go/ethwire"
|
"github.com/ethereum/eth-go/ethwire"
|
||||||
"math/big"
|
"math/big"
|
||||||
@ -50,8 +51,6 @@ type StateManager struct {
|
|||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
// Canonical block chain
|
// Canonical block chain
|
||||||
bc *BlockChain
|
bc *BlockChain
|
||||||
// Stack for processing contracts
|
|
||||||
stack *Stack
|
|
||||||
// non-persistent key/value memory storage
|
// non-persistent key/value memory storage
|
||||||
mem map[string]*big.Int
|
mem map[string]*big.Int
|
||||||
// Proof of work used for validating
|
// Proof of work used for validating
|
||||||
@ -62,10 +61,10 @@ type StateManager struct {
|
|||||||
// Transiently state. The trans state isn't ever saved, validated and
|
// Transiently state. The trans state isn't ever saved, validated and
|
||||||
// it could be used for setting account nonces without effecting
|
// it could be used for setting account nonces without effecting
|
||||||
// the main states.
|
// the main states.
|
||||||
transState *State
|
transState *ethstate.State
|
||||||
// Mining state. The mining state is used purely and solely by the mining
|
// Mining state. The mining state is used purely and solely by the mining
|
||||||
// operation.
|
// operation.
|
||||||
miningState *State
|
miningState *ethstate.State
|
||||||
|
|
||||||
// The last attempted block is mainly used for debugging purposes
|
// The last attempted block is mainly used for debugging purposes
|
||||||
// This does not have to be a valid block and will be set during
|
// This does not have to be a valid block and will be set during
|
||||||
@ -75,7 +74,6 @@ type StateManager struct {
|
|||||||
|
|
||||||
func NewStateManager(ethereum EthManager) *StateManager {
|
func NewStateManager(ethereum EthManager) *StateManager {
|
||||||
sm := &StateManager{
|
sm := &StateManager{
|
||||||
stack: NewStack(),
|
|
||||||
mem: make(map[string]*big.Int),
|
mem: make(map[string]*big.Int),
|
||||||
Pow: &EasyPow{},
|
Pow: &EasyPow{},
|
||||||
Ethereum: ethereum,
|
Ethereum: ethereum,
|
||||||
@ -87,19 +85,19 @@ func NewStateManager(ethereum EthManager) *StateManager {
|
|||||||
return sm
|
return sm
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *StateManager) CurrentState() *State {
|
func (sm *StateManager) CurrentState() *ethstate.State {
|
||||||
return sm.Ethereum.BlockChain().CurrentBlock.State()
|
return sm.Ethereum.BlockChain().CurrentBlock.State()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *StateManager) TransState() *State {
|
func (sm *StateManager) TransState() *ethstate.State {
|
||||||
return sm.transState
|
return sm.transState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *StateManager) MiningState() *State {
|
func (sm *StateManager) MiningState() *ethstate.State {
|
||||||
return sm.miningState
|
return sm.miningState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *StateManager) NewMiningState() *State {
|
func (sm *StateManager) NewMiningState() *ethstate.State {
|
||||||
sm.miningState = sm.Ethereum.BlockChain().CurrentBlock.State().Copy()
|
sm.miningState = sm.Ethereum.BlockChain().CurrentBlock.State().Copy()
|
||||||
|
|
||||||
return sm.miningState
|
return sm.miningState
|
||||||
@ -109,7 +107,7 @@ func (sm *StateManager) BlockChain() *BlockChain {
|
|||||||
return sm.bc
|
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 (
|
var (
|
||||||
receipts Receipts
|
receipts Receipts
|
||||||
handled, unhandled Transactions
|
handled, unhandled Transactions
|
||||||
@ -123,9 +121,9 @@ done:
|
|||||||
|
|
||||||
cb := state.GetStateObject(coinbase.Address())
|
cb := state.GetStateObject(coinbase.Address())
|
||||||
st := NewStateTransition(cb, tx, state, block)
|
st := NewStateTransition(cb, tx, state, block)
|
||||||
//fmt.Printf("#%d\n", i+1)
|
|
||||||
err = st.TransitionState()
|
err = st.TransitionState()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
statelogger.Infoln(err)
|
||||||
switch {
|
switch {
|
||||||
case IsNonceErr(err):
|
case IsNonceErr(err):
|
||||||
err = nil // ignore error
|
err = nil // ignore error
|
||||||
@ -225,7 +223,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !block.State().Cmp(state) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +240,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
|
|||||||
if dontReact == false {
|
if dontReact == false {
|
||||||
sm.Ethereum.Reactor().Post("newBlock", block)
|
sm.Ethereum.Reactor().Post("newBlock", block)
|
||||||
|
|
||||||
state.manifest.Reset()
|
state.Manifest().Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.Ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val})
|
sm.Ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val})
|
||||||
@ -255,7 +253,7 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
|
|||||||
return nil
|
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 := state.GetOrNewStateObject(block.Coinbase)
|
||||||
coinbase.SetGasPool(block.CalcGasLimit(parent))
|
coinbase.SetGasPool(block.CalcGasLimit(parent))
|
||||||
|
|
||||||
@ -340,7 +338,7 @@ func CalculateUncleReward(block *Block) *big.Int {
|
|||||||
return UncleReward
|
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
|
// Get the account associated with the coinbase
|
||||||
account := state.GetAccount(block.Coinbase)
|
account := state.GetAccount(block.Coinbase)
|
||||||
// Reward amount of ether to the coinbase address
|
// Reward amount of ether to the coinbase address
|
||||||
@ -364,14 +362,14 @@ func (sm *StateManager) Stop() {
|
|||||||
sm.bc.Stop()
|
sm.bc.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *StateManager) notifyChanges(state *State) {
|
func (sm *StateManager) notifyChanges(state *ethstate.State) {
|
||||||
for addr, stateObject := range state.manifest.objectChanges {
|
for addr, stateObject := range state.Manifest().ObjectChanges {
|
||||||
sm.Ethereum.Reactor().Post("object:"+addr, stateObject)
|
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 {
|
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,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)
|
|
||||||
}
|
|
@ -3,6 +3,11 @@ package ethchain
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethstate"
|
||||||
|
"github.com/ethereum/eth-go/ethtrie"
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"github.com/ethereum/eth-go/ethvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -27,17 +32,17 @@ type StateTransition struct {
|
|||||||
gas, gasPrice *big.Int
|
gas, gasPrice *big.Int
|
||||||
value *big.Int
|
value *big.Int
|
||||||
data []byte
|
data []byte
|
||||||
state *State
|
state *ethstate.State
|
||||||
block *Block
|
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}
|
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 {
|
if self.cb != nil {
|
||||||
return self.cb
|
return self.cb
|
||||||
}
|
}
|
||||||
@ -45,7 +50,7 @@ func (self *StateTransition) Coinbase() *StateObject {
|
|||||||
self.cb = self.state.GetOrNewStateObject(self.coinbase)
|
self.cb = self.state.GetOrNewStateObject(self.coinbase)
|
||||||
return self.cb
|
return self.cb
|
||||||
}
|
}
|
||||||
func (self *StateTransition) Sender() *StateObject {
|
func (self *StateTransition) Sender() *ethstate.StateObject {
|
||||||
if self.sen != nil {
|
if self.sen != nil {
|
||||||
return self.sen
|
return self.sen
|
||||||
}
|
}
|
||||||
@ -54,7 +59,7 @@ func (self *StateTransition) Sender() *StateObject {
|
|||||||
|
|
||||||
return self.sen
|
return self.sen
|
||||||
}
|
}
|
||||||
func (self *StateTransition) Receiver() *StateObject {
|
func (self *StateTransition) Receiver() *ethstate.StateObject {
|
||||||
if self.tx != nil && self.tx.CreatesContract() {
|
if self.tx != nil && self.tx.CreatesContract() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -67,7 +72,7 @@ func (self *StateTransition) Receiver() *StateObject {
|
|||||||
return self.rec
|
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)
|
contract := MakeContract(tx, state)
|
||||||
|
|
||||||
return contract
|
return contract
|
||||||
@ -90,8 +95,8 @@ func (self *StateTransition) BuyGas() error {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
sender := self.Sender()
|
sender := self.Sender()
|
||||||
if sender.Amount.Cmp(self.tx.GasValue()) < 0 {
|
if sender.Balance.Cmp(self.tx.GasValue()) < 0 {
|
||||||
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Amount)
|
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
coinbase := self.Coinbase()
|
coinbase := self.Coinbase()
|
||||||
@ -154,7 +159,7 @@ func (self *StateTransition) TransitionState() (err error) {
|
|||||||
var (
|
var (
|
||||||
tx = self.tx
|
tx = self.tx
|
||||||
sender = self.Sender()
|
sender = self.Sender()
|
||||||
receiver *StateObject
|
receiver *ethstate.StateObject
|
||||||
)
|
)
|
||||||
|
|
||||||
defer self.RefundGas()
|
defer self.RefundGas()
|
||||||
@ -163,22 +168,22 @@ func (self *StateTransition) TransitionState() (err error) {
|
|||||||
sender.Nonce += 1
|
sender.Nonce += 1
|
||||||
|
|
||||||
// Transaction gas
|
// Transaction gas
|
||||||
if err = self.UseGas(GasTx); err != nil {
|
if err = self.UseGas(ethvm.GasTx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pay data gas
|
// Pay data gas
|
||||||
dataPrice := big.NewInt(int64(len(self.data)))
|
dataPrice := big.NewInt(int64(len(self.data)))
|
||||||
dataPrice.Mul(dataPrice, GasData)
|
dataPrice.Mul(dataPrice, ethvm.GasData)
|
||||||
if err = self.UseGas(dataPrice); err != nil {
|
if err = self.UseGas(dataPrice); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if sender.Amount.Cmp(self.value) < 0 {
|
if sender.Balance.Cmp(self.value) < 0 {
|
||||||
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount)
|
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
var snapshot *State
|
var snapshot *ethstate.State
|
||||||
// If the receiver is nil it's a contract (\0*32).
|
// If the receiver is nil it's a contract (\0*32).
|
||||||
if tx.CreatesContract() {
|
if tx.CreatesContract() {
|
||||||
// Subtract the (irreversible) amount from the senders account
|
// Subtract the (irreversible) amount from the senders account
|
||||||
@ -220,10 +225,10 @@ func (self *StateTransition) TransitionState() (err error) {
|
|||||||
return fmt.Errorf("Error during init execution %v", err)
|
return fmt.Errorf("Error during init execution %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
receiver.script = code
|
receiver.Code = code
|
||||||
} else {
|
} else {
|
||||||
if len(receiver.Script()) > 0 {
|
if len(receiver.Code) > 0 {
|
||||||
_, err = self.Eval(receiver.Script(), receiver, "code")
|
_, err = self.Eval(receiver.Code, receiver, "code")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
self.state.Set(snapshot)
|
self.state.Set(snapshot)
|
||||||
|
|
||||||
@ -235,9 +240,9 @@ func (self *StateTransition) TransitionState() (err error) {
|
|||||||
return
|
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 {
|
if sender.Balance.Cmp(self.value) < 0 {
|
||||||
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Amount)
|
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, sender.Balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subtract the amount from the senders account
|
// Subtract the amount from the senders account
|
||||||
@ -248,34 +253,35 @@ func (self *StateTransition) transferValue(sender, receiver *StateObject) error
|
|||||||
return nil
|
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 (
|
var (
|
||||||
block = self.block
|
transactor = self.Sender()
|
||||||
initiator = self.Sender()
|
state = self.state
|
||||||
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 := ethvm.New(env)
|
||||||
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.Verbose = true
|
||||||
vm.Fn = typ
|
vm.Fn = typ
|
||||||
|
|
||||||
ret, err = Call(vm, closure, self.data)
|
ret, _, err = callerClosure.Call(vm, self.tx.Data)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func Call(vm *Vm, closure *Closure, data []byte) (ret []byte, err error) {
|
// Converts an transaction in to a state object
|
||||||
ret, _, err = closure.Call(vm, data)
|
func MakeContract(tx *Transaction, state *ethstate.State) *ethstate.StateObject {
|
||||||
|
// Create contract if there's no recipient
|
||||||
|
if tx.IsContract() {
|
||||||
|
addr := tx.CreationAddress()
|
||||||
|
|
||||||
return
|
contract := state.NewStateObject(addr)
|
||||||
|
contract.InitCode = tx.Data
|
||||||
|
contract.State = ethstate.NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
|
||||||
|
|
||||||
|
return contract
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/eth-go/ethlog"
|
|
||||||
"github.com/ethereum/eth-go/ethwire"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethlog"
|
||||||
|
"github.com/ethereum/eth-go/ethstate"
|
||||||
|
"github.com/ethereum/eth-go/ethwire"
|
||||||
)
|
)
|
||||||
|
|
||||||
var txplogger = ethlog.NewLogger("TXP")
|
var txplogger = ethlog.NewLogger("TXP")
|
||||||
@ -90,78 +92,6 @@ func (pool *TxPool) addTransaction(tx *Transaction) {
|
|||||||
pool.Ethereum.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()})
|
pool.Ethereum.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// Process transaction validates the Tx and processes funds from the
|
|
||||||
// sender to the recipient.
|
|
||||||
func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract bool) (gas *big.Int, err error) {
|
|
||||||
fmt.Printf("state root before update %x\n", state.Root())
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
txplogger.Infoln(r)
|
|
||||||
err = fmt.Errorf("%v", r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
gas = new(big.Int)
|
|
||||||
addGas := func(g *big.Int) { gas.Add(gas, g) }
|
|
||||||
addGas(GasTx)
|
|
||||||
|
|
||||||
// Get the sender
|
|
||||||
sender := state.GetAccount(tx.Sender())
|
|
||||||
|
|
||||||
if sender.Nonce != tx.Nonce {
|
|
||||||
err = NonceError(tx.Nonce, sender.Nonce)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sender.Nonce += 1
|
|
||||||
defer func() {
|
|
||||||
//state.UpdateStateObject(sender)
|
|
||||||
// Notify all subscribers
|
|
||||||
pool.Ethereum.Reactor().Post("newTx:post", tx)
|
|
||||||
}()
|
|
||||||
|
|
||||||
txTotalBytes := big.NewInt(int64(len(tx.Data)))
|
|
||||||
txTotalBytes.Div(txTotalBytes, ethutil.Big32)
|
|
||||||
addGas(new(big.Int).Mul(txTotalBytes, GasSStore))
|
|
||||||
|
|
||||||
rGas := new(big.Int).Set(gas)
|
|
||||||
rGas.Mul(gas, tx.GasPrice)
|
|
||||||
|
|
||||||
// Make sure there's enough in the sender's account. Having insufficient
|
|
||||||
// funds won't invalidate this transaction but simple ignores it.
|
|
||||||
totAmount := new(big.Int).Add(tx.Value, rGas)
|
|
||||||
if sender.Amount.Cmp(totAmount) < 0 {
|
|
||||||
err = fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
state.UpdateStateObject(sender)
|
|
||||||
fmt.Printf("state root after sender update %x\n", state.Root())
|
|
||||||
|
|
||||||
// Get the receiver
|
|
||||||
receiver := state.GetAccount(tx.Recipient)
|
|
||||||
|
|
||||||
// Send Tx to self
|
|
||||||
if bytes.Compare(tx.Recipient, tx.Sender()) == 0 {
|
|
||||||
// Subtract the fee
|
|
||||||
sender.SubAmount(rGas)
|
|
||||||
} else {
|
|
||||||
// Subtract the amount from the senders account
|
|
||||||
sender.SubAmount(totAmount)
|
|
||||||
|
|
||||||
// Add the amount to receivers account which should conclude this transaction
|
|
||||||
receiver.AddAmount(tx.Value)
|
|
||||||
|
|
||||||
state.UpdateStateObject(receiver)
|
|
||||||
fmt.Printf("state root after receiver update %x\n", state.Root())
|
|
||||||
}
|
|
||||||
|
|
||||||
txplogger.Infof("[TXPL] Processed Tx %x\n", tx.Hash())
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
|
func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
|
||||||
// Get the last block so we can retrieve the sender and receiver from
|
// Get the last block so we can retrieve the sender and receiver from
|
||||||
// the merkle trie
|
// the merkle trie
|
||||||
@ -182,7 +112,7 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
|
|||||||
totAmount := new(big.Int).Set(tx.Value)
|
totAmount := new(big.Int).Set(tx.Value)
|
||||||
// Make sure there's enough in the sender's account. Having insufficient
|
// Make sure there's enough in the sender's account. Having insufficient
|
||||||
// funds won't invalidate this transaction but simple ignores it.
|
// funds won't invalidate this transaction but simple ignores it.
|
||||||
if sender.Amount.Cmp(totAmount) < 0 {
|
if sender.Balance.Cmp(totAmount) < 0 {
|
||||||
return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
|
return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +182,7 @@ func (pool *TxPool) CurrentTransactions() []*Transaction {
|
|||||||
return txList
|
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() {
|
for e := pool.pool.Front(); e != nil; e = e.Next() {
|
||||||
tx := e.Value.(*Transaction)
|
tx := e.Value.(*Transaction)
|
||||||
sender := state.GetAccount(tx.Sender())
|
sender := state.GetAccount(tx.Sender())
|
||||||
|
29
ethchain/vm_env.go
Normal file
29
ethchain/vm_env.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
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,
|
||||||
|
tx: tx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
|
||||||
}
|
|
40
ethereum.go
40
ethereum.go
@ -3,6 +3,14 @@ package eth
|
|||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/eth-go/ethchain"
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
"github.com/ethereum/eth-go/ethcrypto"
|
"github.com/ethereum/eth-go/ethcrypto"
|
||||||
"github.com/ethereum/eth-go/ethlog"
|
"github.com/ethereum/eth-go/ethlog"
|
||||||
@ -10,18 +18,12 @@ import (
|
|||||||
"github.com/ethereum/eth-go/ethrpc"
|
"github.com/ethereum/eth-go/ethrpc"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"github.com/ethereum/eth-go/ethwire"
|
"github.com/ethereum/eth-go/ethwire"
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt"
|
const (
|
||||||
|
seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt"
|
||||||
|
seedNodeAddress = "54.76.56.74:30303"
|
||||||
|
)
|
||||||
|
|
||||||
var ethlogger = ethlog.NewLogger("SERV")
|
var ethlogger = ethlog.NewLogger("SERV")
|
||||||
|
|
||||||
@ -422,22 +424,10 @@ func (s *Ethereum) Seed() {
|
|||||||
}
|
}
|
||||||
// Connect to Peer list
|
// Connect to Peer list
|
||||||
s.ProcessPeerList(peers)
|
s.ProcessPeerList(peers)
|
||||||
} else {
|
|
||||||
// Fallback to servers.poc3.txt
|
|
||||||
resp, err := http.Get(seedTextFileUri)
|
|
||||||
if err != nil {
|
|
||||||
ethlogger.Warnln("Fetching seed failed:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
ethlogger.Warnln("Reading seed failed:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.ConnectToPeer(string(body))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX tmp
|
||||||
|
s.ConnectToPeer(seedNodeAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Ethereum) peerHandler(listener net.Listener) {
|
func (s *Ethereum) peerHandler(listener net.Listener) {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/ethereum/eth-go/ethchain"
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
"github.com/ethereum/eth-go/ethcrypto"
|
"github.com/ethereum/eth-go/ethcrypto"
|
||||||
"github.com/ethereum/eth-go/ethlog"
|
"github.com/ethereum/eth-go/ethlog"
|
||||||
|
"github.com/ethereum/eth-go/ethstate"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
@ -24,11 +25,11 @@ type helper struct {
|
|||||||
func EthereumConfig(stateManager *ethchain.StateManager) helper {
|
func EthereumConfig(stateManager *ethchain.StateManager) helper {
|
||||||
return helper{stateManager}
|
return helper{stateManager}
|
||||||
}
|
}
|
||||||
func (self helper) obj() *ethchain.StateObject {
|
func (self helper) obj() *ethstate.StateObject {
|
||||||
return self.sm.CurrentState().GetStateObject(cnfCtr)
|
return self.sm.CurrentState().GetStateObject(cnfCtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self helper) NameReg() *ethchain.StateObject {
|
func (self helper) NameReg() *ethstate.StateObject {
|
||||||
if self.obj() != nil {
|
if self.obj() != nil {
|
||||||
addr := self.obj().GetStorage(big.NewInt(0))
|
addr := self.obj().GetStorage(big.NewInt(0))
|
||||||
if len(addr.Bytes()) > 0 {
|
if len(addr.Bytes()) > 0 {
|
||||||
@ -48,6 +49,12 @@ type PEthereum struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewPEthereum(manager ethchain.EthManager) *PEthereum {
|
func NewPEthereum(manager ethchain.EthManager) *PEthereum {
|
||||||
|
logger.Warnln("DEPRECATED: ethpub.New should be used in favour of ethpub.NewPEthereum")
|
||||||
|
|
||||||
|
return New(manager)
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(manager ethchain.EthManager) *PEthereum {
|
||||||
return &PEthereum{
|
return &PEthereum{
|
||||||
manager,
|
manager,
|
||||||
manager.StateManager(),
|
manager.StateManager(),
|
||||||
|
@ -3,11 +3,13 @@ package ethpub
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/eth-go/ethchain"
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
"github.com/ethereum/eth-go/ethcrypto"
|
"github.com/ethereum/eth-go/ethcrypto"
|
||||||
|
"github.com/ethereum/eth-go/ethstate"
|
||||||
"github.com/ethereum/eth-go/ethtrie"
|
"github.com/ethereum/eth-go/ethtrie"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Peer interface exposed to QML
|
// Peer interface exposed to QML
|
||||||
@ -154,10 +156,10 @@ func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PStateObject struct {
|
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}
|
return &PStateObject{object: object}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,9 +176,9 @@ func (c *PStateObject) GetStorage(address string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PStateObject) Value() string {
|
func (c *PStateObject) Balance() string {
|
||||||
if c.object != nil {
|
if c.object != nil {
|
||||||
return c.object.Amount.String()
|
return c.object.Balance.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
@ -200,7 +202,7 @@ func (c *PStateObject) Nonce() int {
|
|||||||
|
|
||||||
func (c *PStateObject) Root() string {
|
func (c *PStateObject) Root() string {
|
||||||
if c.object != nil {
|
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>"
|
return "<err>"
|
||||||
@ -208,7 +210,7 @@ func (c *PStateObject) Root() string {
|
|||||||
|
|
||||||
func (c *PStateObject) IsContract() bool {
|
func (c *PStateObject) IsContract() bool {
|
||||||
if c.object != nil {
|
if c.object != nil {
|
||||||
return len(c.object.Script()) > 0
|
return len(c.object.Code) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
@ -245,7 +247,7 @@ func (c *PStateObject) StateKeyVal(asJson bool) interface{} {
|
|||||||
|
|
||||||
func (c *PStateObject) Script() string {
|
func (c *PStateObject) Script() string {
|
||||||
if c.object != nil {
|
if c.object != nil {
|
||||||
return strings.Join(ethchain.Disassemble(c.object.Script()), " ")
|
return strings.Join(ethchain.Disassemble(c.object.Code), " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
@ -253,7 +255,7 @@ func (c *PStateObject) Script() string {
|
|||||||
|
|
||||||
func (c *PStateObject) HexScript() string {
|
func (c *PStateObject) HexScript() string {
|
||||||
if c.object != nil {
|
if c.object != nil {
|
||||||
return ethutil.Bytes2Hex(c.object.Script())
|
return ethutil.Bytes2Hex(c.object.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
@ -265,6 +267,6 @@ type PStorageState struct {
|
|||||||
Value string
|
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()}
|
return &PStorageState{ethutil.Bytes2Hex(storageObject.StateAddress), ethutil.Bytes2Hex(storageObject.Address), storageObject.Value.String()}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,11 @@ package ethrpc
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/ethereum/eth-go/ethpub"
|
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethpub"
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EthereumApi struct {
|
type EthereumApi struct {
|
||||||
@ -272,7 +273,7 @@ func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
state := p.ethp.GetStateObject(args.Address)
|
state := p.ethp.GetStateObject(args.Address)
|
||||||
*reply = NewSuccessRes(BalanceRes{Balance: state.Value(), Address: args.Address})
|
*reply = NewSuccessRes(BalanceRes{Balance: state.Balance(), Address: args.Address})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
0
ethstate/.ethtest
Normal file
0
ethstate/.ethtest
Normal file
23
ethstate/errors.go
Normal file
23
ethstate/errors.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package ethstate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GasLimitErr struct {
|
||||||
|
Message string
|
||||||
|
Is, Max *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsGasLimitErr(err error) bool {
|
||||||
|
_, ok := err.(*GasLimitErr)
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
func (err *GasLimitErr) Error() string {
|
||||||
|
return err.Message
|
||||||
|
}
|
||||||
|
func GasLimitError(is, max *big.Int) *GasLimitErr {
|
||||||
|
return &GasLimitErr{Message: fmt.Sprintf("GasLimit error. Max %s, transaction would take it to %s", max, is), Is: is, Max: max}
|
||||||
|
}
|
@ -1,12 +1,16 @@
|
|||||||
package ethchain
|
package ethstate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/eth-go/ethcrypto"
|
"github.com/ethereum/eth-go/ethcrypto"
|
||||||
|
"github.com/ethereum/eth-go/ethlog"
|
||||||
"github.com/ethereum/eth-go/ethtrie"
|
"github.com/ethereum/eth-go/ethtrie"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"math/big"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var statelogger = ethlog.NewLogger("STATE")
|
||||||
|
|
||||||
// States within the ethereum protocol are used to store anything
|
// States within the ethereum protocol are used to store anything
|
||||||
// within the merkle trie. States take care of caching and storing
|
// within the merkle trie. States take care of caching and storing
|
||||||
// nested states. It's the general query interface to retrieve:
|
// nested states. It's the general query interface to retrieve:
|
||||||
@ -14,7 +18,7 @@ import (
|
|||||||
// * Accounts
|
// * Accounts
|
||||||
type State struct {
|
type State struct {
|
||||||
// The trie for this structure
|
// The trie for this structure
|
||||||
trie *ethtrie.Trie
|
Trie *ethtrie.Trie
|
||||||
|
|
||||||
stateObjects map[string]*StateObject
|
stateObjects map[string]*StateObject
|
||||||
|
|
||||||
@ -23,14 +27,14 @@ type State struct {
|
|||||||
|
|
||||||
// Create a new state from a given trie
|
// Create a new state from a given trie
|
||||||
func NewState(trie *ethtrie.Trie) *State {
|
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
|
// Retrieve the balance from the given address or 0 if object not found
|
||||||
func (self *State) GetBalance(addr []byte) *big.Int {
|
func (self *State) GetBalance(addr []byte) *big.Int {
|
||||||
stateObject := self.GetStateObject(addr)
|
stateObject := self.GetStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.Amount
|
return stateObject.Balance
|
||||||
}
|
}
|
||||||
|
|
||||||
return ethutil.Big0
|
return ethutil.Big0
|
||||||
@ -53,16 +57,16 @@ func (self *State) GetNonce(addr []byte) uint64 {
|
|||||||
func (self *State) UpdateStateObject(stateObject *StateObject) {
|
func (self *State) UpdateStateObject(stateObject *StateObject) {
|
||||||
addr := stateObject.Address()
|
addr := stateObject.Address()
|
||||||
|
|
||||||
ethutil.Config.Db.Put(ethcrypto.Sha3Bin(stateObject.Script()), stateObject.Script())
|
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)
|
self.manifest.AddObjectChange(stateObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the given state object and delete it from the state trie
|
// Delete the given state object and delete it from the state trie
|
||||||
func (self *State) DeleteStateObject(stateObject *StateObject) {
|
func (self *State) DeleteStateObject(stateObject *StateObject) {
|
||||||
self.trie.Delete(string(stateObject.Address()))
|
self.Trie.Delete(string(stateObject.Address()))
|
||||||
|
|
||||||
delete(self.stateObjects, string(stateObject.Address()))
|
delete(self.stateObjects, string(stateObject.Address()))
|
||||||
}
|
}
|
||||||
@ -76,7 +80,7 @@ func (self *State) GetStateObject(addr []byte) *StateObject {
|
|||||||
return stateObject
|
return stateObject
|
||||||
}
|
}
|
||||||
|
|
||||||
data := self.trie.Get(string(addr))
|
data := self.Trie.Get(string(addr))
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -99,6 +103,8 @@ func (self *State) GetOrNewStateObject(addr []byte) *StateObject {
|
|||||||
|
|
||||||
// Create a state object whether it exist in the trie or not
|
// Create a state object whether it exist in the trie or not
|
||||||
func (self *State) NewStateObject(addr []byte) *StateObject {
|
func (self *State) NewStateObject(addr []byte) *StateObject {
|
||||||
|
addr = ethutil.Address(addr)
|
||||||
|
|
||||||
statelogger.Infof("(+) %x\n", addr)
|
statelogger.Infof("(+) %x\n", addr)
|
||||||
|
|
||||||
stateObject := NewStateObject(addr)
|
stateObject := NewStateObject(addr)
|
||||||
@ -117,12 +123,12 @@ func (self *State) GetAccount(addr []byte) *StateObject {
|
|||||||
//
|
//
|
||||||
|
|
||||||
func (s *State) Cmp(other *State) bool {
|
func (s *State) Cmp(other *State) bool {
|
||||||
return s.trie.Cmp(other.trie)
|
return s.Trie.Cmp(other.Trie)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *State) Copy() *State {
|
func (self *State) Copy() *State {
|
||||||
if self.trie != nil {
|
if self.Trie != nil {
|
||||||
state := NewState(self.trie.Copy())
|
state := NewState(self.Trie.Copy())
|
||||||
for k, stateObject := range self.stateObjects {
|
for k, stateObject := range self.stateObjects {
|
||||||
state.stateObjects[k] = stateObject.Copy()
|
state.stateObjects[k] = stateObject.Copy()
|
||||||
}
|
}
|
||||||
@ -138,21 +144,21 @@ func (self *State) Set(state *State) {
|
|||||||
panic("Tried setting 'state' to nil through 'Set'")
|
panic("Tried setting 'state' to nil through 'Set'")
|
||||||
}
|
}
|
||||||
|
|
||||||
self.trie = state.trie
|
self.Trie = state.Trie
|
||||||
self.stateObjects = state.stateObjects
|
self.stateObjects = state.stateObjects
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) Root() interface{} {
|
func (s *State) Root() interface{} {
|
||||||
return s.trie.Root
|
return s.Trie.Root
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resets the trie and all siblings
|
// Resets the trie and all siblings
|
||||||
func (s *State) Reset() {
|
func (s *State) Reset() {
|
||||||
s.trie.Undo()
|
s.Trie.Undo()
|
||||||
|
|
||||||
// Reset all nested states
|
// Reset all nested states
|
||||||
for _, stateObject := range s.stateObjects {
|
for _, stateObject := range s.stateObjects {
|
||||||
if stateObject.state == nil {
|
if stateObject.State == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,14 +175,14 @@ func (s *State) Sync() {
|
|||||||
for _, stateObject := range s.stateObjects {
|
for _, stateObject := range s.stateObjects {
|
||||||
//s.UpdateStateObject(stateObject)
|
//s.UpdateStateObject(stateObject)
|
||||||
|
|
||||||
if stateObject.state == nil {
|
if stateObject.State == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
stateObject.state.Sync()
|
stateObject.State.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
s.trie.Sync()
|
s.Trie.Sync()
|
||||||
|
|
||||||
s.Empty()
|
s.Empty()
|
||||||
}
|
}
|
||||||
@ -197,11 +203,11 @@ func (self *State) Update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME trie delete is broken
|
// FIXME trie delete is broken
|
||||||
valid, t2 := ethtrie.ParanoiaCheck(self.trie)
|
valid, t2 := ethtrie.ParanoiaCheck(self.Trie)
|
||||||
if !valid {
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,6 +218,10 @@ func (self *State) CreateOutputForDiff() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *State) Manifest() *Manifest {
|
||||||
|
return self.manifest
|
||||||
|
}
|
||||||
|
|
||||||
// Object manifest
|
// Object manifest
|
||||||
//
|
//
|
||||||
// The object manifest is used to keep changes to the state so we can keep track of the changes
|
// The object manifest is used to keep changes to the state so we can keep track of the changes
|
||||||
@ -221,8 +231,8 @@ type Manifest struct {
|
|||||||
objectAddresses map[string]bool
|
objectAddresses map[string]bool
|
||||||
storageAddresses map[string]map[string]bool
|
storageAddresses map[string]map[string]bool
|
||||||
|
|
||||||
objectChanges map[string]*StateObject
|
ObjectChanges map[string]*StateObject
|
||||||
storageChanges map[string]map[string]*big.Int
|
StorageChanges map[string]map[string]*big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewManifest() *Manifest {
|
func NewManifest() *Manifest {
|
||||||
@ -233,18 +243,18 @@ func NewManifest() *Manifest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manifest) Reset() {
|
func (m *Manifest) Reset() {
|
||||||
m.objectChanges = make(map[string]*StateObject)
|
m.ObjectChanges = make(map[string]*StateObject)
|
||||||
m.storageChanges = make(map[string]map[string]*big.Int)
|
m.StorageChanges = make(map[string]map[string]*big.Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manifest) AddObjectChange(stateObject *StateObject) {
|
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) {
|
func (m *Manifest) AddStorageChange(stateObject *StateObject, storageAddr []byte, storage *big.Int) {
|
||||||
if m.storageChanges[string(stateObject.Address())] == nil {
|
if m.StorageChanges[string(stateObject.Address())] == nil {
|
||||||
m.storageChanges[string(stateObject.Address())] = make(map[string]*big.Int)
|
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
|
||||||
}
|
}
|
@ -1,18 +1,18 @@
|
|||||||
package ethchain
|
package ethstate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/eth-go/ethcrypto"
|
"github.com/ethereum/eth-go/ethcrypto"
|
||||||
"github.com/ethereum/eth-go/ethtrie"
|
"github.com/ethereum/eth-go/ethtrie"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"math/big"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Code []byte
|
type Code []byte
|
||||||
|
|
||||||
func (self Code) String() string {
|
func (self Code) String() string {
|
||||||
return strings.Join(Disassemble(self), " ")
|
return string(self) //strings.Join(Disassemble(self), " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
type Storage map[string]*ethutil.Value
|
type Storage map[string]*ethutil.Value
|
||||||
@ -31,13 +31,13 @@ type StateObject struct {
|
|||||||
// Address of the object
|
// Address of the object
|
||||||
address []byte
|
address []byte
|
||||||
// Shared attributes
|
// Shared attributes
|
||||||
Amount *big.Int
|
Balance *big.Int
|
||||||
ScriptHash []byte
|
CodeHash []byte
|
||||||
Nonce uint64
|
Nonce uint64
|
||||||
// Contract related attributes
|
// Contract related attributes
|
||||||
state *State
|
State *State
|
||||||
script Code
|
Code Code
|
||||||
initScript Code
|
InitCode Code
|
||||||
|
|
||||||
storage Storage
|
storage Storage
|
||||||
|
|
||||||
@ -54,9 +54,10 @@ type StateObject struct {
|
|||||||
|
|
||||||
func (self *StateObject) Reset() {
|
func (self *StateObject) Reset() {
|
||||||
self.storage = make(Storage)
|
self.storage = make(Storage)
|
||||||
self.state.Reset()
|
self.State.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// Converts an transaction in to a state object
|
// Converts an transaction in to a state object
|
||||||
func MakeContract(tx *Transaction, state *State) *StateObject {
|
func MakeContract(tx *Transaction, state *State) *StateObject {
|
||||||
// Create contract if there's no recipient
|
// Create contract if there's no recipient
|
||||||
@ -64,7 +65,7 @@ func MakeContract(tx *Transaction, state *State) *StateObject {
|
|||||||
addr := tx.CreationAddress()
|
addr := tx.CreationAddress()
|
||||||
|
|
||||||
contract := state.NewStateObject(addr)
|
contract := state.NewStateObject(addr)
|
||||||
contract.initScript = tx.Data
|
contract.initCode = tx.Data
|
||||||
contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
|
contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, ""))
|
||||||
|
|
||||||
return contract
|
return contract
|
||||||
@ -72,23 +73,24 @@ func MakeContract(tx *Transaction, state *State) *StateObject {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func NewStateObject(addr []byte) *StateObject {
|
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.
|
// This to ensure that it has 20 bytes (and not 0 bytes), thus left or right pad doesn't matter.
|
||||||
address := ethutil.Address(addr)
|
address := ethutil.Address(addr)
|
||||||
|
|
||||||
object := &StateObject{address: address, Amount: new(big.Int), gasPool: new(big.Int)}
|
object := &StateObject{address: address, Balance: 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.storage = make(Storage)
|
||||||
object.gasPool = new(big.Int)
|
object.gasPool = new(big.Int)
|
||||||
|
|
||||||
return object
|
return object
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject {
|
func NewContract(address []byte, balance *big.Int, root []byte) *StateObject {
|
||||||
contract := NewStateObject(address)
|
contract := NewStateObject(address)
|
||||||
contract.Amount = Amount
|
contract.Balance = balance
|
||||||
contract.state = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root)))
|
contract.State = NewState(ethtrie.NewTrie(ethutil.Config.Db, string(root)))
|
||||||
|
|
||||||
return contract
|
return contract
|
||||||
}
|
}
|
||||||
@ -102,15 +104,15 @@ func NewStateObjectFromBytes(address, data []byte) *StateObject {
|
|||||||
|
|
||||||
func (self *StateObject) MarkForDeletion() {
|
func (self *StateObject) MarkForDeletion() {
|
||||||
self.remove = true
|
self.remove = true
|
||||||
statelogger.DebugDetailf("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.Amount)
|
statelogger.DebugDetailf("%x: #%d %v (deletion)\n", self.Address(), self.Nonce, self.Balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *StateObject) GetAddr(addr []byte) *ethutil.Value {
|
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{}) {
|
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 {
|
func (self *StateObject) GetStorage(key *big.Int) *ethutil.Value {
|
||||||
@ -140,15 +142,6 @@ func (self *StateObject) getStorage(k []byte) *ethutil.Value {
|
|||||||
func (self *StateObject) setStorage(k []byte, value *ethutil.Value) {
|
func (self *StateObject) setStorage(k []byte, value *ethutil.Value) {
|
||||||
key := ethutil.LeftPadBytes(k, 32)
|
key := ethutil.LeftPadBytes(k, 32)
|
||||||
self.storage[string(key)] = value.Copy()
|
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
|
// Iterate over each storage address and yield callback
|
||||||
@ -160,7 +153,7 @@ func (self *StateObject) EachStorage(cb ethtrie.EachCallback) {
|
|||||||
cb(key, encoded)
|
cb(key, encoded)
|
||||||
}
|
}
|
||||||
|
|
||||||
it := self.state.trie.NewIterator()
|
it := self.State.Trie.NewIterator()
|
||||||
it.Each(func(key string, value *ethutil.Value) {
|
it.Each(func(key string, value *ethutil.Value) {
|
||||||
// If it's cached don't call the callback.
|
// If it's cached don't call the callback.
|
||||||
if self.storage[key] == nil {
|
if self.storage[key] == nil {
|
||||||
@ -170,63 +163,47 @@ func (self *StateObject) EachStorage(cb ethtrie.EachCallback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateObject) Sync() {
|
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 {
|
for key, value := range self.storage {
|
||||||
if value.Len() == 0 { // value.BigInt().Cmp(ethutil.Big0) == 0 {
|
if value.Len() == 0 { // value.BigInt().Cmp(ethutil.Big0) == 0 {
|
||||||
//data := self.getStorage([]byte(key))
|
//data := self.getStorage([]byte(key))
|
||||||
//fmt.Printf("deleting %x %x 0x%x\n", self.Address(), []byte(key), data)
|
//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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
self.SetAddr([]byte(key), value)
|
self.SetAddr([]byte(key), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
valid, t2 := ethtrie.ParanoiaCheck(self.state.trie)
|
valid, t2 := ethtrie.ParanoiaCheck(self.State.Trie)
|
||||||
if !valid {
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
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 {
|
func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value {
|
||||||
if int64(len(c.script)-1) < pc.Int64() {
|
if int64(len(c.Code)-1) < pc.Int64() {
|
||||||
return ethutil.NewValue(0)
|
return ethutil.NewValue(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]})
|
return ethutil.NewValueFromBytes([]byte{c.Code[pc.Int64()]})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *StateObject) AddAmount(amount *big.Int) {
|
func (c *StateObject) AddAmount(amount *big.Int) {
|
||||||
c.SetAmount(new(big.Int).Add(c.Amount, amount))
|
c.SetBalance(new(big.Int).Add(c.Balance, amount))
|
||||||
|
|
||||||
statelogger.Debugf("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount)
|
statelogger.Debugf("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Balance, amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *StateObject) SubAmount(amount *big.Int) {
|
func (c *StateObject) SubAmount(amount *big.Int) {
|
||||||
c.SetAmount(new(big.Int).Sub(c.Amount, amount))
|
c.SetBalance(new(big.Int).Sub(c.Balance, amount))
|
||||||
|
|
||||||
statelogger.Debugf("%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount)
|
statelogger.Debugf("%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Balance, amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *StateObject) SetAmount(amount *big.Int) {
|
func (c *StateObject) SetBalance(amount *big.Int) {
|
||||||
c.Amount = amount
|
c.Balance = amount
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -234,11 +211,11 @@ func (c *StateObject) SetAmount(amount *big.Int) {
|
|||||||
//
|
//
|
||||||
|
|
||||||
// Return the gas back to the origin. Used by the Virtual machine or Closures
|
// 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) ReturnGas(gas, price *big.Int) {}
|
||||||
func (c *StateObject) ConvertGas(gas, price *big.Int) error {
|
func (c *StateObject) ConvertGas(gas, price *big.Int) error {
|
||||||
total := new(big.Int).Mul(gas, price)
|
total := new(big.Int).Mul(gas, price)
|
||||||
if total.Cmp(c.Amount) > 0 {
|
if total.Cmp(c.Balance) > 0 {
|
||||||
return fmt.Errorf("insufficient amount: %v, %v", c.Amount, total)
|
return fmt.Errorf("insufficient amount: %v, %v", c.Balance, total)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.SubAmount(total)
|
c.SubAmount(total)
|
||||||
@ -271,19 +248,19 @@ func (self *StateObject) RefundGas(gas, price *big.Int) {
|
|||||||
rGas := new(big.Int).Set(gas)
|
rGas := new(big.Int).Set(gas)
|
||||||
rGas.Mul(rGas, price)
|
rGas.Mul(rGas, price)
|
||||||
|
|
||||||
self.Amount.Sub(self.Amount, rGas)
|
self.Balance.Sub(self.Balance, rGas)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateObject) Copy() *StateObject {
|
func (self *StateObject) Copy() *StateObject {
|
||||||
stateObject := NewStateObject(self.Address())
|
stateObject := NewStateObject(self.Address())
|
||||||
stateObject.Amount.Set(self.Amount)
|
stateObject.Balance.Set(self.Balance)
|
||||||
stateObject.ScriptHash = ethutil.CopyBytes(self.ScriptHash)
|
stateObject.CodeHash = ethutil.CopyBytes(self.CodeHash)
|
||||||
stateObject.Nonce = self.Nonce
|
stateObject.Nonce = self.Nonce
|
||||||
if self.state != nil {
|
if self.State != nil {
|
||||||
stateObject.state = self.state.Copy()
|
stateObject.State = self.State.Copy()
|
||||||
}
|
}
|
||||||
stateObject.script = ethutil.CopyBytes(self.script)
|
stateObject.Code = ethutil.CopyBytes(self.Code)
|
||||||
stateObject.initScript = ethutil.CopyBytes(self.initScript)
|
stateObject.InitCode = ethutil.CopyBytes(self.InitCode)
|
||||||
stateObject.storage = self.storage.Copy()
|
stateObject.storage = self.storage.Copy()
|
||||||
stateObject.gasPool.Set(self.gasPool)
|
stateObject.gasPool.Set(self.gasPool)
|
||||||
|
|
||||||
@ -298,10 +275,6 @@ func (self *StateObject) Set(stateObject *StateObject) {
|
|||||||
// Attribute accessors
|
// Attribute accessors
|
||||||
//
|
//
|
||||||
|
|
||||||
func (c *StateObject) State() *State {
|
|
||||||
return c.state
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *StateObject) N() *big.Int {
|
func (c *StateObject) N() *big.Int {
|
||||||
return big.NewInt(int64(c.Nonce))
|
return big.NewInt(int64(c.Nonce))
|
||||||
}
|
}
|
||||||
@ -311,19 +284,14 @@ func (c *StateObject) Address() []byte {
|
|||||||
return c.address
|
return c.address
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the main script body
|
// Returns the initialization Code
|
||||||
func (c *StateObject) Script() Code {
|
|
||||||
return c.script
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the initialization script
|
|
||||||
func (c *StateObject) Init() Code {
|
func (c *StateObject) Init() Code {
|
||||||
return c.initScript
|
return c.InitCode
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug stuff
|
// Debug stuff
|
||||||
func (self *StateObject) CreateOutputForDiff() {
|
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.Balance.Bytes(), self.Nonce)
|
||||||
self.EachStorage(func(addr string, value *ethutil.Value) {
|
self.EachStorage(func(addr string, value *ethutil.Value) {
|
||||||
fmt.Printf("%x %x\n", addr, value.Bytes())
|
fmt.Printf("%x %x\n", addr, value.Bytes())
|
||||||
})
|
})
|
||||||
@ -336,27 +304,27 @@ func (self *StateObject) CreateOutputForDiff() {
|
|||||||
// State object encoding methods
|
// State object encoding methods
|
||||||
func (c *StateObject) RlpEncode() []byte {
|
func (c *StateObject) RlpEncode() []byte {
|
||||||
var root interface{}
|
var root interface{}
|
||||||
if c.state != nil {
|
if c.State != nil {
|
||||||
root = c.state.trie.Root
|
root = c.State.Trie.Root
|
||||||
} else {
|
} else {
|
||||||
root = ""
|
root = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return ethutil.Encode([]interface{}{c.Nonce, c.Amount, root, ethcrypto.Sha3Bin(c.script)})
|
return ethutil.Encode([]interface{}{c.Nonce, c.Balance, root, ethcrypto.Sha3Bin(c.Code)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *StateObject) RlpDecode(data []byte) {
|
func (c *StateObject) RlpDecode(data []byte) {
|
||||||
decoder := ethutil.NewValueFromBytes(data)
|
decoder := ethutil.NewValueFromBytes(data)
|
||||||
|
|
||||||
c.Nonce = decoder.Get(0).Uint()
|
c.Nonce = decoder.Get(0).Uint()
|
||||||
c.Amount = decoder.Get(1).BigInt()
|
c.Balance = 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.storage = make(map[string]*ethutil.Value)
|
||||||
c.gasPool = new(big.Int)
|
c.gasPool = new(big.Int)
|
||||||
|
|
||||||
c.ScriptHash = decoder.Get(3).Bytes()
|
c.CodeHash = decoder.Get(3).Bytes()
|
||||||
|
|
||||||
c.script, _ = ethutil.Config.Db.Get(c.ScriptHash)
|
c.Code, _ = ethutil.Config.Db.Get(c.CodeHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Storage change object. Used by the manifest for notifying changes to
|
// Storage change object. Used by the manifest for notifying changes to
|
@ -1,19 +1,23 @@
|
|||||||
package ethchain
|
package ethstate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ethereum/eth-go/ethdb"
|
"github.com/ethereum/eth-go/ethdb"
|
||||||
|
"github.com/ethereum/eth-go/ethtrie"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ZeroHash256 = make([]byte, 32)
|
||||||
|
|
||||||
func TestSnapshot(t *testing.T) {
|
func TestSnapshot(t *testing.T) {
|
||||||
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "", "ETH")
|
|
||||||
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
state := NewState(ethutil.NewTrie(db, ""))
|
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
|
||||||
|
ethutil.Config.Db = db
|
||||||
|
|
||||||
|
state := NewState(ethtrie.NewTrie(db, ""))
|
||||||
|
|
||||||
|
stateObject := state.GetOrNewStateObject([]byte("aa"))
|
||||||
|
|
||||||
stateObject := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256)
|
|
||||||
state.UpdateStateObject(stateObject)
|
|
||||||
stateObject.SetStorage(ethutil.Big("0"), ethutil.NewValue(42))
|
stateObject.SetStorage(ethutil.Big("0"), ethutil.NewValue(42))
|
||||||
|
|
||||||
snapshot := state.Copy()
|
snapshot := state.Copy()
|
||||||
@ -24,7 +28,8 @@ func TestSnapshot(t *testing.T) {
|
|||||||
state.Set(snapshot)
|
state.Set(snapshot)
|
||||||
|
|
||||||
stateObject = state.GetStateObject([]byte("aa"))
|
stateObject = state.GetStateObject([]byte("aa"))
|
||||||
if !stateObject.GetStorage(ethutil.Big("0")).Cmp(ethutil.NewValue(42)) {
|
res := stateObject.GetStorage(ethutil.Big("0"))
|
||||||
t.Error("Expected storage 0 to be 42")
|
if !res.Cmp(ethutil.NewValue(42)) {
|
||||||
|
t.Error("Expected storage 0 to be 42", res)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,14 +4,6 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
var BigInt0 *big.Int = big.NewInt(0)
|
|
||||||
|
|
||||||
// True
|
|
||||||
var BigTrue *big.Int = big.NewInt(1)
|
|
||||||
|
|
||||||
// False
|
|
||||||
var BigFalse *big.Int = big.NewInt(0)
|
|
||||||
|
|
||||||
// Big pow
|
// Big pow
|
||||||
//
|
//
|
||||||
// Returns the power of two big integers
|
// Returns the power of two big integers
|
||||||
@ -73,3 +65,14 @@ func BigMax(x, y *big.Int) *big.Int {
|
|||||||
|
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Big min
|
||||||
|
//
|
||||||
|
// Returns the minimum size big integer
|
||||||
|
func BigMin(x, y *big.Int) *big.Int {
|
||||||
|
if x.Cmp(y) >= 0 {
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
@ -44,26 +44,28 @@ func BytesToNumber(b []byte) uint64 {
|
|||||||
// Read variable int
|
// Read variable int
|
||||||
//
|
//
|
||||||
// Read a variable length number in big endian byte order
|
// Read a variable length number in big endian byte order
|
||||||
func ReadVarint(reader *bytes.Reader) (ret uint64) {
|
func ReadVarInt(buff []byte) (ret uint64) {
|
||||||
if reader.Len() == 8 {
|
switch l := len(buff); {
|
||||||
var num uint64
|
case l > 4:
|
||||||
binary.Read(reader, binary.BigEndian, &num)
|
d := LeftPadBytes(buff, 8)
|
||||||
ret = uint64(num)
|
binary.Read(bytes.NewReader(d), binary.BigEndian, &ret)
|
||||||
} else if reader.Len() == 4 {
|
case l > 2:
|
||||||
var num uint32
|
var num uint32
|
||||||
binary.Read(reader, binary.BigEndian, &num)
|
d := LeftPadBytes(buff, 4)
|
||||||
|
binary.Read(bytes.NewReader(d), binary.BigEndian, &num)
|
||||||
ret = uint64(num)
|
ret = uint64(num)
|
||||||
} else if reader.Len() == 2 {
|
case l > 1:
|
||||||
var num uint16
|
var num uint16
|
||||||
binary.Read(reader, binary.BigEndian, &num)
|
d := LeftPadBytes(buff, 2)
|
||||||
|
binary.Read(bytes.NewReader(d), binary.BigEndian, &num)
|
||||||
ret = uint64(num)
|
ret = uint64(num)
|
||||||
} else {
|
default:
|
||||||
var num uint8
|
var num uint8
|
||||||
binary.Read(reader, binary.BigEndian, &num)
|
binary.Read(bytes.NewReader(buff), binary.BigEndian, &num)
|
||||||
ret = uint64(num)
|
ret = uint64(num)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Binary length
|
// Binary length
|
||||||
@ -98,6 +100,7 @@ func Bytes2Hex(d []byte) string {
|
|||||||
|
|
||||||
func Hex2Bytes(str string) []byte {
|
func Hex2Bytes(str string) []byte {
|
||||||
h, _ := hex.DecodeString(str)
|
h, _ := hex.DecodeString(str)
|
||||||
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,9 +58,11 @@ func CurrencyToString(num *big.Int) string {
|
|||||||
|
|
||||||
// Common big integers often used
|
// Common big integers often used
|
||||||
var (
|
var (
|
||||||
Big1 = big.NewInt(1)
|
Big1 = big.NewInt(1)
|
||||||
Big2 = big.NewInt(2)
|
Big2 = big.NewInt(2)
|
||||||
Big0 = big.NewInt(0)
|
Big0 = big.NewInt(0)
|
||||||
Big32 = big.NewInt(32)
|
BigTrue = Big1
|
||||||
Big256 = big.NewInt(0xff)
|
BigFalse = Big0
|
||||||
|
Big32 = big.NewInt(32)
|
||||||
|
Big256 = big.NewInt(0xff)
|
||||||
)
|
)
|
||||||
|
@ -3,8 +3,9 @@ package ethutil
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/rakyll/globalconf"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/rakyll/globalconf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config struct
|
// Config struct
|
||||||
@ -28,8 +29,7 @@ var Config *ConfigManager
|
|||||||
func ReadConfig(ConfigFile string, Datadir string, EnvPrefix string) *ConfigManager {
|
func ReadConfig(ConfigFile string, Datadir string, EnvPrefix string) *ConfigManager {
|
||||||
if Config == nil {
|
if Config == nil {
|
||||||
// create ConfigFile if does not exist, otherwise globalconf panic when trying to persist flags
|
// create ConfigFile if does not exist, otherwise globalconf panic when trying to persist flags
|
||||||
_, err := os.Stat(ConfigFile)
|
if !FileExist(ConfigFile) {
|
||||||
if err != nil && os.IsNotExist(err) {
|
|
||||||
fmt.Printf("config file '%s' doesn't exist, creating it\n", ConfigFile)
|
fmt.Printf("config file '%s' doesn't exist, creating it\n", ConfigFile)
|
||||||
os.Create(ConfigFile)
|
os.Create(ConfigFile)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package ethutil
|
package ethutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -18,3 +20,41 @@ func ExpandHomePath(p string) (path string) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FileExist(filePath string) bool {
|
||||||
|
_, err := os.Stat(filePath)
|
||||||
|
if err != nil && os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadAllFile(filePath string) (string, error) {
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteFile(filePath string, content []byte) error {
|
||||||
|
fh, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fh.Close()
|
||||||
|
|
||||||
|
_, err = fh.Write(content)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
143
ethutil/rlp.go
143
ethutil/rlp.go
@ -55,8 +55,7 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} {
|
|||||||
return reader.Next(int(char - 0x80))
|
return reader.Next(int(char - 0x80))
|
||||||
|
|
||||||
case char <= 0xbf:
|
case char <= 0xbf:
|
||||||
buff := bytes.NewReader(reader.Next(int(char - 0xb8)))
|
length := ReadVarInt(reader.Next(int(char - 0xb7)))
|
||||||
length := ReadVarint(buff)
|
|
||||||
|
|
||||||
return reader.Next(int(length))
|
return reader.Next(int(length))
|
||||||
|
|
||||||
@ -72,76 +71,22 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return slice
|
return slice
|
||||||
|
case char <= 0xff:
|
||||||
|
length := ReadVarInt(reader.Next(int(char - 0xf7)))
|
||||||
|
for i := uint64(0); i < length; i++ {
|
||||||
|
obj := DecodeWithReader(reader)
|
||||||
|
if obj != nil {
|
||||||
|
slice = append(slice, obj)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Use a bytes.Buffer instead of a raw byte slice.
|
|
||||||
// Cleaner code, and use draining instead of seeking the next bytes to read
|
|
||||||
func Decode(data []byte, pos uint64) (interface{}, uint64) {
|
|
||||||
var slice []interface{}
|
|
||||||
char := int(data[pos])
|
|
||||||
switch {
|
|
||||||
case char <= 0x7f:
|
|
||||||
return data[pos], pos + 1
|
|
||||||
|
|
||||||
case char <= 0xb7:
|
|
||||||
b := uint64(data[pos]) - 0x80
|
|
||||||
|
|
||||||
return data[pos+1 : pos+1+b], pos + 1 + b
|
|
||||||
|
|
||||||
case char <= 0xbf:
|
|
||||||
b := uint64(data[pos]) - 0xb7
|
|
||||||
|
|
||||||
b2 := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+b]))
|
|
||||||
|
|
||||||
return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2
|
|
||||||
|
|
||||||
case char <= 0xf7:
|
|
||||||
b := uint64(data[pos]) - 0xc0
|
|
||||||
prevPos := pos
|
|
||||||
pos++
|
|
||||||
for i := uint64(0); i < b; {
|
|
||||||
var obj interface{}
|
|
||||||
|
|
||||||
// Get the next item in the data list and append it
|
|
||||||
obj, prevPos = Decode(data, pos)
|
|
||||||
slice = append(slice, obj)
|
|
||||||
|
|
||||||
// Increment i by the amount bytes read in the previous
|
|
||||||
// read
|
|
||||||
i += (prevPos - pos)
|
|
||||||
pos = prevPos
|
|
||||||
}
|
|
||||||
return slice, pos
|
|
||||||
|
|
||||||
case char <= 0xff:
|
|
||||||
l := uint64(data[pos]) - 0xf7
|
|
||||||
b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l]))
|
|
||||||
|
|
||||||
pos = pos + l + 1
|
|
||||||
|
|
||||||
prevPos := b
|
|
||||||
for i := uint64(0); i < uint64(b); {
|
|
||||||
var obj interface{}
|
|
||||||
|
|
||||||
obj, prevPos = Decode(data, pos)
|
|
||||||
slice = append(slice, obj)
|
|
||||||
|
|
||||||
i += (prevPos - pos)
|
|
||||||
pos = prevPos
|
|
||||||
}
|
|
||||||
return slice, pos
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("byte not supported: %q", char))
|
|
||||||
}
|
|
||||||
|
|
||||||
return slice, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
directRlp = big.NewInt(0x7f)
|
directRlp = big.NewInt(0x7f)
|
||||||
numberRlp = big.NewInt(0xb7)
|
numberRlp = big.NewInt(0xb7)
|
||||||
@ -223,3 +168,67 @@ func Encode(object interface{}) []byte {
|
|||||||
|
|
||||||
return buff.Bytes()
|
return buff.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Use a bytes.Buffer instead of a raw byte slice.
|
||||||
|
// Cleaner code, and use draining instead of seeking the next bytes to read
|
||||||
|
func Decode(data []byte, pos uint64) (interface{}, uint64) {
|
||||||
|
var slice []interface{}
|
||||||
|
char := int(data[pos])
|
||||||
|
switch {
|
||||||
|
case char <= 0x7f:
|
||||||
|
return data[pos], pos + 1
|
||||||
|
|
||||||
|
case char <= 0xb7:
|
||||||
|
b := uint64(data[pos]) - 0x80
|
||||||
|
|
||||||
|
return data[pos+1 : pos+1+b], pos + 1 + b
|
||||||
|
|
||||||
|
case char <= 0xbf:
|
||||||
|
b := uint64(data[pos]) - 0xb7
|
||||||
|
|
||||||
|
b2 := ReadVarInt(data[pos+1 : pos+1+b])
|
||||||
|
|
||||||
|
return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2
|
||||||
|
|
||||||
|
case char <= 0xf7:
|
||||||
|
b := uint64(data[pos]) - 0xc0
|
||||||
|
prevPos := pos
|
||||||
|
pos++
|
||||||
|
for i := uint64(0); i < b; {
|
||||||
|
var obj interface{}
|
||||||
|
|
||||||
|
// Get the next item in the data list and append it
|
||||||
|
obj, prevPos = Decode(data, pos)
|
||||||
|
slice = append(slice, obj)
|
||||||
|
|
||||||
|
// Increment i by the amount bytes read in the previous
|
||||||
|
// read
|
||||||
|
i += (prevPos - pos)
|
||||||
|
pos = prevPos
|
||||||
|
}
|
||||||
|
return slice, pos
|
||||||
|
|
||||||
|
case char <= 0xff:
|
||||||
|
l := uint64(data[pos]) - 0xf7
|
||||||
|
b := ReadVarInt(data[pos+1 : pos+1+l])
|
||||||
|
|
||||||
|
pos = pos + l + 1
|
||||||
|
|
||||||
|
prevPos := b
|
||||||
|
for i := uint64(0); i < uint64(b); {
|
||||||
|
var obj interface{}
|
||||||
|
|
||||||
|
obj, prevPos = Decode(data, pos)
|
||||||
|
slice = append(slice, obj)
|
||||||
|
|
||||||
|
i += (prevPos - pos)
|
||||||
|
pos = prevPos
|
||||||
|
}
|
||||||
|
return slice, pos
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("byte not supported: %q", char))
|
||||||
|
}
|
||||||
|
|
||||||
|
return slice, 0
|
||||||
|
}
|
||||||
|
@ -44,6 +44,17 @@ func TestValueSlice(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLargeData(t *testing.T) {
|
||||||
|
data := make([]byte, 100000)
|
||||||
|
enc := Encode(data)
|
||||||
|
value := NewValue(enc)
|
||||||
|
value.Decode()
|
||||||
|
|
||||||
|
if value.Len() != len(data) {
|
||||||
|
t.Error("Expected data to be", len(data), "got", value.Len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValue(t *testing.T) {
|
func TestValue(t *testing.T) {
|
||||||
value := NewValueFromBytes([]byte("\xcd\x83dog\x83god\x83cat\x01"))
|
value := NewValueFromBytes([]byte("\xcd\x83dog\x83god\x83cat\x01"))
|
||||||
if value.Get(0).Str() != "dog" {
|
if value.Get(0).Str() != "dog" {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package ethutil
|
package ethutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -67,7 +66,9 @@ func (val *Value) Uint() uint64 {
|
|||||||
} else if Val, ok := val.Val.(uint); ok {
|
} else if Val, ok := val.Val.(uint); ok {
|
||||||
return uint64(Val)
|
return uint64(Val)
|
||||||
} else if Val, ok := val.Val.([]byte); ok {
|
} else if Val, ok := val.Val.([]byte); ok {
|
||||||
return ReadVarint(bytes.NewReader(Val))
|
return new(big.Int).SetBytes(Val).Uint64()
|
||||||
|
} else if Val, ok := val.Val.(*big.Int); ok {
|
||||||
|
return Val.Uint64()
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@ -205,6 +206,13 @@ func (val *Value) Cmp(o *Value) bool {
|
|||||||
return reflect.DeepEqual(val.Val, o.Val)
|
return reflect.DeepEqual(val.Val, o.Val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Value) DeepCmp(o *Value) bool {
|
||||||
|
a := NewValue(self.BigInt())
|
||||||
|
b := NewValue(o.BigInt())
|
||||||
|
|
||||||
|
return a.Cmp(b)
|
||||||
|
}
|
||||||
|
|
||||||
func (val *Value) Encode() []byte {
|
func (val *Value) Encode() []byte {
|
||||||
return Encode(val.Val)
|
return Encode(val.Val)
|
||||||
}
|
}
|
||||||
@ -260,6 +268,55 @@ func (val *Value) Append(v interface{}) *Value {
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
valOpAdd = iota
|
||||||
|
valOpDiv
|
||||||
|
valOpMul
|
||||||
|
valOpPow
|
||||||
|
valOpSub
|
||||||
|
)
|
||||||
|
|
||||||
|
// Math stuff
|
||||||
|
func (self *Value) doOp(op int, other interface{}) *Value {
|
||||||
|
left := self.BigInt()
|
||||||
|
right := NewValue(other).BigInt()
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
case valOpAdd:
|
||||||
|
self.Val = left.Add(left, right)
|
||||||
|
case valOpDiv:
|
||||||
|
self.Val = left.Div(left, right)
|
||||||
|
case valOpMul:
|
||||||
|
self.Val = left.Mul(left, right)
|
||||||
|
case valOpPow:
|
||||||
|
self.Val = left.Exp(left, right, Big0)
|
||||||
|
case valOpSub:
|
||||||
|
self.Val = left.Sub(left, right)
|
||||||
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Value) Add(other interface{}) *Value {
|
||||||
|
return self.doOp(valOpAdd, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Value) Sub(other interface{}) *Value {
|
||||||
|
return self.doOp(valOpSub, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Value) Div(other interface{}) *Value {
|
||||||
|
return self.doOp(valOpDiv, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Value) Mul(other interface{}) *Value {
|
||||||
|
return self.doOp(valOpMul, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Value) Pow(other interface{}) *Value {
|
||||||
|
return self.doOp(valOpPow, other)
|
||||||
|
}
|
||||||
|
|
||||||
type ValueIterator struct {
|
type ValueIterator struct {
|
||||||
value *Value
|
value *Value
|
||||||
currentValue *Value
|
currentValue *Value
|
||||||
|
@ -63,3 +63,18 @@ func TestIterator(t *testing.T) {
|
|||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMath(t *testing.T) {
|
||||||
|
a := NewValue(1)
|
||||||
|
a.Add(1).Add(1)
|
||||||
|
|
||||||
|
if !a.DeepCmp(NewValue(3)) {
|
||||||
|
t.Error("Expected 3, got", a)
|
||||||
|
}
|
||||||
|
|
||||||
|
a = NewValue(2)
|
||||||
|
a.Sub(1).Sub(1)
|
||||||
|
if !a.DeepCmp(NewValue(0)) {
|
||||||
|
t.Error("Expected 0, got", a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
ethvm/.ethtest
Normal file
0
ethvm/.ethtest
Normal file
44
ethvm/asm.go
Normal file
44
ethvm/asm.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package ethvm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Disassemble(script []byte) (asm []string) {
|
||||||
|
pc := new(big.Int)
|
||||||
|
for {
|
||||||
|
if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the memory location of pc
|
||||||
|
val := script[pc.Int64()]
|
||||||
|
// Get the opcode (it must be an opcode!)
|
||||||
|
op := OpCode(val)
|
||||||
|
|
||||||
|
asm = append(asm, fmt.Sprintf("%v", op))
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
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:
|
||||||
|
pc.Add(pc, ethutil.Big1)
|
||||||
|
a := int64(op) - int64(PUSH1) + 1
|
||||||
|
if int(pc.Int64()+a) > len(script) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data := script[pc.Int64() : pc.Int64()+a]
|
||||||
|
if len(data) == 0 {
|
||||||
|
data = []byte{0}
|
||||||
|
}
|
||||||
|
asm = append(asm, fmt.Sprintf("0x%x", data))
|
||||||
|
|
||||||
|
pc.Add(pc, big.NewInt(a-1))
|
||||||
|
}
|
||||||
|
|
||||||
|
pc.Add(pc, ethutil.Big1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -1,26 +1,25 @@
|
|||||||
package ethchain
|
package ethvm
|
||||||
|
|
||||||
// TODO Re write VM to use values instead of big integers?
|
// TODO Re write VM to use values instead of big integers?
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/ethereum/eth-go/ethstate"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClosureRef interface {
|
type ClosureRef interface {
|
||||||
ReturnGas(*big.Int, *big.Int, *State)
|
ReturnGas(*big.Int, *big.Int)
|
||||||
Address() []byte
|
Address() []byte
|
||||||
GetStorage(*big.Int) *ethutil.Value
|
GetStorage(*big.Int) *ethutil.Value
|
||||||
SetStorage(*big.Int, *ethutil.Value)
|
SetStorage(*big.Int, *ethutil.Value)
|
||||||
N() *big.Int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Basic inline closure object which implement the 'closure' interface
|
// Basic inline closure object which implement the 'closure' interface
|
||||||
type Closure struct {
|
type Closure struct {
|
||||||
caller ClosureRef
|
caller ClosureRef
|
||||||
object *StateObject
|
object *ethstate.StateObject
|
||||||
Script []byte
|
Code []byte
|
||||||
State *State
|
|
||||||
|
|
||||||
Gas, UsedGas, Price *big.Int
|
Gas, UsedGas, Price *big.Int
|
||||||
|
|
||||||
@ -28,8 +27,8 @@ type Closure struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a new closure for the given data items
|
// Create a new closure for the given data items
|
||||||
func NewClosure(caller ClosureRef, object *StateObject, script []byte, state *State, gas, price *big.Int) *Closure {
|
func NewClosure(caller ClosureRef, object *ethstate.StateObject, code []byte, gas, price *big.Int) *Closure {
|
||||||
c := &Closure{caller: caller, object: object, Script: script, State: state, Args: nil}
|
c := &Closure{caller: caller, object: object, Code: code, Args: nil}
|
||||||
|
|
||||||
// Gas should be a pointer so it can safely be reduced through the run
|
// Gas should be a pointer so it can safely be reduced through the run
|
||||||
// This pointer will be off the state transition
|
// This pointer will be off the state transition
|
||||||
@ -57,11 +56,11 @@ func (c *Closure) Get(x *big.Int) *ethutil.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Closure) Gets(x, y *big.Int) *ethutil.Value {
|
func (c *Closure) Gets(x, y *big.Int) *ethutil.Value {
|
||||||
if x.Int64() >= int64(len(c.Script)) || y.Int64() >= int64(len(c.Script)) {
|
if x.Int64() >= int64(len(c.Code)) || y.Int64() >= int64(len(c.Code)) {
|
||||||
return ethutil.NewValue(0)
|
return ethutil.NewValue(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
partial := c.Script[x.Int64() : x.Int64()+y.Int64()]
|
partial := c.Code[x.Int64() : x.Int64()+y.Int64()]
|
||||||
|
|
||||||
return ethutil.NewValue(partial)
|
return ethutil.NewValue(partial)
|
||||||
}
|
}
|
||||||
@ -84,7 +83,7 @@ func (c *Closure) Call(vm *Vm, args []byte) ([]byte, *big.Int, error) {
|
|||||||
|
|
||||||
func (c *Closure) Return(ret []byte) []byte {
|
func (c *Closure) Return(ret []byte) []byte {
|
||||||
// Return the remaining gas to the caller
|
// Return the remaining gas to the caller
|
||||||
c.caller.ReturnGas(c.Gas, c.Price, c.State)
|
c.caller.ReturnGas(c.Gas, c.Price)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
@ -102,20 +101,16 @@ func (c *Closure) UseGas(gas *big.Int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Implement the caller interface
|
// Implement the caller interface
|
||||||
func (c *Closure) ReturnGas(gas, price *big.Int, state *State) {
|
func (c *Closure) ReturnGas(gas, price *big.Int) {
|
||||||
// Return the gas to the closure
|
// Return the gas to the closure
|
||||||
c.Gas.Add(c.Gas, gas)
|
c.Gas.Add(c.Gas, gas)
|
||||||
c.UsedGas.Sub(c.UsedGas, gas)
|
c.UsedGas.Sub(c.UsedGas, gas)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Closure) Object() *StateObject {
|
func (c *Closure) Object() *ethstate.StateObject {
|
||||||
return c.object
|
return c.object
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Closure) Caller() ClosureRef {
|
func (c *Closure) Caller() ClosureRef {
|
||||||
return c.caller
|
return c.caller
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Closure) N() *big.Int {
|
|
||||||
return c.object.N()
|
|
||||||
}
|
|
27
ethvm/common.go
Normal file
27
ethvm/common.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package ethvm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/eth-go/ethlog"
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"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)
|
||||||
|
|
||||||
|
Pow256 = ethutil.BigPow(2, 256)
|
||||||
|
|
||||||
|
LogTyPretty byte = 0x1
|
||||||
|
LogTyDiff byte = 0x2
|
||||||
|
)
|
@ -1,4 +1,4 @@
|
|||||||
package ethchain
|
package ethvm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
346
ethvm/types.go
Normal file
346
ethvm/types.go
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
package ethvm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OpCode int
|
||||||
|
|
||||||
|
// Op codes
|
||||||
|
const (
|
||||||
|
// 0x0 range - arithmetic ops
|
||||||
|
STOP = 0x00
|
||||||
|
ADD = 0x01
|
||||||
|
MUL = 0x02
|
||||||
|
SUB = 0x03
|
||||||
|
DIV = 0x04
|
||||||
|
SDIV = 0x05
|
||||||
|
MOD = 0x06
|
||||||
|
SMOD = 0x07
|
||||||
|
EXP = 0x08
|
||||||
|
NEG = 0x09
|
||||||
|
LT = 0x0a
|
||||||
|
GT = 0x0b
|
||||||
|
SLT = 0x0c
|
||||||
|
SGT = 0x0d
|
||||||
|
EQ = 0x0e
|
||||||
|
NOT = 0x0f
|
||||||
|
|
||||||
|
// 0x10 range - bit ops
|
||||||
|
AND = 0x10
|
||||||
|
OR = 0x11
|
||||||
|
XOR = 0x12
|
||||||
|
BYTE = 0x13
|
||||||
|
|
||||||
|
// 0x20 range - crypto
|
||||||
|
SHA3 = 0x20
|
||||||
|
|
||||||
|
// 0x30 range - closure state
|
||||||
|
ADDRESS = 0x30
|
||||||
|
BALANCE = 0x31
|
||||||
|
ORIGIN = 0x32
|
||||||
|
CALLER = 0x33
|
||||||
|
CALLVALUE = 0x34
|
||||||
|
CALLDATALOAD = 0x35
|
||||||
|
CALLDATASIZE = 0x36
|
||||||
|
CALLDATACOPY = 0x37
|
||||||
|
CODESIZE = 0x38
|
||||||
|
CODECOPY = 0x39
|
||||||
|
GASPRICE = 0x3a
|
||||||
|
|
||||||
|
// 0x40 range - block operations
|
||||||
|
PREVHASH = 0x40
|
||||||
|
COINBASE = 0x41
|
||||||
|
TIMESTAMP = 0x42
|
||||||
|
NUMBER = 0x43
|
||||||
|
DIFFICULTY = 0x44
|
||||||
|
GASLIMIT = 0x45
|
||||||
|
|
||||||
|
// 0x50 range - 'storage' and execution
|
||||||
|
POP = 0x50
|
||||||
|
DUP = 0x51
|
||||||
|
SWAP = 0x52
|
||||||
|
MLOAD = 0x53
|
||||||
|
MSTORE = 0x54
|
||||||
|
MSTORE8 = 0x55
|
||||||
|
SLOAD = 0x56
|
||||||
|
SSTORE = 0x57
|
||||||
|
JUMP = 0x58
|
||||||
|
JUMPI = 0x59
|
||||||
|
PC = 0x5a
|
||||||
|
MSIZE = 0x5b
|
||||||
|
GAS = 0x5c
|
||||||
|
|
||||||
|
// 0x60 range
|
||||||
|
PUSH1 = 0x60
|
||||||
|
PUSH2 = 0x61
|
||||||
|
PUSH3 = 0x62
|
||||||
|
PUSH4 = 0x63
|
||||||
|
PUSH5 = 0x64
|
||||||
|
PUSH6 = 0x65
|
||||||
|
PUSH7 = 0x66
|
||||||
|
PUSH8 = 0x67
|
||||||
|
PUSH9 = 0x68
|
||||||
|
PUSH10 = 0x69
|
||||||
|
PUSH11 = 0x6a
|
||||||
|
PUSH12 = 0x6b
|
||||||
|
PUSH13 = 0x6c
|
||||||
|
PUSH14 = 0x6d
|
||||||
|
PUSH15 = 0x6e
|
||||||
|
PUSH16 = 0x6f
|
||||||
|
PUSH17 = 0x70
|
||||||
|
PUSH18 = 0x71
|
||||||
|
PUSH19 = 0x72
|
||||||
|
PUSH20 = 0x73
|
||||||
|
PUSH21 = 0x74
|
||||||
|
PUSH22 = 0x75
|
||||||
|
PUSH23 = 0x76
|
||||||
|
PUSH24 = 0x77
|
||||||
|
PUSH25 = 0x78
|
||||||
|
PUSH26 = 0x79
|
||||||
|
PUSH27 = 0x7a
|
||||||
|
PUSH28 = 0x7b
|
||||||
|
PUSH29 = 0x7c
|
||||||
|
PUSH30 = 0x7d
|
||||||
|
PUSH31 = 0x7e
|
||||||
|
PUSH32 = 0x7f
|
||||||
|
|
||||||
|
// 0xf0 range - closures
|
||||||
|
CREATE = 0xf0
|
||||||
|
CALL = 0xf1
|
||||||
|
RETURN = 0xf2
|
||||||
|
|
||||||
|
// 0x70 range - other
|
||||||
|
LOG = 0xfe // XXX Unofficial
|
||||||
|
SUICIDE = 0xff
|
||||||
|
)
|
||||||
|
|
||||||
|
// Since the opcodes aren't all in order we can't use a regular slice
|
||||||
|
var opCodeToString = map[OpCode]string{
|
||||||
|
// 0x0 range - arithmetic ops
|
||||||
|
STOP: "STOP",
|
||||||
|
ADD: "ADD",
|
||||||
|
MUL: "MUL",
|
||||||
|
SUB: "SUB",
|
||||||
|
DIV: "DIV",
|
||||||
|
SDIV: "SDIV",
|
||||||
|
MOD: "MOD",
|
||||||
|
SMOD: "SMOD",
|
||||||
|
EXP: "EXP",
|
||||||
|
NEG: "NEG",
|
||||||
|
LT: "LT",
|
||||||
|
GT: "GT",
|
||||||
|
SLT: "SLT",
|
||||||
|
SGT: "SGT",
|
||||||
|
EQ: "EQ",
|
||||||
|
NOT: "NOT",
|
||||||
|
|
||||||
|
// 0x10 range - bit ops
|
||||||
|
AND: "AND",
|
||||||
|
OR: "OR",
|
||||||
|
XOR: "XOR",
|
||||||
|
BYTE: "BYTE",
|
||||||
|
|
||||||
|
// 0x20 range - crypto
|
||||||
|
SHA3: "SHA3",
|
||||||
|
|
||||||
|
// 0x30 range - closure state
|
||||||
|
ADDRESS: "ADDRESS",
|
||||||
|
BALANCE: "BALANCE",
|
||||||
|
ORIGIN: "ORIGIN",
|
||||||
|
CALLER: "CALLER",
|
||||||
|
CALLVALUE: "CALLVALUE",
|
||||||
|
CALLDATALOAD: "CALLDATALOAD",
|
||||||
|
CALLDATASIZE: "CALLDATASIZE",
|
||||||
|
CALLDATACOPY: "CALLDATACOPY",
|
||||||
|
CODESIZE: "CODESIZE",
|
||||||
|
CODECOPY: "CODECOPY",
|
||||||
|
GASPRICE: "TXGASPRICE",
|
||||||
|
|
||||||
|
// 0x40 range - block operations
|
||||||
|
PREVHASH: "PREVHASH",
|
||||||
|
COINBASE: "COINBASE",
|
||||||
|
TIMESTAMP: "TIMESTAMP",
|
||||||
|
NUMBER: "NUMBER",
|
||||||
|
DIFFICULTY: "DIFFICULTY",
|
||||||
|
GASLIMIT: "GASLIMIT",
|
||||||
|
|
||||||
|
// 0x50 range - 'storage' and execution
|
||||||
|
POP: "POP",
|
||||||
|
DUP: "DUP",
|
||||||
|
SWAP: "SWAP",
|
||||||
|
MLOAD: "MLOAD",
|
||||||
|
MSTORE: "MSTORE",
|
||||||
|
MSTORE8: "MSTORE8",
|
||||||
|
SLOAD: "SLOAD",
|
||||||
|
SSTORE: "SSTORE",
|
||||||
|
JUMP: "JUMP",
|
||||||
|
JUMPI: "JUMPI",
|
||||||
|
PC: "PC",
|
||||||
|
MSIZE: "MSIZE",
|
||||||
|
GAS: "GAS",
|
||||||
|
|
||||||
|
// 0x60 range - push
|
||||||
|
PUSH1: "PUSH1",
|
||||||
|
PUSH2: "PUSH2",
|
||||||
|
PUSH3: "PUSH3",
|
||||||
|
PUSH4: "PUSH4",
|
||||||
|
PUSH5: "PUSH5",
|
||||||
|
PUSH6: "PUSH6",
|
||||||
|
PUSH7: "PUSH7",
|
||||||
|
PUSH8: "PUSH8",
|
||||||
|
PUSH9: "PUSH9",
|
||||||
|
PUSH10: "PUSH10",
|
||||||
|
PUSH11: "PUSH11",
|
||||||
|
PUSH12: "PUSH12",
|
||||||
|
PUSH13: "PUSH13",
|
||||||
|
PUSH14: "PUSH14",
|
||||||
|
PUSH15: "PUSH15",
|
||||||
|
PUSH16: "PUSH16",
|
||||||
|
PUSH17: "PUSH17",
|
||||||
|
PUSH18: "PUSH18",
|
||||||
|
PUSH19: "PUSH19",
|
||||||
|
PUSH20: "PUSH20",
|
||||||
|
PUSH21: "PUSH21",
|
||||||
|
PUSH22: "PUSH22",
|
||||||
|
PUSH23: "PUSH23",
|
||||||
|
PUSH24: "PUSH24",
|
||||||
|
PUSH25: "PUSH25",
|
||||||
|
PUSH26: "PUSH26",
|
||||||
|
PUSH27: "PUSH27",
|
||||||
|
PUSH28: "PUSH28",
|
||||||
|
PUSH29: "PUSH29",
|
||||||
|
PUSH30: "PUSH30",
|
||||||
|
PUSH31: "PUSH31",
|
||||||
|
PUSH32: "PUSH32",
|
||||||
|
|
||||||
|
// 0xf0 range
|
||||||
|
CREATE: "CREATE",
|
||||||
|
CALL: "CALL",
|
||||||
|
RETURN: "RETURN",
|
||||||
|
|
||||||
|
// 0x70 range - other
|
||||||
|
LOG: "LOG",
|
||||||
|
SUICIDE: "SUICIDE",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o OpCode) String() string {
|
||||||
|
str := opCodeToString[o]
|
||||||
|
if len(str) == 0 {
|
||||||
|
return fmt.Sprintf("Missing opcode 0x%x", int(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// Op codes for assembling
|
||||||
|
var OpCodes = map[string]byte{
|
||||||
|
// 0x0 range - arithmetic ops
|
||||||
|
"STOP": 0x00,
|
||||||
|
"ADD": 0x01,
|
||||||
|
"MUL": 0x02,
|
||||||
|
"SUB": 0x03,
|
||||||
|
"DIV": 0x04,
|
||||||
|
"SDIV": 0x05,
|
||||||
|
"MOD": 0x06,
|
||||||
|
"SMOD": 0x07,
|
||||||
|
"EXP": 0x08,
|
||||||
|
"NEG": 0x09,
|
||||||
|
"LT": 0x0a,
|
||||||
|
"GT": 0x0b,
|
||||||
|
"EQ": 0x0c,
|
||||||
|
"NOT": 0x0d,
|
||||||
|
|
||||||
|
// 0x10 range - bit ops
|
||||||
|
"AND": 0x10,
|
||||||
|
"OR": 0x11,
|
||||||
|
"XOR": 0x12,
|
||||||
|
"BYTE": 0x13,
|
||||||
|
|
||||||
|
// 0x20 range - crypto
|
||||||
|
"SHA3": 0x20,
|
||||||
|
|
||||||
|
// 0x30 range - closure state
|
||||||
|
"ADDRESS": 0x30,
|
||||||
|
"BALANCE": 0x31,
|
||||||
|
"ORIGIN": 0x32,
|
||||||
|
"CALLER": 0x33,
|
||||||
|
"CALLVALUE": 0x34,
|
||||||
|
"CALLDATALOAD": 0x35,
|
||||||
|
"CALLDATASIZE": 0x36,
|
||||||
|
"GASPRICE": 0x38,
|
||||||
|
|
||||||
|
// 0x40 range - block operations
|
||||||
|
"PREVHASH": 0x40,
|
||||||
|
"COINBASE": 0x41,
|
||||||
|
"TIMESTAMP": 0x42,
|
||||||
|
"NUMBER": 0x43,
|
||||||
|
"DIFFICULTY": 0x44,
|
||||||
|
"GASLIMIT": 0x45,
|
||||||
|
|
||||||
|
// 0x50 range - 'storage' and execution
|
||||||
|
"POP": 0x51,
|
||||||
|
"DUP": 0x52,
|
||||||
|
"SWAP": 0x53,
|
||||||
|
"MLOAD": 0x54,
|
||||||
|
"MSTORE": 0x55,
|
||||||
|
"MSTORE8": 0x56,
|
||||||
|
"SLOAD": 0x57,
|
||||||
|
"SSTORE": 0x58,
|
||||||
|
"JUMP": 0x59,
|
||||||
|
"JUMPI": 0x5a,
|
||||||
|
"PC": 0x5b,
|
||||||
|
"MSIZE": 0x5c,
|
||||||
|
|
||||||
|
// 0x70 range - 'push'
|
||||||
|
"PUSH1": 0x60,
|
||||||
|
"PUSH2": 0x61,
|
||||||
|
"PUSH3": 0x62,
|
||||||
|
"PUSH4": 0x63,
|
||||||
|
"PUSH5": 0x64,
|
||||||
|
"PUSH6": 0x65,
|
||||||
|
"PUSH7": 0x66,
|
||||||
|
"PUSH8": 0x67,
|
||||||
|
"PUSH9": 0x68,
|
||||||
|
"PUSH10": 0x69,
|
||||||
|
"PUSH11": 0x6a,
|
||||||
|
"PUSH12": 0x6b,
|
||||||
|
"PUSH13": 0x6c,
|
||||||
|
"PUSH14": 0x6d,
|
||||||
|
"PUSH15": 0x6e,
|
||||||
|
"PUSH16": 0x6f,
|
||||||
|
"PUSH17": 0x70,
|
||||||
|
"PUSH18": 0x71,
|
||||||
|
"PUSH19": 0x72,
|
||||||
|
"PUSH20": 0x73,
|
||||||
|
"PUSH21": 0x74,
|
||||||
|
"PUSH22": 0x75,
|
||||||
|
"PUSH23": 0x76,
|
||||||
|
"PUSH24": 0x77,
|
||||||
|
"PUSH25": 0x78,
|
||||||
|
"PUSH26": 0x70,
|
||||||
|
"PUSH27": 0x7a,
|
||||||
|
"PUSH28": 0x7b,
|
||||||
|
"PUSH29": 0x7c,
|
||||||
|
"PUSH30": 0x7d,
|
||||||
|
"PUSH31": 0x7e,
|
||||||
|
"PUSH32": 0x7f,
|
||||||
|
|
||||||
|
// 0xf0 range - closures
|
||||||
|
"CREATE": 0xf0,
|
||||||
|
"CALL": 0xf1,
|
||||||
|
"RETURN": 0xf2,
|
||||||
|
|
||||||
|
// 0x70 range - other
|
||||||
|
"LOG": 0xfe,
|
||||||
|
"SUICIDE": 0x7f,
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsOpCode(s string) bool {
|
||||||
|
for key, _ := range OpCodes {
|
||||||
|
if key == s {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -1,35 +1,18 @@
|
|||||||
package ethchain
|
package ethvm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/eth-go/ethcrypto"
|
|
||||||
"github.com/ethereum/eth-go/ethlog"
|
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
)
|
|
||||||
|
|
||||||
var vmlogger = ethlog.NewLogger("VM")
|
"github.com/ethereum/eth-go/ethcrypto"
|
||||||
|
"github.com/ethereum/eth-go/ethstate"
|
||||||
var (
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
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 {
|
type Debugger interface {
|
||||||
BreakHook(step int, op OpCode, mem *Memory, stack *Stack, stateObject *StateObject) bool
|
BreakHook(step int, op OpCode, mem *Memory, stack *Stack, object *ethstate.StateObject) bool
|
||||||
StepHook(step int, op OpCode, mem *Memory, stack *Stack, stateObject *StateObject) bool
|
StepHook(step int, op OpCode, mem *Memory, stack *Stack, object *ethstate.StateObject) bool
|
||||||
BreakPoints() []int64
|
BreakPoints() []int64
|
||||||
SetCode(byteCode []byte)
|
SetCode(byteCode []byte)
|
||||||
}
|
}
|
||||||
@ -40,11 +23,7 @@ type Vm struct {
|
|||||||
// non-persistent key/value memory storage
|
// non-persistent key/value memory storage
|
||||||
mem map[string]*big.Int
|
mem map[string]*big.Int
|
||||||
|
|
||||||
vars RuntimeVars
|
env Environment
|
||||||
|
|
||||||
state *State
|
|
||||||
|
|
||||||
stateManager *StateManager
|
|
||||||
|
|
||||||
Verbose bool
|
Verbose bool
|
||||||
|
|
||||||
@ -59,71 +38,59 @@ type Vm struct {
|
|||||||
BreakPoints []int64
|
BreakPoints []int64
|
||||||
Stepping bool
|
Stepping bool
|
||||||
Fn string
|
Fn string
|
||||||
|
|
||||||
|
Recoverable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuntimeVars struct {
|
type Environment interface {
|
||||||
Origin []byte
|
State() *ethstate.State
|
||||||
Block *Block
|
|
||||||
BlockNumber *big.Int
|
Origin() []byte
|
||||||
PrevHash []byte
|
BlockNumber() *big.Int
|
||||||
Coinbase []byte
|
PrevHash() []byte
|
||||||
Time int64
|
Coinbase() []byte
|
||||||
Diff *big.Int
|
Time() int64
|
||||||
TxData []string
|
Difficulty() *big.Int
|
||||||
Value *big.Int
|
Value() *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Vm) Printf(format string, v ...interface{}) *Vm {
|
type Object interface {
|
||||||
if self.Verbose && self.logTy == LogTyPretty {
|
GetStorage(key *big.Int) *ethutil.Value
|
||||||
self.logStr += fmt.Sprintf(format, v...)
|
SetStorage(key *big.Int, value *ethutil.Value)
|
||||||
}
|
|
||||||
|
|
||||||
return self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Vm) Endl() *Vm {
|
func New(env Environment) *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
|
lt := LogTyPretty
|
||||||
if ethutil.Config.Diff {
|
if ethutil.Config.Diff {
|
||||||
lt = LogTyDiff
|
lt = LogTyDiff
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Vm{vars: vars, state: state, stateManager: stateManager, logTy: lt}
|
return &Vm{env: env, logTy: lt, Recoverable: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
var Pow256 = ethutil.BigPow(2, 256)
|
func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
||||||
|
if self.Recoverable {
|
||||||
var isRequireError = false
|
// Recover from any require exception
|
||||||
|
defer func() {
|
||||||
func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
if r := recover(); r != nil {
|
||||||
// Recover from any require exception
|
ret = closure.Return(nil)
|
||||||
defer func() {
|
err = fmt.Errorf("%v", r)
|
||||||
if r := recover(); r != nil {
|
vmlogger.Errorln("vm err", err)
|
||||||
ret = closure.Return(nil)
|
}
|
||||||
err = fmt.Errorf("%v", r)
|
}()
|
||||||
vmlogger.Errorln("vm err", err)
|
}
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Debug hook
|
// Debug hook
|
||||||
if vm.Dbg != nil {
|
if self.Dbg != nil {
|
||||||
vm.Dbg.SetCode(closure.Script)
|
self.Dbg.SetCode(closure.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't bother with the execution if there's no code.
|
// Don't bother with the execution if there's no code.
|
||||||
if len(closure.Script) == 0 {
|
if len(closure.Code) == 0 {
|
||||||
return closure.Return(nil), nil
|
return closure.Return(nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
vmlogger.Debugf("(%s) %x gas: %v (d) %x\n", vm.Fn, closure.Address(), closure.Gas, closure.Args)
|
vmlogger.Debugf("(%s) %x gas: %v (d) %x\n", self.Fn, closure.Address(), closure.Gas, closure.Args)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
op OpCode
|
op OpCode
|
||||||
@ -135,7 +102,6 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
prevStep = 0
|
prevStep = 0
|
||||||
require = func(m int) {
|
require = func(m int) {
|
||||||
if stack.Len() < m {
|
if stack.Len() < m {
|
||||||
isRequireError = true
|
|
||||||
panic(fmt.Sprintf("%04v (%v) stack err size = %d, required = %d", pc, op, stack.Len(), m))
|
panic(fmt.Sprintf("%04v (%v) stack err size = %d, required = %d", pc, op, stack.Len(), m))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,22 +120,23 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
|
|
||||||
// XXX Leave this Println intact. Don't change this to the log system.
|
// XXX Leave this Println intact. Don't change this to the log system.
|
||||||
// Used for creating diffs between implementations
|
// Used for creating diffs between implementations
|
||||||
if vm.logTy == LogTyDiff {
|
if self.logTy == LogTyDiff {
|
||||||
switch op {
|
/*
|
||||||
case STOP, RETURN, SUICIDE:
|
switch op {
|
||||||
closure.object.Sync()
|
case STOP, RETURN, SUICIDE:
|
||||||
closure.object.EachStorage(func(key string, value *ethutil.Value) {
|
closure.object.EachStorage(func(key string, value *ethutil.Value) {
|
||||||
value.Decode()
|
value.Decode()
|
||||||
fmt.Printf("%x %x\n", new(big.Int).SetBytes([]byte(key)).Bytes(), value.Bytes())
|
fmt.Printf("%x %x\n", new(big.Int).SetBytes([]byte(key)).Bytes(), value.Bytes())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
b := pc.Bytes()
|
b := pc.Bytes()
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
b = []byte{0}
|
b = []byte{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%x %x %x %x\n", closure.Address(), b, []byte{byte(op)}, closure.Gas.Bytes())
|
fmt.Printf("%x %x %x %x\n", closure.Address(), b, []byte{byte(op)}, closure.Gas.Bytes())
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
gas := new(big.Int)
|
gas := new(big.Int)
|
||||||
@ -261,8 +228,8 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
return closure.Return(nil), err
|
return closure.Return(nil), err
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.Printf("(pc) %-3d -o- %-14s", pc, op.String())
|
self.Printf("(pc) %-3d -o- %-14s", pc, op.String())
|
||||||
vm.Printf(" (g) %-3v (%v)", gas, closure.Gas)
|
self.Printf(" (g) %-3v (%v)", gas, closure.Gas)
|
||||||
|
|
||||||
mem.Resize(newMemSize)
|
mem.Resize(newMemSize)
|
||||||
|
|
||||||
@ -274,96 +241,87 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
case ADD:
|
case ADD:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
vm.Printf(" %v + %v", y, x)
|
self.Printf(" %v + %v", y, x)
|
||||||
|
|
||||||
base.Add(y, x)
|
base.Add(y, x)
|
||||||
|
|
||||||
vm.Printf(" = %v", base)
|
self.Printf(" = %v", base)
|
||||||
// Pop result back on the stack
|
// Pop result back on the stack
|
||||||
stack.Push(base)
|
stack.Push(base)
|
||||||
case SUB:
|
case SUB:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
vm.Printf(" %v - %v", y, x)
|
self.Printf(" %v - %v", y, x)
|
||||||
|
|
||||||
base.Sub(y, x)
|
base.Sub(y, x)
|
||||||
|
|
||||||
vm.Printf(" = %v", base)
|
self.Printf(" = %v", base)
|
||||||
// Pop result back on the stack
|
// Pop result back on the stack
|
||||||
stack.Push(base)
|
stack.Push(base)
|
||||||
case MUL:
|
case MUL:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
vm.Printf(" %v * %v", y, x)
|
self.Printf(" %v * %v", y, x)
|
||||||
|
|
||||||
base.Mul(y, x)
|
base.Mul(y, x)
|
||||||
|
|
||||||
vm.Printf(" = %v", base)
|
self.Printf(" = %v", base)
|
||||||
// Pop result back on the stack
|
// Pop result back on the stack
|
||||||
stack.Push(base)
|
stack.Push(base)
|
||||||
case DIV:
|
case DIV:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
vm.Printf(" %v / %v", y, x)
|
self.Printf(" %v / %v", y, x)
|
||||||
|
|
||||||
base.Div(y, x)
|
if x.Cmp(ethutil.Big0) != 0 {
|
||||||
|
base.Div(y, x)
|
||||||
|
}
|
||||||
|
|
||||||
vm.Printf(" = %v", base)
|
self.Printf(" = %v", base)
|
||||||
// Pop result back on the stack
|
// Pop result back on the stack
|
||||||
stack.Push(base)
|
stack.Push(base)
|
||||||
case SDIV:
|
case SDIV:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
// n > 2**255
|
self.Printf(" %v / %v", y, x)
|
||||||
if x.Cmp(Pow256) > 0 {
|
|
||||||
x.Sub(Pow256, x)
|
if x.Cmp(ethutil.Big0) != 0 {
|
||||||
|
base.Div(y, x)
|
||||||
}
|
}
|
||||||
if y.Cmp(Pow256) > 0 {
|
|
||||||
y.Sub(Pow256, y)
|
self.Printf(" = %v", base)
|
||||||
}
|
// Pop result back on the stack
|
||||||
z := new(big.Int)
|
stack.Push(base)
|
||||||
z.Div(x, y)
|
|
||||||
if z.Cmp(Pow256) > 0 {
|
|
||||||
z.Sub(Pow256, z)
|
|
||||||
}
|
|
||||||
// Push result on to the stack
|
|
||||||
stack.Push(z)
|
|
||||||
case MOD:
|
case MOD:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
|
|
||||||
vm.Printf(" %v %% %v", y, x)
|
self.Printf(" %v %% %v", y, x)
|
||||||
|
|
||||||
base.Mod(y, x)
|
base.Mod(y, x)
|
||||||
|
|
||||||
vm.Printf(" = %v", base)
|
self.Printf(" = %v", base)
|
||||||
stack.Push(base)
|
stack.Push(base)
|
||||||
case SMOD:
|
case SMOD:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
// n > 2**255
|
|
||||||
if x.Cmp(Pow256) > 0 {
|
self.Printf(" %v %% %v", y, x)
|
||||||
x.Sub(Pow256, x)
|
|
||||||
}
|
base.Mod(y, x)
|
||||||
if y.Cmp(Pow256) > 0 {
|
|
||||||
y.Sub(Pow256, y)
|
self.Printf(" = %v", base)
|
||||||
}
|
stack.Push(base)
|
||||||
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:
|
case EXP:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
|
|
||||||
vm.Printf(" %v ** %v", y, x)
|
self.Printf(" %v ** %v", y, x)
|
||||||
|
|
||||||
base.Exp(y, x, Pow256)
|
base.Exp(y, x, Pow256)
|
||||||
|
|
||||||
vm.Printf(" = %v", base)
|
self.Printf(" = %v", base)
|
||||||
|
|
||||||
stack.Push(base)
|
stack.Push(base)
|
||||||
case NEG:
|
case NEG:
|
||||||
@ -373,7 +331,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
case LT:
|
case LT:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
vm.Printf(" %v < %v", y, x)
|
self.Printf(" %v < %v", y, x)
|
||||||
// x < y
|
// x < y
|
||||||
if y.Cmp(x) < 0 {
|
if y.Cmp(x) < 0 {
|
||||||
stack.Push(ethutil.BigTrue)
|
stack.Push(ethutil.BigTrue)
|
||||||
@ -383,7 +341,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
case GT:
|
case GT:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
vm.Printf(" %v > %v", y, x)
|
self.Printf(" %v > %v", y, x)
|
||||||
|
|
||||||
// x > y
|
// x > y
|
||||||
if y.Cmp(x) > 0 {
|
if y.Cmp(x) > 0 {
|
||||||
@ -395,7 +353,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
case SLT:
|
case SLT:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
vm.Printf(" %v < %v", y, x)
|
self.Printf(" %v < %v", y, x)
|
||||||
// x < y
|
// x < y
|
||||||
if y.Cmp(x) < 0 {
|
if y.Cmp(x) < 0 {
|
||||||
stack.Push(ethutil.BigTrue)
|
stack.Push(ethutil.BigTrue)
|
||||||
@ -405,7 +363,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
case SGT:
|
case SGT:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
vm.Printf(" %v > %v", y, x)
|
self.Printf(" %v > %v", y, x)
|
||||||
|
|
||||||
// x > y
|
// x > y
|
||||||
if y.Cmp(x) > 0 {
|
if y.Cmp(x) > 0 {
|
||||||
@ -417,7 +375,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
case EQ:
|
case EQ:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
vm.Printf(" %v == %v", y, x)
|
self.Printf(" %v == %v", y, x)
|
||||||
|
|
||||||
// x == y
|
// x == y
|
||||||
if x.Cmp(y) == 0 {
|
if x.Cmp(y) == 0 {
|
||||||
@ -438,29 +396,29 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
case AND:
|
case AND:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
vm.Printf(" %v & %v", y, x)
|
self.Printf(" %v & %v", y, x)
|
||||||
|
|
||||||
stack.Push(base.And(y, x))
|
stack.Push(base.And(y, x))
|
||||||
case OR:
|
case OR:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
vm.Printf(" %v | %v", y, x)
|
self.Printf(" %v | %v", y, x)
|
||||||
|
|
||||||
stack.Push(base.Or(y, x))
|
stack.Push(base.Or(y, x))
|
||||||
case XOR:
|
case XOR:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
vm.Printf(" %v ^ %v", y, x)
|
self.Printf(" %v ^ %v", y, x)
|
||||||
|
|
||||||
stack.Push(base.Xor(y, x))
|
stack.Push(base.Xor(y, x))
|
||||||
case BYTE:
|
case BYTE:
|
||||||
require(2)
|
require(2)
|
||||||
val, th := stack.Popn()
|
val, th := stack.Popn()
|
||||||
if th.Cmp(big.NewInt(32)) < 0 && th.Cmp(big.NewInt(int64(len(val.Bytes())))) < 0 {
|
if th.Cmp(big.NewInt(32)) < 0 && th.Cmp(big.NewInt(int64(len(val.Bytes())))) < 0 {
|
||||||
byt := big.NewInt(int64(val.Bytes()[th.Int64()]))
|
byt := big.NewInt(int64(ethutil.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
|
||||||
stack.Push(byt)
|
stack.Push(byt)
|
||||||
|
|
||||||
vm.Printf(" => 0x%x", byt.Bytes())
|
self.Printf(" => 0x%x", byt.Bytes())
|
||||||
} else {
|
} else {
|
||||||
stack.Push(ethutil.BigFalse)
|
stack.Push(ethutil.BigFalse)
|
||||||
}
|
}
|
||||||
@ -473,53 +431,61 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
|
|
||||||
stack.Push(ethutil.BigD(data))
|
stack.Push(ethutil.BigD(data))
|
||||||
|
|
||||||
vm.Printf(" => %x", data)
|
self.Printf(" => %x", data)
|
||||||
// 0x30 range
|
// 0x30 range
|
||||||
case ADDRESS:
|
case ADDRESS:
|
||||||
stack.Push(ethutil.BigD(closure.Address()))
|
stack.Push(ethutil.BigD(closure.Address()))
|
||||||
|
|
||||||
vm.Printf(" => %x", closure.Address())
|
self.Printf(" => %x", closure.Address())
|
||||||
case BALANCE:
|
case BALANCE:
|
||||||
require(1)
|
require(1)
|
||||||
|
|
||||||
addr := stack.Pop().Bytes()
|
addr := stack.Pop().Bytes()
|
||||||
balance := vm.state.GetBalance(addr)
|
balance := self.env.State().GetBalance(addr)
|
||||||
|
|
||||||
stack.Push(balance)
|
stack.Push(balance)
|
||||||
|
|
||||||
vm.Printf(" => %v (%x)", balance, addr)
|
self.Printf(" => %v (%x)", balance, addr)
|
||||||
case ORIGIN:
|
case ORIGIN:
|
||||||
stack.Push(ethutil.BigD(vm.vars.Origin))
|
origin := self.env.Origin()
|
||||||
|
|
||||||
vm.Printf(" => %x", vm.vars.Origin)
|
stack.Push(ethutil.BigD(origin))
|
||||||
|
|
||||||
|
self.Printf(" => %x", origin)
|
||||||
case CALLER:
|
case CALLER:
|
||||||
caller := closure.caller.Address()
|
caller := closure.caller.Address()
|
||||||
stack.Push(ethutil.BigD(caller))
|
stack.Push(ethutil.BigD(caller))
|
||||||
|
|
||||||
vm.Printf(" => %x", caller)
|
self.Printf(" => %x", caller)
|
||||||
case CALLVALUE:
|
case CALLVALUE:
|
||||||
stack.Push(vm.vars.Value)
|
value := self.env.Value()
|
||||||
|
|
||||||
vm.Printf(" => %v", vm.vars.Value)
|
stack.Push(value)
|
||||||
|
|
||||||
|
self.Printf(" => %v", value)
|
||||||
case CALLDATALOAD:
|
case CALLDATALOAD:
|
||||||
require(1)
|
require(1)
|
||||||
offset := stack.Pop().Int64()
|
var (
|
||||||
|
offset = stack.Pop()
|
||||||
|
data = make([]byte, 32)
|
||||||
|
lenData = big.NewInt(int64(len(closure.Args)))
|
||||||
|
)
|
||||||
|
|
||||||
data := make([]byte, 32)
|
if lenData.Cmp(offset) >= 0 {
|
||||||
if len(closure.Args) >= int(offset) {
|
length := new(big.Int).Add(offset, ethutil.Big32)
|
||||||
l := int64(math.Min(float64(offset+32), float64(len(closure.Args))))
|
length = ethutil.BigMin(length, lenData)
|
||||||
|
|
||||||
copy(data, closure.Args[offset:l])
|
copy(data, closure.Args[offset.Int64():length.Int64()])
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.Printf(" => 0x%x", data)
|
self.Printf(" => 0x%x", data)
|
||||||
|
|
||||||
stack.Push(ethutil.BigD(data))
|
stack.Push(ethutil.BigD(data))
|
||||||
case CALLDATASIZE:
|
case CALLDATASIZE:
|
||||||
l := int64(len(closure.Args))
|
l := int64(len(closure.Args))
|
||||||
stack.Push(big.NewInt(l))
|
stack.Push(big.NewInt(l))
|
||||||
|
|
||||||
vm.Printf(" => %d", l)
|
self.Printf(" => %d", l)
|
||||||
case CALLDATACOPY:
|
case CALLDATACOPY:
|
||||||
var (
|
var (
|
||||||
size = int64(len(closure.Args))
|
size = int64(len(closure.Args))
|
||||||
@ -539,13 +505,13 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
|
|
||||||
mem.Set(mOff, l, code)
|
mem.Set(mOff, l, code)
|
||||||
case CODESIZE:
|
case CODESIZE:
|
||||||
l := big.NewInt(int64(len(closure.Script)))
|
l := big.NewInt(int64(len(closure.Code)))
|
||||||
stack.Push(l)
|
stack.Push(l)
|
||||||
|
|
||||||
vm.Printf(" => %d", l)
|
self.Printf(" => %d", l)
|
||||||
case CODECOPY:
|
case CODECOPY:
|
||||||
var (
|
var (
|
||||||
size = int64(len(closure.Script))
|
size = int64(len(closure.Code))
|
||||||
mOff = stack.Pop().Int64()
|
mOff = stack.Pop().Int64()
|
||||||
cOff = stack.Pop().Int64()
|
cOff = stack.Pop().Int64()
|
||||||
l = stack.Pop().Int64()
|
l = stack.Pop().Int64()
|
||||||
@ -558,25 +524,45 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
l = 0
|
l = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
code := closure.Script[cOff : cOff+l]
|
code := closure.Code[cOff : cOff+l]
|
||||||
//fmt.Println("len:", l, "code off:", cOff, "mem off:", mOff)
|
|
||||||
|
|
||||||
mem.Set(mOff, l, code)
|
mem.Set(mOff, l, code)
|
||||||
//fmt.Println(Code(mem.Get(mOff, l)))
|
|
||||||
case GASPRICE:
|
case GASPRICE:
|
||||||
stack.Push(closure.Price)
|
stack.Push(closure.Price)
|
||||||
|
|
||||||
|
self.Printf(" => %v", closure.Price)
|
||||||
|
|
||||||
// 0x40 range
|
// 0x40 range
|
||||||
case PREVHASH:
|
case PREVHASH:
|
||||||
stack.Push(ethutil.BigD(vm.vars.PrevHash))
|
prevHash := self.env.PrevHash()
|
||||||
|
|
||||||
|
stack.Push(ethutil.BigD(prevHash))
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", prevHash)
|
||||||
case COINBASE:
|
case COINBASE:
|
||||||
stack.Push(ethutil.BigD(vm.vars.Coinbase))
|
coinbase := self.env.Coinbase()
|
||||||
|
|
||||||
|
stack.Push(ethutil.BigD(coinbase))
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", coinbase)
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
stack.Push(big.NewInt(vm.vars.Time))
|
time := self.env.Time()
|
||||||
|
|
||||||
|
stack.Push(big.NewInt(time))
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", time)
|
||||||
case NUMBER:
|
case NUMBER:
|
||||||
stack.Push(vm.vars.BlockNumber)
|
number := self.env.BlockNumber()
|
||||||
|
|
||||||
|
stack.Push(number)
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", number.Bytes())
|
||||||
case DIFFICULTY:
|
case DIFFICULTY:
|
||||||
stack.Push(vm.vars.Diff)
|
difficulty := self.env.Difficulty()
|
||||||
|
|
||||||
|
stack.Push(difficulty)
|
||||||
|
|
||||||
|
self.Printf(" => 0x%x", difficulty.Bytes())
|
||||||
case GASLIMIT:
|
case GASLIMIT:
|
||||||
// TODO
|
// TODO
|
||||||
stack.Push(big.NewInt(0))
|
stack.Push(big.NewInt(0))
|
||||||
@ -593,7 +579,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
|
|
||||||
step += int(op) - int(PUSH1) + 1
|
step += int(op) - int(PUSH1) + 1
|
||||||
|
|
||||||
vm.Printf(" => 0x%x", data.Bytes())
|
self.Printf(" => 0x%x", data.Bytes())
|
||||||
case POP:
|
case POP:
|
||||||
require(1)
|
require(1)
|
||||||
stack.Pop()
|
stack.Pop()
|
||||||
@ -601,7 +587,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
require(1)
|
require(1)
|
||||||
stack.Push(stack.Peek())
|
stack.Push(stack.Peek())
|
||||||
|
|
||||||
vm.Printf(" => 0x%x", stack.Peek().Bytes())
|
self.Printf(" => 0x%x", stack.Peek().Bytes())
|
||||||
case SWAP:
|
case SWAP:
|
||||||
require(2)
|
require(2)
|
||||||
x, y := stack.Popn()
|
x, y := stack.Popn()
|
||||||
@ -613,14 +599,14 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
val := ethutil.BigD(mem.Get(offset.Int64(), 32))
|
val := ethutil.BigD(mem.Get(offset.Int64(), 32))
|
||||||
stack.Push(val)
|
stack.Push(val)
|
||||||
|
|
||||||
vm.Printf(" => 0x%x", val.Bytes())
|
self.Printf(" => 0x%x", val.Bytes())
|
||||||
case MSTORE: // Store the value at stack top-1 in to memory at location stack top
|
case MSTORE: // Store the value at stack top-1 in to memory at location stack top
|
||||||
require(2)
|
require(2)
|
||||||
// Pop value of the stack
|
// Pop value of the stack
|
||||||
val, mStart := stack.Popn()
|
val, mStart := stack.Popn()
|
||||||
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256))
|
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256))
|
||||||
|
|
||||||
vm.Printf(" => 0x%x", val)
|
self.Printf(" => 0x%x", val)
|
||||||
case MSTORE8:
|
case MSTORE8:
|
||||||
require(2)
|
require(2)
|
||||||
val, mStart := stack.Popn()
|
val, mStart := stack.Popn()
|
||||||
@ -628,7 +614,7 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
//mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256))
|
//mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256))
|
||||||
mem.store[mStart.Int64()] = byte(val.Int64() & 0xff)
|
mem.store[mStart.Int64()] = byte(val.Int64() & 0xff)
|
||||||
|
|
||||||
vm.Printf(" => 0x%x", val)
|
self.Printf(" => 0x%x", val)
|
||||||
case SLOAD:
|
case SLOAD:
|
||||||
require(1)
|
require(1)
|
||||||
loc := stack.Pop()
|
loc := stack.Pop()
|
||||||
@ -636,21 +622,21 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
|
|
||||||
stack.Push(val.BigInt())
|
stack.Push(val.BigInt())
|
||||||
|
|
||||||
vm.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
|
self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
|
||||||
case SSTORE:
|
case SSTORE:
|
||||||
require(2)
|
require(2)
|
||||||
val, loc := stack.Popn()
|
val, loc := stack.Popn()
|
||||||
closure.SetStorage(loc, ethutil.NewValue(val))
|
closure.SetStorage(loc, ethutil.NewValue(val))
|
||||||
|
|
||||||
// Add the change to manifest
|
// Add the change to manifest
|
||||||
vm.state.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val)
|
self.env.State().Manifest().AddStorageChange(closure.Object(), loc.Bytes(), val)
|
||||||
|
|
||||||
vm.Printf(" {0x%x : 0x%x}", loc, val)
|
self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes())
|
||||||
case JUMP:
|
case JUMP:
|
||||||
require(1)
|
require(1)
|
||||||
pc = stack.Pop()
|
pc = stack.Pop()
|
||||||
// Reduce pc by one because of the increment that's at the end of this for loop
|
// Reduce pc by one because of the increment that's at the end of this for loop
|
||||||
vm.Printf(" ~> %v", pc).Endl()
|
self.Printf(" ~> %v", pc).Endl()
|
||||||
|
|
||||||
continue
|
continue
|
||||||
case JUMPI:
|
case JUMPI:
|
||||||
@ -659,11 +645,11 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
if cond.Cmp(ethutil.BigTrue) >= 0 {
|
if cond.Cmp(ethutil.BigTrue) >= 0 {
|
||||||
pc = pos
|
pc = pos
|
||||||
|
|
||||||
vm.Printf(" ~> %v (t)", pc).Endl()
|
self.Printf(" ~> %v (t)", pc).Endl()
|
||||||
|
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
vm.Printf(" (f)")
|
self.Printf(" (f)")
|
||||||
}
|
}
|
||||||
case PC:
|
case PC:
|
||||||
stack.Push(pc)
|
stack.Push(pc)
|
||||||
@ -682,21 +668,21 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
|
|
||||||
// Snapshot the current stack so we are able to
|
// Snapshot the current stack so we are able to
|
||||||
// revert back to it later.
|
// revert back to it later.
|
||||||
snapshot = vm.state.Copy()
|
snapshot = self.env.State().Copy()
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generate a new address
|
// Generate a new address
|
||||||
addr := ethcrypto.CreateAddress(closure.Address(), closure.N().Uint64())
|
addr := ethcrypto.CreateAddress(closure.Address(), closure.object.Nonce)
|
||||||
for i := uint64(0); vm.state.GetStateObject(addr) != nil; i++ {
|
for i := uint64(0); self.env.State().GetStateObject(addr) != nil; i++ {
|
||||||
ethcrypto.CreateAddress(closure.Address(), closure.N().Uint64()+i)
|
ethcrypto.CreateAddress(closure.Address(), closure.object.Nonce+i)
|
||||||
}
|
}
|
||||||
closure.object.Nonce++
|
closure.object.Nonce++
|
||||||
|
|
||||||
vm.Printf(" (*) %x", addr).Endl()
|
self.Printf(" (*) %x", addr).Endl()
|
||||||
|
|
||||||
// Create a new contract
|
// Create a new contract
|
||||||
contract := vm.state.NewStateObject(addr)
|
contract := self.env.State().NewStateObject(addr)
|
||||||
if contract.Amount.Cmp(value) >= 0 {
|
if contract.Balance.Cmp(value) >= 0 {
|
||||||
closure.object.SubAmount(value)
|
closure.object.SubAmount(value)
|
||||||
contract.AddAmount(value)
|
contract.AddAmount(value)
|
||||||
|
|
||||||
@ -709,35 +695,35 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
closure.UseGas(closure.Gas)
|
closure.UseGas(closure.Gas)
|
||||||
|
|
||||||
// Create the closure
|
// Create the closure
|
||||||
c := NewClosure(closure, contract, initCode, vm.state, gas, closure.Price)
|
c := NewClosure(closure, contract, initCode, gas, closure.Price)
|
||||||
// Call the closure and set the return value as
|
// Call the closure and set the return value as
|
||||||
// main script.
|
// main script.
|
||||||
contract.script, err = Call(vm, c, nil)
|
contract.Code, _, err = c.Call(self, nil)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount)
|
err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stack.Push(ethutil.BigFalse)
|
stack.Push(ethutil.BigFalse)
|
||||||
|
|
||||||
// Revert the state as it was before.
|
// Revert the state as it was before.
|
||||||
vm.state.Set(snapshot)
|
self.env.State().Set(snapshot)
|
||||||
|
|
||||||
vm.Printf("CREATE err %v", err)
|
self.Printf("CREATE err %v", err)
|
||||||
} else {
|
} else {
|
||||||
stack.Push(ethutil.BigD(addr))
|
stack.Push(ethutil.BigD(addr))
|
||||||
vm.Printf("CREATE success")
|
self.Printf("CREATE success")
|
||||||
}
|
}
|
||||||
vm.Endl()
|
self.Endl()
|
||||||
|
|
||||||
// Debug hook
|
// Debug hook
|
||||||
if vm.Dbg != nil {
|
if self.Dbg != nil {
|
||||||
vm.Dbg.SetCode(closure.Script)
|
self.Dbg.SetCode(closure.Code)
|
||||||
}
|
}
|
||||||
case CALL:
|
case CALL:
|
||||||
require(7)
|
require(7)
|
||||||
|
|
||||||
vm.Endl()
|
self.Endl()
|
||||||
|
|
||||||
gas := stack.Pop()
|
gas := stack.Pop()
|
||||||
// Pop gas and value of the stack.
|
// Pop gas and value of the stack.
|
||||||
@ -750,30 +736,30 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
// Get the arguments from the memory
|
// Get the arguments from the memory
|
||||||
args := mem.Get(inOffset.Int64(), inSize.Int64())
|
args := mem.Get(inOffset.Int64(), inSize.Int64())
|
||||||
|
|
||||||
if closure.object.Amount.Cmp(value) < 0 {
|
if closure.object.Balance.Cmp(value) < 0 {
|
||||||
vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount)
|
vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Balance)
|
||||||
|
|
||||||
closure.ReturnGas(gas, nil, nil)
|
closure.ReturnGas(gas, nil)
|
||||||
|
|
||||||
stack.Push(ethutil.BigFalse)
|
stack.Push(ethutil.BigFalse)
|
||||||
} else {
|
} else {
|
||||||
snapshot := vm.state.Copy()
|
snapshot := self.env.State().Copy()
|
||||||
|
|
||||||
stateObject := vm.state.GetOrNewStateObject(addr.Bytes())
|
stateObject := self.env.State().GetOrNewStateObject(addr.Bytes())
|
||||||
|
|
||||||
closure.object.SubAmount(value)
|
closure.object.SubAmount(value)
|
||||||
stateObject.AddAmount(value)
|
stateObject.AddAmount(value)
|
||||||
|
|
||||||
// Create a new callable closure
|
// Create a new callable closure
|
||||||
c := NewClosure(closure, stateObject, stateObject.script, vm.state, gas, closure.Price)
|
c := NewClosure(closure, stateObject, stateObject.Code, gas, closure.Price)
|
||||||
// Executer the closure and get the return value (if any)
|
// Executer the closure and get the return value (if any)
|
||||||
ret, err := Call(vm, c, args)
|
ret, _, err := c.Call(self, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stack.Push(ethutil.BigFalse)
|
stack.Push(ethutil.BigFalse)
|
||||||
|
|
||||||
vmlogger.Debugf("Closure execution failed. %v\n", err)
|
vmlogger.Debugf("Closure execution failed. %v\n", err)
|
||||||
|
|
||||||
vm.state.Set(snapshot)
|
self.env.State().Set(snapshot)
|
||||||
} else {
|
} else {
|
||||||
stack.Push(ethutil.BigTrue)
|
stack.Push(ethutil.BigTrue)
|
||||||
|
|
||||||
@ -781,8 +767,8 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Debug hook
|
// Debug hook
|
||||||
if vm.Dbg != nil {
|
if self.Dbg != nil {
|
||||||
vm.Dbg.SetCode(closure.Script)
|
self.Dbg.SetCode(closure.Code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case RETURN:
|
case RETURN:
|
||||||
@ -790,44 +776,43 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
size, offset := stack.Popn()
|
size, offset := stack.Popn()
|
||||||
ret := mem.Get(offset.Int64(), size.Int64())
|
ret := mem.Get(offset.Int64(), size.Int64())
|
||||||
|
|
||||||
vm.Printf(" => (%d) 0x%x", len(ret), ret).Endl()
|
self.Printf(" => (%d) 0x%x", len(ret), ret).Endl()
|
||||||
|
|
||||||
return closure.Return(ret), nil
|
return closure.Return(ret), nil
|
||||||
case SUICIDE:
|
case SUICIDE:
|
||||||
require(1)
|
require(1)
|
||||||
|
|
||||||
receiver := vm.state.GetOrNewStateObject(stack.Pop().Bytes())
|
receiver := self.env.State().GetOrNewStateObject(stack.Pop().Bytes())
|
||||||
|
|
||||||
receiver.AddAmount(closure.object.Amount)
|
receiver.AddAmount(closure.object.Balance)
|
||||||
|
|
||||||
closure.object.MarkForDeletion()
|
closure.object.MarkForDeletion()
|
||||||
|
|
||||||
fallthrough
|
fallthrough
|
||||||
case STOP: // Stop the closure
|
case STOP: // Stop the closure
|
||||||
vm.Endl()
|
self.Endl()
|
||||||
|
|
||||||
return closure.Return(nil), nil
|
return closure.Return(nil), nil
|
||||||
default:
|
default:
|
||||||
vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op)
|
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)
|
return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op)
|
||||||
}
|
}
|
||||||
|
|
||||||
pc.Add(pc, ethutil.Big1)
|
pc.Add(pc, ethutil.Big1)
|
||||||
|
|
||||||
vm.Endl()
|
self.Endl()
|
||||||
|
|
||||||
if vm.Dbg != nil {
|
if self.Dbg != nil {
|
||||||
for _, instrNo := range vm.Dbg.BreakPoints() {
|
for _, instrNo := range self.Dbg.BreakPoints() {
|
||||||
if pc.Cmp(big.NewInt(instrNo)) == 0 {
|
if pc.Cmp(big.NewInt(instrNo)) == 0 {
|
||||||
vm.Stepping = true
|
self.Stepping = true
|
||||||
|
|
||||||
if !vm.Dbg.BreakHook(prevStep, op, mem, stack, closure.Object()) {
|
if !self.Dbg.BreakHook(prevStep, op, mem, stack, closure.Object()) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
} else if vm.Stepping {
|
} else if self.Stepping {
|
||||||
if !vm.Dbg.StepHook(prevStep, op, mem, stack, closure.Object()) {
|
if !self.Dbg.StepHook(prevStep, op, mem, stack, closure.Object()) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -836,3 +821,20 @@ func (vm *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
45
ethvm/vm_test.go
Normal file
45
ethvm/vm_test.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package ethvm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/eth-go/ethdb"
|
||||||
|
"github.com/ethereum/eth-go/ethlog"
|
||||||
|
"github.com/ethereum/eth-go/ethstate"
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"log"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestEnv struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self TestEnv) Origin() []byte { return nil }
|
||||||
|
func (self TestEnv) BlockNumber() *big.Int { return nil }
|
||||||
|
func (self TestEnv) PrevHash() []byte { return nil }
|
||||||
|
func (self TestEnv) Coinbase() []byte { return nil }
|
||||||
|
func (self TestEnv) Time() int64 { return 0 }
|
||||||
|
func (self TestEnv) Difficulty() *big.Int { return nil }
|
||||||
|
func (self TestEnv) Value() *big.Int { return nil }
|
||||||
|
func (self TestEnv) State() *ethstate.State { return nil }
|
||||||
|
|
||||||
|
func TestVm(t *testing.T) {
|
||||||
|
ethlog.AddLogSystem(ethlog.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlog.LogLevel(4)))
|
||||||
|
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
|
||||||
|
ethutil.Config.Db = db
|
||||||
|
|
||||||
|
stateObject := ethstate.NewStateObject([]byte{'j', 'e', 'f', 'f'})
|
||||||
|
callerClosure := NewClosure(stateObject, stateObject, []byte{0x60, 0x01}, big.NewInt(1000000), big.NewInt(0))
|
||||||
|
|
||||||
|
vm := New(TestEnv{})
|
||||||
|
vm.Verbose = true
|
||||||
|
|
||||||
|
ret, _, e := callerClosure.Call(vm, nil)
|
||||||
|
if e != nil {
|
||||||
|
fmt.Println("error", e)
|
||||||
|
}
|
||||||
|
fmt.Println(ret)
|
||||||
|
}
|
165
peer.go
165
peer.go
@ -4,15 +4,16 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/eth-go/ethchain"
|
|
||||||
"github.com/ethereum/eth-go/ethlog"
|
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
|
||||||
"github.com/ethereum/eth-go/ethwire"
|
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
|
"github.com/ethereum/eth-go/ethlog"
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"github.com/ethereum/eth-go/ethwire"
|
||||||
)
|
)
|
||||||
|
|
||||||
var peerlogger = ethlog.NewLogger("PEER")
|
var peerlogger = ethlog.NewLogger("PEER")
|
||||||
@ -21,7 +22,7 @@ const (
|
|||||||
// The size of the output buffer for writing messages
|
// The size of the output buffer for writing messages
|
||||||
outputBufferSize = 50
|
outputBufferSize = 50
|
||||||
// Current protocol version
|
// Current protocol version
|
||||||
ProtocolVersion = 23
|
ProtocolVersion = 25
|
||||||
// Interval for ping/pong message
|
// Interval for ping/pong message
|
||||||
pingPongTimer = 2 * time.Second
|
pingPongTimer = 2 * time.Second
|
||||||
)
|
)
|
||||||
@ -121,10 +122,8 @@ type Peer struct {
|
|||||||
versionKnown bool
|
versionKnown bool
|
||||||
|
|
||||||
// Last received pong message
|
// Last received pong message
|
||||||
lastPong int64
|
lastPong int64
|
||||||
// Indicates whether a MsgGetPeersTy was requested of the peer
|
lastBlockReceived time.Time
|
||||||
// this to prevent receiving false peers.
|
|
||||||
requestedPeerList bool
|
|
||||||
|
|
||||||
host []byte
|
host []byte
|
||||||
port uint16
|
port uint16
|
||||||
@ -180,10 +179,9 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
|
|||||||
|
|
||||||
// Set up the connection in another goroutine so we don't block the main thread
|
// Set up the connection in another goroutine so we don't block the main thread
|
||||||
go func() {
|
go func() {
|
||||||
conn, err := net.DialTimeout("tcp", addr, 10*time.Second)
|
conn, err := p.Connect(addr)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
peerlogger.Debugln("Connection to peer failed", err)
|
peerlogger.Debugln("Connection to peer failed. Giving up.", err)
|
||||||
p.Stop()
|
p.Stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -199,6 +197,23 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Peer) Connect(addr string) (conn net.Conn, err error) {
|
||||||
|
const maxTries = 3
|
||||||
|
for attempts := 0; attempts < maxTries; attempts++ {
|
||||||
|
conn, err = net.DialTimeout("tcp", addr, 10*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
//peerlogger.Debugf("Peer connection failed. Retrying (%d/%d) (%s)\n", attempts+1, maxTries, addr)
|
||||||
|
time.Sleep(time.Duration(attempts*20) * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
func (p *Peer) PingTime() string {
|
func (p *Peer) PingTime() string {
|
||||||
return p.pingTime.String()
|
return p.pingTime.String()
|
||||||
@ -279,12 +294,14 @@ out:
|
|||||||
|
|
||||||
// Ping timer
|
// Ping timer
|
||||||
case <-pingTimer.C:
|
case <-pingTimer.C:
|
||||||
timeSince := time.Since(time.Unix(p.lastPong, 0))
|
/*
|
||||||
if !p.pingStartTime.IsZero() && p.lastPong != 0 && timeSince > (pingPongTimer+30*time.Second) {
|
timeSince := time.Since(time.Unix(p.lastPong, 0))
|
||||||
peerlogger.Infof("Peer did not respond to latest pong fast enough, it took %s, disconnecting.\n", timeSince)
|
if !p.pingStartTime.IsZero() && p.lastPong != 0 && timeSince > (pingPongTimer+30*time.Second) {
|
||||||
p.Stop()
|
peerlogger.Infof("Peer did not respond to latest pong fast enough, it took %s, disconnecting.\n", timeSince)
|
||||||
return
|
p.Stop()
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, ""))
|
p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, ""))
|
||||||
p.pingStartTime = time.Now()
|
p.pingStartTime = time.Now()
|
||||||
|
|
||||||
@ -339,7 +356,7 @@ func (p *Peer) HandleInbound() {
|
|||||||
}
|
}
|
||||||
case ethwire.MsgDiscTy:
|
case ethwire.MsgDiscTy:
|
||||||
p.Stop()
|
p.Stop()
|
||||||
peerlogger.Infoln("Disconnect peer:", DiscReason(msg.Data.Get(0).Uint()))
|
peerlogger.Infoln("Disconnect peer: ", DiscReason(msg.Data.Get(0).Uint()))
|
||||||
case ethwire.MsgPingTy:
|
case ethwire.MsgPingTy:
|
||||||
// Respond back with pong
|
// Respond back with pong
|
||||||
p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, ""))
|
p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, ""))
|
||||||
@ -348,11 +365,17 @@ func (p *Peer) HandleInbound() {
|
|||||||
// last pong so the peer handler knows this peer is still
|
// last pong so the peer handler knows this peer is still
|
||||||
// active.
|
// active.
|
||||||
p.lastPong = time.Now().Unix()
|
p.lastPong = time.Now().Unix()
|
||||||
p.pingTime = time.Now().Sub(p.pingStartTime)
|
p.pingTime = time.Since(p.pingStartTime)
|
||||||
case ethwire.MsgBlockTy:
|
case ethwire.MsgBlockTy:
|
||||||
// Get all blocks and process them
|
// Get all blocks and process them
|
||||||
var block, lastBlock *ethchain.Block
|
//var block, lastBlock *ethchain.Block
|
||||||
var err error
|
//var err error
|
||||||
|
|
||||||
|
var (
|
||||||
|
block, lastBlock *ethchain.Block
|
||||||
|
blockChain = p.ethereum.BlockChain()
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
// Make sure we are actually receiving anything
|
// Make sure we are actually receiving anything
|
||||||
if msg.Data.Len()-1 > 1 && p.diverted {
|
if msg.Data.Len()-1 > 1 && p.diverted {
|
||||||
@ -368,11 +391,11 @@ func (p *Peer) HandleInbound() {
|
|||||||
for i := msg.Data.Len() - 1; i >= 0; i-- {
|
for i := msg.Data.Len() - 1; i >= 0; i-- {
|
||||||
block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
|
block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
|
||||||
// Do we have this block on our chain? If so we can continue
|
// Do we have this block on our chain? If so we can continue
|
||||||
if !p.ethereum.StateManager().BlockChain().HasBlock(block.Hash()) {
|
if !blockChain.HasBlock(block.Hash()) {
|
||||||
// We don't have this block, but we do have a block with the same prevHash, diversion time!
|
// We don't have this block, but we do have a block with the same prevHash, diversion time!
|
||||||
if p.ethereum.StateManager().BlockChain().HasBlockWithPrevHash(block.PrevHash) {
|
if blockChain.HasBlockWithPrevHash(block.PrevHash) {
|
||||||
p.diverted = false
|
p.diverted = false
|
||||||
if !p.ethereum.StateManager().BlockChain().FindCanonicalChainFromMsg(msg, block.PrevHash) {
|
if !blockChain.FindCanonicalChainFromMsg(msg, block.PrevHash) {
|
||||||
p.SyncWithPeerToLastKnown()
|
p.SyncWithPeerToLastKnown()
|
||||||
break nextMsg
|
break nextMsg
|
||||||
}
|
}
|
||||||
@ -397,10 +420,7 @@ func (p *Peer) HandleInbound() {
|
|||||||
for i := msg.Data.Len() - 1; i >= 0; i-- {
|
for i := msg.Data.Len() - 1; i >= 0; i-- {
|
||||||
block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
|
block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
|
||||||
|
|
||||||
//p.ethereum.StateManager().PrepareDefault(block)
|
|
||||||
//state := p.ethereum.StateManager().CurrentState()
|
|
||||||
err = p.ethereum.StateManager().Process(block, false)
|
err = p.ethereum.StateManager().Process(block, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ethutil.Config.Debug {
|
if ethutil.Config.Debug {
|
||||||
peerlogger.Infof("Block %x failed\n", block.Hash())
|
peerlogger.Infof("Block %x failed\n", block.Hash())
|
||||||
@ -411,6 +431,8 @@ func (p *Peer) HandleInbound() {
|
|||||||
} else {
|
} else {
|
||||||
lastBlock = block
|
lastBlock = block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.lastBlockReceived = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg.Data.Len() <= 1 {
|
if msg.Data.Len() <= 1 {
|
||||||
@ -422,19 +444,20 @@ func (p *Peer) HandleInbound() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// If the parent is unknown try to catch up with this peer
|
// If the parent is unknown try to catch up with this peer
|
||||||
if ethchain.IsParentErr(err) {
|
if ethchain.IsParentErr(err) {
|
||||||
/*
|
b := ethchain.NewBlockFromRlpValue(msg.Data.Get(0))
|
||||||
b := ethchain.NewBlockFromRlpValue(msg.Data.Get(0))
|
|
||||||
|
|
||||||
peerlogger.Infof("Attempting to catch (%x). Parent known\n", b.Hash())
|
peerlogger.Infof("Attempting to catch (%x). Parent unknown\n", b.Hash())
|
||||||
p.catchingUp = false
|
|
||||||
|
|
||||||
p.CatchupWithPeer(b.Hash())
|
|
||||||
|
|
||||||
peerlogger.Infoln(b)
|
|
||||||
*/
|
|
||||||
peerlogger.Infoln("Attempting to catch. Parent known")
|
|
||||||
p.catchingUp = false
|
p.catchingUp = false
|
||||||
p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
|
|
||||||
|
p.CatchupWithPeer(b.Hash())
|
||||||
|
|
||||||
|
peerlogger.Infoln(b)
|
||||||
|
|
||||||
|
/*
|
||||||
|
peerlogger.Infoln("Attempting to catch. Parent known")
|
||||||
|
p.catchingUp = false
|
||||||
|
p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
|
||||||
|
*/
|
||||||
} else if ethchain.IsValidationErr(err) {
|
} else if ethchain.IsValidationErr(err) {
|
||||||
fmt.Println("Err:", err)
|
fmt.Println("Err:", err)
|
||||||
p.catchingUp = false
|
p.catchingUp = false
|
||||||
@ -463,30 +486,21 @@ func (p *Peer) HandleInbound() {
|
|||||||
p.ethereum.TxPool().QueueTransaction(tx)
|
p.ethereum.TxPool().QueueTransaction(tx)
|
||||||
}
|
}
|
||||||
case ethwire.MsgGetPeersTy:
|
case ethwire.MsgGetPeersTy:
|
||||||
// Flag this peer as a 'requested of new peers' this to
|
|
||||||
// prevent malicious peers being forced.
|
|
||||||
p.requestedPeerList = true
|
|
||||||
// Peer asked for list of connected peers
|
// Peer asked for list of connected peers
|
||||||
p.pushPeers()
|
p.pushPeers()
|
||||||
case ethwire.MsgPeersTy:
|
case ethwire.MsgPeersTy:
|
||||||
// Received a list of peers (probably because MsgGetPeersTy was send)
|
// Received a list of peers (probably because MsgGetPeersTy was send)
|
||||||
// Only act on message if we actually requested for a peers list
|
data := msg.Data
|
||||||
if p.requestedPeerList {
|
// Create new list of possible peers for the ethereum to process
|
||||||
data := msg.Data
|
peers := make([]string, data.Len())
|
||||||
// Create new list of possible peers for the ethereum to process
|
// Parse each possible peer
|
||||||
peers := make([]string, data.Len())
|
for i := 0; i < data.Len(); i++ {
|
||||||
// Parse each possible peer
|
value := data.Get(i)
|
||||||
for i := 0; i < data.Len(); i++ {
|
peers[i] = unpackAddr(value.Get(0), value.Get(1).Uint())
|
||||||
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
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connect to the list of peers
|
||||||
|
p.ethereum.ProcessPeerList(peers)
|
||||||
case ethwire.MsgGetChainTy:
|
case ethwire.MsgGetChainTy:
|
||||||
var parent *ethchain.Block
|
var parent *ethchain.Block
|
||||||
// Length minus one since the very last element in the array is a count
|
// Length minus one since the very last element in the array is a count
|
||||||
@ -559,6 +573,25 @@ func (p *Peer) HandleInbound() {
|
|||||||
p.Stop()
|
p.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// General update method
|
||||||
|
func (self *Peer) update() {
|
||||||
|
serviceTimer := time.NewTicker(5 * time.Second)
|
||||||
|
|
||||||
|
out:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-serviceTimer.C:
|
||||||
|
if time.Since(self.lastBlockReceived) > 10*time.Second {
|
||||||
|
self.catchingUp = false
|
||||||
|
}
|
||||||
|
case <-self.quit:
|
||||||
|
break out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceTimer.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Peer) Start() {
|
func (p *Peer) Start() {
|
||||||
peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String())
|
peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String())
|
||||||
servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
|
servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
|
||||||
@ -581,6 +614,8 @@ func (p *Peer) Start() {
|
|||||||
go p.HandleOutbound()
|
go p.HandleOutbound()
|
||||||
// Run the inbound handler in a new goroutine
|
// Run the inbound handler in a new goroutine
|
||||||
go p.HandleInbound()
|
go p.HandleInbound()
|
||||||
|
// Run the general update handler
|
||||||
|
go p.update()
|
||||||
|
|
||||||
// Wait a few seconds for startup and then ask for an initial ping
|
// Wait a few seconds for startup and then ask for an initial ping
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
@ -698,11 +733,13 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
|
|||||||
|
|
||||||
ethlogger.Infof("Added peer (%s) %d / %d\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers)
|
ethlogger.Infof("Added peer (%s) %d / %d\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers)
|
||||||
|
|
||||||
// Catch up with the connected peer
|
/*
|
||||||
if !p.ethereum.IsUpToDate() {
|
// Catch up with the connected peer
|
||||||
peerlogger.Debugln("Already syncing up with a peer; sleeping")
|
if !p.ethereum.IsUpToDate() {
|
||||||
time.Sleep(10 * time.Second)
|
peerlogger.Debugln("Already syncing up with a peer; sleeping")
|
||||||
}
|
time.Sleep(10 * time.Second)
|
||||||
|
}
|
||||||
|
*/
|
||||||
p.SyncWithPeerToLastKnown()
|
p.SyncWithPeerToLastKnown()
|
||||||
|
|
||||||
peerlogger.Debugln(p)
|
peerlogger.Debugln(p)
|
||||||
@ -757,7 +794,7 @@ func (p *Peer) CatchupWithPeer(blockHash []byte) {
|
|||||||
if !p.catchingUp {
|
if !p.catchingUp {
|
||||||
// Make sure nobody else is catching up when you want to do this
|
// Make sure nobody else is catching up when you want to do this
|
||||||
p.catchingUp = true
|
p.catchingUp = true
|
||||||
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(30)})
|
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(100)})
|
||||||
p.QueueMessage(msg)
|
p.QueueMessage(msg)
|
||||||
|
|
||||||
peerlogger.DebugDetailf("Requesting blockchain %x... from peer %s\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4], p.conn.RemoteAddr())
|
peerlogger.DebugDetailf("Requesting blockchain %x... from peer %s\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4], p.conn.RemoteAddr())
|
||||||
|
Loading…
Reference in New Issue
Block a user