Merge branch 'release/poc5-rc1'
This commit is contained in:
commit
2096b3a9ed
@ -6,7 +6,7 @@ Ethereum
|
||||
Ethereum Go Development package (C) Jeffrey Wilcke
|
||||
|
||||
Ethereum is currently in its testing phase. The current state is "Proof
|
||||
of Concept 3.5". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)).
|
||||
of Concept 5.0 RC1". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)).
|
||||
|
||||
Ethereum Go is split up in several sub packages Please refer to each
|
||||
individual package for more information.
|
||||
|
@ -1,76 +0,0 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
address []byte
|
||||
Amount *big.Int
|
||||
Nonce uint64
|
||||
}
|
||||
|
||||
func NewAccount(address []byte, amount *big.Int) *Account {
|
||||
return &Account{address, amount, 0}
|
||||
}
|
||||
|
||||
func NewAccountFromData(address, data []byte) *Account {
|
||||
account := &Account{address: address}
|
||||
account.RlpDecode(data)
|
||||
|
||||
return account
|
||||
}
|
||||
|
||||
func (a *Account) AddFee(fee *big.Int) {
|
||||
a.AddFunds(fee)
|
||||
}
|
||||
|
||||
func (a *Account) AddFunds(funds *big.Int) {
|
||||
a.Amount.Add(a.Amount, funds)
|
||||
}
|
||||
|
||||
func (a *Account) Address() []byte {
|
||||
return a.address
|
||||
}
|
||||
|
||||
// Implements Callee
|
||||
func (a *Account) ReturnGas(value *big.Int, state *State) {
|
||||
// Return the value back to the sender
|
||||
a.AddFunds(value)
|
||||
state.UpdateAccount(a.address, a)
|
||||
}
|
||||
|
||||
func (a *Account) RlpEncode() []byte {
|
||||
return ethutil.Encode([]interface{}{a.Amount, a.Nonce})
|
||||
}
|
||||
|
||||
func (a *Account) RlpDecode(data []byte) {
|
||||
decoder := ethutil.NewValueFromBytes(data)
|
||||
|
||||
a.Amount = decoder.Get(0).BigInt()
|
||||
a.Nonce = decoder.Get(1).Uint()
|
||||
}
|
||||
|
||||
type AddrStateStore struct {
|
||||
states map[string]*AccountState
|
||||
}
|
||||
|
||||
func NewAddrStateStore() *AddrStateStore {
|
||||
return &AddrStateStore{states: make(map[string]*AccountState)}
|
||||
}
|
||||
|
||||
func (s *AddrStateStore) Add(addr []byte, account *Account) *AccountState {
|
||||
state := &AccountState{Nonce: account.Nonce, Account: account}
|
||||
s.states[string(addr)] = state
|
||||
return state
|
||||
}
|
||||
|
||||
func (s *AddrStateStore) Get(addr []byte) *AccountState {
|
||||
return s.states[string(addr)]
|
||||
}
|
||||
|
||||
type AccountState struct {
|
||||
Nonce uint64
|
||||
Account *Account
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddressState(t *testing.T) {
|
||||
}
|
59
ethchain/asm.go
Normal file
59
ethchain/asm.go
Normal file
@ -0,0 +1,59 @@
|
||||
package ethchain
|
||||
|
||||
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 oPUSH: // Push PC+1 on to the stack
|
||||
pc.Add(pc, ethutil.Big1)
|
||||
data := script[pc.Int64() : pc.Int64()+32]
|
||||
val := ethutil.BigD(data)
|
||||
|
||||
var b []byte
|
||||
if val.Int64() == 0 {
|
||||
b = []byte{0}
|
||||
} else {
|
||||
b = val.Bytes()
|
||||
}
|
||||
|
||||
asm = append(asm, fmt.Sprintf("0x%x", b))
|
||||
|
||||
pc.Add(pc, big.NewInt(31))
|
||||
case oPUSH20:
|
||||
pc.Add(pc, ethutil.Big1)
|
||||
data := script[pc.Int64() : pc.Int64()+20]
|
||||
val := ethutil.BigD(data)
|
||||
var b []byte
|
||||
if val.Int64() == 0 {
|
||||
b = []byte{0}
|
||||
} else {
|
||||
b = val.Bytes()
|
||||
}
|
||||
|
||||
asm = append(asm, fmt.Sprintf("0x%x", b))
|
||||
|
||||
pc.Add(pc, big.NewInt(19))
|
||||
}
|
||||
|
||||
pc.Add(pc, ethutil.Big1)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@ -113,11 +113,6 @@ func (block *Block) HashNoNonce() []byte {
|
||||
return ethutil.Sha3Bin(ethutil.Encode([]interface{}{block.PrevHash, block.UncleSha, block.Coinbase, block.state.trie.Root, block.TxSha, block.Difficulty, block.Time, block.Extra}))
|
||||
}
|
||||
|
||||
func (block *Block) PrintHash() {
|
||||
fmt.Println(block)
|
||||
fmt.Println(ethutil.NewValue(ethutil.Encode([]interface{}{block.PrevHash, block.UncleSha, block.Coinbase, block.state.trie.Root, block.TxSha, block.Difficulty, block.Time, block.Extra, block.Nonce})))
|
||||
}
|
||||
|
||||
func (block *Block) State() *State {
|
||||
return block.state
|
||||
}
|
||||
@ -142,12 +137,13 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool {
|
||||
data := block.state.trie.Get(string(block.Coinbase))
|
||||
|
||||
// Get the ether (Coinbase) and add the fee (gief fee to miner)
|
||||
ether := NewAccountFromData(block.Coinbase, []byte(data))
|
||||
account := NewStateObjectFromBytes(block.Coinbase, []byte(data))
|
||||
|
||||
base = new(big.Int)
|
||||
ether.Amount = base.Add(ether.Amount, fee)
|
||||
account.Amount = base.Add(account.Amount, fee)
|
||||
|
||||
block.state.trie.Update(string(block.Coinbase), string(ether.RlpEncode()))
|
||||
//block.state.trie.Update(string(block.Coinbase), string(ether.RlpEncode()))
|
||||
block.state.UpdateStateObject(account)
|
||||
|
||||
return true
|
||||
}
|
||||
@ -178,26 +174,6 @@ func (block *Block) MakeContract(tx *Transaction) {
|
||||
}
|
||||
|
||||
/////// Block Encoding
|
||||
func (block *Block) encodedUncles() interface{} {
|
||||
uncles := make([]interface{}, len(block.Uncles))
|
||||
for i, uncle := range block.Uncles {
|
||||
uncles[i] = uncle.RlpEncode()
|
||||
}
|
||||
|
||||
return uncles
|
||||
}
|
||||
|
||||
func (block *Block) encodedTxs() interface{} {
|
||||
// Marshal the transactions of this block
|
||||
encTx := make([]interface{}, len(block.transactions))
|
||||
for i, tx := range block.transactions {
|
||||
// Cast it to a string (safe)
|
||||
encTx[i] = tx.RlpData()
|
||||
}
|
||||
|
||||
return encTx
|
||||
}
|
||||
|
||||
func (block *Block) rlpTxs() interface{} {
|
||||
// Marshal the transactions of this block
|
||||
encTx := make([]interface{}, len(block.transactions))
|
||||
@ -304,6 +280,9 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block {
|
||||
func (block *Block) String() string {
|
||||
return fmt.Sprintf("Block(%x):\nPrevHash:%x\nUncleSha:%x\nCoinbase:%x\nRoot:%x\nTxSha:%x\nDiff:%v\nTime:%d\nNonce:%x\nTxs:%d\n", block.Hash(), block.PrevHash, block.UncleSha, block.Coinbase, block.state.trie.Root, block.TxSha, block.Difficulty, block.Time, block.Nonce, len(block.transactions))
|
||||
}
|
||||
func (block *Block) GetRoot() interface{} {
|
||||
return block.state.trie.Root
|
||||
}
|
||||
|
||||
//////////// UNEXPORTED /////////////////
|
||||
func (block *Block) header() []interface{} {
|
||||
|
@ -3,6 +3,7 @@ package ethchain
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/eth-go/ethwire"
|
||||
"log"
|
||||
"math"
|
||||
"math/big"
|
||||
@ -23,7 +24,8 @@ type BlockChain struct {
|
||||
|
||||
func NewBlockChain(ethereum EthManager) *BlockChain {
|
||||
bc := &BlockChain{}
|
||||
bc.genesisBlock = NewBlockFromData(ethutil.Encode(Genesis))
|
||||
bc.genesisBlock = NewBlockFromBytes(ethutil.Encode(Genesis))
|
||||
bc.Ethereum = ethereum
|
||||
|
||||
bc.setLastBlock()
|
||||
|
||||
@ -78,6 +80,128 @@ func (bc *BlockChain) HasBlock(hash []byte) bool {
|
||||
return len(data) != 0
|
||||
}
|
||||
|
||||
// TODO: At one point we might want to save a block by prevHash in the db to optimise this...
|
||||
func (bc *BlockChain) HasBlockWithPrevHash(hash []byte) bool {
|
||||
block := bc.CurrentBlock
|
||||
|
||||
for ; block != nil; block = bc.GetBlock(block.PrevHash) {
|
||||
if bytes.Compare(hash, block.PrevHash) == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (bc *BlockChain) CalculateBlockTD(block *Block) *big.Int {
|
||||
blockDiff := new(big.Int)
|
||||
|
||||
for _, uncle := range block.Uncles {
|
||||
blockDiff = blockDiff.Add(blockDiff, uncle.Difficulty)
|
||||
}
|
||||
blockDiff = blockDiff.Add(blockDiff, block.Difficulty)
|
||||
|
||||
return blockDiff
|
||||
}
|
||||
func (bc *BlockChain) FindCanonicalChainFromMsg(msg *ethwire.Msg, commonBlockHash []byte) bool {
|
||||
var blocks []*Block
|
||||
for i := 0; i < (msg.Data.Len() - 1); i++ {
|
||||
block := NewBlockFromRlpValue(msg.Data.Get(i))
|
||||
blocks = append(blocks, block)
|
||||
}
|
||||
return bc.FindCanonicalChain(blocks, commonBlockHash)
|
||||
}
|
||||
|
||||
// Is tasked by finding the CanonicalChain and resetting the chain if we are not the Conical one
|
||||
// Return true if we are the using the canonical chain false if not
|
||||
func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte) bool {
|
||||
// 1. Calculate TD of the current chain
|
||||
// 2. Calculate TD of the new chain
|
||||
// Reset state to the correct one
|
||||
|
||||
chainDifficulty := new(big.Int)
|
||||
|
||||
// Calculate the entire chain until the block we both have
|
||||
// Start with the newest block we got, all the way back to the common block we both know
|
||||
for _, block := range blocks {
|
||||
if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
|
||||
log.Println("[CHAIN] We have found the common parent block, breaking")
|
||||
break
|
||||
}
|
||||
log.Println("Checking incoming blocks:")
|
||||
chainDifficulty.Add(chainDifficulty, bc.CalculateBlockTD(block))
|
||||
}
|
||||
|
||||
log.Println("[CHAIN] Incoming chain difficulty:", chainDifficulty)
|
||||
|
||||
curChainDifficulty := new(big.Int)
|
||||
block := bc.CurrentBlock
|
||||
for i := 0; block != nil; block = bc.GetBlock(block.PrevHash) {
|
||||
i++
|
||||
if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
|
||||
log.Println("[CHAIN] We have found the common parent block, breaking")
|
||||
break
|
||||
}
|
||||
anOtherBlock := bc.GetBlock(block.PrevHash)
|
||||
if anOtherBlock == nil {
|
||||
// We do not want to count the genesis block for difficulty since that's not being sent
|
||||
log.Println("[CHAIN] At genesis block, breaking")
|
||||
break
|
||||
}
|
||||
curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block))
|
||||
}
|
||||
|
||||
log.Println("[CHAIN] Current chain difficulty:", curChainDifficulty)
|
||||
if chainDifficulty.Cmp(curChainDifficulty) == 1 {
|
||||
log.Printf("[CHAIN] The incoming Chain beat our asses, resetting to block: %x", commonBlockHash)
|
||||
bc.ResetTillBlockHash(commonBlockHash)
|
||||
return false
|
||||
} else {
|
||||
log.Println("[CHAIN] Our chain showed the incoming chain who is boss. Ignoring.")
|
||||
return true
|
||||
}
|
||||
}
|
||||
func (bc *BlockChain) ResetTillBlockHash(hash []byte) error {
|
||||
lastBlock := bc.CurrentBlock
|
||||
var returnTo *Block
|
||||
// Reset to Genesis if that's all the origin there is.
|
||||
if bytes.Compare(hash, bc.genesisBlock.Hash()) == 0 {
|
||||
returnTo = bc.genesisBlock
|
||||
bc.CurrentBlock = bc.genesisBlock
|
||||
bc.LastBlockHash = bc.genesisBlock.Hash()
|
||||
bc.LastBlockNumber = 1
|
||||
} else {
|
||||
// TODO: Somehow this doesn't really give the right numbers, double check.
|
||||
// TODO: Change logs into debug lines
|
||||
returnTo = bc.GetBlock(hash)
|
||||
bc.CurrentBlock = returnTo
|
||||
bc.LastBlockHash = returnTo.Hash()
|
||||
info := bc.BlockInfo(returnTo)
|
||||
bc.LastBlockNumber = info.Number
|
||||
}
|
||||
|
||||
// XXX Why are we resetting? This is the block chain, it has nothing to do with states
|
||||
//bc.Ethereum.StateManager().PrepareDefault(returnTo)
|
||||
|
||||
err := ethutil.Config.Db.Delete(lastBlock.Hash())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var block *Block
|
||||
for ; block != nil; block = bc.GetBlock(block.PrevHash) {
|
||||
if bytes.Compare(block.Hash(), hash) == 0 {
|
||||
log.Println("[CHAIN] We have arrived at the the common parent block, breaking")
|
||||
break
|
||||
}
|
||||
err = ethutil.Config.Db.Delete(block.Hash())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Println("[CHAIN] Split chain deleted and reverted to common parent block.")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bc *BlockChain) GenesisBlock() *Block {
|
||||
return bc.genesisBlock
|
||||
}
|
||||
@ -136,12 +260,13 @@ func AddTestNetFunds(block *Block) {
|
||||
"e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey
|
||||
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
|
||||
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex
|
||||
"2ef47100e0787b915105fd5e3f4ff6752079d5cb", // Maran
|
||||
} {
|
||||
//log.Println("2^200 Wei to", addr)
|
||||
codedAddr := ethutil.FromHex(addr)
|
||||
addr := block.state.GetAccount(codedAddr)
|
||||
addr.Amount = ethutil.BigPow(2, 200)
|
||||
block.state.UpdateAccount(codedAddr, addr)
|
||||
account := block.state.GetAccount(codedAddr)
|
||||
account.Amount = ethutil.BigPow(2, 200)
|
||||
block.state.UpdateStateObject(account)
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,8 +305,8 @@ func (bc *BlockChain) SetTotalDifficulty(td *big.Int) {
|
||||
// Add a block to the chain and record addition information
|
||||
func (bc *BlockChain) Add(block *Block) {
|
||||
bc.writeBlockInfo(block)
|
||||
|
||||
// Prepare the genesis block
|
||||
|
||||
bc.CurrentBlock = block
|
||||
bc.LastBlockHash = block.Hash()
|
||||
|
||||
@ -196,7 +321,7 @@ func (bc *BlockChain) GetBlock(hash []byte) *Block {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NewBlockFromData(data)
|
||||
return NewBlockFromBytes(data)
|
||||
}
|
||||
|
||||
func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo {
|
||||
|
115
ethchain/block_chain_test.go
Normal file
115
ethchain/block_chain_test.go
Normal file
@ -0,0 +1,115 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethdb"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/eth-go/ethwire"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Implement our EthTest Manager
|
||||
type TestManager struct {
|
||||
stateManager *StateManager
|
||||
reactor *ethutil.ReactorEngine
|
||||
|
||||
txPool *TxPool
|
||||
blockChain *BlockChain
|
||||
Blocks []*Block
|
||||
}
|
||||
|
||||
func (s *TestManager) BlockChain() *BlockChain {
|
||||
return s.blockChain
|
||||
}
|
||||
|
||||
func (tm *TestManager) TxPool() *TxPool {
|
||||
return tm.txPool
|
||||
}
|
||||
|
||||
func (tm *TestManager) StateManager() *StateManager {
|
||||
return tm.stateManager
|
||||
}
|
||||
|
||||
func (tm *TestManager) Reactor() *ethutil.ReactorEngine {
|
||||
return tm.reactor
|
||||
}
|
||||
func (tm *TestManager) Broadcast(msgType ethwire.MsgType, data []interface{}) {
|
||||
fmt.Println("Broadcast not implemented")
|
||||
}
|
||||
|
||||
func NewTestManager() *TestManager {
|
||||
ethutil.ReadConfig(".ethtest")
|
||||
|
||||
db, err := ethdb.NewMemDatabase()
|
||||
if err != nil {
|
||||
fmt.Println("Could not create mem-db, failing")
|
||||
return nil
|
||||
}
|
||||
ethutil.Config.Db = db
|
||||
|
||||
testManager := &TestManager{}
|
||||
testManager.reactor = ethutil.NewReactorEngine()
|
||||
|
||||
testManager.txPool = NewTxPool(testManager)
|
||||
testManager.blockChain = NewBlockChain(testManager)
|
||||
testManager.stateManager = NewStateManager(testManager)
|
||||
|
||||
// Start the tx pool
|
||||
testManager.txPool.Start()
|
||||
|
||||
return testManager
|
||||
}
|
||||
func (tm *TestManager) AddFakeBlock(blk []byte) error {
|
||||
block := NewBlockFromBytes(blk)
|
||||
tm.Blocks = append(tm.Blocks, block)
|
||||
tm.StateManager().PrepareDefault(block)
|
||||
err := tm.StateManager().ProcessBlock(block, false)
|
||||
return err
|
||||
}
|
||||
func (tm *TestManager) CreateChain1() error {
|
||||
err := tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 58, 253, 98, 206, 198, 181, 152, 223, 201, 116, 197, 154, 111, 104, 54, 113, 249, 184, 246, 15, 226, 142, 187, 47, 138, 60, 201, 66, 226, 237, 29, 7, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 103, 109, 19, 120, 219, 91, 248, 48, 204, 17, 28, 7, 146, 72, 203, 15, 207, 251, 31, 216, 138, 26, 59, 34, 238, 40, 114, 233, 1, 13, 207, 90, 71, 136, 124, 86, 196, 127, 10, 176, 193, 154, 165, 76, 155, 154, 59, 45, 34, 96, 183, 212, 99, 41, 27, 40, 119, 171, 231, 160, 114, 56, 218, 173, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 240, 0, 132, 83, 48, 32, 251, 128, 160, 4, 10, 11, 225, 132, 86, 146, 227, 229, 137, 164, 245, 16, 139, 219, 12, 251, 178, 154, 168, 210, 18, 84, 40, 250, 41, 124, 92, 169, 242, 246, 180, 192, 192})
|
||||
err = tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 222, 229, 152, 228, 200, 163, 244, 144, 120, 18, 203, 253, 195, 185, 105, 131, 163, 226, 116, 40, 140, 68, 249, 198, 221, 152, 121, 0, 124, 11, 180, 125, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 103, 109, 19, 120, 219, 91, 248, 48, 204, 17, 28, 7, 146, 72, 203, 15, 207, 251, 31, 216, 138, 26, 59, 34, 238, 40, 114, 233, 1, 13, 207, 90, 71, 136, 124, 86, 196, 127, 10, 176, 193, 154, 165, 76, 155, 154, 59, 45, 34, 96, 183, 212, 99, 41, 27, 40, 119, 171, 231, 160, 114, 56, 218, 173, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 224, 4, 132, 83, 48, 36, 250, 128, 160, 79, 58, 51, 246, 238, 249, 210, 253, 136, 83, 71, 134, 49, 114, 190, 189, 242, 78, 100, 238, 101, 84, 204, 176, 198, 25, 139, 151, 60, 84, 51, 126, 192, 192})
|
||||
err = tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 68, 52, 33, 210, 160, 189, 217, 255, 78, 37, 196, 217, 94, 247, 166, 169, 224, 199, 102, 110, 85, 213, 45, 13, 173, 106, 4, 103, 151, 195, 38, 86, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 103, 109, 19, 120, 219, 91, 248, 48, 204, 17, 28, 7, 146, 72, 203, 15, 207, 251, 31, 216, 138, 26, 59, 34, 238, 40, 114, 233, 1, 13, 207, 90, 71, 136, 124, 86, 196, 127, 10, 176, 193, 154, 165, 76, 155, 154, 59, 45, 34, 96, 183, 212, 99, 41, 27, 40, 119, 171, 231, 160, 114, 56, 218, 173, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 208, 12, 132, 83, 48, 38, 206, 128, 160, 65, 147, 32, 128, 177, 198, 131, 57, 57, 68, 135, 65, 198, 178, 138, 43, 25, 135, 92, 174, 208, 119, 103, 225, 26, 207, 243, 31, 225, 29, 173, 119, 192, 192})
|
||||
return err
|
||||
}
|
||||
func (tm *TestManager) CreateChain2() error {
|
||||
err := tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 58, 253, 98, 206, 198, 181, 152, 223, 201, 116, 197, 154, 111, 104, 54, 113, 249, 184, 246, 15, 226, 142, 187, 47, 138, 60, 201, 66, 226, 237, 29, 7, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 72, 201, 77, 81, 160, 103, 70, 18, 102, 204, 82, 192, 86, 157, 40, 30, 117, 218, 224, 202, 1, 36, 249, 88, 82, 210, 19, 156, 112, 31, 13, 117, 227, 0, 125, 221, 190, 165, 16, 193, 163, 161, 175, 33, 37, 184, 235, 62, 201, 93, 102, 185, 143, 54, 146, 114, 30, 253, 178, 245, 87, 38, 191, 214, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 240, 0, 132, 83, 48, 40, 35, 128, 160, 162, 214, 119, 207, 212, 186, 64, 47, 14, 186, 98, 118, 203, 79, 172, 205, 33, 206, 225, 177, 225, 194, 98, 188, 63, 219, 13, 151, 47, 32, 204, 27, 192, 192})
|
||||
err = tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 0, 210, 76, 6, 13, 18, 219, 190, 18, 250, 23, 178, 198, 117, 254, 85, 14, 74, 104, 116, 56, 144, 116, 172, 14, 3, 236, 99, 248, 228, 142, 91, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 72, 201, 77, 81, 160, 103, 70, 18, 102, 204, 82, 192, 86, 157, 40, 30, 117, 218, 224, 202, 1, 36, 249, 88, 82, 210, 19, 156, 112, 31, 13, 117, 227, 0, 125, 221, 190, 165, 16, 193, 163, 161, 175, 33, 37, 184, 235, 62, 201, 93, 102, 185, 143, 54, 146, 114, 30, 253, 178, 245, 87, 38, 191, 214, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 255, 252, 132, 83, 48, 40, 74, 128, 160, 185, 20, 138, 2, 210, 15, 71, 144, 89, 167, 94, 155, 148, 118, 170, 157, 122, 70, 70, 114, 50, 221, 231, 8, 132, 167, 115, 239, 44, 245, 41, 226, 192, 192})
|
||||
return err
|
||||
}
|
||||
|
||||
func TestNegativeBlockChainReorg(t *testing.T) {
|
||||
// We are resetting the database between creation so we need to cache our information
|
||||
testManager2 := NewTestManager()
|
||||
testManager2.CreateChain2()
|
||||
tm2Blocks := testManager2.Blocks
|
||||
|
||||
testManager := NewTestManager()
|
||||
testManager.CreateChain1()
|
||||
oldState := testManager.BlockChain().CurrentBlock.State()
|
||||
|
||||
if testManager.BlockChain().FindCanonicalChain(tm2Blocks, testManager.BlockChain().GenesisBlock().Hash()) != true {
|
||||
t.Error("I expected TestManager to have the longest chain, but it was TestManager2 instead.")
|
||||
}
|
||||
if testManager.BlockChain().CurrentBlock.State() != oldState {
|
||||
t.Error("I expected the top state to be the same as it was as before the reorg")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPositiveBlockChainReorg(t *testing.T) {
|
||||
testManager := NewTestManager()
|
||||
testManager.CreateChain1()
|
||||
tm1Blocks := testManager.Blocks
|
||||
|
||||
testManager2 := NewTestManager()
|
||||
testManager2.CreateChain2()
|
||||
oldState := testManager2.BlockChain().CurrentBlock.State()
|
||||
|
||||
if testManager2.BlockChain().FindCanonicalChain(tm1Blocks, testManager.BlockChain().GenesisBlock().Hash()) == true {
|
||||
t.Error("I expected TestManager to have the longest chain, but it was TestManager2 instead.")
|
||||
}
|
||||
if testManager2.BlockChain().CurrentBlock.State() == oldState {
|
||||
t.Error("I expected the top state to have been modified but it was not")
|
||||
}
|
||||
}
|
@ -7,33 +7,39 @@ import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type Callee interface {
|
||||
ReturnGas(*big.Int, *State)
|
||||
type ClosureRef interface {
|
||||
ReturnGas(*big.Int, *big.Int, *State)
|
||||
Address() []byte
|
||||
}
|
||||
|
||||
type ClosureBody interface {
|
||||
Callee
|
||||
ethutil.RlpEncodable
|
||||
GetMem(*big.Int) *ethutil.Value
|
||||
SetMem(*big.Int, *ethutil.Value)
|
||||
N() *big.Int
|
||||
}
|
||||
|
||||
// Basic inline closure object which implement the 'closure' interface
|
||||
type Closure struct {
|
||||
callee Callee
|
||||
object ClosureBody
|
||||
callee *StateObject
|
||||
object *StateObject
|
||||
Script []byte
|
||||
State *State
|
||||
|
||||
Gas *big.Int
|
||||
Price *big.Int
|
||||
Value *big.Int
|
||||
|
||||
Args []byte
|
||||
}
|
||||
|
||||
// Create a new closure for the given data items
|
||||
func NewClosure(callee Callee, object ClosureBody, state *State, gas, val *big.Int) *Closure {
|
||||
return &Closure{callee, object, state, gas, val, nil}
|
||||
func NewClosure(callee, object *StateObject, script []byte, state *State, gas, price, val *big.Int) *Closure {
|
||||
c := &Closure{callee: callee, object: object, Script: script, State: state, Args: nil}
|
||||
|
||||
// In most cases gas, price and value are pointers to transaction objects
|
||||
// and we don't want the transaction's values to change.
|
||||
c.Gas = new(big.Int).Set(gas)
|
||||
c.Price = new(big.Int).Set(price)
|
||||
c.Value = new(big.Int).Set(val)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Retuns the x element in data slice
|
||||
@ -46,6 +52,20 @@ func (c *Closure) GetMem(x *big.Int) *ethutil.Value {
|
||||
return m
|
||||
}
|
||||
|
||||
func (c *Closure) Get(x *big.Int) *ethutil.Value {
|
||||
return c.Gets(x, big.NewInt(1))
|
||||
}
|
||||
|
||||
func (c *Closure) Gets(x, y *big.Int) *ethutil.Value {
|
||||
if x.Int64() >= int64(len(c.Script)) || y.Int64() >= int64(len(c.Script)) {
|
||||
return ethutil.NewValue(0)
|
||||
}
|
||||
|
||||
partial := c.Script[x.Int64() : x.Int64()+y.Int64()]
|
||||
|
||||
return ethutil.NewValue(partial)
|
||||
}
|
||||
|
||||
func (c *Closure) SetMem(x *big.Int, val *ethutil.Value) {
|
||||
c.object.SetMem(x, val)
|
||||
}
|
||||
@ -54,10 +74,12 @@ func (c *Closure) Address() []byte {
|
||||
return c.object.Address()
|
||||
}
|
||||
|
||||
func (c *Closure) Call(vm *Vm, args []byte) []byte {
|
||||
type DebugHook func(step int, op OpCode, mem *Memory, stack *Stack)
|
||||
|
||||
func (c *Closure) Call(vm *Vm, args []byte, hook DebugHook) ([]byte, error) {
|
||||
c.Args = args
|
||||
|
||||
return vm.RunClosure(c)
|
||||
return vm.RunClosure(c, hook)
|
||||
}
|
||||
|
||||
func (c *Closure) Return(ret []byte) []byte {
|
||||
@ -65,26 +87,28 @@ func (c *Closure) Return(ret []byte) []byte {
|
||||
// If no callee is present return it to
|
||||
// the origin (i.e. contract or tx)
|
||||
if c.callee != nil {
|
||||
c.callee.ReturnGas(c.Gas, c.State)
|
||||
c.callee.ReturnGas(c.Gas, c.Price, c.State)
|
||||
} else {
|
||||
c.object.ReturnGas(c.Gas, c.State)
|
||||
// TODO incase it's a POST contract we gotta serialise the contract again.
|
||||
// But it's not yet defined
|
||||
c.object.ReturnGas(c.Gas, c.Price, c.State)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Implement the Callee interface
|
||||
func (c *Closure) ReturnGas(gas *big.Int, state *State) {
|
||||
func (c *Closure) ReturnGas(gas, price *big.Int, state *State) {
|
||||
// Return the gas to the closure
|
||||
c.Gas.Add(c.Gas, gas)
|
||||
}
|
||||
|
||||
func (c *Closure) Object() ClosureBody {
|
||||
func (c *Closure) Object() *StateObject {
|
||||
return c.object
|
||||
}
|
||||
|
||||
func (c *Closure) Callee() Callee {
|
||||
func (c *Closure) Callee() *StateObject {
|
||||
return c.callee
|
||||
}
|
||||
|
||||
func (c *Closure) N() *big.Int {
|
||||
return c.object.N()
|
||||
}
|
||||
|
@ -1,94 +0,0 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type Contract struct {
|
||||
Amount *big.Int
|
||||
Nonce uint64
|
||||
//state *ethutil.Trie
|
||||
state *State
|
||||
address []byte
|
||||
}
|
||||
|
||||
func NewContract(address []byte, Amount *big.Int, root []byte) *Contract {
|
||||
contract := &Contract{address: address, Amount: Amount, Nonce: 0}
|
||||
contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root)))
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
func NewContractFromBytes(address, data []byte) *Contract {
|
||||
contract := &Contract{address: address}
|
||||
contract.RlpDecode(data)
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
func (c *Contract) Addr(addr []byte) *ethutil.Value {
|
||||
return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr))))
|
||||
}
|
||||
|
||||
func (c *Contract) SetAddr(addr []byte, value interface{}) {
|
||||
c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode()))
|
||||
}
|
||||
|
||||
func (c *Contract) State() *State {
|
||||
return c.state
|
||||
}
|
||||
|
||||
func (c *Contract) GetMem(num *big.Int) *ethutil.Value {
|
||||
nb := ethutil.BigToBytes(num, 256)
|
||||
|
||||
return c.Addr(nb)
|
||||
}
|
||||
|
||||
func (c *Contract) SetMem(num *big.Int, val *ethutil.Value) {
|
||||
addr := ethutil.BigToBytes(num, 256)
|
||||
c.state.trie.Update(string(addr), string(val.Encode()))
|
||||
}
|
||||
|
||||
// Return the gas back to the origin. Used by the Virtual machine or Closures
|
||||
func (c *Contract) ReturnGas(val *big.Int, state *State) {
|
||||
c.Amount.Add(c.Amount, val)
|
||||
}
|
||||
|
||||
func (c *Contract) Address() []byte {
|
||||
return c.address
|
||||
}
|
||||
|
||||
func (c *Contract) RlpEncode() []byte {
|
||||
return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.trie.Root})
|
||||
}
|
||||
|
||||
func (c *Contract) RlpDecode(data []byte) {
|
||||
decoder := ethutil.NewValueFromBytes(data)
|
||||
|
||||
c.Amount = decoder.Get(0).BigInt()
|
||||
c.Nonce = decoder.Get(1).Uint()
|
||||
c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
|
||||
}
|
||||
|
||||
func MakeContract(tx *Transaction, state *State) *Contract {
|
||||
// Create contract if there's no recipient
|
||||
if tx.IsContract() {
|
||||
addr := tx.Hash()[12:]
|
||||
|
||||
value := tx.Value
|
||||
contract := NewContract(addr, value, []byte(""))
|
||||
state.trie.Update(string(addr), string(contract.RlpEncode()))
|
||||
for i, val := range tx.Data {
|
||||
if len(val) > 0 {
|
||||
bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256)
|
||||
contract.state.trie.Update(string(bytNum), string(ethutil.Encode(val)))
|
||||
}
|
||||
}
|
||||
state.trie.Update(string(addr), string(contract.RlpEncode()))
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
type PoW interface {
|
||||
Search(block *Block) []byte
|
||||
Search(block *Block, reactChan chan ethutil.React) []byte
|
||||
Verify(hash []byte, diff *big.Int, nonce []byte) bool
|
||||
}
|
||||
|
||||
@ -19,15 +19,30 @@ type EasyPow struct {
|
||||
hash *big.Int
|
||||
}
|
||||
|
||||
func (pow *EasyPow) Search(block *Block) []byte {
|
||||
func (pow *EasyPow) Search(block *Block, reactChan chan ethutil.React) []byte {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
hash := block.HashNoNonce()
|
||||
diff := block.Difficulty
|
||||
i := int64(0)
|
||||
start := time.Now().UnixNano()
|
||||
|
||||
for {
|
||||
sha := ethutil.Sha3Bin(big.NewInt(r.Int63()).Bytes())
|
||||
if pow.Verify(hash, diff, sha) {
|
||||
return sha
|
||||
select {
|
||||
case <-reactChan:
|
||||
log.Println("[POW] Received reactor event; breaking out.")
|
||||
return nil
|
||||
default:
|
||||
i++
|
||||
if i%1234567 == 0 {
|
||||
elapsed := time.Now().UnixNano() - start
|
||||
hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000
|
||||
log.Println("[POW] Hashing @", int64(hashes), "khash")
|
||||
}
|
||||
|
||||
sha := ethutil.Sha3Bin(big.NewInt(r.Int63()).Bytes())
|
||||
if pow.Verify(hash, diff, sha) {
|
||||
return sha
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,9 +113,9 @@ func (dag *Dagger) Search(hash, diff *big.Int) *big.Int {
|
||||
|
||||
for k := 0; k < amountOfRoutines; k++ {
|
||||
go dag.Find(obj, resChan)
|
||||
}
|
||||
|
||||
// Wait for each go routine to finish
|
||||
// Wait for each go routine to finish
|
||||
}
|
||||
for k := 0; k < amountOfRoutines; k++ {
|
||||
// Get the result from the channel. 0 = quit
|
||||
if r := <-resChan; r != 0 {
|
||||
|
@ -1,6 +1,8 @@
|
||||
package ethchain
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Parent error. In case a parent is unknown this error will be thrown
|
||||
// by the block manager
|
||||
@ -40,3 +42,22 @@ func IsValidationErr(err error) bool {
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
type NonceErr struct {
|
||||
Message string
|
||||
Is, Exp uint64
|
||||
}
|
||||
|
||||
func (err *NonceErr) Error() string {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
func NonceError(is, exp uint64) *NonceErr {
|
||||
return &NonceErr{Message: fmt.Sprintf("Nonce err. Is %d, expected %d", is, exp), Is: is, Exp: exp}
|
||||
}
|
||||
|
||||
func IsNonceErr(err error) bool {
|
||||
_, ok := err.(*NonceErr)
|
||||
|
||||
return ok
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package ethchain
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/obscuren/secp256k1-go"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
@ -10,10 +11,19 @@ type KeyPair struct {
|
||||
PublicKey []byte
|
||||
|
||||
// The associated account
|
||||
account *Account
|
||||
account *StateObject
|
||||
state *State
|
||||
}
|
||||
|
||||
func NewKeyPairFromSec(seckey []byte) (*KeyPair, error) {
|
||||
pubkey, err := secp256k1.GeneratePubKey(seckey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &KeyPair{PrivateKey: seckey, PublicKey: pubkey}, nil
|
||||
}
|
||||
|
||||
func NewKeyPairFromValue(val *ethutil.Value) *KeyPair {
|
||||
keyPair := &KeyPair{PrivateKey: val.Get(0).Bytes(), PublicKey: val.Get(1).Bytes()}
|
||||
|
||||
@ -24,7 +34,7 @@ func (k *KeyPair) Address() []byte {
|
||||
return ethutil.Sha3Bin(k.PublicKey[1:])[12:]
|
||||
}
|
||||
|
||||
func (k *KeyPair) Account() *Account {
|
||||
func (k *KeyPair) Account() *StateObject {
|
||||
if k.account == nil {
|
||||
k.account = k.state.GetAccount(k.Address())
|
||||
}
|
||||
@ -34,6 +44,7 @@ func (k *KeyPair) Account() *Account {
|
||||
|
||||
// Create transaction, creates a new and signed transaction, ready for processing
|
||||
func (k *KeyPair) CreateTx(receiver []byte, value *big.Int, data []string) *Transaction {
|
||||
/* TODO
|
||||
tx := NewTransaction(receiver, value, data)
|
||||
tx.Nonce = k.account.Nonce
|
||||
|
||||
@ -41,6 +52,8 @@ func (k *KeyPair) CreateTx(receiver []byte, value *big.Int, data []string) *Tran
|
||||
tx.Sign(k.PrivateKey)
|
||||
|
||||
return tx
|
||||
*/
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *KeyPair) RlpEncode() []byte {
|
||||
|
@ -6,152 +6,6 @@ import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type OpCode int
|
||||
|
||||
// Op codes
|
||||
const (
|
||||
// 0x0 range - arithmetic ops
|
||||
oSTOP = 0x00
|
||||
oADD = 0x01
|
||||
oMUL = 0x02
|
||||
oSUB = 0x03
|
||||
oDIV = 0x04
|
||||
oSDIV = 0x05
|
||||
oMOD = 0x06
|
||||
oSMOD = 0x07
|
||||
oEXP = 0x08
|
||||
oNEG = 0x09
|
||||
oLT = 0x0a
|
||||
oGT = 0x0b
|
||||
oEQ = 0x0c
|
||||
oNOT = 0x0d
|
||||
|
||||
// 0x10 range - bit ops
|
||||
oAND = 0x10
|
||||
oOR = 0x11
|
||||
oXOR = 0x12
|
||||
oBYTE = 0x13
|
||||
|
||||
// 0x20 range - crypto
|
||||
oSHA3 = 0x20
|
||||
|
||||
// 0x30 range - closure state
|
||||
oADDRESS = 0x30
|
||||
oBALANCE = 0x31
|
||||
oORIGIN = 0x32
|
||||
oCALLER = 0x33
|
||||
oCALLVALUE = 0x34
|
||||
oCALLDATA = 0x35
|
||||
oCALLDATASIZE = 0x36
|
||||
oGASPRICE = 0x37
|
||||
|
||||
// 0x40 range - block operations
|
||||
oPREVHASH = 0x40
|
||||
oCOINBASE = 0x41
|
||||
oTIMESTAMP = 0x42
|
||||
oNUMBER = 0x43
|
||||
oDIFFICULTY = 0x44
|
||||
oGASLIMIT = 0x45
|
||||
|
||||
// 0x50 range - 'storage' and execution
|
||||
oPUSH = 0x50
|
||||
oPOP = 0x51
|
||||
oDUP = 0x52
|
||||
oSWAP = 0x53
|
||||
oMLOAD = 0x54
|
||||
oMSTORE = 0x55
|
||||
oMSTORE8 = 0x56
|
||||
oSLOAD = 0x57
|
||||
oSSTORE = 0x58
|
||||
oJUMP = 0x59
|
||||
oJUMPI = 0x5a
|
||||
oPC = 0x5b
|
||||
oMSIZE = 0x5c
|
||||
|
||||
// 0x60 range - closures
|
||||
oCREATE = 0x60
|
||||
oCALL = 0x61
|
||||
oRETURN = 0x62
|
||||
|
||||
// 0x70 range - other
|
||||
oLOG = 0x70 // XXX Unofficial
|
||||
oSUICIDE = 0x7f
|
||||
)
|
||||
|
||||
// Since the opcodes aren't all in order we can't use a regular slice
|
||||
var opCodeToString = map[OpCode]string{
|
||||
// 0x0 range - arithmetic ops
|
||||
oSTOP: "STOP",
|
||||
oADD: "ADD",
|
||||
oMUL: "MUL",
|
||||
oSUB: "SUB",
|
||||
oDIV: "DIV",
|
||||
oSDIV: "SDIV",
|
||||
oMOD: "MOD",
|
||||
oSMOD: "SMOD",
|
||||
oEXP: "EXP",
|
||||
oNEG: "NEG",
|
||||
oLT: "LT",
|
||||
oGT: "GT",
|
||||
oEQ: "EQ",
|
||||
oNOT: "NOT",
|
||||
|
||||
// 0x10 range - bit ops
|
||||
oAND: "AND",
|
||||
oOR: "OR",
|
||||
oXOR: "XOR",
|
||||
oBYTE: "BYTE",
|
||||
|
||||
// 0x20 range - crypto
|
||||
oSHA3: "SHA3",
|
||||
|
||||
// 0x30 range - closure state
|
||||
oADDRESS: "ADDRESS",
|
||||
oBALANCE: "BALANCE",
|
||||
oORIGIN: "ORIGIN",
|
||||
oCALLER: "CALLER",
|
||||
oCALLVALUE: "CALLVALUE",
|
||||
oCALLDATA: "CALLDATA",
|
||||
oCALLDATASIZE: "CALLDATASIZE",
|
||||
oGASPRICE: "TXGASPRICE",
|
||||
|
||||
// 0x40 range - block operations
|
||||
oPREVHASH: "PREVHASH",
|
||||
oCOINBASE: "COINBASE",
|
||||
oTIMESTAMP: "TIMESTAMP",
|
||||
oNUMBER: "NUMBER",
|
||||
oDIFFICULTY: "DIFFICULTY",
|
||||
oGASLIMIT: "GASLIMIT",
|
||||
|
||||
// 0x50 range - 'storage' and execution
|
||||
oPUSH: "PUSH",
|
||||
oPOP: "POP",
|
||||
oDUP: "DUP",
|
||||
oSWAP: "SWAP",
|
||||
oMLOAD: "MLOAD",
|
||||
oMSTORE: "MSTORE",
|
||||
oMSTORE8: "MSTORE8",
|
||||
oSLOAD: "SLOAD",
|
||||
oSSTORE: "SSTORE",
|
||||
oJUMP: "JUMP",
|
||||
oJUMPI: "JUMPI",
|
||||
oPC: "PC",
|
||||
oMSIZE: "MSIZE",
|
||||
|
||||
// 0x60 range - closures
|
||||
oCREATE: "CREATE",
|
||||
oCALL: "CALL",
|
||||
oRETURN: "RETURN",
|
||||
|
||||
// 0x70 range - other
|
||||
oLOG: "LOG",
|
||||
oSUICIDE: "SUICIDE",
|
||||
}
|
||||
|
||||
func (o OpCode) String() string {
|
||||
return opCodeToString[o]
|
||||
}
|
||||
|
||||
type OpType int
|
||||
|
||||
const (
|
||||
@ -172,22 +26,34 @@ func NewStack() *Stack {
|
||||
return &Stack{}
|
||||
}
|
||||
|
||||
func (st *Stack) Data() []*big.Int {
|
||||
return st.data
|
||||
}
|
||||
|
||||
func (st *Stack) Len() int {
|
||||
return len(st.data)
|
||||
}
|
||||
|
||||
func (st *Stack) Pop() *big.Int {
|
||||
str := st.data[0]
|
||||
st.data = st.data[1:]
|
||||
str := st.data[len(st.data)-1]
|
||||
|
||||
copy(st.data[:len(st.data)-1], st.data[:len(st.data)-1])
|
||||
st.data = st.data[:len(st.data)-1]
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func (st *Stack) Popn() (*big.Int, *big.Int) {
|
||||
ints := st.data[:2]
|
||||
st.data = st.data[2:]
|
||||
ints := st.data[len(st.data)-2:]
|
||||
|
||||
copy(st.data[:len(st.data)-2], st.data[:len(st.data)-2])
|
||||
st.data = st.data[:len(st.data)-2]
|
||||
|
||||
return ints[0], ints[1]
|
||||
}
|
||||
|
||||
func (st *Stack) Peek() *big.Int {
|
||||
str := st.data[0]
|
||||
str := st.data[len(st.data)-1]
|
||||
|
||||
return str
|
||||
}
|
||||
@ -201,8 +67,20 @@ func (st *Stack) Peekn() (*big.Int, *big.Int) {
|
||||
func (st *Stack) Push(d *big.Int) {
|
||||
st.data = append(st.data, d)
|
||||
}
|
||||
|
||||
func (st *Stack) Get(amount *big.Int) []*big.Int {
|
||||
// offset + size <= len(data)
|
||||
length := big.NewInt(int64(len(st.data)))
|
||||
if amount.Cmp(length) <= 0 {
|
||||
start := new(big.Int).Sub(length, amount)
|
||||
return st.data[start.Int64():length.Int64()]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *Stack) Print() {
|
||||
fmt.Println("### STACK ###")
|
||||
fmt.Println("### stack ###")
|
||||
if len(st.data) > 0 {
|
||||
for i, val := range st.data {
|
||||
fmt.Printf("%-3d %v\n", i, val)
|
||||
@ -241,16 +119,20 @@ func (m *Memory) Len() int {
|
||||
return len(m.store)
|
||||
}
|
||||
|
||||
func (m *Memory) Data() []byte {
|
||||
return m.store
|
||||
}
|
||||
|
||||
func (m *Memory) Print() {
|
||||
fmt.Println("### MEM ###")
|
||||
fmt.Printf("### mem %d bytes ###\n", len(m.store))
|
||||
if len(m.store) > 0 {
|
||||
addr := 0
|
||||
for i := 0; i+32 < len(m.store); i += 32 {
|
||||
fmt.Printf("%03d %v\n", addr, m.store[i:i+32])
|
||||
for i := 0; i+32 <= len(m.store); i += 32 {
|
||||
fmt.Printf("%03d: % x\n", addr, m.store[i:i+32])
|
||||
addr++
|
||||
}
|
||||
} else {
|
||||
fmt.Println("-- empty --")
|
||||
}
|
||||
fmt.Println("###########")
|
||||
fmt.Println("####################")
|
||||
}
|
||||
|
@ -34,12 +34,12 @@ func (s *State) Reset() {
|
||||
|
||||
// Syncs the trie and all siblings
|
||||
func (s *State) Sync() {
|
||||
s.trie.Sync()
|
||||
|
||||
// Sync all nested states
|
||||
for _, state := range s.states {
|
||||
state.Sync()
|
||||
}
|
||||
|
||||
s.trie.Sync()
|
||||
}
|
||||
|
||||
// Purges the current trie.
|
||||
@ -47,23 +47,15 @@ func (s *State) Purge() int {
|
||||
return s.trie.NewIterator().Purge()
|
||||
}
|
||||
|
||||
func (s *State) GetContract(addr []byte) *Contract {
|
||||
// XXX Deprecated
|
||||
func (s *State) GetContract(addr []byte) *StateObject {
|
||||
data := s.trie.Get(string(addr))
|
||||
if data == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Whet get contract is called the retrieved value might
|
||||
// be an account. The StateManager uses this to check
|
||||
// to see if the address a tx was sent to is a contract
|
||||
// or an account
|
||||
value := ethutil.NewValueFromBytes([]byte(data))
|
||||
if value.Len() == 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// build contract
|
||||
contract := NewContractFromBytes(addr, []byte(data))
|
||||
contract := NewStateObjectFromBytes(addr, []byte(data))
|
||||
|
||||
// Check if there's a cached state for this contract
|
||||
cachedState := s.states[string(addr)]
|
||||
@ -77,28 +69,43 @@ func (s *State) GetContract(addr []byte) *Contract {
|
||||
return contract
|
||||
}
|
||||
|
||||
func (s *State) UpdateContract(contract *Contract) {
|
||||
addr := contract.Address()
|
||||
func (s *State) GetStateObject(addr []byte) *StateObject {
|
||||
data := s.trie.Get(string(addr))
|
||||
if data == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.states[string(addr)] = contract.state
|
||||
s.trie.Update(string(addr), string(contract.RlpEncode()))
|
||||
stateObject := NewStateObjectFromBytes(addr, []byte(data))
|
||||
|
||||
// Check if there's a cached state for this contract
|
||||
cachedStateObject := s.states[string(addr)]
|
||||
if cachedStateObject != nil {
|
||||
stateObject.state = cachedStateObject
|
||||
} else {
|
||||
// If it isn't cached, cache the state
|
||||
s.states[string(addr)] = stateObject.state
|
||||
}
|
||||
|
||||
return stateObject
|
||||
}
|
||||
|
||||
func (s *State) GetAccount(addr []byte) (account *Account) {
|
||||
func (s *State) SetStateObject(stateObject *StateObject) {
|
||||
s.states[string(stateObject.address)] = stateObject.state
|
||||
|
||||
s.UpdateStateObject(stateObject)
|
||||
}
|
||||
|
||||
func (s *State) GetAccount(addr []byte) (account *StateObject) {
|
||||
data := s.trie.Get(string(addr))
|
||||
if data == "" {
|
||||
account = NewAccount(addr, big.NewInt(0))
|
||||
} else {
|
||||
account = NewAccountFromData(addr, []byte(data))
|
||||
account = NewStateObjectFromBytes(addr, []byte(data))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *State) UpdateAccount(addr []byte, account *Account) {
|
||||
s.trie.Update(string(addr), string(account.RlpEncode()))
|
||||
}
|
||||
|
||||
func (s *State) Cmp(other *State) bool {
|
||||
return s.trie.Cmp(other.trie)
|
||||
}
|
||||
@ -117,9 +124,10 @@ const (
|
||||
UnknownTy
|
||||
)
|
||||
|
||||
/*
|
||||
// Returns the object stored at key and the type stored at key
|
||||
// Returns nil if nothing is stored
|
||||
func (s *State) Get(key []byte) (*ethutil.Value, ObjType) {
|
||||
func (s *State) GetStateObject(key []byte) (*ethutil.Value, ObjType) {
|
||||
// Fetch data from the trie
|
||||
data := s.trie.Get(string(key))
|
||||
// Returns the nil type, indicating nothing could be retrieved.
|
||||
@ -144,6 +152,18 @@ func (s *State) Get(key []byte) (*ethutil.Value, ObjType) {
|
||||
|
||||
return val, typ
|
||||
}
|
||||
*/
|
||||
|
||||
// Updates any given state object
|
||||
func (s *State) UpdateStateObject(object *StateObject) {
|
||||
addr := object.Address()
|
||||
|
||||
if object.state != nil {
|
||||
s.states[string(addr)] = object.state
|
||||
}
|
||||
|
||||
s.trie.Update(string(addr), string(object.RlpEncode()))
|
||||
}
|
||||
|
||||
func (s *State) Put(key, object []byte) {
|
||||
s.trie.Update(string(key), string(object))
|
||||
@ -152,27 +172,3 @@ func (s *State) Put(key, object []byte) {
|
||||
func (s *State) Root() interface{} {
|
||||
return s.trie.Root
|
||||
}
|
||||
|
||||
// Script compilation functions
|
||||
// Compiles strings to machine code
|
||||
func Compile(code []string) (script []string) {
|
||||
script = make([]string, len(code))
|
||||
for i, val := range code {
|
||||
instr, _ := ethutil.CompileInstr(val)
|
||||
|
||||
script[i] = string(instr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func CompileToValues(code []string) (script []*ethutil.Value) {
|
||||
script = make([]*ethutil.Value, len(code))
|
||||
for i, val := range code {
|
||||
instr, _ := ethutil.CompileInstr(val)
|
||||
|
||||
script[i] = ethutil.NewValue(instr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ type EthManager interface {
|
||||
BlockChain() *BlockChain
|
||||
TxPool() *TxPool
|
||||
Broadcast(msgType ethwire.MsgType, data []interface{})
|
||||
Reactor() *ethutil.ReactorEngine
|
||||
}
|
||||
|
||||
type StateManager struct {
|
||||
@ -29,7 +30,7 @@ type StateManager struct {
|
||||
bc *BlockChain
|
||||
// States for addresses. You can watch any address
|
||||
// at any given time
|
||||
addrStateStore *AddrStateStore
|
||||
stateObjectCache *StateObjectCache
|
||||
|
||||
// Stack for processing contracts
|
||||
stack *Stack
|
||||
@ -50,20 +51,20 @@ type StateManager struct {
|
||||
// results
|
||||
compState *State
|
||||
|
||||
miningState *State
|
||||
manifest *Manifest
|
||||
}
|
||||
|
||||
func NewStateManager(ethereum EthManager) *StateManager {
|
||||
sm := &StateManager{
|
||||
stack: NewStack(),
|
||||
mem: make(map[string]*big.Int),
|
||||
Pow: &EasyPow{},
|
||||
Ethereum: ethereum,
|
||||
addrStateStore: NewAddrStateStore(),
|
||||
bc: ethereum.BlockChain(),
|
||||
stack: NewStack(),
|
||||
mem: make(map[string]*big.Int),
|
||||
Pow: &EasyPow{},
|
||||
Ethereum: ethereum,
|
||||
stateObjectCache: NewStateObjectCache(),
|
||||
bc: ethereum.BlockChain(),
|
||||
manifest: NewManifest(),
|
||||
}
|
||||
sm.procState = ethereum.BlockChain().CurrentBlock.State()
|
||||
|
||||
return sm
|
||||
}
|
||||
|
||||
@ -72,18 +73,18 @@ func (sm *StateManager) ProcState() *State {
|
||||
}
|
||||
|
||||
// Watches any given address and puts it in the address state store
|
||||
func (sm *StateManager) WatchAddr(addr []byte) *AccountState {
|
||||
func (sm *StateManager) WatchAddr(addr []byte) *CachedStateObject {
|
||||
//XXX account := sm.bc.CurrentBlock.state.GetAccount(addr)
|
||||
account := sm.procState.GetAccount(addr)
|
||||
|
||||
return sm.addrStateStore.Add(addr, account)
|
||||
return sm.stateObjectCache.Add(addr, account)
|
||||
}
|
||||
|
||||
func (sm *StateManager) GetAddrState(addr []byte) *AccountState {
|
||||
account := sm.addrStateStore.Get(addr)
|
||||
func (sm *StateManager) GetAddrState(addr []byte) *CachedStateObject {
|
||||
account := sm.stateObjectCache.Get(addr)
|
||||
if account == nil {
|
||||
a := sm.bc.CurrentBlock.state.GetAccount(addr)
|
||||
account = &AccountState{Nonce: a.Nonce, Account: a}
|
||||
a := sm.procState.GetAccount(addr)
|
||||
account = &CachedStateObject{Nonce: a.Nonce, Object: a}
|
||||
}
|
||||
|
||||
return account
|
||||
@ -93,29 +94,44 @@ func (sm *StateManager) BlockChain() *BlockChain {
|
||||
return sm.bc
|
||||
}
|
||||
|
||||
func (sm *StateManager) MakeContract(tx *Transaction) {
|
||||
func (sm *StateManager) MakeContract(tx *Transaction) *StateObject {
|
||||
contract := MakeContract(tx, sm.procState)
|
||||
if contract != nil {
|
||||
sm.procState.states[string(tx.Hash()[12:])] = contract.state
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply transactions uses the transaction passed to it and applies them onto
|
||||
// the current processing state.
|
||||
func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) {
|
||||
// Process each transaction/contract
|
||||
for _, tx := range txs {
|
||||
// If there's no recipient, it's a contract
|
||||
// Check if this is a contract creation traction and if so
|
||||
// create a contract of this tx.
|
||||
if tx.IsContract() {
|
||||
sm.MakeContract(tx)
|
||||
//XXX block.MakeContract(tx)
|
||||
} else {
|
||||
if contract := sm.procState.GetContract(tx.Recipient); contract != nil {
|
||||
//XXX if contract := block.state.GetContract(tx.Recipient); contract != nil {
|
||||
sm.ProcessContract(contract, tx, block)
|
||||
} else {
|
||||
err := sm.Ethereum.TxPool().ProcessTransaction(tx, block)
|
||||
if err != nil {
|
||||
ethutil.Config.Log.Infoln("[STATE]", err)
|
||||
err := sm.Ethereum.TxPool().ProcessTransaction(tx, block, false)
|
||||
if err == nil {
|
||||
contract := sm.MakeContract(tx)
|
||||
if contract != nil {
|
||||
sm.EvalScript(contract.Init(), contract, tx, block)
|
||||
} else {
|
||||
ethutil.Config.Log.Infoln("[STATE] Unable to create contract")
|
||||
}
|
||||
} else {
|
||||
ethutil.Config.Log.Infoln("[STATE] contract create:", err)
|
||||
}
|
||||
} else {
|
||||
err := sm.Ethereum.TxPool().ProcessTransaction(tx, block, false)
|
||||
contract := sm.procState.GetContract(tx.Recipient)
|
||||
if err == nil && len(contract.Script()) > 0 {
|
||||
sm.EvalScript(contract.Script(), contract, tx, block)
|
||||
} else if err != nil {
|
||||
ethutil.Config.Log.Infoln("[STATE] process:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,9 +139,9 @@ func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) {
|
||||
|
||||
// The prepare function, prepares the state manager for the next
|
||||
// "ProcessBlock" action.
|
||||
func (sm *StateManager) Prepare(processer *State, comparative *State) {
|
||||
func (sm *StateManager) Prepare(processor *State, comparative *State) {
|
||||
sm.compState = comparative
|
||||
sm.procState = processer
|
||||
sm.procState = processor
|
||||
}
|
||||
|
||||
// Default prepare function
|
||||
@ -134,22 +150,23 @@ func (sm *StateManager) PrepareDefault(block *Block) {
|
||||
}
|
||||
|
||||
// Block processing and validating with a given (temporarily) state
|
||||
func (sm *StateManager) ProcessBlock(block *Block) error {
|
||||
func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error {
|
||||
// Processing a blocks may never happen simultaneously
|
||||
sm.mutex.Lock()
|
||||
defer sm.mutex.Unlock()
|
||||
hash := block.Hash()
|
||||
|
||||
if sm.bc.HasBlock(hash) {
|
||||
//fmt.Println("[STATE] We already have this block, ignoring")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Defer the Undo on the Trie. If the block processing happened
|
||||
// we don't want to undo but since undo only happens on dirty
|
||||
// nodes this won't happen because Commit would have been called
|
||||
// before that.
|
||||
defer sm.bc.CurrentBlock.Undo()
|
||||
|
||||
hash := block.Hash()
|
||||
|
||||
if sm.bc.HasBlock(hash) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if we have the parent hash, if it isn't known we discard it
|
||||
// Reasons might be catching up or simply an invalid block
|
||||
if !sm.bc.HasBlock(block.PrevHash) && sm.bc.CurrentBlock != nil {
|
||||
@ -161,30 +178,26 @@ func (sm *StateManager) ProcessBlock(block *Block) error {
|
||||
|
||||
// Block validation
|
||||
if err := sm.ValidateBlock(block); err != nil {
|
||||
fmt.Println("[SM] Error validating block:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// I'm not sure, but I don't know if there should be thrown
|
||||
// any errors at this time.
|
||||
if err := sm.AccumelateRewards(block); err != nil {
|
||||
fmt.Println("[SM] Error accumulating reward", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// if !sm.compState.Cmp(sm.procState)
|
||||
if !sm.compState.Cmp(sm.procState) {
|
||||
//XXX return fmt.Errorf("Invalid merkle root. Expected %x, got %x", block.State().trie.Root, sm.bc.CurrentBlock.State().trie.Root)
|
||||
return fmt.Errorf("Invalid merkle root. Expected %x, got %x", sm.compState.trie.Root, sm.procState.trie.Root)
|
||||
}
|
||||
|
||||
// Calculate the new total difficulty and sync back to the db
|
||||
if sm.CalculateTD(block) {
|
||||
// Sync the current block's state to the database and cancelling out the deferred Undo
|
||||
//XXX sm.bc.CurrentBlock.Sync()
|
||||
sm.procState.Sync()
|
||||
|
||||
// Broadcast the valid block back to the wire
|
||||
//sm.Ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val})
|
||||
|
||||
// Add the block to the chain
|
||||
sm.bc.Add(block)
|
||||
|
||||
@ -195,13 +208,19 @@ func (sm *StateManager) ProcessBlock(block *Block) error {
|
||||
}
|
||||
|
||||
ethutil.Config.Log.Infof("[STATE] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash())
|
||||
if dontReact == false {
|
||||
sm.Ethereum.Reactor().Post("newBlock", block)
|
||||
|
||||
sm.notifyChanges()
|
||||
|
||||
sm.manifest.Reset()
|
||||
}
|
||||
} else {
|
||||
fmt.Println("total diff failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) CalculateTD(block *Block) bool {
|
||||
uncleDiff := new(big.Int)
|
||||
for _, uncle := range block.Uncles {
|
||||
@ -272,21 +291,20 @@ func CalculateUncleReward(block *Block) *big.Int {
|
||||
}
|
||||
|
||||
func (sm *StateManager) AccumelateRewards(block *Block) error {
|
||||
// Get the coinbase rlp data
|
||||
//XXX addr := processor.state.GetAccount(block.Coinbase)
|
||||
addr := sm.procState.GetAccount(block.Coinbase)
|
||||
// Get the account associated with the coinbase
|
||||
account := sm.procState.GetAccount(block.Coinbase)
|
||||
// Reward amount of ether to the coinbase address
|
||||
addr.AddFee(CalculateBlockReward(block, len(block.Uncles)))
|
||||
account.AddAmount(CalculateBlockReward(block, len(block.Uncles)))
|
||||
|
||||
//XXX processor.state.UpdateAccount(block.Coinbase, addr)
|
||||
sm.procState.UpdateAccount(block.Coinbase, addr)
|
||||
addr := make([]byte, len(block.Coinbase))
|
||||
copy(addr, block.Coinbase)
|
||||
sm.procState.UpdateStateObject(account)
|
||||
|
||||
for _, uncle := range block.Uncles {
|
||||
uncleAddr := sm.procState.GetAccount(uncle.Coinbase)
|
||||
uncleAddr.AddFee(CalculateUncleReward(uncle))
|
||||
uncleAccount := sm.procState.GetAccount(uncle.Coinbase)
|
||||
uncleAccount.AddAmount(CalculateUncleReward(uncle))
|
||||
|
||||
//processor.state.UpdateAccount(uncle.Coinbase, uncleAddr)
|
||||
sm.procState.UpdateAccount(uncle.Coinbase, uncleAddr)
|
||||
sm.procState.UpdateStateObject(uncleAccount)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -296,26 +314,76 @@ func (sm *StateManager) Stop() {
|
||||
sm.bc.Stop()
|
||||
}
|
||||
|
||||
func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, block *Block) {
|
||||
// Recovering function in case the VM had any errors
|
||||
/*
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("Recovered from VM execution with err =", r)
|
||||
}
|
||||
}()
|
||||
*/
|
||||
caller := sm.procState.GetAccount(tx.Sender())
|
||||
closure := NewClosure(caller, contract, sm.procState, tx.Gas, tx.Value)
|
||||
vm := NewVm(sm.procState, RuntimeVars{
|
||||
origin: caller.Address(),
|
||||
blockNumber: block.BlockInfo().Number,
|
||||
prevHash: block.PrevHash,
|
||||
coinbase: block.Coinbase,
|
||||
time: block.Time,
|
||||
diff: block.Difficulty,
|
||||
// XXX Tx data? Could be just an argument to the closure instead
|
||||
txData: nil,
|
||||
func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Transaction, block *Block) {
|
||||
account := sm.procState.GetAccount(tx.Sender())
|
||||
|
||||
err := account.ConvertGas(tx.Gas, tx.GasPrice)
|
||||
if err != nil {
|
||||
ethutil.Config.Log.Debugln(err)
|
||||
return
|
||||
}
|
||||
|
||||
closure := NewClosure(account, object, script, sm.procState, tx.Gas, tx.GasPrice, tx.Value)
|
||||
vm := NewVm(sm.procState, sm, RuntimeVars{
|
||||
Origin: account.Address(),
|
||||
BlockNumber: block.BlockInfo().Number,
|
||||
PrevHash: block.PrevHash,
|
||||
Coinbase: block.Coinbase,
|
||||
Time: block.Time,
|
||||
Diff: block.Difficulty,
|
||||
//Price: tx.GasPrice,
|
||||
})
|
||||
closure.Call(vm, nil)
|
||||
closure.Call(vm, tx.Data, nil)
|
||||
|
||||
// Update the account (refunds)
|
||||
sm.procState.UpdateStateObject(account)
|
||||
sm.manifest.AddObjectChange(account)
|
||||
|
||||
sm.procState.UpdateStateObject(object)
|
||||
sm.manifest.AddObjectChange(object)
|
||||
}
|
||||
|
||||
func (sm *StateManager) notifyChanges() {
|
||||
for addr, stateObject := range sm.manifest.objectChanges {
|
||||
sm.Ethereum.Reactor().Post("object:"+addr, stateObject)
|
||||
}
|
||||
|
||||
for stateObjectAddr, mappedObjects := range sm.manifest.storageChanges {
|
||||
for addr, value := range mappedObjects {
|
||||
sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, &StorageState{[]byte(stateObjectAddr), []byte(addr), value})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Manifest struct {
|
||||
// XXX These will be handy in the future. Not important for now.
|
||||
objectAddresses map[string]bool
|
||||
storageAddresses map[string]map[string]bool
|
||||
|
||||
objectChanges map[string]*StateObject
|
||||
storageChanges map[string]map[string]*big.Int
|
||||
}
|
||||
|
||||
func NewManifest() *Manifest {
|
||||
m := &Manifest{objectAddresses: make(map[string]bool), storageAddresses: make(map[string]map[string]bool)}
|
||||
m.Reset()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Manifest) Reset() {
|
||||
m.objectChanges = make(map[string]*StateObject)
|
||||
m.storageChanges = make(map[string]map[string]*big.Int)
|
||||
}
|
||||
|
||||
func (m *Manifest) AddObjectChange(stateObject *StateObject) {
|
||||
m.objectChanges[string(stateObject.Address())] = stateObject
|
||||
}
|
||||
|
||||
func (m *Manifest) AddStorageChange(stateObject *StateObject, storageAddr []byte, storage *big.Int) {
|
||||
if m.storageChanges[string(stateObject.Address())] == nil {
|
||||
m.storageChanges[string(stateObject.Address())] = make(map[string]*big.Int)
|
||||
}
|
||||
|
||||
m.storageChanges[string(stateObject.Address())][string(storageAddr)] = storage
|
||||
}
|
||||
|
194
ethchain/state_object.go
Normal file
194
ethchain/state_object.go
Normal file
@ -0,0 +1,194 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type StateObject struct {
|
||||
// Address of the object
|
||||
address []byte
|
||||
// Shared attributes
|
||||
Amount *big.Int
|
||||
Nonce uint64
|
||||
// Contract related attributes
|
||||
state *State
|
||||
script []byte
|
||||
initScript []byte
|
||||
}
|
||||
|
||||
// Converts an transaction in to a state object
|
||||
func MakeContract(tx *Transaction, state *State) *StateObject {
|
||||
// Create contract if there's no recipient
|
||||
if tx.IsContract() {
|
||||
// FIXME
|
||||
addr := tx.Hash()[12:]
|
||||
|
||||
value := tx.Value
|
||||
contract := NewContract(addr, value, []byte(""))
|
||||
state.UpdateStateObject(contract)
|
||||
|
||||
contract.script = tx.Data
|
||||
contract.initScript = tx.Init
|
||||
|
||||
state.UpdateStateObject(contract)
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject {
|
||||
contract := &StateObject{address: address, Amount: Amount, Nonce: 0}
|
||||
contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root)))
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
// Returns a newly created account
|
||||
func NewAccount(address []byte, amount *big.Int) *StateObject {
|
||||
account := &StateObject{address: address, Amount: amount, Nonce: 0}
|
||||
|
||||
return account
|
||||
}
|
||||
|
||||
func NewStateObjectFromBytes(address, data []byte) *StateObject {
|
||||
object := &StateObject{address: address}
|
||||
object.RlpDecode(data)
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (c *StateObject) State() *State {
|
||||
return c.state
|
||||
}
|
||||
|
||||
func (c *StateObject) N() *big.Int {
|
||||
return big.NewInt(int64(c.Nonce))
|
||||
}
|
||||
|
||||
func (c *StateObject) Addr(addr []byte) *ethutil.Value {
|
||||
return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr))))
|
||||
}
|
||||
|
||||
func (c *StateObject) SetAddr(addr []byte, value interface{}) {
|
||||
c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode()))
|
||||
}
|
||||
|
||||
func (c *StateObject) SetMem(num *big.Int, val *ethutil.Value) {
|
||||
addr := ethutil.BigToBytes(num, 256)
|
||||
c.SetAddr(addr, val)
|
||||
}
|
||||
|
||||
func (c *StateObject) GetMem(num *big.Int) *ethutil.Value {
|
||||
nb := ethutil.BigToBytes(num, 256)
|
||||
|
||||
return c.Addr(nb)
|
||||
}
|
||||
|
||||
func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value {
|
||||
if int64(len(c.script)-1) < pc.Int64() {
|
||||
return ethutil.NewValue(0)
|
||||
}
|
||||
|
||||
return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]})
|
||||
}
|
||||
|
||||
// Return the gas back to the origin. Used by the Virtual machine or Closures
|
||||
func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) {
|
||||
remainder := new(big.Int).Mul(gas, price)
|
||||
c.AddAmount(remainder)
|
||||
}
|
||||
|
||||
func (c *StateObject) AddAmount(amount *big.Int) {
|
||||
c.SetAmount(new(big.Int).Add(c.Amount, amount))
|
||||
}
|
||||
|
||||
func (c *StateObject) SubAmount(amount *big.Int) {
|
||||
c.SetAmount(new(big.Int).Sub(c.Amount, amount))
|
||||
}
|
||||
|
||||
func (c *StateObject) SetAmount(amount *big.Int) {
|
||||
c.Amount = amount
|
||||
}
|
||||
|
||||
func (c *StateObject) ConvertGas(gas, price *big.Int) error {
|
||||
total := new(big.Int).Mul(gas, price)
|
||||
if total.Cmp(c.Amount) > 0 {
|
||||
return fmt.Errorf("insufficient amount: %v, %v", c.Amount, total)
|
||||
}
|
||||
|
||||
c.SubAmount(total)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the address of the contract/account
|
||||
func (c *StateObject) Address() []byte {
|
||||
return c.address
|
||||
}
|
||||
|
||||
// Returns the main script body
|
||||
func (c *StateObject) Script() []byte {
|
||||
return c.script
|
||||
}
|
||||
|
||||
// Returns the initialization script
|
||||
func (c *StateObject) Init() []byte {
|
||||
return c.initScript
|
||||
}
|
||||
|
||||
// State object encoding methods
|
||||
func (c *StateObject) RlpEncode() []byte {
|
||||
var root interface{}
|
||||
if c.state != nil {
|
||||
root = c.state.trie.Root
|
||||
} else {
|
||||
root = nil
|
||||
}
|
||||
return ethutil.Encode([]interface{}{c.Amount, c.Nonce, root, c.script})
|
||||
}
|
||||
|
||||
func (c *StateObject) RlpDecode(data []byte) {
|
||||
decoder := ethutil.NewValueFromBytes(data)
|
||||
|
||||
c.Amount = decoder.Get(0).BigInt()
|
||||
c.Nonce = decoder.Get(1).Uint()
|
||||
c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
|
||||
c.script = decoder.Get(3).Bytes()
|
||||
}
|
||||
|
||||
// The cached state and state object cache are helpers which will give you somewhat
|
||||
// control over the nonce. When creating new transactions you're interested in the 'next'
|
||||
// nonce rather than the current nonce. This to avoid creating invalid-nonce transactions.
|
||||
type StateObjectCache struct {
|
||||
cachedObjects map[string]*CachedStateObject
|
||||
}
|
||||
|
||||
func NewStateObjectCache() *StateObjectCache {
|
||||
return &StateObjectCache{cachedObjects: make(map[string]*CachedStateObject)}
|
||||
}
|
||||
|
||||
func (s *StateObjectCache) Add(addr []byte, object *StateObject) *CachedStateObject {
|
||||
state := &CachedStateObject{Nonce: object.Nonce, Object: object}
|
||||
s.cachedObjects[string(addr)] = state
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
func (s *StateObjectCache) Get(addr []byte) *CachedStateObject {
|
||||
return s.cachedObjects[string(addr)]
|
||||
}
|
||||
|
||||
type CachedStateObject struct {
|
||||
Nonce uint64
|
||||
Object *StateObject
|
||||
}
|
||||
|
||||
type StorageState struct {
|
||||
StateAddress []byte
|
||||
Address []byte
|
||||
Value *big.Int
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/obscuren/secp256k1-go"
|
||||
"math/big"
|
||||
@ -14,33 +13,22 @@ type Transaction struct {
|
||||
Recipient []byte
|
||||
Value *big.Int
|
||||
Gas *big.Int
|
||||
Gasprice *big.Int
|
||||
Data []string
|
||||
GasPrice *big.Int
|
||||
Data []byte
|
||||
Init []byte
|
||||
v byte
|
||||
r, s []byte
|
||||
|
||||
// Indicates whether this tx is a contract creation transaction
|
||||
contractCreation bool
|
||||
}
|
||||
|
||||
func NewTransaction(to []byte, value *big.Int, data []string) *Transaction {
|
||||
tx := Transaction{Recipient: to, Value: value, Nonce: 0, Data: data}
|
||||
|
||||
return &tx
|
||||
func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte, init []byte) *Transaction {
|
||||
return &Transaction{Value: value, Gas: gas, GasPrice: gasPrice, Data: script, Init: init, contractCreation: true}
|
||||
}
|
||||
|
||||
func NewContractCreationTx(value, gasprice *big.Int, data []string) *Transaction {
|
||||
return &Transaction{Value: value, Gasprice: gasprice, Data: data}
|
||||
}
|
||||
|
||||
func NewContractMessageTx(to []byte, value, gasprice, gas *big.Int, data []string) *Transaction {
|
||||
return &Transaction{Recipient: to, Value: value, Gasprice: gasprice, Gas: gas, Data: data}
|
||||
}
|
||||
|
||||
func NewTx(to []byte, value *big.Int, data []string) *Transaction {
|
||||
return &Transaction{Recipient: to, Value: value, Gasprice: big.NewInt(0), Gas: big.NewInt(0), Nonce: 0, Data: data}
|
||||
}
|
||||
|
||||
// XXX Deprecated
|
||||
func NewTransactionFromData(data []byte) *Transaction {
|
||||
return NewTransactionFromBytes(data)
|
||||
func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction {
|
||||
return &Transaction{Recipient: to, Value: value, GasPrice: gasPrice, Gas: gas, Data: data}
|
||||
}
|
||||
|
||||
func NewTransactionFromBytes(data []byte) *Transaction {
|
||||
@ -58,23 +46,21 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction {
|
||||
}
|
||||
|
||||
func (tx *Transaction) Hash() []byte {
|
||||
data := make([]interface{}, len(tx.Data))
|
||||
for i, val := range tx.Data {
|
||||
data[i] = val
|
||||
data := []interface{}{tx.Nonce, tx.Value, tx.GasPrice, tx.Gas, tx.Recipient, tx.Data}
|
||||
|
||||
if tx.contractCreation {
|
||||
data = append(data, tx.Init)
|
||||
}
|
||||
|
||||
preEnc := []interface{}{
|
||||
tx.Nonce,
|
||||
tx.Recipient,
|
||||
tx.Value,
|
||||
data,
|
||||
}
|
||||
|
||||
return ethutil.Sha3Bin(ethutil.Encode(preEnc))
|
||||
return ethutil.Sha3Bin(ethutil.NewValue(data).Encode())
|
||||
}
|
||||
|
||||
func (tx *Transaction) IsContract() bool {
|
||||
return bytes.Compare(tx.Recipient, ContractAddr) == 0
|
||||
return tx.contractCreation
|
||||
}
|
||||
|
||||
func (tx *Transaction) CreationAddress() []byte {
|
||||
return tx.Hash()[12:]
|
||||
}
|
||||
|
||||
func (tx *Transaction) Signature(key []byte) []byte {
|
||||
@ -123,17 +109,16 @@ func (tx *Transaction) Sign(privk []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// [ NONCE, VALUE, GASPRICE, GAS, TO, DATA, V, R, S ]
|
||||
// [ NONCE, VALUE, GASPRICE, GAS, 0, CODE, INIT, V, R, S ]
|
||||
func (tx *Transaction) RlpData() interface{} {
|
||||
// Prepare the transaction for serialization
|
||||
return []interface{}{
|
||||
tx.Nonce,
|
||||
tx.Recipient,
|
||||
tx.Value,
|
||||
ethutil.NewSliceValue(tx.Data).Slice(),
|
||||
tx.v,
|
||||
tx.r,
|
||||
tx.s,
|
||||
data := []interface{}{tx.Nonce, tx.Value, tx.GasPrice, tx.Gas, tx.Recipient, tx.Data}
|
||||
|
||||
if tx.contractCreation {
|
||||
data = append(data, tx.Init)
|
||||
}
|
||||
|
||||
return append(data, tx.v, tx.r, tx.s)
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpValue() *ethutil.Value {
|
||||
@ -150,17 +135,23 @@ func (tx *Transaction) RlpDecode(data []byte) {
|
||||
|
||||
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
|
||||
tx.Nonce = decoder.Get(0).Uint()
|
||||
tx.Recipient = decoder.Get(1).Bytes()
|
||||
tx.Value = decoder.Get(2).BigInt()
|
||||
tx.Value = decoder.Get(1).BigInt()
|
||||
tx.GasPrice = decoder.Get(2).BigInt()
|
||||
tx.Gas = decoder.Get(3).BigInt()
|
||||
tx.Recipient = decoder.Get(4).Bytes()
|
||||
tx.Data = decoder.Get(5).Bytes()
|
||||
|
||||
d := decoder.Get(3)
|
||||
tx.Data = make([]string, d.Len())
|
||||
for i := 0; i < d.Len(); i++ {
|
||||
tx.Data[i] = d.Get(i).Str()
|
||||
// If the list is of length 10 it's a contract creation tx
|
||||
if decoder.Len() == 10 {
|
||||
tx.contractCreation = true
|
||||
tx.Init = decoder.Get(6).Bytes()
|
||||
|
||||
tx.v = byte(decoder.Get(7).Uint())
|
||||
tx.r = decoder.Get(8).Bytes()
|
||||
tx.s = decoder.Get(9).Bytes()
|
||||
} else {
|
||||
tx.v = byte(decoder.Get(6).Uint())
|
||||
tx.r = decoder.Get(7).Bytes()
|
||||
tx.s = decoder.Get(8).Bytes()
|
||||
}
|
||||
|
||||
// TODO something going wrong here
|
||||
tx.v = byte(decoder.Get(4).Uint())
|
||||
tx.r = decoder.Get(5).Bytes()
|
||||
tx.s = decoder.Get(6).Bytes()
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ func (pool *TxPool) addTransaction(tx *Transaction) {
|
||||
|
||||
// Process transaction validates the Tx and processes funds from the
|
||||
// sender to the recipient.
|
||||
func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) {
|
||||
func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract bool) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Println(r)
|
||||
@ -100,19 +100,15 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error
|
||||
// Get the sender
|
||||
sender := block.state.GetAccount(tx.Sender())
|
||||
|
||||
if sender.Nonce != tx.Nonce {
|
||||
return fmt.Errorf("[TXPL] Invalid account nonce, state nonce is %d transaction nonce is %d instead", sender.Nonce, tx.Nonce)
|
||||
}
|
||||
|
||||
// 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, new(big.Int).Mul(TxFee, TxFeeRat))
|
||||
if sender.Amount.Cmp(totAmount) < 0 {
|
||||
return errors.New("Insufficient amount in sender's account")
|
||||
}
|
||||
|
||||
if sender.Nonce != tx.Nonce {
|
||||
if ethutil.Config.Debug {
|
||||
return fmt.Errorf("Invalid nonce %d(%d) continueing anyway", tx.Nonce, sender.Nonce)
|
||||
} else {
|
||||
return fmt.Errorf("Invalid nonce %d(%d)", tx.Nonce, sender.Nonce)
|
||||
}
|
||||
return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
|
||||
}
|
||||
|
||||
// Get the receiver
|
||||
@ -122,22 +118,21 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error
|
||||
// Send Tx to self
|
||||
if bytes.Compare(tx.Recipient, tx.Sender()) == 0 {
|
||||
// Subtract the fee
|
||||
sender.Amount.Sub(sender.Amount, new(big.Int).Mul(TxFee, TxFeeRat))
|
||||
sender.SubAmount(new(big.Int).Mul(TxFee, TxFeeRat))
|
||||
} else {
|
||||
// Subtract the amount from the senders account
|
||||
sender.Amount.Sub(sender.Amount, totAmount)
|
||||
sender.SubAmount(totAmount)
|
||||
|
||||
// Add the amount to receivers account which should conclude this transaction
|
||||
receiver.Amount.Add(receiver.Amount, tx.Value)
|
||||
receiver.AddAmount(tx.Value)
|
||||
|
||||
block.state.UpdateAccount(tx.Recipient, receiver)
|
||||
block.state.UpdateStateObject(receiver)
|
||||
}
|
||||
|
||||
block.state.UpdateAccount(tx.Sender(), sender)
|
||||
block.state.UpdateStateObject(sender)
|
||||
|
||||
log.Printf("[TXPL] Processed Tx %x\n", tx.Hash())
|
||||
|
||||
// Notify the subscribers
|
||||
pool.notifySubscribers(TxPost, tx)
|
||||
|
||||
return
|
||||
@ -149,18 +144,18 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
|
||||
block := pool.Ethereum.BlockChain().CurrentBlock
|
||||
// Something has gone horribly wrong if this happens
|
||||
if block == nil {
|
||||
return errors.New("No last block on the block chain")
|
||||
return errors.New("[TXPL] No last block on the block chain")
|
||||
}
|
||||
|
||||
// Get the sender
|
||||
accountState := pool.Ethereum.StateManager().GetAddrState(tx.Sender())
|
||||
sender := accountState.Account
|
||||
sender := accountState.Object
|
||||
|
||||
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat))
|
||||
// Make sure there's enough in the sender's account. Having insufficient
|
||||
// funds won't invalidate this transaction but simple ignores it.
|
||||
if sender.Amount.Cmp(totAmount) < 0 {
|
||||
return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.Sender())
|
||||
return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
|
||||
}
|
||||
|
||||
// Increment the nonce making each tx valid only once to prevent replay
|
||||
@ -190,10 +185,12 @@ out:
|
||||
log.Println("Validating Tx failed", err)
|
||||
}
|
||||
} else {
|
||||
// Call blocking version. At this point it
|
||||
// doesn't matter since this is a goroutine
|
||||
// Call blocking version.
|
||||
pool.addTransaction(tx)
|
||||
|
||||
// Notify the subscribers
|
||||
pool.Ethereum.Reactor().Post("newTx", tx)
|
||||
|
||||
// Notify the subscribers
|
||||
pool.notifySubscribers(TxPre, tx)
|
||||
}
|
||||
@ -207,7 +204,7 @@ func (pool *TxPool) QueueTransaction(tx *Transaction) {
|
||||
pool.queueChan <- tx
|
||||
}
|
||||
|
||||
func (pool *TxPool) Flush() []*Transaction {
|
||||
func (pool *TxPool) CurrentTransactions() []*Transaction {
|
||||
pool.mutex.Lock()
|
||||
defer pool.mutex.Unlock()
|
||||
|
||||
@ -221,6 +218,12 @@ func (pool *TxPool) Flush() []*Transaction {
|
||||
i++
|
||||
}
|
||||
|
||||
return txList
|
||||
}
|
||||
|
||||
func (pool *TxPool) Flush() []*Transaction {
|
||||
txList := pool.CurrentTransactions()
|
||||
|
||||
// Recreate a new list all together
|
||||
// XXX Is this the fastest way?
|
||||
pool.pool = list.New()
|
||||
|
@ -1,54 +1 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddressRetrieval(t *testing.T) {
|
||||
// TODO
|
||||
// 88f9b82462f6c4bf4a0fb15e5c3971559a316e7f
|
||||
key, _ := hex.DecodeString("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4")
|
||||
|
||||
tx := &Transaction{
|
||||
Nonce: 0,
|
||||
Recipient: ZeroHash160,
|
||||
Value: big.NewInt(0),
|
||||
Data: nil,
|
||||
}
|
||||
//fmt.Printf("rlp %x\n", tx.RlpEncode())
|
||||
//fmt.Printf("sha rlp %x\n", tx.Hash())
|
||||
|
||||
tx.Sign(key)
|
||||
|
||||
//fmt.Printf("hex tx key %x\n", tx.PublicKey())
|
||||
//fmt.Printf("seder %x\n", tx.Sender())
|
||||
}
|
||||
|
||||
func TestAddressRetrieval2(t *testing.T) {
|
||||
// TODO
|
||||
// 88f9b82462f6c4bf4a0fb15e5c3971559a316e7f
|
||||
key, _ := hex.DecodeString("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4")
|
||||
addr, _ := hex.DecodeString("944400f4b88ac9589a0f17ed4671da26bddb668b")
|
||||
tx := &Transaction{
|
||||
Nonce: 0,
|
||||
Recipient: addr,
|
||||
Value: big.NewInt(1000),
|
||||
Data: nil,
|
||||
}
|
||||
tx.Sign(key)
|
||||
//data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7")
|
||||
//tx := NewTransactionFromData(data)
|
||||
/*
|
||||
fmt.Println(tx.RlpValue())
|
||||
|
||||
fmt.Printf("rlp %x\n", tx.RlpEncode())
|
||||
fmt.Printf("sha rlp %x\n", tx.Hash())
|
||||
|
||||
//tx.Sign(key)
|
||||
|
||||
fmt.Printf("hex tx key %x\n", tx.PublicKey())
|
||||
fmt.Printf("seder %x\n", tx.Sender())
|
||||
*/
|
||||
}
|
||||
|
230
ethchain/types.go
Normal file
230
ethchain/types.go
Normal file
@ -0,0 +1,230 @@
|
||||
package ethchain
|
||||
|
||||
type OpCode int
|
||||
|
||||
// Op codes
|
||||
const (
|
||||
// 0x0 range - arithmetic ops
|
||||
oSTOP = 0x00
|
||||
oADD = 0x01
|
||||
oMUL = 0x02
|
||||
oSUB = 0x03
|
||||
oDIV = 0x04
|
||||
oSDIV = 0x05
|
||||
oMOD = 0x06
|
||||
oSMOD = 0x07
|
||||
oEXP = 0x08
|
||||
oNEG = 0x09
|
||||
oLT = 0x0a
|
||||
oGT = 0x0b
|
||||
oEQ = 0x0c
|
||||
oNOT = 0x0d
|
||||
|
||||
// 0x10 range - bit ops
|
||||
oAND = 0x10
|
||||
oOR = 0x11
|
||||
oXOR = 0x12
|
||||
oBYTE = 0x13
|
||||
|
||||
// 0x20 range - crypto
|
||||
oSHA3 = 0x20
|
||||
|
||||
// 0x30 range - closure state
|
||||
oADDRESS = 0x30
|
||||
oBALANCE = 0x31
|
||||
oORIGIN = 0x32
|
||||
oCALLER = 0x33
|
||||
oCALLVALUE = 0x34
|
||||
oCALLDATALOAD = 0x35
|
||||
oCALLDATASIZE = 0x36
|
||||
oGASPRICE = 0x37
|
||||
|
||||
// 0x40 range - block operations
|
||||
oPREVHASH = 0x40
|
||||
oCOINBASE = 0x41
|
||||
oTIMESTAMP = 0x42
|
||||
oNUMBER = 0x43
|
||||
oDIFFICULTY = 0x44
|
||||
oGASLIMIT = 0x45
|
||||
|
||||
// 0x50 range - 'storage' and execution
|
||||
oPUSH = 0x50
|
||||
oPUSH20 = 0x80
|
||||
oPOP = 0x51
|
||||
oDUP = 0x52
|
||||
oSWAP = 0x53
|
||||
oMLOAD = 0x54
|
||||
oMSTORE = 0x55
|
||||
oMSTORE8 = 0x56
|
||||
oSLOAD = 0x57
|
||||
oSSTORE = 0x58
|
||||
oJUMP = 0x59
|
||||
oJUMPI = 0x5a
|
||||
oPC = 0x5b
|
||||
oMSIZE = 0x5c
|
||||
|
||||
// 0x60 range - closures
|
||||
oCREATE = 0x60
|
||||
oCALL = 0x61
|
||||
oRETURN = 0x62
|
||||
|
||||
// 0x70 range - other
|
||||
oLOG = 0x70 // XXX Unofficial
|
||||
oSUICIDE = 0x7f
|
||||
)
|
||||
|
||||
// Since the opcodes aren't all in order we can't use a regular slice
|
||||
var opCodeToString = map[OpCode]string{
|
||||
// 0x0 range - arithmetic ops
|
||||
oSTOP: "STOP",
|
||||
oADD: "ADD",
|
||||
oMUL: "MUL",
|
||||
oSUB: "SUB",
|
||||
oDIV: "DIV",
|
||||
oSDIV: "SDIV",
|
||||
oMOD: "MOD",
|
||||
oSMOD: "SMOD",
|
||||
oEXP: "EXP",
|
||||
oNEG: "NEG",
|
||||
oLT: "LT",
|
||||
oGT: "GT",
|
||||
oEQ: "EQ",
|
||||
oNOT: "NOT",
|
||||
|
||||
// 0x10 range - bit ops
|
||||
oAND: "AND",
|
||||
oOR: "OR",
|
||||
oXOR: "XOR",
|
||||
oBYTE: "BYTE",
|
||||
|
||||
// 0x20 range - crypto
|
||||
oSHA3: "SHA3",
|
||||
|
||||
// 0x30 range - closure state
|
||||
oADDRESS: "ADDRESS",
|
||||
oBALANCE: "BALANCE",
|
||||
oORIGIN: "ORIGIN",
|
||||
oCALLER: "CALLER",
|
||||
oCALLVALUE: "CALLVALUE",
|
||||
oCALLDATALOAD: "CALLDATALOAD",
|
||||
oCALLDATASIZE: "CALLDATASIZE",
|
||||
oGASPRICE: "TXGASPRICE",
|
||||
|
||||
// 0x40 range - block operations
|
||||
oPREVHASH: "PREVHASH",
|
||||
oCOINBASE: "COINBASE",
|
||||
oTIMESTAMP: "TIMESTAMP",
|
||||
oNUMBER: "NUMBER",
|
||||
oDIFFICULTY: "DIFFICULTY",
|
||||
oGASLIMIT: "GASLIMIT",
|
||||
|
||||
// 0x50 range - 'storage' and execution
|
||||
oPUSH: "PUSH",
|
||||
oPOP: "POP",
|
||||
oDUP: "DUP",
|
||||
oSWAP: "SWAP",
|
||||
oMLOAD: "MLOAD",
|
||||
oMSTORE: "MSTORE",
|
||||
oMSTORE8: "MSTORE8",
|
||||
oSLOAD: "SLOAD",
|
||||
oSSTORE: "SSTORE",
|
||||
oJUMP: "JUMP",
|
||||
oJUMPI: "JUMPI",
|
||||
oPC: "PC",
|
||||
oMSIZE: "MSIZE",
|
||||
|
||||
// 0x60 range - closures
|
||||
oCREATE: "CREATE",
|
||||
oCALL: "CALL",
|
||||
oRETURN: "RETURN",
|
||||
|
||||
// 0x70 range - other
|
||||
oLOG: "LOG",
|
||||
oSUICIDE: "SUICIDE",
|
||||
}
|
||||
|
||||
func (o OpCode) String() string {
|
||||
return opCodeToString[o]
|
||||
}
|
||||
|
||||
// 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
|
||||
"PUSH": 0x50,
|
||||
|
||||
"PUSH20": 0x80,
|
||||
|
||||
"POP": 0x51,
|
||||
"DUP": 0x52,
|
||||
"SWAP": 0x53,
|
||||
"MLOAD": 0x54,
|
||||
"MSTORE": 0x55,
|
||||
"MSTORE8": 0x56,
|
||||
"SLOAD": 0x57,
|
||||
"SSTORE": 0x58,
|
||||
"JUMP": 0x59,
|
||||
"JUMPI": 0x5a,
|
||||
"PC": 0x5b,
|
||||
"MSIZE": 0x5c,
|
||||
|
||||
// 0x60 range - closures
|
||||
"CREATE": 0x60,
|
||||
"CALL": 0x61,
|
||||
"RETURN": 0x62,
|
||||
|
||||
// 0x70 range - other
|
||||
"LOG": 0x70,
|
||||
"SUICIDE": 0x7f,
|
||||
}
|
||||
|
||||
func IsOpCode(s string) bool {
|
||||
for key, _ := range OpCodes {
|
||||
if key == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
430
ethchain/vm.go
430
ethchain/vm.go
@ -2,14 +2,35 @@ package ethchain
|
||||
|
||||
import (
|
||||
_ "bytes"
|
||||
_ "fmt"
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
_ "github.com/obscuren/secp256k1-go"
|
||||
"log"
|
||||
_ "math"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
func CalculateTxGas(initSize, scriptSize *big.Int) *big.Int {
|
||||
totalGas := new(big.Int)
|
||||
totalGas.Add(totalGas, GasCreate)
|
||||
|
||||
txTotalBytes := new(big.Int).Add(initSize, scriptSize)
|
||||
txTotalBytes.Div(txTotalBytes, ethutil.Big32)
|
||||
totalGas.Add(totalGas, new(big.Int).Mul(txTotalBytes, GasSStore))
|
||||
|
||||
return totalGas
|
||||
}
|
||||
|
||||
type Vm struct {
|
||||
txPool *TxPool
|
||||
// Stack for processing contracts
|
||||
@ -20,99 +41,158 @@ type Vm struct {
|
||||
vars RuntimeVars
|
||||
|
||||
state *State
|
||||
|
||||
stateManager *StateManager
|
||||
}
|
||||
|
||||
type RuntimeVars struct {
|
||||
origin []byte
|
||||
blockNumber uint64
|
||||
prevHash []byte
|
||||
coinbase []byte
|
||||
time int64
|
||||
diff *big.Int
|
||||
txData []string
|
||||
Origin []byte
|
||||
BlockNumber uint64
|
||||
PrevHash []byte
|
||||
Coinbase []byte
|
||||
Time int64
|
||||
Diff *big.Int
|
||||
TxData []string
|
||||
}
|
||||
|
||||
func NewVm(state *State, vars RuntimeVars) *Vm {
|
||||
return &Vm{vars: vars, state: state}
|
||||
func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm {
|
||||
return &Vm{vars: vars, state: state, stateManager: stateManager}
|
||||
}
|
||||
|
||||
var Pow256 = ethutil.BigPow(2, 256)
|
||||
|
||||
func (vm *Vm) RunClosure(closure *Closure) []byte {
|
||||
// If the amount of gas supplied is less equal to 0
|
||||
if closure.Gas.Cmp(big.NewInt(0)) <= 0 {
|
||||
// TODO Do something
|
||||
}
|
||||
var isRequireError = false
|
||||
|
||||
func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err error) {
|
||||
// Recover from any require exception
|
||||
defer func() {
|
||||
if r := recover(); r != nil /*&& isRequireError*/ {
|
||||
ret = closure.Return(nil)
|
||||
err = fmt.Errorf("%v", r)
|
||||
fmt.Println("vm err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
ethutil.Config.Log.Debugf("[VM] Running closure %x\n", closure.object.Address())
|
||||
|
||||
// Memory for the current closure
|
||||
mem := &Memory{}
|
||||
// New stack (should this be shared?)
|
||||
stack := NewStack()
|
||||
require := func(m int) {
|
||||
if stack.Len() < m {
|
||||
isRequireError = true
|
||||
panic(fmt.Sprintf("stack = %d, req = %d", stack.Len(), m))
|
||||
}
|
||||
}
|
||||
|
||||
// Instruction pointer
|
||||
pc := big.NewInt(0)
|
||||
// Current step count
|
||||
step := 0
|
||||
// The base for all big integer arithmetic
|
||||
base := new(big.Int)
|
||||
|
||||
if ethutil.Config.Debug {
|
||||
ethutil.Config.Log.Debugf("# op\n")
|
||||
}
|
||||
|
||||
for {
|
||||
// The base for all big integer arithmetic
|
||||
base := new(big.Int)
|
||||
|
||||
step++
|
||||
// Get the memory location of pc
|
||||
val := closure.GetMem(pc)
|
||||
val := closure.Get(pc)
|
||||
// Get the opcode (it must be an opcode!)
|
||||
op := OpCode(val.Uint())
|
||||
if ethutil.Config.Debug {
|
||||
ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String())
|
||||
/*
|
||||
if ethutil.Config.Debug {
|
||||
ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String())
|
||||
}
|
||||
*/
|
||||
|
||||
gas := new(big.Int)
|
||||
useGas := func(amount *big.Int) {
|
||||
gas.Add(gas, amount)
|
||||
}
|
||||
|
||||
// TODO Get each instruction cost properly
|
||||
fee := new(big.Int)
|
||||
fee.Add(fee, big.NewInt(1000))
|
||||
switch op {
|
||||
case oSHA3:
|
||||
useGas(GasSha)
|
||||
case oSLOAD:
|
||||
useGas(GasSLoad)
|
||||
case oSSTORE:
|
||||
var mult *big.Int
|
||||
y, x := stack.Peekn()
|
||||
val := closure.GetMem(x)
|
||||
if val.IsEmpty() && len(y.Bytes()) > 0 {
|
||||
mult = ethutil.Big2
|
||||
} else if !val.IsEmpty() && len(y.Bytes()) == 0 {
|
||||
mult = ethutil.Big0
|
||||
} else {
|
||||
mult = ethutil.Big1
|
||||
}
|
||||
useGas(new(big.Int).Mul(mult, GasSStore))
|
||||
case oBALANCE:
|
||||
useGas(GasBalance)
|
||||
case oCREATE:
|
||||
require(3)
|
||||
|
||||
if closure.Gas.Cmp(fee) < 0 {
|
||||
return closure.Return(nil)
|
||||
args := stack.Get(big.NewInt(3))
|
||||
initSize := new(big.Int).Add(args[1], args[0])
|
||||
|
||||
useGas(CalculateTxGas(initSize, ethutil.Big0))
|
||||
case oCALL:
|
||||
useGas(GasCall)
|
||||
case oMLOAD, oMSIZE, oMSTORE8, oMSTORE:
|
||||
useGas(GasMemory)
|
||||
default:
|
||||
useGas(GasStep)
|
||||
}
|
||||
|
||||
if closure.Gas.Cmp(gas) < 0 {
|
||||
ethutil.Config.Log.Debugln("Insufficient gas", closure.Gas, gas)
|
||||
|
||||
return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas)
|
||||
}
|
||||
|
||||
// Sub the amount of gas from the remaining
|
||||
closure.Gas.Sub(closure.Gas, gas)
|
||||
|
||||
switch op {
|
||||
case oLOG:
|
||||
stack.Print()
|
||||
mem.Print()
|
||||
case oSTOP: // Stop the closure
|
||||
return closure.Return(nil)
|
||||
|
||||
// 0x20 range
|
||||
// 0x20 range
|
||||
case oADD:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
// (x + y) % 2 ** 256
|
||||
base.Add(x, y)
|
||||
base.Mod(base, Pow256)
|
||||
// Pop result back on the stack
|
||||
stack.Push(base)
|
||||
case oSUB:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
// (x - y) % 2 ** 256
|
||||
base.Sub(x, y)
|
||||
base.Mod(base, Pow256)
|
||||
// Pop result back on the stack
|
||||
stack.Push(base)
|
||||
case oMUL:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
// (x * y) % 2 ** 256
|
||||
base.Mul(x, y)
|
||||
base.Mod(base, Pow256)
|
||||
// Pop result back on the stack
|
||||
stack.Push(base)
|
||||
case oDIV:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
// floor(x / y)
|
||||
base.Div(x, y)
|
||||
// Pop result back on the stack
|
||||
stack.Push(base)
|
||||
case oSDIV:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
// n > 2**255
|
||||
if x.Cmp(Pow256) > 0 {
|
||||
@ -129,10 +209,12 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
|
||||
// Push result on to the stack
|
||||
stack.Push(z)
|
||||
case oMOD:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
base.Mod(x, y)
|
||||
stack.Push(base)
|
||||
case oSMOD:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
// n > 2**255
|
||||
if x.Cmp(Pow256) > 0 {
|
||||
@ -149,14 +231,17 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
|
||||
// Push result on to the stack
|
||||
stack.Push(z)
|
||||
case oEXP:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
base.Exp(x, y, Pow256)
|
||||
|
||||
stack.Push(base)
|
||||
case oNEG:
|
||||
require(1)
|
||||
base.Sub(Pow256, stack.Pop())
|
||||
stack.Push(base)
|
||||
case oLT:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
// x < y
|
||||
if x.Cmp(y) < 0 {
|
||||
@ -165,6 +250,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oGT:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
// x > y
|
||||
if x.Cmp(y) > 0 {
|
||||
@ -172,184 +258,286 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
|
||||
} else {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oNOT:
|
||||
case oEQ:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
// x != y
|
||||
if x.Cmp(y) != 0 {
|
||||
// x == y
|
||||
if x.Cmp(y) == 0 {
|
||||
stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oNOT:
|
||||
require(1)
|
||||
x := stack.Pop()
|
||||
if x.Cmp(ethutil.BigFalse) == 0 {
|
||||
stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
|
||||
// 0x10 range
|
||||
// 0x10 range
|
||||
case oAND:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
if (x.Cmp(ethutil.BigTrue) >= 0) && (y.Cmp(ethutil.BigTrue) >= 0) {
|
||||
stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
|
||||
case oOR:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
if (x.Cmp(ethutil.BigInt0) >= 0) || (y.Cmp(ethutil.BigInt0) >= 0) {
|
||||
stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oXOR:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
stack.Push(base.Xor(x, y))
|
||||
case oBYTE:
|
||||
require(2)
|
||||
val, th := stack.Popn()
|
||||
if th.Cmp(big.NewInt(32)) < 0 {
|
||||
stack.Push(big.NewInt(int64(len(val.Bytes())-1) - th.Int64()))
|
||||
} else {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
|
||||
// 0x20 range
|
||||
// 0x20 range
|
||||
case oSHA3:
|
||||
require(2)
|
||||
size, offset := stack.Popn()
|
||||
data := mem.Get(offset.Int64(), size.Int64())
|
||||
|
||||
// 0x30 range
|
||||
stack.Push(ethutil.BigD(data))
|
||||
// 0x30 range
|
||||
case oADDRESS:
|
||||
stack.Push(ethutil.BigD(closure.Object().Address()))
|
||||
case oBALANCE:
|
||||
stack.Push(closure.Value)
|
||||
case oORIGIN:
|
||||
stack.Push(ethutil.BigD(vm.vars.origin))
|
||||
stack.Push(ethutil.BigD(vm.vars.Origin))
|
||||
case oCALLER:
|
||||
stack.Push(ethutil.BigD(closure.Callee().Address()))
|
||||
case oCALLVALUE:
|
||||
// FIXME: Original value of the call, not the current value
|
||||
stack.Push(closure.Value)
|
||||
case oCALLDATA:
|
||||
offset := stack.Pop()
|
||||
mem.Set(offset.Int64(), int64(len(closure.Args)), closure.Args)
|
||||
case oCALLDATALOAD:
|
||||
require(1)
|
||||
offset := stack.Pop().Int64()
|
||||
val := closure.Args[offset : offset+32]
|
||||
|
||||
stack.Push(ethutil.BigD(val))
|
||||
case oCALLDATASIZE:
|
||||
stack.Push(big.NewInt(int64(len(closure.Args))))
|
||||
case oGASPRICE:
|
||||
// TODO
|
||||
stack.Push(closure.Price)
|
||||
|
||||
// 0x40 range
|
||||
// 0x40 range
|
||||
case oPREVHASH:
|
||||
stack.Push(ethutil.BigD(vm.vars.prevHash))
|
||||
stack.Push(ethutil.BigD(vm.vars.PrevHash))
|
||||
case oCOINBASE:
|
||||
stack.Push(ethutil.BigD(vm.vars.coinbase))
|
||||
stack.Push(ethutil.BigD(vm.vars.Coinbase))
|
||||
case oTIMESTAMP:
|
||||
stack.Push(big.NewInt(vm.vars.time))
|
||||
stack.Push(big.NewInt(vm.vars.Time))
|
||||
case oNUMBER:
|
||||
stack.Push(big.NewInt(int64(vm.vars.blockNumber)))
|
||||
stack.Push(big.NewInt(int64(vm.vars.BlockNumber)))
|
||||
case oDIFFICULTY:
|
||||
stack.Push(vm.vars.diff)
|
||||
stack.Push(vm.vars.Diff)
|
||||
case oGASLIMIT:
|
||||
// TODO
|
||||
stack.Push(big.NewInt(0))
|
||||
|
||||
// 0x50 range
|
||||
case oPUSH: // Push PC+1 on to the stack
|
||||
pc.Add(pc, ethutil.Big1)
|
||||
data := closure.Gets(pc, big.NewInt(32))
|
||||
val := ethutil.BigD(data.Bytes())
|
||||
|
||||
val := closure.GetMem(pc).BigInt()
|
||||
// Push value to stack
|
||||
stack.Push(val)
|
||||
|
||||
pc.Add(pc, big.NewInt(31))
|
||||
step++
|
||||
case oPUSH20:
|
||||
pc.Add(pc, ethutil.Big1)
|
||||
data := closure.Gets(pc, big.NewInt(20))
|
||||
val := ethutil.BigD(data.Bytes())
|
||||
|
||||
// Push value to stack
|
||||
stack.Push(val)
|
||||
|
||||
pc.Add(pc, big.NewInt(19))
|
||||
step++
|
||||
case oPOP:
|
||||
require(1)
|
||||
stack.Pop()
|
||||
case oDUP:
|
||||
require(1)
|
||||
stack.Push(stack.Peek())
|
||||
case oSWAP:
|
||||
require(2)
|
||||
x, y := stack.Popn()
|
||||
stack.Push(y)
|
||||
stack.Push(x)
|
||||
case oMLOAD:
|
||||
require(1)
|
||||
offset := stack.Pop()
|
||||
stack.Push(ethutil.BigD(mem.Get(offset.Int64(), 32)))
|
||||
case oMSTORE: // Store the value at stack top-1 in to memory at location stack top
|
||||
require(2)
|
||||
// Pop value of the stack
|
||||
val, mStart := stack.Popn()
|
||||
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256))
|
||||
case oMSTORE8:
|
||||
require(2)
|
||||
val, mStart := stack.Popn()
|
||||
base.And(val, new(big.Int).SetInt64(0xff))
|
||||
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256))
|
||||
case oSLOAD:
|
||||
require(1)
|
||||
loc := stack.Pop()
|
||||
val := closure.GetMem(loc)
|
||||
stack.Push(val.BigInt())
|
||||
case oSSTORE:
|
||||
require(2)
|
||||
val, loc := stack.Popn()
|
||||
closure.SetMem(loc, ethutil.NewValue(val))
|
||||
|
||||
// Add the change to manifest
|
||||
vm.stateManager.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val)
|
||||
case oJUMP:
|
||||
require(1)
|
||||
pc = stack.Pop()
|
||||
// Reduce pc by one because of the increment that's at the end of this for loop
|
||||
pc.Sub(pc, ethutil.Big1)
|
||||
case oJUMPI:
|
||||
pos, cond := stack.Popn()
|
||||
if cond.Cmp(big.NewInt(0)) > 0 {
|
||||
require(2)
|
||||
cond, pos := stack.Popn()
|
||||
if cond.Cmp(ethutil.BigTrue) == 0 {
|
||||
pc = pos
|
||||
pc.Sub(pc, ethutil.Big1)
|
||||
}
|
||||
case oPC:
|
||||
stack.Push(pc)
|
||||
case oMSIZE:
|
||||
stack.Push(big.NewInt(int64(mem.Len())))
|
||||
// 0x60 range
|
||||
// 0x60 range
|
||||
case oCREATE:
|
||||
require(3)
|
||||
|
||||
value := stack.Pop()
|
||||
size, offset := stack.Popn()
|
||||
|
||||
// Generate a new address
|
||||
addr := ethutil.CreateAddress(closure.callee.Address(), closure.callee.N())
|
||||
// Create a new contract
|
||||
contract := NewContract(addr, value, []byte(""))
|
||||
// Set the init script
|
||||
contract.initScript = mem.Get(offset.Int64(), size.Int64())
|
||||
// Transfer all remaining gas to the new
|
||||
// contract so it may run the init script
|
||||
gas := new(big.Int).Set(closure.Gas)
|
||||
closure.Gas.Sub(closure.Gas, gas)
|
||||
// Create the closure
|
||||
closure := NewClosure(closure.callee,
|
||||
closure.Object(),
|
||||
contract.initScript,
|
||||
vm.state,
|
||||
gas,
|
||||
closure.Price,
|
||||
value)
|
||||
// Call the closure and set the return value as
|
||||
// main script.
|
||||
closure.Script, err = closure.Call(vm, nil, hook)
|
||||
if err != nil {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
} else {
|
||||
stack.Push(ethutil.BigD(addr))
|
||||
|
||||
vm.state.SetStateObject(contract)
|
||||
}
|
||||
case oCALL:
|
||||
// Pop return size and offset
|
||||
retSize, retOffset := stack.Popn()
|
||||
// Pop input size and offset
|
||||
inSize, inOffset := stack.Popn()
|
||||
// Get the arguments from the memory
|
||||
args := mem.Get(inOffset.Int64(), inSize.Int64())
|
||||
// Pop gas and value of the stack.
|
||||
gas, value := stack.Popn()
|
||||
require(7)
|
||||
// Closure addr
|
||||
addr := stack.Pop()
|
||||
// Pop gas and value of the stack.
|
||||
gas, value := stack.Popn()
|
||||
// Pop input size and offset
|
||||
inSize, inOffset := stack.Popn()
|
||||
// Pop return size and offset
|
||||
retSize, retOffset := stack.Popn()
|
||||
// Make sure there's enough gas
|
||||
if closure.Gas.Cmp(gas) < 0 {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
|
||||
break
|
||||
}
|
||||
// Get the arguments from the memory
|
||||
args := mem.Get(inOffset.Int64(), inSize.Int64())
|
||||
// Fetch the contract which will serve as the closure body
|
||||
contract := vm.state.GetContract(addr.Bytes())
|
||||
// Create a new callable closure
|
||||
closure := NewClosure(closure, contract, vm.state, gas, value)
|
||||
// Executer the closure and get the return value (if any)
|
||||
ret := closure.Call(vm, args)
|
||||
|
||||
mem.Set(retOffset.Int64(), retSize.Int64(), ret)
|
||||
if contract != nil {
|
||||
// Prepay for the gas
|
||||
// If gas is set to 0 use all remaining gas for the next call
|
||||
if gas.Cmp(big.NewInt(0)) == 0 {
|
||||
// Copy
|
||||
gas = new(big.Int).Set(closure.Gas)
|
||||
}
|
||||
closure.Gas.Sub(closure.Gas, gas)
|
||||
// Create a new callable closure
|
||||
closure := NewClosure(closure.Object(), contract, contract.script, vm.state, gas, closure.Price, value)
|
||||
// Executer the closure and get the return value (if any)
|
||||
ret, err := closure.Call(vm, args, hook)
|
||||
if err != nil {
|
||||
stack.Push(ethutil.BigFalse)
|
||||
// Reset the changes applied this object
|
||||
//contract.State().Reset()
|
||||
} else {
|
||||
stack.Push(ethutil.BigTrue)
|
||||
// Notify of the changes
|
||||
vm.stateManager.manifest.AddObjectChange(contract)
|
||||
}
|
||||
|
||||
mem.Set(retOffset.Int64(), retSize.Int64(), ret)
|
||||
} else {
|
||||
ethutil.Config.Log.Debugf("Contract %x not found\n", addr.Bytes())
|
||||
stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oRETURN:
|
||||
require(2)
|
||||
size, offset := stack.Popn()
|
||||
ret := mem.Get(offset.Int64(), size.Int64())
|
||||
|
||||
return closure.Return(ret)
|
||||
return closure.Return(ret), nil
|
||||
case oSUICIDE:
|
||||
/*
|
||||
recAddr := stack.Pop().Bytes()
|
||||
// Purge all memory
|
||||
deletedMemory := contract.state.Purge()
|
||||
// Add refunds to the pop'ed address
|
||||
refund := new(big.Int).Mul(StoreFee, big.NewInt(int64(deletedMemory)))
|
||||
account := state.GetAccount(recAddr)
|
||||
account.Amount.Add(account.Amount, refund)
|
||||
// Update the refunding address
|
||||
state.UpdateAccount(recAddr, account)
|
||||
// Delete the contract
|
||||
state.trie.Update(string(addr), "")
|
||||
require(1)
|
||||
|
||||
ethutil.Config.Log.Debugf("(%d) => %x\n", deletedMemory, recAddr)
|
||||
break out
|
||||
*/
|
||||
receiver := vm.state.GetAccount(stack.Pop().Bytes())
|
||||
receiver.AddAmount(closure.object.Amount)
|
||||
|
||||
vm.stateManager.manifest.AddObjectChange(receiver)
|
||||
|
||||
closure.object.state.Purge()
|
||||
|
||||
fallthrough
|
||||
case oSTOP: // Stop the closure
|
||||
return closure.Return(nil), nil
|
||||
default:
|
||||
ethutil.Config.Log.Debugln("Invalid opcode", op)
|
||||
ethutil.Config.Log.Debugf("Invalid opcode %x\n", op)
|
||||
|
||||
return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op)
|
||||
}
|
||||
|
||||
pc.Add(pc, ethutil.Big1)
|
||||
|
||||
if hook != nil {
|
||||
hook(step-1, op, mem, stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) {
|
||||
ethutil.Config.Log.Debugf(" => creating inline tx %x %v %v %v", addr, value, from, length)
|
||||
j := int64(0)
|
||||
dataItems := make([]string, int(length.Uint64()))
|
||||
for i := from.Int64(); i < length.Int64(); i++ {
|
||||
dataItems[j] = contract.GetMem(big.NewInt(j)).Str()
|
||||
j++
|
||||
}
|
||||
|
||||
tx := NewTransaction(addr, value, dataItems)
|
||||
if tx.IsContract() {
|
||||
contract := MakeContract(tx, state)
|
||||
state.UpdateContract(contract)
|
||||
} else {
|
||||
account := state.GetAccount(tx.Recipient)
|
||||
account.Amount.Add(account.Amount, tx.Value)
|
||||
state.UpdateAccount(tx.Recipient, account)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an address from the specified contract's address
|
||||
func contractMemory(state *State, contractAddr []byte, memAddr *big.Int) *big.Int {
|
||||
contract := state.GetContract(contractAddr)
|
||||
if contract == nil {
|
||||
log.Panicf("invalid contract addr %x", contractAddr)
|
||||
}
|
||||
val := state.trie.Get(memAddr.String())
|
||||
|
||||
// decode the object as a big integer
|
||||
decoder := ethutil.NewValueFromBytes([]byte(val))
|
||||
if decoder.IsNil() {
|
||||
return ethutil.BigFalse
|
||||
}
|
||||
|
||||
return decoder.BigInt()
|
||||
}
|
||||
|
@ -1,114 +1,17 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "bytes"
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethdb"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/obscuren/mutan"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
InitFees()
|
||||
|
||||
ethutil.ReadConfig("")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state := NewState(ethutil.NewTrie(db, ""))
|
||||
|
||||
script := Compile([]string{
|
||||
"TXSENDER",
|
||||
"SUICIDE",
|
||||
})
|
||||
|
||||
tx := NewTransaction(ContractAddr, big.NewInt(1e17), script)
|
||||
fmt.Printf("contract addr %x\n", tx.Hash()[12:])
|
||||
contract := MakeContract(tx, state)
|
||||
vm := &Vm{}
|
||||
|
||||
vm.Process(contract, state, RuntimeVars{
|
||||
address: tx.Hash()[12:],
|
||||
blockNumber: 1,
|
||||
sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
|
||||
prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
|
||||
coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
|
||||
time: 1,
|
||||
diff: big.NewInt(256),
|
||||
txValue: tx.Value,
|
||||
txData: tx.Data,
|
||||
})
|
||||
}
|
||||
|
||||
func TestRun1(t *testing.T) {
|
||||
ethutil.ReadConfig("")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state := NewState(ethutil.NewTrie(db, ""))
|
||||
|
||||
script := Compile([]string{
|
||||
"PUSH", "0",
|
||||
"PUSH", "0",
|
||||
"TXSENDER",
|
||||
"PUSH", "10000000",
|
||||
"MKTX",
|
||||
})
|
||||
fmt.Println(ethutil.NewValue(script))
|
||||
|
||||
tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
|
||||
fmt.Printf("contract addr %x\n", tx.Hash()[12:])
|
||||
contract := MakeContract(tx, state)
|
||||
vm := &Vm{}
|
||||
|
||||
vm.Process(contract, state, RuntimeVars{
|
||||
address: tx.Hash()[12:],
|
||||
blockNumber: 1,
|
||||
sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
|
||||
prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
|
||||
coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
|
||||
time: 1,
|
||||
diff: big.NewInt(256),
|
||||
txValue: tx.Value,
|
||||
txData: tx.Data,
|
||||
})
|
||||
}
|
||||
|
||||
func TestRun2(t *testing.T) {
|
||||
ethutil.ReadConfig("")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state := NewState(ethutil.NewTrie(db, ""))
|
||||
|
||||
script := Compile([]string{
|
||||
"PUSH", "0",
|
||||
"PUSH", "0",
|
||||
"TXSENDER",
|
||||
"PUSH", "10000000",
|
||||
"MKTX",
|
||||
})
|
||||
fmt.Println(ethutil.NewValue(script))
|
||||
|
||||
tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
|
||||
fmt.Printf("contract addr %x\n", tx.Hash()[12:])
|
||||
contract := MakeContract(tx, state)
|
||||
vm := &Vm{}
|
||||
|
||||
vm.Process(contract, state, RuntimeVars{
|
||||
address: tx.Hash()[12:],
|
||||
blockNumber: 1,
|
||||
sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
|
||||
prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
|
||||
coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
|
||||
time: 1,
|
||||
diff: big.NewInt(256),
|
||||
txValue: tx.Value,
|
||||
txData: tx.Data,
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
||||
// XXX Full stack test
|
||||
func TestRun3(t *testing.T) {
|
||||
ethutil.ReadConfig("")
|
||||
|
||||
@ -127,12 +30,12 @@ func TestRun3(t *testing.T) {
|
||||
"PUSH", "0",
|
||||
"RETURN",
|
||||
})
|
||||
tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
|
||||
tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script)
|
||||
addr := tx.Hash()[12:]
|
||||
contract := MakeContract(tx, state)
|
||||
state.UpdateContract(contract)
|
||||
|
||||
callerScript := ethutil.Compile(
|
||||
callerScript := ethutil.Assemble(
|
||||
"PUSH", 1337, // Argument
|
||||
"PUSH", 65, // argument mem offset
|
||||
"MSTORE",
|
||||
@ -149,7 +52,7 @@ func TestRun3(t *testing.T) {
|
||||
"PUSH", 0,
|
||||
"RETURN",
|
||||
)
|
||||
callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript)
|
||||
callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript)
|
||||
|
||||
// Contract addr as test address
|
||||
account := NewAccount(ContractAddr, big.NewInt(10000000))
|
||||
@ -171,4 +74,87 @@ func TestRun3(t *testing.T) {
|
||||
if bytes.Compare(ret, exp) != 0 {
|
||||
t.Errorf("expected return value to be %v, got %v", exp, ret)
|
||||
}
|
||||
}*/
|
||||
|
||||
func TestRun4(t *testing.T) {
|
||||
ethutil.ReadConfig("")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state := NewState(ethutil.NewTrie(db, ""))
|
||||
|
||||
script, err := mutan.Compile(strings.NewReader(`
|
||||
int32 a = 10
|
||||
int32 b = 20
|
||||
if a > b {
|
||||
int32 c = this.Caller()
|
||||
}
|
||||
Exit()
|
||||
`), false)
|
||||
tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), script, nil)
|
||||
addr := tx.Hash()[12:]
|
||||
contract := MakeContract(tx, state)
|
||||
state.UpdateStateObject(contract)
|
||||
fmt.Printf("%x\n", addr)
|
||||
|
||||
callerScript, err := mutan.Compile(strings.NewReader(`
|
||||
// Check if there's any cash in the initial store
|
||||
if store[1000] == 0 {
|
||||
store[1000] = 10^20
|
||||
}
|
||||
|
||||
|
||||
store[1001] = this.Value() * 20
|
||||
store[this.Origin()] = store[this.Origin()] + 1000
|
||||
|
||||
if store[1001] > 20 {
|
||||
store[1001] = 10^50
|
||||
}
|
||||
|
||||
int8 ret = 0
|
||||
int8 arg = 10
|
||||
Call(0xe6a12555fad1fb6eaaaed69001a87313d1fd7b54, 0, 100, arg, ret)
|
||||
|
||||
big t
|
||||
for int8 i = 0; i < 10; i++ {
|
||||
t = i
|
||||
}
|
||||
|
||||
if 10 > 20 {
|
||||
int8 shouldnt = 2
|
||||
} else {
|
||||
int8 should = 1
|
||||
}
|
||||
`), false)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), callerScript, nil)
|
||||
|
||||
// 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, c.script, state, gas, gasPrice, big.NewInt(0))
|
||||
|
||||
vm := NewVm(state, nil, RuntimeVars{
|
||||
Origin: account.Address(),
|
||||
BlockNumber: 1,
|
||||
PrevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
|
||||
Coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
|
||||
Time: 1,
|
||||
Diff: big.NewInt(256),
|
||||
})
|
||||
_, e = callerClosure.Call(vm, nil, nil)
|
||||
if e != nil {
|
||||
fmt.Println("error", e)
|
||||
}
|
||||
fmt.Println("account.Amount =", account.Amount)
|
||||
}
|
||||
|
14
ethereum.go
14
ethereum.go
@ -4,6 +4,7 @@ import (
|
||||
"container/list"
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethdb"
|
||||
"github.com/ethereum/eth-go/ethrpc"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/eth-go/ethwire"
|
||||
"io/ioutil"
|
||||
@ -60,6 +61,10 @@ type Ethereum struct {
|
||||
|
||||
// Specifies the desired amount of maximum peers
|
||||
MaxPeers int
|
||||
|
||||
reactor *ethutil.ReactorEngine
|
||||
|
||||
RpcServer *ethrpc.JsonRpcServer
|
||||
}
|
||||
|
||||
func New(caps Caps, usePnp bool) (*Ethereum, error) {
|
||||
@ -89,6 +94,8 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
|
||||
serverCaps: caps,
|
||||
nat: nat,
|
||||
}
|
||||
ethereum.reactor = ethutil.NewReactorEngine()
|
||||
|
||||
ethereum.txPool = ethchain.NewTxPool(ethereum)
|
||||
ethereum.blockChain = ethchain.NewBlockChain(ethereum)
|
||||
ethereum.stateManager = ethchain.NewStateManager(ethereum)
|
||||
@ -99,6 +106,10 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
|
||||
return ethereum, nil
|
||||
}
|
||||
|
||||
func (s *Ethereum) Reactor() *ethutil.ReactorEngine {
|
||||
return s.reactor
|
||||
}
|
||||
|
||||
func (s *Ethereum) BlockChain() *ethchain.BlockChain {
|
||||
return s.blockChain
|
||||
}
|
||||
@ -328,6 +339,7 @@ func (s *Ethereum) Stop() {
|
||||
|
||||
close(s.quit)
|
||||
|
||||
s.RpcServer.Stop()
|
||||
s.txPool.Stop()
|
||||
s.stateManager.Stop()
|
||||
|
||||
@ -342,7 +354,7 @@ func (s *Ethereum) WaitForShutdown() {
|
||||
func (s *Ethereum) upnpUpdateThread() {
|
||||
// Go off immediately to prevent code duplication, thereafter we renew
|
||||
// lease every 15 minutes.
|
||||
timer := time.NewTimer(0 * time.Second)
|
||||
timer := time.NewTimer(5 * time.Minute)
|
||||
lport, _ := strconv.ParseInt(s.Port, 10, 16)
|
||||
first := true
|
||||
out:
|
||||
|
156
ethminer/miner.go
Normal file
156
ethminer/miner.go
Normal file
@ -0,0 +1,156 @@
|
||||
package ethminer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/eth-go/ethwire"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Miner struct {
|
||||
pow ethchain.PoW
|
||||
ethereum ethchain.EthManager
|
||||
coinbase []byte
|
||||
reactChan chan ethutil.React
|
||||
txs []*ethchain.Transaction
|
||||
uncles []*ethchain.Block
|
||||
block *ethchain.Block
|
||||
powChan chan []byte
|
||||
quitChan chan ethutil.React
|
||||
}
|
||||
|
||||
func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) Miner {
|
||||
reactChan := make(chan ethutil.React, 1) // This is the channel that receives 'updates' when ever a new transaction or block comes in
|
||||
powChan := make(chan []byte, 1) // This is the channel that receives valid sha hases for a given block
|
||||
quitChan := make(chan ethutil.React, 1) // This is the channel that can exit the miner thread
|
||||
|
||||
ethereum.Reactor().Subscribe("newBlock", reactChan)
|
||||
ethereum.Reactor().Subscribe("newTx", reactChan)
|
||||
|
||||
// We need the quit chan to be a Reactor event.
|
||||
// The POW search method is actually blocking and if we don't
|
||||
// listen to the reactor events inside of the pow itself
|
||||
// The miner overseer will never get the reactor events themselves
|
||||
// Only after the miner will find the sha
|
||||
ethereum.Reactor().Subscribe("newBlock", quitChan)
|
||||
ethereum.Reactor().Subscribe("newTx", quitChan)
|
||||
|
||||
miner := Miner{
|
||||
pow: ðchain.EasyPow{},
|
||||
ethereum: ethereum,
|
||||
coinbase: coinbase,
|
||||
reactChan: reactChan,
|
||||
powChan: powChan,
|
||||
quitChan: quitChan,
|
||||
}
|
||||
|
||||
// Insert initial TXs in our little miner 'pool'
|
||||
miner.txs = ethereum.TxPool().Flush()
|
||||
miner.block = ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
|
||||
|
||||
return miner
|
||||
}
|
||||
func (miner *Miner) Start() {
|
||||
// Prepare inital block
|
||||
miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
|
||||
go func() { miner.listener() }()
|
||||
}
|
||||
func (miner *Miner) listener() {
|
||||
for {
|
||||
select {
|
||||
case chanMessage := <-miner.reactChan:
|
||||
if block, ok := chanMessage.Resource.(*ethchain.Block); ok {
|
||||
log.Println("[MINER] Got new block via Reactor")
|
||||
if bytes.Compare(miner.ethereum.BlockChain().CurrentBlock.Hash(), block.Hash()) == 0 {
|
||||
// TODO: Perhaps continue mining to get some uncle rewards
|
||||
log.Println("[MINER] New top block found resetting state")
|
||||
|
||||
// Filter out which Transactions we have that were not in this block
|
||||
var newtxs []*ethchain.Transaction
|
||||
for _, tx := range miner.txs {
|
||||
found := false
|
||||
for _, othertx := range block.Transactions() {
|
||||
if bytes.Compare(tx.Hash(), othertx.Hash()) == 0 {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if found == false {
|
||||
newtxs = append(newtxs, tx)
|
||||
}
|
||||
}
|
||||
miner.txs = newtxs
|
||||
|
||||
// Setup a fresh state to mine on
|
||||
miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
|
||||
|
||||
} else {
|
||||
if bytes.Compare(block.PrevHash, miner.ethereum.BlockChain().CurrentBlock.PrevHash) == 0 {
|
||||
log.Println("[MINER] Adding uncle block")
|
||||
miner.uncles = append(miner.uncles, block)
|
||||
miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tx, ok := chanMessage.Resource.(*ethchain.Transaction); ok {
|
||||
//log.Println("[MINER] Got new transaction from Reactor", tx)
|
||||
found := false
|
||||
for _, ctx := range miner.txs {
|
||||
if found = bytes.Compare(ctx.Hash(), tx.Hash()) == 0; found {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
if found == false {
|
||||
//log.Println("[MINER] We did not know about this transaction, adding")
|
||||
miner.txs = append(miner.txs, tx)
|
||||
miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
|
||||
miner.block.SetTransactions(miner.txs)
|
||||
} else {
|
||||
//log.Println("[MINER] We already had this transaction, ignoring")
|
||||
}
|
||||
}
|
||||
default:
|
||||
log.Println("[MINER] Mining on block. Includes", len(miner.txs), "transactions")
|
||||
|
||||
// Apply uncles
|
||||
if len(miner.uncles) > 0 {
|
||||
miner.block.SetUncles(miner.uncles)
|
||||
}
|
||||
|
||||
// FIXME @ maranh, first block doesn't need this. Everything after the first block does.
|
||||
// Please check and fix
|
||||
miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
|
||||
// Apply all transactions to the block
|
||||
miner.ethereum.StateManager().ApplyTransactions(miner.block, miner.block.Transactions())
|
||||
miner.ethereum.StateManager().AccumelateRewards(miner.block)
|
||||
|
||||
// Search the nonce
|
||||
//log.Println("[MINER] Initialision complete, starting mining")
|
||||
miner.block.Nonce = miner.pow.Search(miner.block, miner.quitChan)
|
||||
if miner.block.Nonce != nil {
|
||||
miner.ethereum.StateManager().PrepareDefault(miner.block)
|
||||
err := miner.ethereum.StateManager().ProcessBlock(miner.block, true)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
miner.txs = []*ethchain.Transaction{} // Move this somewhere neat
|
||||
miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
|
||||
} else {
|
||||
|
||||
/*
|
||||
// XXX @maranh This is already done in the state manager, why a 2nd time?
|
||||
if !miner.ethereum.StateManager().Pow.Verify(miner.block.HashNoNonce(), miner.block.Difficulty, miner.block.Nonce) {
|
||||
log.Printf("Second stage verification error: Block's nonce is invalid (= %v)\n", ethutil.Hex(miner.block.Nonce))
|
||||
}
|
||||
*/
|
||||
miner.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{miner.block.Value().Val})
|
||||
log.Printf("[MINER] 🔨 Mined block %x\n", miner.block.Hash())
|
||||
|
||||
miner.txs = []*ethchain.Transaction{} // Move this somewhere neat
|
||||
miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
147
ethpub/pub.go
Normal file
147
ethpub/pub.go
Normal file
@ -0,0 +1,147 @@
|
||||
package ethpub
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
)
|
||||
|
||||
type PEthereum struct {
|
||||
stateManager *ethchain.StateManager
|
||||
blockChain *ethchain.BlockChain
|
||||
txPool *ethchain.TxPool
|
||||
}
|
||||
|
||||
func NewPEthereum(sm *ethchain.StateManager, bc *ethchain.BlockChain, txp *ethchain.TxPool) *PEthereum {
|
||||
return &PEthereum{
|
||||
sm,
|
||||
bc,
|
||||
txp,
|
||||
}
|
||||
}
|
||||
|
||||
func (lib *PEthereum) GetBlock(hexHash string) *PBlock {
|
||||
hash := ethutil.FromHex(hexHash)
|
||||
|
||||
block := lib.blockChain.GetBlock(hash)
|
||||
|
||||
var blockInfo *PBlock
|
||||
|
||||
if block != nil {
|
||||
blockInfo = &PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
|
||||
} else {
|
||||
blockInfo = &PBlock{Number: -1, Hash: ""}
|
||||
}
|
||||
|
||||
return blockInfo
|
||||
}
|
||||
|
||||
func (lib *PEthereum) GetKey() *PKey {
|
||||
keyPair, err := ethchain.NewKeyPairFromSec(ethutil.Config.Db.GetKeys()[0].PrivateKey)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NewPKey(keyPair)
|
||||
}
|
||||
|
||||
func (lib *PEthereum) GetStateObject(address string) *PStateObject {
|
||||
stateObject := lib.stateManager.ProcState().GetContract(ethutil.FromHex(address))
|
||||
if stateObject != nil {
|
||||
return NewPStateObject(stateObject)
|
||||
}
|
||||
|
||||
// See GetStorage for explanation on "nil"
|
||||
return NewPStateObject(nil)
|
||||
}
|
||||
|
||||
func (lib *PEthereum) GetStorage(address, storageAddress string) string {
|
||||
return lib.GetStateObject(address).GetStorage(storageAddress)
|
||||
}
|
||||
|
||||
func (lib *PEthereum) GetTxCount(address string) int {
|
||||
return lib.GetStateObject(address).Nonce()
|
||||
}
|
||||
|
||||
func (lib *PEthereum) IsContract(address string) bool {
|
||||
return lib.GetStateObject(address).IsContract()
|
||||
}
|
||||
|
||||
func (lib *PEthereum) SecretToAddress(key string) string {
|
||||
pair, err := ethchain.NewKeyPairFromSec(ethutil.FromHex(key))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return ethutil.Hex(pair.Address())
|
||||
}
|
||||
|
||||
func (lib *PEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) (*PReceipt, error) {
|
||||
return lib.createTx(key, recipient, valueStr, gasStr, gasPriceStr, dataStr, "")
|
||||
}
|
||||
|
||||
func (lib *PEthereum) Create(key, valueStr, gasStr, gasPriceStr, initStr, bodyStr string) (*PReceipt, error) {
|
||||
return lib.createTx(key, "", valueStr, gasStr, gasPriceStr, initStr, bodyStr)
|
||||
}
|
||||
|
||||
func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, initStr, scriptStr string) (*PReceipt, error) {
|
||||
var hash []byte
|
||||
var contractCreation bool
|
||||
if len(recipient) == 0 {
|
||||
contractCreation = true
|
||||
} else {
|
||||
hash = ethutil.FromHex(recipient)
|
||||
}
|
||||
|
||||
keyPair, err := ethchain.NewKeyPairFromSec([]byte(ethutil.FromHex(key)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value := ethutil.Big(valueStr)
|
||||
gas := ethutil.Big(gasStr)
|
||||
gasPrice := ethutil.Big(gasPriceStr)
|
||||
var tx *ethchain.Transaction
|
||||
// Compile and assemble the given data
|
||||
if contractCreation {
|
||||
var initScript, mainScript []byte
|
||||
var err error
|
||||
if ethutil.IsHex(initStr) {
|
||||
initScript = ethutil.FromHex(initStr[2:])
|
||||
} else {
|
||||
initScript, err = ethutil.Compile(initStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if ethutil.IsHex(scriptStr) {
|
||||
mainScript = ethutil.FromHex(scriptStr[2:])
|
||||
} else {
|
||||
mainScript, err = ethutil.Compile(scriptStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
tx = ethchain.NewContractCreationTx(value, gas, gasPrice, mainScript, initScript)
|
||||
} else {
|
||||
// Just in case it was submitted as a 0x prefixed string
|
||||
if len(initStr) > 0 && initStr[0:2] == "0x" {
|
||||
initStr = initStr[2:len(initStr)]
|
||||
}
|
||||
tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, ethutil.FromHex(initStr))
|
||||
}
|
||||
|
||||
acc := lib.stateManager.GetAddrState(keyPair.Address())
|
||||
tx.Nonce = acc.Nonce
|
||||
tx.Sign(keyPair.PrivateKey)
|
||||
lib.txPool.QueueTransaction(tx)
|
||||
|
||||
if contractCreation {
|
||||
ethutil.Config.Log.Infof("Contract addr %x", tx.CreationAddress())
|
||||
} else {
|
||||
ethutil.Config.Log.Infof("Tx hash %x", tx.Hash())
|
||||
}
|
||||
|
||||
return NewPReciept(contractCreation, tx.CreationAddress(), tx.Hash(), keyPair.Address()), nil
|
||||
}
|
123
ethpub/types.go
Normal file
123
ethpub/types.go
Normal file
@ -0,0 +1,123 @@
|
||||
package ethpub
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"github.com/ethereum/eth-go/ethchain"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
)
|
||||
|
||||
// Block interface exposed to QML
|
||||
type PBlock struct {
|
||||
Number int `json:"number"`
|
||||
Hash string `json:"hash"`
|
||||
}
|
||||
|
||||
// Creates a new QML Block from a chain block
|
||||
func NewPBlock(block *ethchain.Block) *PBlock {
|
||||
info := block.BlockInfo()
|
||||
hash := hex.EncodeToString(block.Hash())
|
||||
|
||||
return &PBlock{Number: int(info.Number), Hash: hash}
|
||||
}
|
||||
|
||||
type PTx struct {
|
||||
Value, Hash, Address string
|
||||
Contract bool
|
||||
}
|
||||
|
||||
func NewPTx(tx *ethchain.Transaction) *PTx {
|
||||
hash := hex.EncodeToString(tx.Hash())
|
||||
sender := hex.EncodeToString(tx.Recipient)
|
||||
isContract := len(tx.Data) > 0
|
||||
|
||||
return &PTx{Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: sender, Contract: isContract}
|
||||
}
|
||||
|
||||
type PKey struct {
|
||||
Address string `json:"address"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
}
|
||||
|
||||
func NewPKey(key *ethchain.KeyPair) *PKey {
|
||||
return &PKey{ethutil.Hex(key.Address()), ethutil.Hex(key.PrivateKey), ethutil.Hex(key.PublicKey)}
|
||||
}
|
||||
|
||||
type PReceipt struct {
|
||||
CreatedContract bool `json:"createdContract"`
|
||||
Address string `json:"address"`
|
||||
Hash string `json:"hash"`
|
||||
Sender string `json:"sender"`
|
||||
}
|
||||
|
||||
func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt {
|
||||
return &PReceipt{
|
||||
contractCreation,
|
||||
ethutil.Hex(creationAddress),
|
||||
ethutil.Hex(hash),
|
||||
ethutil.Hex(address),
|
||||
}
|
||||
}
|
||||
|
||||
type PStateObject struct {
|
||||
object *ethchain.StateObject
|
||||
}
|
||||
|
||||
func NewPStateObject(object *ethchain.StateObject) *PStateObject {
|
||||
return &PStateObject{object: object}
|
||||
}
|
||||
|
||||
func (c *PStateObject) GetStorage(address string) string {
|
||||
// Because somehow, even if you return nil to QML it
|
||||
// still has some magical object so we can't rely on
|
||||
// undefined or null at the QML side
|
||||
if c.object != nil {
|
||||
val := c.object.GetMem(ethutil.Big("0x" + address))
|
||||
|
||||
return val.BigInt().String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *PStateObject) Value() string {
|
||||
if c.object != nil {
|
||||
return c.object.Amount.String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *PStateObject) Address() string {
|
||||
if c.object != nil {
|
||||
return ethutil.Hex(c.object.Address())
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *PStateObject) Nonce() int {
|
||||
if c.object != nil {
|
||||
return int(c.object.Nonce)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *PStateObject) IsContract() bool {
|
||||
if c.object != nil {
|
||||
return len(c.object.Script()) > 0
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type PStorageState struct {
|
||||
StateAddress string
|
||||
Address string
|
||||
Value string
|
||||
}
|
||||
|
||||
func NewPStorageState(storageObject *ethchain.StorageState) *PStorageState {
|
||||
return &PStorageState{ethutil.Hex(storageObject.StateAddress), ethutil.Hex(storageObject.Address), storageObject.Value.String()}
|
||||
}
|
215
ethrpc/packages.go
Normal file
215
ethrpc/packages.go
Normal file
@ -0,0 +1,215 @@
|
||||
package ethrpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/ethereum/eth-go/ethpub"
|
||||
_ "log"
|
||||
)
|
||||
|
||||
type EthereumApi struct {
|
||||
ethp *ethpub.PEthereum
|
||||
}
|
||||
|
||||
type JsonArgs interface {
|
||||
requirements() error
|
||||
}
|
||||
|
||||
type BlockResponse struct {
|
||||
JsonResponse
|
||||
}
|
||||
type GetBlockArgs struct {
|
||||
BlockNumber int
|
||||
Hash string
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
Error bool `json:"error"`
|
||||
ErrorText string `json:"errorText"`
|
||||
}
|
||||
|
||||
type JsonResponse interface {
|
||||
}
|
||||
|
||||
type SuccessRes struct {
|
||||
Error bool `json:"error"`
|
||||
Result JsonResponse `json:"result"`
|
||||
}
|
||||
|
||||
func NewSuccessRes(object JsonResponse) string {
|
||||
e := SuccessRes{Error: false, Result: object}
|
||||
res, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
// This should never happen
|
||||
panic("Creating json error response failed, help")
|
||||
}
|
||||
success := string(res)
|
||||
return success
|
||||
}
|
||||
|
||||
func NewErrorResponse(msg string) error {
|
||||
e := ErrorResponse{Error: true, ErrorText: msg}
|
||||
res, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
// This should never happen
|
||||
panic("Creating json error response failed, help")
|
||||
}
|
||||
newErr := errors.New(string(res))
|
||||
return newErr
|
||||
}
|
||||
|
||||
func (b *GetBlockArgs) requirements() error {
|
||||
if b.BlockNumber == 0 && b.Hash == "" {
|
||||
return NewErrorResponse("GetBlock requires either a block 'number' or a block 'hash' as argument")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *string) error {
|
||||
err := args.requirements()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Do something
|
||||
block := p.ethp.GetBlock(args.Hash)
|
||||
*reply = NewSuccessRes(block)
|
||||
return nil
|
||||
}
|
||||
|
||||
type NewTxArgs struct {
|
||||
Sec string
|
||||
Recipient string
|
||||
Value string
|
||||
Gas string
|
||||
GasPrice string
|
||||
Init string
|
||||
Body string
|
||||
}
|
||||
type TxResponse struct {
|
||||
Hash string
|
||||
}
|
||||
|
||||
func (a *NewTxArgs) requirements() error {
|
||||
if a.Recipient == "" {
|
||||
return NewErrorResponse("Transact requires a 'recipient' address as argument")
|
||||
}
|
||||
if a.Value == "" {
|
||||
return NewErrorResponse("Transact requires a 'value' as argument")
|
||||
}
|
||||
if a.Gas == "" {
|
||||
return NewErrorResponse("Transact requires a 'gas' value as argument")
|
||||
}
|
||||
if a.GasPrice == "" {
|
||||
return NewErrorResponse("Transact requires a 'gasprice' value as argument")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *NewTxArgs) requirementsContract() error {
|
||||
if a.Value == "" {
|
||||
return NewErrorResponse("Create requires a 'value' as argument")
|
||||
}
|
||||
if a.Gas == "" {
|
||||
return NewErrorResponse("Create requires a 'gas' value as argument")
|
||||
}
|
||||
if a.GasPrice == "" {
|
||||
return NewErrorResponse("Create requires a 'gasprice' value as argument")
|
||||
}
|
||||
if a.Body == "" {
|
||||
return NewErrorResponse("Create requires a 'body' value as argument")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *EthereumApi) Transact(args *NewTxArgs, reply *string) error {
|
||||
err := args.requirements()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, _ := p.ethp.Transact(p.ethp.GetKey().PrivateKey, args.Recipient, args.Value, args.Gas, args.GasPrice, args.Body)
|
||||
*reply = NewSuccessRes(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *EthereumApi) Create(args *NewTxArgs, reply *string) error {
|
||||
err := args.requirementsContract()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, _ := p.ethp.Create(p.ethp.GetKey().PrivateKey, args.Value, args.Gas, args.GasPrice, args.Init, args.Body)
|
||||
*reply = NewSuccessRes(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *EthereumApi) GetKey(args interface{}, reply *string) error {
|
||||
*reply = NewSuccessRes(p.ethp.GetKey())
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetStorageArgs struct {
|
||||
Address string
|
||||
Key string
|
||||
}
|
||||
|
||||
func (a *GetStorageArgs) requirements() error {
|
||||
if a.Address == "" {
|
||||
return NewErrorResponse("GetStorageAt requires an 'address' value as argument")
|
||||
}
|
||||
if a.Key == "" {
|
||||
return NewErrorResponse("GetStorageAt requires an 'key' value as argument")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetStorageAtRes struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *string) error {
|
||||
err := args.requirements()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
state := p.ethp.GetStateObject(args.Address)
|
||||
value := state.GetStorage(args.Key)
|
||||
*reply = NewSuccessRes(GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value})
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetBalanceArgs struct {
|
||||
Address string
|
||||
}
|
||||
|
||||
func (a *GetBalanceArgs) requirements() error {
|
||||
if a.Address == "" {
|
||||
return NewErrorResponse("GetBalanceAt requires an 'address' value as argument")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BalanceRes struct {
|
||||
Balance string `json:"balance"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *string) error {
|
||||
err := args.requirements()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
state := p.ethp.GetStateObject(args.Address)
|
||||
*reply = NewSuccessRes(BalanceRes{Balance: state.Value(), Address: args.Address})
|
||||
return nil
|
||||
}
|
||||
|
||||
type TestRes struct {
|
||||
JsonResponse `json:"-"`
|
||||
Answer int `json:"answer"`
|
||||
}
|
||||
|
||||
func (p *EthereumApi) Test(args *GetBlockArgs, reply *string) error {
|
||||
*reply = NewSuccessRes(TestRes{Answer: 15})
|
||||
return nil
|
||||
}
|
62
ethrpc/server.go
Normal file
62
ethrpc/server.go
Normal file
@ -0,0 +1,62 @@
|
||||
package ethrpc
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethpub"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"net"
|
||||
"net/rpc"
|
||||
"net/rpc/jsonrpc"
|
||||
)
|
||||
|
||||
type JsonRpcServer struct {
|
||||
quit chan bool
|
||||
listener net.Listener
|
||||
ethp *ethpub.PEthereum
|
||||
}
|
||||
|
||||
func (s *JsonRpcServer) exitHandler() {
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-s.quit:
|
||||
s.listener.Close()
|
||||
break out
|
||||
}
|
||||
}
|
||||
|
||||
ethutil.Config.Log.Infoln("[JSON] Shutdown JSON-RPC server")
|
||||
}
|
||||
|
||||
func (s *JsonRpcServer) Stop() {
|
||||
close(s.quit)
|
||||
}
|
||||
|
||||
func (s *JsonRpcServer) Start() {
|
||||
ethutil.Config.Log.Infoln("[JSON] Starting JSON-RPC server")
|
||||
go s.exitHandler()
|
||||
rpc.Register(&EthereumApi{ethp: s.ethp})
|
||||
rpc.HandleHTTP()
|
||||
|
||||
for {
|
||||
conn, err := s.listener.Accept()
|
||||
if err != nil {
|
||||
ethutil.Config.Log.Infoln("[JSON] Error starting JSON-RPC:", err)
|
||||
break
|
||||
}
|
||||
ethutil.Config.Log.Debugln("[JSON] Incoming request.")
|
||||
go jsonrpc.ServeConn(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func NewJsonRpcServer(ethp *ethpub.PEthereum) *JsonRpcServer {
|
||||
l, err := net.Listen("tcp", ":30304")
|
||||
if err != nil {
|
||||
ethutil.Config.Log.Infoln("Error starting JSON-RPC")
|
||||
}
|
||||
|
||||
return &JsonRpcServer{
|
||||
listener: l,
|
||||
quit: make(chan bool),
|
||||
ethp: ethp,
|
||||
}
|
||||
}
|
@ -12,7 +12,9 @@ var BigTrue *big.Int = big.NewInt(1)
|
||||
// False
|
||||
var BigFalse *big.Int = big.NewInt(0)
|
||||
|
||||
// Returns the power of two integers
|
||||
// Big pow
|
||||
//
|
||||
// Returns the power of two big integers
|
||||
func BigPow(a, b int) *big.Int {
|
||||
c := new(big.Int)
|
||||
c.Exp(big.NewInt(int64(a)), big.NewInt(int64(b)), big.NewInt(0))
|
||||
@ -20,7 +22,9 @@ func BigPow(a, b int) *big.Int {
|
||||
return c
|
||||
}
|
||||
|
||||
// Like big.NewInt(uint64); this takes a string instead.
|
||||
// Big
|
||||
//
|
||||
// Shortcut for new(big.Int).SetString(..., 0)
|
||||
func Big(num string) *big.Int {
|
||||
n := new(big.Int)
|
||||
n.SetString(num, 0)
|
||||
@ -28,7 +32,9 @@ func Big(num string) *big.Int {
|
||||
return n
|
||||
}
|
||||
|
||||
// Like big.NewInt(uint64); this takes a byte buffer instead.
|
||||
// BigD
|
||||
//
|
||||
// Shortcut for new(big.Int).SetBytes(...)
|
||||
func BigD(data []byte) *big.Int {
|
||||
n := new(big.Int)
|
||||
n.SetBytes(data)
|
||||
@ -36,17 +42,30 @@ func BigD(data []byte) *big.Int {
|
||||
return n
|
||||
}
|
||||
|
||||
// Big to bytes
|
||||
//
|
||||
// Returns the bytes of a big integer with the size specified by **base**
|
||||
// Attempts to pad the byte array with zeros.
|
||||
func BigToBytes(num *big.Int, base int) []byte {
|
||||
ret := make([]byte, base/8)
|
||||
|
||||
return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...)
|
||||
}
|
||||
|
||||
// Functions like the build in "copy" function
|
||||
// but works on big integers
|
||||
func BigCopy(src *big.Int) (ret *big.Int) {
|
||||
ret = new(big.Int)
|
||||
ret.Add(ret, src)
|
||||
|
||||
return
|
||||
// Big copy
|
||||
//
|
||||
// Creates a copy of the given big integer
|
||||
func BigCopy(src *big.Int) *big.Int {
|
||||
return new(big.Int).Set(src)
|
||||
}
|
||||
|
||||
// Big max
|
||||
//
|
||||
// Returns the maximum size big integer
|
||||
func BigMax(x, y *big.Int) *big.Int {
|
||||
if x.Cmp(y) <= 0 {
|
||||
return x
|
||||
}
|
||||
|
||||
return y
|
||||
}
|
||||
|
@ -6,6 +6,9 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Number to bytes
|
||||
//
|
||||
// Returns the number in bytes with the specified base
|
||||
func NumberToBytes(num interface{}, bits int) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
err := binary.Write(buf, binary.BigEndian, num)
|
||||
@ -16,6 +19,9 @@ func NumberToBytes(num interface{}, bits int) []byte {
|
||||
return buf.Bytes()[buf.Len()-(bits/8):]
|
||||
}
|
||||
|
||||
// Bytes to number
|
||||
//
|
||||
// Attempts to cast a byte slice to a unsigned integer
|
||||
func BytesToNumber(b []byte) uint64 {
|
||||
var number uint64
|
||||
|
||||
@ -32,7 +38,9 @@ func BytesToNumber(b []byte) uint64 {
|
||||
return number
|
||||
}
|
||||
|
||||
// Read variable integer in big endian
|
||||
// Read variable int
|
||||
//
|
||||
// Read a variable length number in big endian byte order
|
||||
func ReadVarint(reader *bytes.Reader) (ret uint64) {
|
||||
if reader.Len() == 8 {
|
||||
var num uint64
|
||||
@ -55,6 +63,9 @@ func ReadVarint(reader *bytes.Reader) (ret uint64) {
|
||||
return ret
|
||||
}
|
||||
|
||||
// Binary length
|
||||
//
|
||||
// Returns the true binary length of the given number
|
||||
func BinaryLength(num int) int {
|
||||
if num == 0 {
|
||||
return 0
|
||||
@ -62,3 +73,18 @@ func BinaryLength(num int) int {
|
||||
|
||||
return 1 + BinaryLength(num>>8)
|
||||
}
|
||||
|
||||
// Copy bytes
|
||||
//
|
||||
// Returns an exact copy of the provided bytes
|
||||
func CopyBytes(b []byte) (copiedBytes []byte) {
|
||||
copiedBytes = make([]byte, len(b))
|
||||
copy(copiedBytes, b)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func IsHex(str string) bool {
|
||||
l := len(str)
|
||||
return l >= 4 && l%2 == 0 && str[0:2] == "0x"
|
||||
}
|
||||
|
@ -5,16 +5,20 @@ import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// The different number of units
|
||||
var (
|
||||
Ether = BigPow(10, 18)
|
||||
Finney = BigPow(10, 15)
|
||||
Szabo = BigPow(10, 12)
|
||||
Vito = BigPow(10, 9)
|
||||
Vita = BigPow(10, 9)
|
||||
Turing = BigPow(10, 6)
|
||||
Eins = BigPow(10, 3)
|
||||
Wei = big.NewInt(1)
|
||||
)
|
||||
|
||||
// Currency to string
|
||||
//
|
||||
// Returns a string representing a human readable format
|
||||
func CurrencyToString(num *big.Int) string {
|
||||
switch {
|
||||
case num.Cmp(Ether) >= 0:
|
||||
@ -23,8 +27,8 @@ func CurrencyToString(num *big.Int) string {
|
||||
return fmt.Sprintf("%v Finney", new(big.Int).Div(num, Finney))
|
||||
case num.Cmp(Szabo) >= 0:
|
||||
return fmt.Sprintf("%v Szabo", new(big.Int).Div(num, Szabo))
|
||||
case num.Cmp(Vito) >= 0:
|
||||
return fmt.Sprintf("%v Vito", new(big.Int).Div(num, Vito))
|
||||
case num.Cmp(Vita) >= 0:
|
||||
return fmt.Sprintf("%v Vita", new(big.Int).Div(num, Vita))
|
||||
case num.Cmp(Turing) >= 0:
|
||||
return fmt.Sprintf("%v Turing", new(big.Int).Div(num, Turing))
|
||||
case num.Cmp(Eins) >= 0:
|
||||
@ -34,8 +38,18 @@ func CurrencyToString(num *big.Int) string {
|
||||
return fmt.Sprintf("%v Wei", num)
|
||||
}
|
||||
|
||||
// Common big integers often used
|
||||
var (
|
||||
Big1 = big.NewInt(1)
|
||||
Big2 = big.NewInt(1)
|
||||
Big0 = big.NewInt(0)
|
||||
Big32 = big.NewInt(32)
|
||||
Big256 = big.NewInt(0xff)
|
||||
)
|
||||
|
||||
// Creates an ethereum address given the bytes and the nonce
|
||||
func CreateAddress(b []byte, nonce *big.Int) []byte {
|
||||
addrBytes := append(b, nonce.Bytes()...)
|
||||
|
||||
return Sha3Bin(addrBytes)[12:]
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Log types available
|
||||
type LogType byte
|
||||
|
||||
const (
|
||||
@ -16,7 +17,7 @@ const (
|
||||
LogTypeFile = 2
|
||||
)
|
||||
|
||||
// Config struct isn't exposed
|
||||
// Config struct
|
||||
type config struct {
|
||||
Db Database
|
||||
|
||||
@ -31,7 +32,9 @@ type config struct {
|
||||
|
||||
var Config *config
|
||||
|
||||
// Read config doesn't read anything yet.
|
||||
// Read config
|
||||
//
|
||||
// Initialize the global Config variable with default settings
|
||||
func ReadConfig(base string) *config {
|
||||
if Config == nil {
|
||||
usr, _ := user.Current()
|
||||
@ -48,7 +51,7 @@ func ReadConfig(base string) *config {
|
||||
}
|
||||
}
|
||||
|
||||
Config = &config{ExecPath: path, Debug: true, Ver: "0.3.1"}
|
||||
Config = &config{ExecPath: path, Debug: true, Ver: "0.5 RC1"}
|
||||
Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug)
|
||||
Config.SetClientString("/Ethereum(G)")
|
||||
}
|
||||
@ -56,6 +59,8 @@ func ReadConfig(base string) *config {
|
||||
return Config
|
||||
}
|
||||
|
||||
// Set client string
|
||||
//
|
||||
func (c *config) SetClientString(str string) {
|
||||
Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, runtime.GOOS)
|
||||
}
|
||||
@ -134,7 +139,7 @@ func (log *Logger) Infoln(v ...interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(len(log.logSys))
|
||||
//fmt.Println(len(log.logSys))
|
||||
for _, logger := range log.logSys {
|
||||
logger.Println(v...)
|
||||
}
|
||||
|
1690
ethutil/mnemonic.go
Normal file
1690
ethutil/mnemonic.go
Normal file
File diff suppressed because it is too large
Load Diff
74
ethutil/mnemonic_test.go
Normal file
74
ethutil/mnemonic_test.go
Normal file
@ -0,0 +1,74 @@
|
||||
package ethutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMnDecode(t *testing.T) {
|
||||
words := []string{
|
||||
"ink",
|
||||
"balance",
|
||||
"gain",
|
||||
"fear",
|
||||
"happen",
|
||||
"melt",
|
||||
"mom",
|
||||
"surface",
|
||||
"stir",
|
||||
"bottle",
|
||||
"unseen",
|
||||
"expression",
|
||||
"important",
|
||||
"curl",
|
||||
"grant",
|
||||
"fairy",
|
||||
"across",
|
||||
"back",
|
||||
"figure",
|
||||
"breast",
|
||||
"nobody",
|
||||
"scratch",
|
||||
"worry",
|
||||
"yesterday",
|
||||
}
|
||||
encode := "c61d43dc5bb7a4e754d111dae8105b6f25356492df5e50ecb33b858d94f8c338"
|
||||
result := MnemonicDecode(words)
|
||||
if encode != result {
|
||||
t.Error("We expected", encode, "got", result, "instead")
|
||||
}
|
||||
}
|
||||
func TestMnEncode(t *testing.T) {
|
||||
encode := "c61d43dc5bb7a4e754d111dae8105b6f25356492df5e50ecb33b858d94f8c338"
|
||||
result := []string{
|
||||
"ink",
|
||||
"balance",
|
||||
"gain",
|
||||
"fear",
|
||||
"happen",
|
||||
"melt",
|
||||
"mom",
|
||||
"surface",
|
||||
"stir",
|
||||
"bottle",
|
||||
"unseen",
|
||||
"expression",
|
||||
"important",
|
||||
"curl",
|
||||
"grant",
|
||||
"fairy",
|
||||
"across",
|
||||
"back",
|
||||
"figure",
|
||||
"breast",
|
||||
"nobody",
|
||||
"scratch",
|
||||
"worry",
|
||||
"yesterday",
|
||||
}
|
||||
words := MnemonicEncode(encode)
|
||||
for i, word := range words {
|
||||
if word != result[i] {
|
||||
t.Error("Mnenonic does not match:", words, result)
|
||||
}
|
||||
}
|
||||
}
|
123
ethutil/package.go
Normal file
123
ethutil/package.go
Normal file
@ -0,0 +1,123 @@
|
||||
package ethutil
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Manifest object
|
||||
//
|
||||
// The manifest object holds all the relevant information supplied with the
|
||||
// the manifest specified in the package
|
||||
type Manifest struct {
|
||||
Entry string
|
||||
Height, Width int
|
||||
}
|
||||
|
||||
// External package
|
||||
//
|
||||
// External package contains the main html file and manifest
|
||||
type ExtPackage struct {
|
||||
EntryHtml string
|
||||
Manifest *Manifest
|
||||
}
|
||||
|
||||
// Read file
|
||||
//
|
||||
// Read a given compressed file and returns the read bytes.
|
||||
// Returns an error otherwise
|
||||
func ReadFile(f *zip.File) ([]byte, error) {
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
content, err := ioutil.ReadAll(rc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return content, nil
|
||||
}
|
||||
|
||||
// Reads manifest
|
||||
//
|
||||
// Reads and returns a manifest object. Returns error otherwise
|
||||
func ReadManifest(m []byte) (*Manifest, error) {
|
||||
var manifest Manifest
|
||||
|
||||
dec := json.NewDecoder(strings.NewReader(string(m)))
|
||||
if err := dec.Decode(&manifest); err == io.EOF {
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &manifest, nil
|
||||
}
|
||||
|
||||
// Find file in archive
|
||||
//
|
||||
// Returns the index of the given file name if it exists. -1 if file not found
|
||||
func FindFileInArchive(fn string, files []*zip.File) (index int) {
|
||||
index = -1
|
||||
// Find the manifest first
|
||||
for i, f := range files {
|
||||
if f.Name == fn {
|
||||
index = i
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Open package
|
||||
//
|
||||
// Opens a prepared ethereum package
|
||||
// Reads the manifest file and determines file contents and returns and
|
||||
// the external package.
|
||||
func OpenPackage(fn string) (*ExtPackage, error) {
|
||||
r, err := zip.OpenReader(fn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
manifestIndex := FindFileInArchive("manifest.json", r.File)
|
||||
|
||||
if manifestIndex < 0 {
|
||||
return nil, fmt.Errorf("No manifest file found in archive")
|
||||
}
|
||||
|
||||
f, err := ReadFile(r.File[manifestIndex])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
manifest, err := ReadManifest(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if manifest.Entry == "" {
|
||||
return nil, fmt.Errorf("Entry file specified but appears to be empty: %s", manifest.Entry)
|
||||
}
|
||||
|
||||
entryIndex := FindFileInArchive(manifest.Entry, r.File)
|
||||
if entryIndex < 0 {
|
||||
return nil, fmt.Errorf("Entry file not found: '%s'", manifest.Entry)
|
||||
}
|
||||
|
||||
f, err = ReadFile(r.File[entryIndex])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extPackage := &ExtPackage{string(f), manifest}
|
||||
|
||||
return extPackage, nil
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
package ethutil
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Op codes
|
||||
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,
|
||||
"CALLDATA": 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
|
||||
"PUSH": 0x50,
|
||||
"POP": 0x51,
|
||||
"DUP": 0x52,
|
||||
"SWAP": 0x53,
|
||||
"MLOAD": 0x54,
|
||||
"MSTORE": 0x55,
|
||||
"MSTORE8": 0x56,
|
||||
"SLOAD": 0x57,
|
||||
"SSTORE": 0x58,
|
||||
"JUMP": 0x59,
|
||||
"JUMPI": 0x5a,
|
||||
"PC": 0x5b,
|
||||
"MSIZE": 0x5c,
|
||||
|
||||
// 0x60 range - closures
|
||||
"CREATE": 0x60,
|
||||
"CALL": 0x61,
|
||||
"RETURN": 0x62,
|
||||
|
||||
// 0x70 range - other
|
||||
"LOG": 0x70,
|
||||
"SUICIDE": 0x7f,
|
||||
}
|
||||
|
||||
func IsOpCode(s string) bool {
|
||||
for key, _ := range OpCodes {
|
||||
if key == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CompileInstr(s interface{}) ([]byte, error) {
|
||||
switch s.(type) {
|
||||
case string:
|
||||
str := s.(string)
|
||||
isOp := IsOpCode(str)
|
||||
if isOp {
|
||||
return []byte{OpCodes[str]}, nil
|
||||
}
|
||||
|
||||
num := new(big.Int)
|
||||
_, success := num.SetString(str, 0)
|
||||
// Assume regular bytes during compilation
|
||||
if !success {
|
||||
num.SetBytes([]byte(str))
|
||||
}
|
||||
|
||||
return num.Bytes(), nil
|
||||
case int:
|
||||
return big.NewInt(int64(s.(int))).Bytes(), nil
|
||||
case []byte:
|
||||
return BigD(s.([]byte)).Bytes(), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func Instr(instr string) (int, []string, error) {
|
||||
|
||||
base := new(big.Int)
|
||||
base.SetString(instr, 0)
|
||||
|
||||
args := make([]string, 7)
|
||||
for i := 0; i < 7; i++ {
|
||||
// int(int(val) / int(math.Pow(256,float64(i)))) % 256
|
||||
exp := BigPow(256, i)
|
||||
num := new(big.Int)
|
||||
num.Div(base, exp)
|
||||
|
||||
args[i] = num.Mod(num, big.NewInt(256)).String()
|
||||
}
|
||||
op, _ := strconv.Atoi(args[0])
|
||||
|
||||
return op, args[1:7], nil
|
||||
}
|
||||
|
||||
// Script compilation functions
|
||||
// Compiles strings to machine code
|
||||
func Compile(instructions ...interface{}) (script []string) {
|
||||
script = make([]string, len(instructions))
|
||||
|
||||
for i, val := range instructions {
|
||||
instr, _ := CompileInstr(val)
|
||||
|
||||
script[i] = string(instr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package ethutil
|
||||
|
||||
/*
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCompile(t *testing.T) {
|
||||
instr, err := CompileInstr("PUSH")
|
||||
|
||||
if err != nil {
|
||||
t.Error("Failed compiling instruction")
|
||||
}
|
||||
|
||||
calc := (48 + 0*256 + 0*int64(math.Pow(256, 2)))
|
||||
if BigD(instr).Int64() != calc {
|
||||
t.Error("Expected", calc, ", got:", instr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidInstr(t *testing.T) {
|
||||
op, args, err := Instr("68163")
|
||||
if err != nil {
|
||||
t.Error("Error decoding instruction")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestInvalidInstr(t *testing.T) {
|
||||
}
|
||||
*/
|
@ -26,16 +26,6 @@ func (coder *RlpEncoder) EncodeData(rlpData interface{}) []byte {
|
||||
return Encode(rlpData)
|
||||
}
|
||||
|
||||
/*
|
||||
func FromBin(data []byte) uint64 {
|
||||
if len(data) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return FromBin(data[:len(data)-1])*256 + uint64(data[len(data)-1])
|
||||
}
|
||||
*/
|
||||
|
||||
const (
|
||||
RlpEmptyList = 0x80
|
||||
RlpEmptyStr = 0x40
|
||||
@ -57,7 +47,7 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} {
|
||||
switch {
|
||||
case char == 0:
|
||||
return nil
|
||||
case char <= 0x7c:
|
||||
case char <= 0x7f:
|
||||
return char
|
||||
|
||||
case char <= 0xb7:
|
||||
@ -186,7 +176,12 @@ func Encode(object interface{}) []byte {
|
||||
case byte:
|
||||
buff.Write(Encode(big.NewInt(int64(t))))
|
||||
case *big.Int:
|
||||
buff.Write(Encode(t.Bytes()))
|
||||
// Not sure how this is possible while we check for
|
||||
if t == nil {
|
||||
buff.WriteByte(0xc0)
|
||||
} else {
|
||||
buff.Write(Encode(t.Bytes()))
|
||||
}
|
||||
case []byte:
|
||||
if len(t) == 1 && t[0] <= 0x7f {
|
||||
buff.Write(t)
|
||||
|
@ -2,6 +2,7 @@ package ethutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
@ -55,6 +56,15 @@ func TestValue(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodeMaran(t *testing.T) {
|
||||
b := NewValue([]interface{}{"dog", 15, []interface{}{"cat", "cat", []interface{}{}}, 1024, "tachikoma"})
|
||||
a := b.Encode()
|
||||
fmt.Println("voor maran", a)
|
||||
f, i := Decode(a, 0)
|
||||
fmt.Println("voor maran 2", f)
|
||||
fmt.Println(i)
|
||||
}
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
strRes := "\x83dog"
|
||||
bytes := Encode("dog")
|
||||
@ -119,6 +129,11 @@ func TestEncodeDecodeBytes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeZero(t *testing.T) {
|
||||
b := NewValue(0).Encode()
|
||||
fmt.Println(b)
|
||||
}
|
||||
|
||||
func BenchmarkEncodeDecode(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
bytes := Encode([]interface{}{"dog", "god", "cat"})
|
||||
|
41
ethutil/script.go
Normal file
41
ethutil/script.go
Normal file
@ -0,0 +1,41 @@
|
||||
package ethutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/obscuren/mutan"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// General compile function
|
||||
func Compile(script string) ([]byte, error) {
|
||||
byteCode, errors := mutan.Compile(strings.NewReader(script), false)
|
||||
if len(errors) > 0 {
|
||||
var errs string
|
||||
for _, er := range errors {
|
||||
if er != nil {
|
||||
errs += er.Error()
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("%v", errs)
|
||||
}
|
||||
|
||||
return byteCode, nil
|
||||
}
|
||||
|
||||
func CompileScript(script string) ([]byte, []byte, error) {
|
||||
// Preprocess
|
||||
mainInput, initInput := mutan.PreProcess(script)
|
||||
// Compile main script
|
||||
mainScript, err := Compile(mainInput)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Compile init script
|
||||
initScript, err := Compile(initInput)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return mainScript, initScript, nil
|
||||
}
|
@ -119,14 +119,29 @@ type Trie struct {
|
||||
cache *Cache
|
||||
}
|
||||
|
||||
func copyRoot(root interface{}) interface{} {
|
||||
var prevRootCopy interface{}
|
||||
if b, ok := root.([]byte); ok {
|
||||
prevRootCopy = CopyBytes(b)
|
||||
} else {
|
||||
prevRootCopy = root
|
||||
}
|
||||
|
||||
return prevRootCopy
|
||||
}
|
||||
|
||||
func NewTrie(db Database, Root interface{}) *Trie {
|
||||
return &Trie{cache: NewCache(db), Root: Root, prevRoot: Root}
|
||||
// Make absolute sure the root is copied
|
||||
r := copyRoot(Root)
|
||||
p := copyRoot(Root)
|
||||
|
||||
return &Trie{cache: NewCache(db), Root: r, prevRoot: p}
|
||||
}
|
||||
|
||||
// Save the cached value to the database.
|
||||
func (t *Trie) Sync() {
|
||||
t.cache.Commit()
|
||||
t.prevRoot = t.Root
|
||||
t.prevRoot = copyRoot(t.Root)
|
||||
}
|
||||
|
||||
func (t *Trie) Undo() {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package ethutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
@ -20,7 +20,12 @@ func (val *Value) String() string {
|
||||
}
|
||||
|
||||
func NewValue(val interface{}) *Value {
|
||||
return &Value{Val: val}
|
||||
t := val
|
||||
if v, ok := val.(*Value); ok {
|
||||
t = v.Val
|
||||
}
|
||||
|
||||
return &Value{Val: t}
|
||||
}
|
||||
|
||||
func (val *Value) Type() reflect.Kind {
|
||||
@ -149,6 +154,15 @@ func (val *Value) IsStr() bool {
|
||||
return val.Type() == reflect.String
|
||||
}
|
||||
|
||||
// Special list checking function. Something is considered
|
||||
// a list if it's of type []interface{}. The list is usually
|
||||
// used in conjunction with rlp decoded streams.
|
||||
func (val *Value) IsList() bool {
|
||||
_, ok := val.Val.([]interface{})
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func (val *Value) IsEmpty() bool {
|
||||
return val.Val == nil || ((val.IsSlice() || val.IsStr()) && val.Len() == 0)
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ const (
|
||||
MsgBlockTy = 0x13
|
||||
MsgGetChainTy = 0x14
|
||||
MsgNotInChainTy = 0x15
|
||||
MsgGetTxsTy = 0x16
|
||||
|
||||
MsgTalkTy = 0xff
|
||||
)
|
||||
@ -46,6 +47,7 @@ var msgTypeToString = map[MsgType]string{
|
||||
MsgTxTy: "Transactions",
|
||||
MsgBlockTy: "Blocks",
|
||||
MsgGetChainTy: "Get chain",
|
||||
MsgGetTxsTy: "Get Txs",
|
||||
MsgNotInChainTy: "Not in chain",
|
||||
}
|
||||
|
||||
|
@ -246,6 +246,10 @@ func soapRequest(url, function, message string) (r *http.Response, err error) {
|
||||
//fmt.Println(fullMessage)
|
||||
|
||||
r, err = http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.Body != nil {
|
||||
defer r.Body.Close()
|
||||
}
|
||||
|
187
peer.go
187
peer.go
@ -125,7 +125,8 @@ type Peer struct {
|
||||
pubkey []byte
|
||||
|
||||
// Indicated whether the node is catching up or not
|
||||
catchingUp bool
|
||||
catchingUp bool
|
||||
blocksRequested int
|
||||
|
||||
Version string
|
||||
}
|
||||
@ -135,15 +136,16 @@ func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer {
|
||||
pubkey := ethutil.NewValueFromBytes(data).Get(2).Bytes()
|
||||
|
||||
return &Peer{
|
||||
outputQueue: make(chan *ethwire.Msg, outputBufferSize),
|
||||
quit: make(chan bool),
|
||||
ethereum: ethereum,
|
||||
conn: conn,
|
||||
inbound: inbound,
|
||||
disconnect: 0,
|
||||
connected: 1,
|
||||
port: 30303,
|
||||
pubkey: pubkey,
|
||||
outputQueue: make(chan *ethwire.Msg, outputBufferSize),
|
||||
quit: make(chan bool),
|
||||
ethereum: ethereum,
|
||||
conn: conn,
|
||||
inbound: inbound,
|
||||
disconnect: 0,
|
||||
connected: 1,
|
||||
port: 30303,
|
||||
pubkey: pubkey,
|
||||
blocksRequested: 10,
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,17 +292,69 @@ func (p *Peer) HandleInbound() {
|
||||
// Get all blocks and process them
|
||||
var block, lastBlock *ethchain.Block
|
||||
var err error
|
||||
|
||||
// 1. Compare the first block over the wire's prev-hash with the hash of your last block
|
||||
// 2. If these two values are the same you can just link the chains together.
|
||||
// [1:0,2:1,3:2] <- Current blocks (format block:previous_block)
|
||||
// [1:0,2:1,3:2,4:3,5:4] <- incoming blocks
|
||||
// == [1,2,3,4,5]
|
||||
// 3. If the values are not the same we will have to go back and calculate the chain with the highest total difficulty
|
||||
// [1:0,2:1,3:2,11:3,12:11,13:12]
|
||||
// [1:0,2:1,3:2,4:3,5:4,6:5]
|
||||
|
||||
// [3:2,11:3,12:11,13:12]
|
||||
// [3:2,4:3,5:4,6:5]
|
||||
// Heb ik dit blok?
|
||||
// Nee: heb ik een blok met PrevHash 3?
|
||||
// Ja: DIVERSION
|
||||
// Nee; Adding to chain
|
||||
|
||||
// See if we can find a common ancestor
|
||||
// 1. Get the earliest block in the package.
|
||||
// 2. Do we have this block?
|
||||
// 3. Yes: Let's continue what we are doing
|
||||
// 4. No: Let's request more blocks back.
|
||||
|
||||
// Make sure we are actually receiving anything
|
||||
if msg.Data.Len()-1 > 1 && p.catchingUp {
|
||||
// We requested blocks and now we need to make sure we have a common ancestor somewhere in these blocks so we can find
|
||||
// common ground to start syncing from
|
||||
lastBlock = ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len() - 1))
|
||||
if !p.ethereum.StateManager().BlockChain().HasBlock(lastBlock.Hash()) {
|
||||
// If we can't find a common ancenstor we need to request more blocks.
|
||||
// FIXME: At one point this won't scale anymore since we are not asking for an offset
|
||||
// we just keep increasing the amount of blocks.
|
||||
//fmt.Println("[PEER] No common ancestor found, requesting more blocks.")
|
||||
p.blocksRequested = p.blocksRequested * 2
|
||||
p.catchingUp = false
|
||||
p.SyncWithBlocks()
|
||||
}
|
||||
|
||||
for i := msg.Data.Len() - 1; i >= 0; i-- {
|
||||
block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
|
||||
// Do we have this block on our chain? If so we can continue
|
||||
if !p.ethereum.StateManager().BlockChain().HasBlock(block.Hash()) {
|
||||
// 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) {
|
||||
//ethutil.Config.Log.Infof("[PEER] Local and foreign chain have diverted after %x, finding best chain!\n", block.PrevHash)
|
||||
if p.ethereum.StateManager().BlockChain().FindCanonicalChainFromMsg(msg, block.PrevHash) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := msg.Data.Len() - 1; i >= 0; i-- {
|
||||
block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
|
||||
|
||||
p.ethereum.StateManager().PrepareDefault(block)
|
||||
err = p.ethereum.StateManager().ProcessBlock(block)
|
||||
err = p.ethereum.StateManager().ProcessBlock(block, false)
|
||||
|
||||
if err != nil {
|
||||
if ethutil.Config.Debug {
|
||||
ethutil.Config.Log.Infof("[PEER] Block %x failed\n", block.Hash())
|
||||
ethutil.Config.Log.Infof("[PEER] %v\n", err)
|
||||
ethutil.Config.Log.Infoln(block)
|
||||
}
|
||||
break
|
||||
} else {
|
||||
@ -313,7 +367,7 @@ func (p *Peer) HandleInbound() {
|
||||
if ethchain.IsParentErr(err) {
|
||||
ethutil.Config.Log.Infoln("Attempting to catch up")
|
||||
p.catchingUp = false
|
||||
p.CatchupWithPeer()
|
||||
p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
|
||||
} else if ethchain.IsValidationErr(err) {
|
||||
// TODO
|
||||
}
|
||||
@ -326,7 +380,7 @@ func (p *Peer) HandleInbound() {
|
||||
ethutil.Config.Log.Infof("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash)
|
||||
}
|
||||
p.catchingUp = false
|
||||
p.CatchupWithPeer()
|
||||
p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
|
||||
}
|
||||
}
|
||||
case ethwire.MsgTxTy:
|
||||
@ -334,7 +388,8 @@ func (p *Peer) HandleInbound() {
|
||||
// in the TxPool where it will undergo validation and
|
||||
// processing when a new block is found
|
||||
for i := 0; i < msg.Data.Len(); i++ {
|
||||
p.ethereum.TxPool().QueueTransaction(ethchain.NewTransactionFromData(msg.Data.Get(i).Encode()))
|
||||
tx := ethchain.NewTransactionFromValue(msg.Data.Get(i))
|
||||
p.ethereum.TxPool().QueueTransaction(tx)
|
||||
}
|
||||
case ethwire.MsgGetPeersTy:
|
||||
// Flag this peer as a 'requested of new peers' this to
|
||||
@ -373,11 +428,11 @@ func (p *Peer) HandleInbound() {
|
||||
// Amount of parents in the canonical chain
|
||||
//amountOfBlocks := msg.Data.Get(l).AsUint()
|
||||
amountOfBlocks := uint64(100)
|
||||
|
||||
// Check each SHA block hash from the message and determine whether
|
||||
// the SHA is in the database
|
||||
for i := 0; i < l; i++ {
|
||||
if data :=
|
||||
msg.Data.Get(i).Bytes(); p.ethereum.StateManager().BlockChain().HasBlock(data) {
|
||||
if data := msg.Data.Get(i).Bytes(); p.ethereum.StateManager().BlockChain().HasBlock(data) {
|
||||
parent = p.ethereum.BlockChain().GetBlock(data)
|
||||
break
|
||||
}
|
||||
@ -385,9 +440,14 @@ func (p *Peer) HandleInbound() {
|
||||
|
||||
// If a parent is found send back a reply
|
||||
if parent != nil {
|
||||
ethutil.Config.Log.Debugf("[PEER] Found conical block, returning chain from: %x ", parent.Hash())
|
||||
chain := p.ethereum.BlockChain().GetChainFromHash(parent.Hash(), amountOfBlocks)
|
||||
p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, chain))
|
||||
if len(chain) > 0 {
|
||||
ethutil.Config.Log.Debugf("[PEER] Returning %d blocks: %x ", len(chain), parent.Hash())
|
||||
p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, chain))
|
||||
}
|
||||
} else {
|
||||
ethutil.Config.Log.Debugf("[PEER] Could not find a similar block")
|
||||
// If no blocks are found we send back a reply with msg not in chain
|
||||
// and the last hash from get chain
|
||||
lastHash := msg.Data.Get(l - 1)
|
||||
@ -395,8 +455,18 @@ func (p *Peer) HandleInbound() {
|
||||
p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()}))
|
||||
}
|
||||
case ethwire.MsgNotInChainTy:
|
||||
ethutil.Config.Log.Infof("Not in chain %x\n", msg.Data)
|
||||
ethutil.Config.Log.Debugf("Not in chain %x\n", msg.Data)
|
||||
// TODO
|
||||
case ethwire.MsgGetTxsTy:
|
||||
// Get the current transactions of the pool
|
||||
txs := p.ethereum.TxPool().CurrentTransactions()
|
||||
// Get the RlpData values from the txs
|
||||
txsInterface := make([]interface{}, len(txs))
|
||||
for i, tx := range txs {
|
||||
txsInterface[i] = tx.RlpData()
|
||||
}
|
||||
// Broadcast it back to the peer
|
||||
p.QueueMessage(ethwire.NewMessage(ethwire.MsgTxTy, txsInterface))
|
||||
|
||||
// Unofficial but fun nonetheless
|
||||
case ethwire.MsgTalkTy:
|
||||
@ -408,29 +478,6 @@ func (p *Peer) HandleInbound() {
|
||||
p.Stop()
|
||||
}
|
||||
|
||||
func packAddr(address, port string) ([]interface{}, uint16) {
|
||||
addr := strings.Split(address, ".")
|
||||
a, _ := strconv.Atoi(addr[0])
|
||||
b, _ := strconv.Atoi(addr[1])
|
||||
c, _ := strconv.Atoi(addr[2])
|
||||
d, _ := strconv.Atoi(addr[3])
|
||||
host := []interface{}{int32(a), int32(b), int32(c), int32(d)}
|
||||
prt, _ := strconv.Atoi(port)
|
||||
|
||||
return host, uint16(prt)
|
||||
}
|
||||
|
||||
func unpackAddr(value *ethutil.Value, p uint64) string {
|
||||
a := strconv.Itoa(int(value.Get(0).Uint()))
|
||||
b := strconv.Itoa(int(value.Get(1).Uint()))
|
||||
c := strconv.Itoa(int(value.Get(2).Uint()))
|
||||
d := strconv.Itoa(int(value.Get(3).Uint()))
|
||||
host := strings.Join([]string{a, b, c, d}, ".")
|
||||
port := strconv.Itoa(int(p))
|
||||
|
||||
return net.JoinHostPort(host, port)
|
||||
}
|
||||
|
||||
func (p *Peer) Start() {
|
||||
peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String())
|
||||
servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
|
||||
@ -526,7 +573,8 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
|
||||
}
|
||||
|
||||
// Catch up with the connected peer
|
||||
p.CatchupWithPeer()
|
||||
// p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
|
||||
p.SyncWithBlocks()
|
||||
|
||||
// Set the peer's caps
|
||||
p.caps = Caps(c.Get(3).Byte())
|
||||
@ -553,17 +601,64 @@ func (p *Peer) String() string {
|
||||
return fmt.Sprintf("[%s] (%s) %v %s [%s]", strConnectType, strBoundType, p.conn.RemoteAddr(), p.Version, p.caps)
|
||||
|
||||
}
|
||||
|
||||
func (p *Peer) CatchupWithPeer() {
|
||||
func (p *Peer) SyncWithBlocks() {
|
||||
if !p.catchingUp {
|
||||
p.catchingUp = true
|
||||
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{p.ethereum.BlockChain().CurrentBlock.Hash(), uint64(50)})
|
||||
// FIXME: THIS SHOULD NOT BE NEEDED
|
||||
if p.blocksRequested == 0 {
|
||||
p.blocksRequested = 10
|
||||
}
|
||||
blocks := p.ethereum.BlockChain().GetChain(p.ethereum.BlockChain().CurrentBlock.Hash(), p.blocksRequested)
|
||||
|
||||
var hashes []interface{}
|
||||
for _, block := range blocks {
|
||||
hashes = append(hashes, block.Hash())
|
||||
}
|
||||
|
||||
msgInfo := append(hashes, uint64(50))
|
||||
|
||||
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, msgInfo)
|
||||
p.QueueMessage(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Peer) CatchupWithPeer(blockHash []byte) {
|
||||
if !p.catchingUp {
|
||||
p.catchingUp = true
|
||||
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(50)})
|
||||
p.QueueMessage(msg)
|
||||
|
||||
ethutil.Config.Log.Debugf("Requesting blockchain %x...\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4])
|
||||
|
||||
msg = ethwire.NewMessage(ethwire.MsgGetTxsTy, []interface{}{})
|
||||
p.QueueMessage(msg)
|
||||
ethutil.Config.Log.Debugln("Requested transactions")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Peer) RlpData() []interface{} {
|
||||
return []interface{}{p.host, p.port, p.pubkey}
|
||||
}
|
||||
|
||||
func packAddr(address, port string) ([]interface{}, uint16) {
|
||||
addr := strings.Split(address, ".")
|
||||
a, _ := strconv.Atoi(addr[0])
|
||||
b, _ := strconv.Atoi(addr[1])
|
||||
c, _ := strconv.Atoi(addr[2])
|
||||
d, _ := strconv.Atoi(addr[3])
|
||||
host := []interface{}{int32(a), int32(b), int32(c), int32(d)}
|
||||
prt, _ := strconv.Atoi(port)
|
||||
|
||||
return host, uint16(prt)
|
||||
}
|
||||
|
||||
func unpackAddr(value *ethutil.Value, p uint64) string {
|
||||
a := strconv.Itoa(int(value.Get(0).Uint()))
|
||||
b := strconv.Itoa(int(value.Get(1).Uint()))
|
||||
c := strconv.Itoa(int(value.Get(2).Uint()))
|
||||
d := strconv.Itoa(int(value.Get(3).Uint()))
|
||||
host := strings.Join([]string{a, b, c, d}, ".")
|
||||
port := strconv.Itoa(int(p))
|
||||
|
||||
return net.JoinHostPort(host, port)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user