Merge branch 'release/poc5-rc1'

This commit is contained in:
obscuren 2014-05-05 15:55:43 +02:00
commit 2096b3a9ed
46 changed files with 4461 additions and 1132 deletions

View File

@ -6,7 +6,7 @@ Ethereum
Ethereum Go Development package (C) Jeffrey Wilcke Ethereum Go Development package (C) Jeffrey Wilcke
Ethereum is currently in its testing phase. The current state is "Proof Ethereum is currently in its testing phase. The current state is "Proof
of Concept 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 Ethereum Go is split up in several sub packages Please refer to each
individual package for more information. individual package for more information.

View File

@ -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
}

View File

@ -1,8 +0,0 @@
package ethchain
import (
"testing"
)
func TestAddressState(t *testing.T) {
}

59
ethchain/asm.go Normal file
View 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
}

View File

@ -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})) 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 { func (block *Block) State() *State {
return block.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)) data := block.state.trie.Get(string(block.Coinbase))
// Get the ether (Coinbase) and add the fee (gief fee to miner) // 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) 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 return true
} }
@ -178,26 +174,6 @@ func (block *Block) MakeContract(tx *Transaction) {
} }
/////// Block Encoding /////// 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{} { func (block *Block) rlpTxs() interface{} {
// Marshal the transactions of this block // Marshal the transactions of this block
encTx := make([]interface{}, len(block.transactions)) encTx := make([]interface{}, len(block.transactions))
@ -304,6 +280,9 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block {
func (block *Block) String() string { 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)) 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 ///////////////// //////////// UNEXPORTED /////////////////
func (block *Block) header() []interface{} { func (block *Block) header() []interface{} {

View File

@ -3,6 +3,7 @@ package ethchain
import ( import (
"bytes" "bytes"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"log" "log"
"math" "math"
"math/big" "math/big"
@ -23,7 +24,8 @@ type BlockChain struct {
func NewBlockChain(ethereum EthManager) *BlockChain { func NewBlockChain(ethereum EthManager) *BlockChain {
bc := &BlockChain{} bc := &BlockChain{}
bc.genesisBlock = NewBlockFromData(ethutil.Encode(Genesis)) bc.genesisBlock = NewBlockFromBytes(ethutil.Encode(Genesis))
bc.Ethereum = ethereum
bc.setLastBlock() bc.setLastBlock()
@ -78,6 +80,128 @@ func (bc *BlockChain) HasBlock(hash []byte) bool {
return len(data) != 0 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 { func (bc *BlockChain) GenesisBlock() *Block {
return bc.genesisBlock return bc.genesisBlock
} }
@ -136,12 +260,13 @@ func AddTestNetFunds(block *Block) {
"e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey "e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit "1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex "1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex
"2ef47100e0787b915105fd5e3f4ff6752079d5cb", // Maran
} { } {
//log.Println("2^200 Wei to", addr) //log.Println("2^200 Wei to", addr)
codedAddr := ethutil.FromHex(addr) codedAddr := ethutil.FromHex(addr)
addr := block.state.GetAccount(codedAddr) account := block.state.GetAccount(codedAddr)
addr.Amount = ethutil.BigPow(2, 200) account.Amount = ethutil.BigPow(2, 200)
block.state.UpdateAccount(codedAddr, addr) 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 // Add a block to the chain and record addition information
func (bc *BlockChain) Add(block *Block) { func (bc *BlockChain) Add(block *Block) {
bc.writeBlockInfo(block) bc.writeBlockInfo(block)
// Prepare the genesis block // Prepare the genesis block
bc.CurrentBlock = block bc.CurrentBlock = block
bc.LastBlockHash = block.Hash() bc.LastBlockHash = block.Hash()
@ -196,7 +321,7 @@ func (bc *BlockChain) GetBlock(hash []byte) *Block {
return nil return nil
} }
return NewBlockFromData(data) return NewBlockFromBytes(data)
} }
func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo { func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo {

View 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")
}
}

View File

@ -7,33 +7,39 @@ import (
"math/big" "math/big"
) )
type Callee interface { type ClosureRef interface {
ReturnGas(*big.Int, *State) ReturnGas(*big.Int, *big.Int, *State)
Address() []byte Address() []byte
}
type ClosureBody interface {
Callee
ethutil.RlpEncodable
GetMem(*big.Int) *ethutil.Value GetMem(*big.Int) *ethutil.Value
SetMem(*big.Int, *ethutil.Value) SetMem(*big.Int, *ethutil.Value)
N() *big.Int
} }
// Basic inline closure object which implement the 'closure' interface // Basic inline closure object which implement the 'closure' interface
type Closure struct { type Closure struct {
callee Callee callee *StateObject
object ClosureBody object *StateObject
Script []byte
State *State State *State
Gas *big.Int Gas *big.Int
Price *big.Int
Value *big.Int Value *big.Int
Args []byte Args []byte
} }
// Create a new closure for the given data items // Create a new closure for the given data items
func NewClosure(callee Callee, object ClosureBody, state *State, gas, val *big.Int) *Closure { func NewClosure(callee, object *StateObject, script []byte, state *State, gas, price, val *big.Int) *Closure {
return &Closure{callee, object, state, gas, val, nil} 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 // Retuns the x element in data slice
@ -46,6 +52,20 @@ func (c *Closure) GetMem(x *big.Int) *ethutil.Value {
return m 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) { func (c *Closure) SetMem(x *big.Int, val *ethutil.Value) {
c.object.SetMem(x, val) c.object.SetMem(x, val)
} }
@ -54,10 +74,12 @@ func (c *Closure) Address() []byte {
return c.object.Address() 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 c.Args = args
return vm.RunClosure(c) return vm.RunClosure(c, hook)
} }
func (c *Closure) Return(ret []byte) []byte { 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 // If no callee is present return it to
// the origin (i.e. contract or tx) // the origin (i.e. contract or tx)
if c.callee != nil { if c.callee != nil {
c.callee.ReturnGas(c.Gas, c.State) c.callee.ReturnGas(c.Gas, c.Price, c.State)
} else { } else {
c.object.ReturnGas(c.Gas, c.State) c.object.ReturnGas(c.Gas, c.Price, c.State)
// TODO incase it's a POST contract we gotta serialise the contract again.
// But it's not yet defined
} }
return ret return ret
} }
// Implement the Callee interface // 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 // Return the gas to the closure
c.Gas.Add(c.Gas, gas) c.Gas.Add(c.Gas, gas)
} }
func (c *Closure) Object() ClosureBody { func (c *Closure) Object() *StateObject {
return c.object return c.object
} }
func (c *Closure) Callee() Callee { func (c *Closure) Callee() *StateObject {
return c.callee return c.callee
} }
func (c *Closure) N() *big.Int {
return c.object.N()
}

View File

@ -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
}

View File

@ -11,7 +11,7 @@ import (
) )
type PoW interface { type PoW interface {
Search(block *Block) []byte Search(block *Block, reactChan chan ethutil.React) []byte
Verify(hash []byte, diff *big.Int, nonce []byte) bool Verify(hash []byte, diff *big.Int, nonce []byte) bool
} }
@ -19,17 +19,32 @@ type EasyPow struct {
hash *big.Int 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())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
hash := block.HashNoNonce() hash := block.HashNoNonce()
diff := block.Difficulty diff := block.Difficulty
i := int64(0)
start := time.Now().UnixNano()
for { for {
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()) sha := ethutil.Sha3Bin(big.NewInt(r.Int63()).Bytes())
if pow.Verify(hash, diff, sha) { if pow.Verify(hash, diff, sha) {
return sha return sha
} }
} }
}
return nil return nil
} }
@ -98,9 +113,9 @@ func (dag *Dagger) Search(hash, diff *big.Int) *big.Int {
for k := 0; k < amountOfRoutines; k++ { for k := 0; k < amountOfRoutines; k++ {
go dag.Find(obj, resChan) 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++ { for k := 0; k < amountOfRoutines; k++ {
// Get the result from the channel. 0 = quit // Get the result from the channel. 0 = quit
if r := <-resChan; r != 0 { if r := <-resChan; r != 0 {

View File

@ -1,6 +1,8 @@
package ethchain package ethchain
import "fmt" import (
"fmt"
)
// Parent error. In case a parent is unknown this error will be thrown // Parent error. In case a parent is unknown this error will be thrown
// by the block manager // by the block manager
@ -40,3 +42,22 @@ func IsValidationErr(err error) bool {
return ok 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
}

View File

@ -2,6 +2,7 @@ package ethchain
import ( import (
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go"
"math/big" "math/big"
) )
@ -10,10 +11,19 @@ type KeyPair struct {
PublicKey []byte PublicKey []byte
// The associated account // The associated account
account *Account account *StateObject
state *State 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 { func NewKeyPairFromValue(val *ethutil.Value) *KeyPair {
keyPair := &KeyPair{PrivateKey: val.Get(0).Bytes(), PublicKey: val.Get(1).Bytes()} 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:] return ethutil.Sha3Bin(k.PublicKey[1:])[12:]
} }
func (k *KeyPair) Account() *Account { func (k *KeyPair) Account() *StateObject {
if k.account == nil { if k.account == nil {
k.account = k.state.GetAccount(k.Address()) 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 // Create transaction, creates a new and signed transaction, ready for processing
func (k *KeyPair) CreateTx(receiver []byte, value *big.Int, data []string) *Transaction { func (k *KeyPair) CreateTx(receiver []byte, value *big.Int, data []string) *Transaction {
/* TODO
tx := NewTransaction(receiver, value, data) tx := NewTransaction(receiver, value, data)
tx.Nonce = k.account.Nonce 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) tx.Sign(k.PrivateKey)
return tx return tx
*/
return nil
} }
func (k *KeyPair) RlpEncode() []byte { func (k *KeyPair) RlpEncode() []byte {

View File

@ -6,152 +6,6 @@ import (
"math/big" "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 type OpType int
const ( const (
@ -172,22 +26,34 @@ func NewStack() *Stack {
return &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 { func (st *Stack) Pop() *big.Int {
str := st.data[0] str := st.data[len(st.data)-1]
st.data = 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 return str
} }
func (st *Stack) Popn() (*big.Int, *big.Int) { func (st *Stack) Popn() (*big.Int, *big.Int) {
ints := st.data[:2] ints := st.data[len(st.data)-2:]
st.data = 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] return ints[0], ints[1]
} }
func (st *Stack) Peek() *big.Int { func (st *Stack) Peek() *big.Int {
str := st.data[0] str := st.data[len(st.data)-1]
return str return str
} }
@ -201,8 +67,20 @@ func (st *Stack) Peekn() (*big.Int, *big.Int) {
func (st *Stack) Push(d *big.Int) { func (st *Stack) Push(d *big.Int) {
st.data = append(st.data, d) 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() { func (st *Stack) Print() {
fmt.Println("### STACK ###") fmt.Println("### stack ###")
if len(st.data) > 0 { if len(st.data) > 0 {
for i, val := range st.data { for i, val := range st.data {
fmt.Printf("%-3d %v\n", i, val) fmt.Printf("%-3d %v\n", i, val)
@ -241,16 +119,20 @@ func (m *Memory) Len() int {
return len(m.store) return len(m.store)
} }
func (m *Memory) Data() []byte {
return m.store
}
func (m *Memory) Print() { func (m *Memory) Print() {
fmt.Println("### MEM ###") fmt.Printf("### mem %d bytes ###\n", len(m.store))
if len(m.store) > 0 { if len(m.store) > 0 {
addr := 0 addr := 0
for i := 0; i+32 < len(m.store); i += 32 { for i := 0; i+32 <= len(m.store); i += 32 {
fmt.Printf("%03d %v\n", addr, m.store[i:i+32]) fmt.Printf("%03d: % x\n", addr, m.store[i:i+32])
addr++ addr++
} }
} else { } else {
fmt.Println("-- empty --") fmt.Println("-- empty --")
} }
fmt.Println("###########") fmt.Println("####################")
} }

View File

@ -34,12 +34,12 @@ func (s *State) Reset() {
// Syncs the trie and all siblings // Syncs the trie and all siblings
func (s *State) Sync() { func (s *State) Sync() {
s.trie.Sync()
// Sync all nested states // Sync all nested states
for _, state := range s.states { for _, state := range s.states {
state.Sync() state.Sync()
} }
s.trie.Sync()
} }
// Purges the current trie. // Purges the current trie.
@ -47,23 +47,15 @@ func (s *State) Purge() int {
return s.trie.NewIterator().Purge() 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)) data := s.trie.Get(string(addr))
if data == "" { if data == "" {
return nil 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 // build contract
contract := NewContractFromBytes(addr, []byte(data)) contract := NewStateObjectFromBytes(addr, []byte(data))
// Check if there's a cached state for this contract // Check if there's a cached state for this contract
cachedState := s.states[string(addr)] cachedState := s.states[string(addr)]
@ -77,28 +69,43 @@ func (s *State) GetContract(addr []byte) *Contract {
return contract return contract
} }
func (s *State) UpdateContract(contract *Contract) { func (s *State) GetStateObject(addr []byte) *StateObject {
addr := contract.Address() data := s.trie.Get(string(addr))
if data == "" {
s.states[string(addr)] = contract.state return nil
s.trie.Update(string(addr), string(contract.RlpEncode()))
} }
func (s *State) GetAccount(addr []byte) (account *Account) { 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) 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)) data := s.trie.Get(string(addr))
if data == "" { if data == "" {
account = NewAccount(addr, big.NewInt(0)) account = NewAccount(addr, big.NewInt(0))
} else { } else {
account = NewAccountFromData(addr, []byte(data)) account = NewStateObjectFromBytes(addr, []byte(data))
} }
return return
} }
func (s *State) UpdateAccount(addr []byte, account *Account) {
s.trie.Update(string(addr), string(account.RlpEncode()))
}
func (s *State) Cmp(other *State) bool { func (s *State) Cmp(other *State) bool {
return s.trie.Cmp(other.trie) return s.trie.Cmp(other.trie)
} }
@ -117,9 +124,10 @@ const (
UnknownTy UnknownTy
) )
/*
// Returns the object stored at key and the type stored at key // Returns the object stored at key and the type stored at key
// Returns nil if nothing is stored // 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 // Fetch data from the trie
data := s.trie.Get(string(key)) data := s.trie.Get(string(key))
// Returns the nil type, indicating nothing could be retrieved. // 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 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) { func (s *State) Put(key, object []byte) {
s.trie.Update(string(key), string(object)) s.trie.Update(string(key), string(object))
@ -152,27 +172,3 @@ func (s *State) Put(key, object []byte) {
func (s *State) Root() interface{} { func (s *State) Root() interface{} {
return s.trie.Root 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
}

View File

@ -19,6 +19,7 @@ type EthManager interface {
BlockChain() *BlockChain BlockChain() *BlockChain
TxPool() *TxPool TxPool() *TxPool
Broadcast(msgType ethwire.MsgType, data []interface{}) Broadcast(msgType ethwire.MsgType, data []interface{})
Reactor() *ethutil.ReactorEngine
} }
type StateManager struct { type StateManager struct {
@ -29,7 +30,7 @@ type StateManager struct {
bc *BlockChain bc *BlockChain
// States for addresses. You can watch any address // States for addresses. You can watch any address
// at any given time // at any given time
addrStateStore *AddrStateStore stateObjectCache *StateObjectCache
// Stack for processing contracts // Stack for processing contracts
stack *Stack stack *Stack
@ -50,7 +51,7 @@ type StateManager struct {
// results // results
compState *State compState *State
miningState *State manifest *Manifest
} }
func NewStateManager(ethereum EthManager) *StateManager { func NewStateManager(ethereum EthManager) *StateManager {
@ -59,11 +60,11 @@ func NewStateManager(ethereum EthManager) *StateManager {
mem: make(map[string]*big.Int), mem: make(map[string]*big.Int),
Pow: &EasyPow{}, Pow: &EasyPow{},
Ethereum: ethereum, Ethereum: ethereum,
addrStateStore: NewAddrStateStore(), stateObjectCache: NewStateObjectCache(),
bc: ethereum.BlockChain(), bc: ethereum.BlockChain(),
manifest: NewManifest(),
} }
sm.procState = ethereum.BlockChain().CurrentBlock.State() sm.procState = ethereum.BlockChain().CurrentBlock.State()
return sm return sm
} }
@ -72,18 +73,18 @@ func (sm *StateManager) ProcState() *State {
} }
// Watches any given address and puts it in the address state store // 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) //XXX account := sm.bc.CurrentBlock.state.GetAccount(addr)
account := sm.procState.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 { func (sm *StateManager) GetAddrState(addr []byte) *CachedStateObject {
account := sm.addrStateStore.Get(addr) account := sm.stateObjectCache.Get(addr)
if account == nil { if account == nil {
a := sm.bc.CurrentBlock.state.GetAccount(addr) a := sm.procState.GetAccount(addr)
account = &AccountState{Nonce: a.Nonce, Account: a} account = &CachedStateObject{Nonce: a.Nonce, Object: a}
} }
return account return account
@ -93,29 +94,44 @@ func (sm *StateManager) BlockChain() *BlockChain {
return sm.bc return sm.bc
} }
func (sm *StateManager) MakeContract(tx *Transaction) { func (sm *StateManager) MakeContract(tx *Transaction) *StateObject {
contract := MakeContract(tx, sm.procState) contract := MakeContract(tx, sm.procState)
if contract != nil { if contract != nil {
sm.procState.states[string(tx.Hash()[12:])] = contract.state 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) { func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) {
// Process each transaction/contract // Process each transaction/contract
for _, tx := range txs { for _, tx := range txs {
// If there's no recipient, it's a contract // 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() { if tx.IsContract() {
sm.MakeContract(tx) err := sm.Ethereum.TxPool().ProcessTransaction(tx, block, false)
//XXX block.MakeContract(tx) if err == nil {
contract := sm.MakeContract(tx)
if contract != nil {
sm.EvalScript(contract.Init(), contract, tx, block)
} else { } else {
if contract := sm.procState.GetContract(tx.Recipient); contract != nil { ethutil.Config.Log.Infoln("[STATE] Unable to create contract")
//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)
} }
} 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 // The prepare function, prepares the state manager for the next
// "ProcessBlock" action. // "ProcessBlock" action.
func (sm *StateManager) Prepare(processer *State, comparative *State) { func (sm *StateManager) Prepare(processor *State, comparative *State) {
sm.compState = comparative sm.compState = comparative
sm.procState = processer sm.procState = processor
} }
// Default prepare function // Default prepare function
@ -134,22 +150,23 @@ func (sm *StateManager) PrepareDefault(block *Block) {
} }
// Block processing and validating with a given (temporarily) state // 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 // Processing a blocks may never happen simultaneously
sm.mutex.Lock() sm.mutex.Lock()
defer sm.mutex.Unlock() 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 // Defer the Undo on the Trie. If the block processing happened
// we don't want to undo but since undo only happens on dirty // we don't want to undo but since undo only happens on dirty
// nodes this won't happen because Commit would have been called // nodes this won't happen because Commit would have been called
// before that. // before that.
defer sm.bc.CurrentBlock.Undo() 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 // 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 // Reasons might be catching up or simply an invalid block
if !sm.bc.HasBlock(block.PrevHash) && sm.bc.CurrentBlock != nil { if !sm.bc.HasBlock(block.PrevHash) && sm.bc.CurrentBlock != nil {
@ -161,30 +178,26 @@ func (sm *StateManager) ProcessBlock(block *Block) error {
// Block validation // Block validation
if err := sm.ValidateBlock(block); err != nil { if err := sm.ValidateBlock(block); err != nil {
fmt.Println("[SM] Error validating block:", err)
return err return err
} }
// I'm not sure, but I don't know if there should be thrown // I'm not sure, but I don't know if there should be thrown
// any errors at this time. // any errors at this time.
if err := sm.AccumelateRewards(block); err != nil { if err := sm.AccumelateRewards(block); err != nil {
fmt.Println("[SM] Error accumulating reward", err)
return err return err
} }
// if !sm.compState.Cmp(sm.procState)
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) 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 // Calculate the new total difficulty and sync back to the db
if sm.CalculateTD(block) { if sm.CalculateTD(block) {
// Sync the current block's state to the database and cancelling out the deferred Undo // Sync the current block's state to the database and cancelling out the deferred Undo
//XXX sm.bc.CurrentBlock.Sync()
sm.procState.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 // Add the block to the chain
sm.bc.Add(block) 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()) 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 { } else {
fmt.Println("total diff failed") fmt.Println("total diff failed")
} }
return nil return nil
} }
func (sm *StateManager) CalculateTD(block *Block) bool { func (sm *StateManager) CalculateTD(block *Block) bool {
uncleDiff := new(big.Int) uncleDiff := new(big.Int)
for _, uncle := range block.Uncles { for _, uncle := range block.Uncles {
@ -272,21 +291,20 @@ func CalculateUncleReward(block *Block) *big.Int {
} }
func (sm *StateManager) AccumelateRewards(block *Block) error { func (sm *StateManager) AccumelateRewards(block *Block) error {
// Get the coinbase rlp data // Get the account associated with the coinbase
//XXX addr := processor.state.GetAccount(block.Coinbase) account := sm.procState.GetAccount(block.Coinbase)
addr := sm.procState.GetAccount(block.Coinbase)
// Reward amount of ether to the coinbase address // 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) addr := make([]byte, len(block.Coinbase))
sm.procState.UpdateAccount(block.Coinbase, addr) copy(addr, block.Coinbase)
sm.procState.UpdateStateObject(account)
for _, uncle := range block.Uncles { for _, uncle := range block.Uncles {
uncleAddr := sm.procState.GetAccount(uncle.Coinbase) uncleAccount := sm.procState.GetAccount(uncle.Coinbase)
uncleAddr.AddFee(CalculateUncleReward(uncle)) uncleAccount.AddAmount(CalculateUncleReward(uncle))
//processor.state.UpdateAccount(uncle.Coinbase, uncleAddr) sm.procState.UpdateStateObject(uncleAccount)
sm.procState.UpdateAccount(uncle.Coinbase, uncleAddr)
} }
return nil return nil
@ -296,26 +314,76 @@ func (sm *StateManager) Stop() {
sm.bc.Stop() sm.bc.Stop()
} }
func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, block *Block) { func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Transaction, block *Block) {
// Recovering function in case the VM had any errors account := sm.procState.GetAccount(tx.Sender())
/*
defer func() { err := account.ConvertGas(tx.Gas, tx.GasPrice)
if r := recover(); r != nil { if err != nil {
fmt.Println("Recovered from VM execution with err =", r) ethutil.Config.Log.Debugln(err)
return
} }
}()
*/ closure := NewClosure(account, object, script, sm.procState, tx.Gas, tx.GasPrice, tx.Value)
caller := sm.procState.GetAccount(tx.Sender()) vm := NewVm(sm.procState, sm, RuntimeVars{
closure := NewClosure(caller, contract, sm.procState, tx.Gas, tx.Value) Origin: account.Address(),
vm := NewVm(sm.procState, RuntimeVars{ BlockNumber: block.BlockInfo().Number,
origin: caller.Address(), PrevHash: block.PrevHash,
blockNumber: block.BlockInfo().Number, Coinbase: block.Coinbase,
prevHash: block.PrevHash, Time: block.Time,
coinbase: block.Coinbase, Diff: block.Difficulty,
time: block.Time, //Price: tx.GasPrice,
diff: block.Difficulty,
// XXX Tx data? Could be just an argument to the closure instead
txData: nil,
}) })
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
View 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
}

View File

@ -1,7 +1,6 @@
package ethchain package ethchain
import ( import (
"bytes"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go" "github.com/obscuren/secp256k1-go"
"math/big" "math/big"
@ -14,33 +13,22 @@ type Transaction struct {
Recipient []byte Recipient []byte
Value *big.Int Value *big.Int
Gas *big.Int Gas *big.Int
Gasprice *big.Int GasPrice *big.Int
Data []string Data []byte
Init []byte
v byte v byte
r, s []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 { func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte, init []byte) *Transaction {
tx := Transaction{Recipient: to, Value: value, Nonce: 0, Data: data} return &Transaction{Value: value, Gas: gas, GasPrice: gasPrice, Data: script, Init: init, contractCreation: true}
return &tx
} }
func NewContractCreationTx(value, gasprice *big.Int, data []string) *Transaction { func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction {
return &Transaction{Value: value, Gasprice: gasprice, Data: data} return &Transaction{Recipient: to, Value: value, GasPrice: gasPrice, Gas: gas, 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 NewTransactionFromBytes(data []byte) *Transaction { func NewTransactionFromBytes(data []byte) *Transaction {
@ -58,23 +46,21 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction {
} }
func (tx *Transaction) Hash() []byte { func (tx *Transaction) Hash() []byte {
data := make([]interface{}, len(tx.Data)) data := []interface{}{tx.Nonce, tx.Value, tx.GasPrice, tx.Gas, tx.Recipient, tx.Data}
for i, val := range tx.Data {
data[i] = val if tx.contractCreation {
data = append(data, tx.Init)
} }
preEnc := []interface{}{ return ethutil.Sha3Bin(ethutil.NewValue(data).Encode())
tx.Nonce,
tx.Recipient,
tx.Value,
data,
}
return ethutil.Sha3Bin(ethutil.Encode(preEnc))
} }
func (tx *Transaction) IsContract() bool { 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 { func (tx *Transaction) Signature(key []byte) []byte {
@ -123,17 +109,16 @@ func (tx *Transaction) Sign(privk []byte) error {
return nil 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{} { func (tx *Transaction) RlpData() interface{} {
// Prepare the transaction for serialization data := []interface{}{tx.Nonce, tx.Value, tx.GasPrice, tx.Gas, tx.Recipient, tx.Data}
return []interface{}{
tx.Nonce, if tx.contractCreation {
tx.Recipient, data = append(data, tx.Init)
tx.Value,
ethutil.NewSliceValue(tx.Data).Slice(),
tx.v,
tx.r,
tx.s,
} }
return append(data, tx.v, tx.r, tx.s)
} }
func (tx *Transaction) RlpValue() *ethutil.Value { func (tx *Transaction) RlpValue() *ethutil.Value {
@ -150,17 +135,23 @@ func (tx *Transaction) RlpDecode(data []byte) {
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
tx.Nonce = decoder.Get(0).Uint() tx.Nonce = decoder.Get(0).Uint()
tx.Recipient = decoder.Get(1).Bytes() tx.Value = decoder.Get(1).BigInt()
tx.Value = decoder.Get(2).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) // If the list is of length 10 it's a contract creation tx
tx.Data = make([]string, d.Len()) if decoder.Len() == 10 {
for i := 0; i < d.Len(); i++ { tx.contractCreation = true
tx.Data[i] = d.Get(i).Str() tx.Init = decoder.Get(6).Bytes()
}
// TODO something going wrong here tx.v = byte(decoder.Get(7).Uint())
tx.v = byte(decoder.Get(4).Uint()) tx.r = decoder.Get(8).Bytes()
tx.r = decoder.Get(5).Bytes() tx.s = decoder.Get(9).Bytes()
tx.s = decoder.Get(6).Bytes() } else {
tx.v = byte(decoder.Get(6).Uint())
tx.r = decoder.Get(7).Bytes()
tx.s = decoder.Get(8).Bytes()
}
} }

View File

@ -90,7 +90,7 @@ func (pool *TxPool) addTransaction(tx *Transaction) {
// Process transaction validates the Tx and processes funds from the // Process transaction validates the Tx and processes funds from the
// sender to the recipient. // 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() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
log.Println(r) log.Println(r)
@ -100,19 +100,15 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error
// Get the sender // Get the sender
sender := block.state.GetAccount(tx.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 // Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it. // funds won't invalidate this transaction but simple ignores it.
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat))
if sender.Amount.Cmp(totAmount) < 0 { if sender.Amount.Cmp(totAmount) < 0 {
return errors.New("Insufficient amount in sender's account") return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
}
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)
}
} }
// Get the receiver // Get the receiver
@ -122,22 +118,21 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error
// Send Tx to self // Send Tx to self
if bytes.Compare(tx.Recipient, tx.Sender()) == 0 { if bytes.Compare(tx.Recipient, tx.Sender()) == 0 {
// Subtract the fee // Subtract the fee
sender.Amount.Sub(sender.Amount, new(big.Int).Mul(TxFee, TxFeeRat)) sender.SubAmount(new(big.Int).Mul(TxFee, TxFeeRat))
} else { } else {
// Subtract the amount from the senders account // 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 // 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()) log.Printf("[TXPL] Processed Tx %x\n", tx.Hash())
// Notify the subscribers
pool.notifySubscribers(TxPost, tx) pool.notifySubscribers(TxPost, tx)
return return
@ -149,18 +144,18 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
block := pool.Ethereum.BlockChain().CurrentBlock block := pool.Ethereum.BlockChain().CurrentBlock
// Something has gone horribly wrong if this happens // Something has gone horribly wrong if this happens
if block == nil { 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 // Get the sender
accountState := pool.Ethereum.StateManager().GetAddrState(tx.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)) 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 // Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it. // funds won't invalidate this transaction but simple ignores it.
if sender.Amount.Cmp(totAmount) < 0 { if sender.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 // Increment the nonce making each tx valid only once to prevent replay
@ -190,10 +185,12 @@ out:
log.Println("Validating Tx failed", err) log.Println("Validating Tx failed", err)
} }
} else { } else {
// Call blocking version. At this point it // Call blocking version.
// doesn't matter since this is a goroutine
pool.addTransaction(tx) pool.addTransaction(tx)
// Notify the subscribers
pool.Ethereum.Reactor().Post("newTx", tx)
// Notify the subscribers // Notify the subscribers
pool.notifySubscribers(TxPre, tx) pool.notifySubscribers(TxPre, tx)
} }
@ -207,7 +204,7 @@ func (pool *TxPool) QueueTransaction(tx *Transaction) {
pool.queueChan <- tx pool.queueChan <- tx
} }
func (pool *TxPool) Flush() []*Transaction { func (pool *TxPool) CurrentTransactions() []*Transaction {
pool.mutex.Lock() pool.mutex.Lock()
defer pool.mutex.Unlock() defer pool.mutex.Unlock()
@ -221,6 +218,12 @@ func (pool *TxPool) Flush() []*Transaction {
i++ i++
} }
return txList
}
func (pool *TxPool) Flush() []*Transaction {
txList := pool.CurrentTransactions()
// Recreate a new list all together // Recreate a new list all together
// XXX Is this the fastest way? // XXX Is this the fastest way?
pool.pool = list.New() pool.pool = list.New()

View File

@ -1,54 +1 @@
package ethchain 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
View 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
}

View File

@ -2,14 +2,35 @@ package ethchain
import ( import (
_ "bytes" _ "bytes"
_ "fmt" "fmt"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
_ "github.com/obscuren/secp256k1-go" _ "github.com/obscuren/secp256k1-go"
"log"
_ "math" _ "math"
"math/big" "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 { type Vm struct {
txPool *TxPool txPool *TxPool
// Stack for processing contracts // Stack for processing contracts
@ -20,99 +41,158 @@ type Vm struct {
vars RuntimeVars vars RuntimeVars
state *State state *State
stateManager *StateManager
} }
type RuntimeVars struct { type RuntimeVars struct {
origin []byte Origin []byte
blockNumber uint64 BlockNumber uint64
prevHash []byte PrevHash []byte
coinbase []byte Coinbase []byte
time int64 Time int64
diff *big.Int Diff *big.Int
txData []string TxData []string
} }
func NewVm(state *State, vars RuntimeVars) *Vm { func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm {
return &Vm{vars: vars, state: state} return &Vm{vars: vars, state: state, stateManager: stateManager}
} }
var Pow256 = ethutil.BigPow(2, 256) var Pow256 = ethutil.BigPow(2, 256)
func (vm *Vm) RunClosure(closure *Closure) []byte { var isRequireError = false
// If the amount of gas supplied is less equal to 0
if closure.Gas.Cmp(big.NewInt(0)) <= 0 { func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err error) {
// TODO Do something // 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 // Memory for the current closure
mem := &Memory{} mem := &Memory{}
// New stack (should this be shared?) // New stack (should this be shared?)
stack := NewStack() stack := NewStack()
require := func(m int) {
if stack.Len() < m {
isRequireError = true
panic(fmt.Sprintf("stack = %d, req = %d", stack.Len(), m))
}
}
// Instruction pointer // Instruction pointer
pc := big.NewInt(0) pc := big.NewInt(0)
// Current step count // Current step count
step := 0 step := 0
// The base for all big integer arithmetic
base := new(big.Int)
if ethutil.Config.Debug { if ethutil.Config.Debug {
ethutil.Config.Log.Debugf("# op\n") ethutil.Config.Log.Debugf("# op\n")
} }
for { for {
// The base for all big integer arithmetic
base := new(big.Int)
step++ step++
// Get the memory location of pc // Get the memory location of pc
val := closure.GetMem(pc) val := closure.Get(pc)
// Get the opcode (it must be an opcode!) // Get the opcode (it must be an opcode!)
op := OpCode(val.Uint()) op := OpCode(val.Uint())
/*
if ethutil.Config.Debug { if ethutil.Config.Debug {
ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String())
} }
*/
// TODO Get each instruction cost properly gas := new(big.Int)
fee := new(big.Int) useGas := func(amount *big.Int) {
fee.Add(fee, big.NewInt(1000)) gas.Add(gas, amount)
if closure.Gas.Cmp(fee) < 0 {
return closure.Return(nil)
} }
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)
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 { switch op {
case oLOG: case oLOG:
stack.Print() stack.Print()
mem.Print() mem.Print()
case oSTOP: // Stop the closure
return closure.Return(nil)
// 0x20 range // 0x20 range
case oADD: case oADD:
require(2)
x, y := stack.Popn() x, y := stack.Popn()
// (x + y) % 2 ** 256 // (x + y) % 2 ** 256
base.Add(x, y) base.Add(x, y)
base.Mod(base, Pow256)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
case oSUB: case oSUB:
require(2)
x, y := stack.Popn() x, y := stack.Popn()
// (x - y) % 2 ** 256 // (x - y) % 2 ** 256
base.Sub(x, y) base.Sub(x, y)
base.Mod(base, Pow256)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
case oMUL: case oMUL:
require(2)
x, y := stack.Popn() x, y := stack.Popn()
// (x * y) % 2 ** 256 // (x * y) % 2 ** 256
base.Mul(x, y) base.Mul(x, y)
base.Mod(base, Pow256)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
case oDIV: case oDIV:
require(2)
x, y := stack.Popn() x, y := stack.Popn()
// floor(x / y) // floor(x / y)
base.Div(x, y) base.Div(x, y)
// Pop result back on the stack // Pop result back on the stack
stack.Push(base) stack.Push(base)
case oSDIV: case oSDIV:
require(2)
x, y := stack.Popn() x, y := stack.Popn()
// n > 2**255 // n > 2**255
if x.Cmp(Pow256) > 0 { if x.Cmp(Pow256) > 0 {
@ -129,10 +209,12 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
// Push result on to the stack // Push result on to the stack
stack.Push(z) stack.Push(z)
case oMOD: case oMOD:
require(2)
x, y := stack.Popn() x, y := stack.Popn()
base.Mod(x, y) base.Mod(x, y)
stack.Push(base) stack.Push(base)
case oSMOD: case oSMOD:
require(2)
x, y := stack.Popn() x, y := stack.Popn()
// n > 2**255 // n > 2**255
if x.Cmp(Pow256) > 0 { if x.Cmp(Pow256) > 0 {
@ -149,14 +231,17 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
// Push result on to the stack // Push result on to the stack
stack.Push(z) stack.Push(z)
case oEXP: case oEXP:
require(2)
x, y := stack.Popn() x, y := stack.Popn()
base.Exp(x, y, Pow256) base.Exp(x, y, Pow256)
stack.Push(base) stack.Push(base)
case oNEG: case oNEG:
require(1)
base.Sub(Pow256, stack.Pop()) base.Sub(Pow256, stack.Pop())
stack.Push(base) stack.Push(base)
case oLT: case oLT:
require(2)
x, y := stack.Popn() x, y := stack.Popn()
// x < y // x < y
if x.Cmp(y) < 0 { if x.Cmp(y) < 0 {
@ -165,6 +250,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
stack.Push(ethutil.BigFalse) stack.Push(ethutil.BigFalse)
} }
case oGT: case oGT:
require(2)
x, y := stack.Popn() x, y := stack.Popn()
// x > y // x > y
if x.Cmp(y) > 0 { if x.Cmp(y) > 0 {
@ -172,10 +258,19 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
} else { } else {
stack.Push(ethutil.BigFalse) stack.Push(ethutil.BigFalse)
} }
case oNOT: case oEQ:
require(2)
x, y := stack.Popn() x, y := stack.Popn()
// x != y // x == y
if x.Cmp(y) != 0 { 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) stack.Push(ethutil.BigTrue)
} else { } else {
stack.Push(ethutil.BigFalse) stack.Push(ethutil.BigFalse)
@ -183,173 +278,266 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
// 0x10 range // 0x10 range
case oAND: 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: 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: case oXOR:
require(2)
x, y := stack.Popn()
stack.Push(base.Xor(x, y))
case oBYTE: 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: case oSHA3:
require(2)
size, offset := stack.Popn()
data := mem.Get(offset.Int64(), size.Int64())
stack.Push(ethutil.BigD(data))
// 0x30 range // 0x30 range
case oADDRESS: case oADDRESS:
stack.Push(ethutil.BigD(closure.Object().Address())) stack.Push(ethutil.BigD(closure.Object().Address()))
case oBALANCE: case oBALANCE:
stack.Push(closure.Value) stack.Push(closure.Value)
case oORIGIN: case oORIGIN:
stack.Push(ethutil.BigD(vm.vars.origin)) stack.Push(ethutil.BigD(vm.vars.Origin))
case oCALLER: case oCALLER:
stack.Push(ethutil.BigD(closure.Callee().Address())) stack.Push(ethutil.BigD(closure.Callee().Address()))
case oCALLVALUE: case oCALLVALUE:
// FIXME: Original value of the call, not the current value // FIXME: Original value of the call, not the current value
stack.Push(closure.Value) stack.Push(closure.Value)
case oCALLDATA: case oCALLDATALOAD:
offset := stack.Pop() require(1)
mem.Set(offset.Int64(), int64(len(closure.Args)), closure.Args) offset := stack.Pop().Int64()
val := closure.Args[offset : offset+32]
stack.Push(ethutil.BigD(val))
case oCALLDATASIZE: case oCALLDATASIZE:
stack.Push(big.NewInt(int64(len(closure.Args)))) stack.Push(big.NewInt(int64(len(closure.Args))))
case oGASPRICE: case oGASPRICE:
// TODO stack.Push(closure.Price)
// 0x40 range // 0x40 range
case oPREVHASH: case oPREVHASH:
stack.Push(ethutil.BigD(vm.vars.prevHash)) stack.Push(ethutil.BigD(vm.vars.PrevHash))
case oCOINBASE: case oCOINBASE:
stack.Push(ethutil.BigD(vm.vars.coinbase)) stack.Push(ethutil.BigD(vm.vars.Coinbase))
case oTIMESTAMP: case oTIMESTAMP:
stack.Push(big.NewInt(vm.vars.time)) stack.Push(big.NewInt(vm.vars.Time))
case oNUMBER: case oNUMBER:
stack.Push(big.NewInt(int64(vm.vars.blockNumber))) stack.Push(big.NewInt(int64(vm.vars.BlockNumber)))
case oDIFFICULTY: case oDIFFICULTY:
stack.Push(vm.vars.diff) stack.Push(vm.vars.Diff)
case oGASLIMIT: case oGASLIMIT:
// TODO // TODO
stack.Push(big.NewInt(0))
// 0x50 range // 0x50 range
case oPUSH: // Push PC+1 on to the stack case oPUSH: // Push PC+1 on to the stack
pc.Add(pc, ethutil.Big1) 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) 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: case oPOP:
require(1)
stack.Pop() stack.Pop()
case oDUP: case oDUP:
require(1)
stack.Push(stack.Peek()) stack.Push(stack.Peek())
case oSWAP: case oSWAP:
require(2)
x, y := stack.Popn() x, y := stack.Popn()
stack.Push(y) stack.Push(y)
stack.Push(x) stack.Push(x)
case oMLOAD: case oMLOAD:
require(1)
offset := stack.Pop() offset := stack.Pop()
stack.Push(ethutil.BigD(mem.Get(offset.Int64(), 32))) 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 case oMSTORE: // Store the value at stack top-1 in to memory at location stack top
require(2)
// Pop value of the stack // Pop value of the stack
val, mStart := stack.Popn() val, mStart := stack.Popn()
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256))
case oMSTORE8: case oMSTORE8:
require(2)
val, mStart := stack.Popn() val, mStart := stack.Popn()
base.And(val, new(big.Int).SetInt64(0xff)) base.And(val, new(big.Int).SetInt64(0xff))
mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256)) mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256))
case oSLOAD: case oSLOAD:
require(1)
loc := stack.Pop() loc := stack.Pop()
val := closure.GetMem(loc) val := closure.GetMem(loc)
stack.Push(val.BigInt()) stack.Push(val.BigInt())
case oSSTORE: case oSSTORE:
require(2)
val, loc := stack.Popn() val, loc := stack.Popn()
closure.SetMem(loc, ethutil.NewValue(val)) closure.SetMem(loc, ethutil.NewValue(val))
// Add the change to manifest
vm.stateManager.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val)
case oJUMP: case oJUMP:
require(1)
pc = stack.Pop() pc = stack.Pop()
// Reduce pc by one because of the increment that's at the end of this for loop
pc.Sub(pc, ethutil.Big1)
case oJUMPI: case oJUMPI:
pos, cond := stack.Popn() require(2)
if cond.Cmp(big.NewInt(0)) > 0 { cond, pos := stack.Popn()
if cond.Cmp(ethutil.BigTrue) == 0 {
pc = pos pc = pos
pc.Sub(pc, ethutil.Big1)
} }
case oPC: case oPC:
stack.Push(pc) stack.Push(pc)
case oMSIZE: case oMSIZE:
stack.Push(big.NewInt(int64(mem.Len()))) 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: case oCALL:
// Pop return size and offset require(7)
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()
// Closure addr // Closure addr
addr := stack.Pop() 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 // Fetch the contract which will serve as the closure body
contract := vm.state.GetContract(addr.Bytes()) contract := vm.state.GetContract(addr.Bytes())
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 // Create a new callable closure
closure := NewClosure(closure, contract, vm.state, gas, value) closure := NewClosure(closure.Object(), contract, contract.script, vm.state, gas, closure.Price, value)
// Executer the closure and get the return value (if any) // Executer the closure and get the return value (if any)
ret := closure.Call(vm, args) 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) 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: case oRETURN:
require(2)
size, offset := stack.Popn() size, offset := stack.Popn()
ret := mem.Get(offset.Int64(), size.Int64()) ret := mem.Get(offset.Int64(), size.Int64())
return closure.Return(ret) return closure.Return(ret), nil
case oSUICIDE: case oSUICIDE:
/* require(1)
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), "")
ethutil.Config.Log.Debugf("(%d) => %x\n", deletedMemory, recAddr) receiver := vm.state.GetAccount(stack.Pop().Bytes())
break out 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: 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) pc.Add(pc, ethutil.Big1)
}
}
func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) { if hook != nil {
ethutil.Config.Log.Debugf(" => creating inline tx %x %v %v %v", addr, value, from, length) hook(step-1, op, mem, stack)
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()
} }

View File

@ -1,114 +1,17 @@
package ethchain package ethchain
import ( import (
"bytes" _ "bytes"
"fmt"
"github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/mutan"
"math/big" "math/big"
"strings"
"testing" "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) { func TestRun3(t *testing.T) {
ethutil.ReadConfig("") ethutil.ReadConfig("")
@ -127,12 +30,12 @@ func TestRun3(t *testing.T) {
"PUSH", "0", "PUSH", "0",
"RETURN", "RETURN",
}) })
tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script) tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script)
addr := tx.Hash()[12:] addr := tx.Hash()[12:]
contract := MakeContract(tx, state) contract := MakeContract(tx, state)
state.UpdateContract(contract) state.UpdateContract(contract)
callerScript := ethutil.Compile( callerScript := ethutil.Assemble(
"PUSH", 1337, // Argument "PUSH", 1337, // Argument
"PUSH", 65, // argument mem offset "PUSH", 65, // argument mem offset
"MSTORE", "MSTORE",
@ -149,7 +52,7 @@ func TestRun3(t *testing.T) {
"PUSH", 0, "PUSH", 0,
"RETURN", "RETURN",
) )
callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript) callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript)
// Contract addr as test address // Contract addr as test address
account := NewAccount(ContractAddr, big.NewInt(10000000)) account := NewAccount(ContractAddr, big.NewInt(10000000))
@ -171,4 +74,87 @@ func TestRun3(t *testing.T) {
if bytes.Compare(ret, exp) != 0 { if bytes.Compare(ret, exp) != 0 {
t.Errorf("expected return value to be %v, got %v", exp, ret) 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)
} }

View File

@ -4,6 +4,7 @@ import (
"container/list" "container/list"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethrpc"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/eth-go/ethwire"
"io/ioutil" "io/ioutil"
@ -60,6 +61,10 @@ type Ethereum struct {
// Specifies the desired amount of maximum peers // Specifies the desired amount of maximum peers
MaxPeers int MaxPeers int
reactor *ethutil.ReactorEngine
RpcServer *ethrpc.JsonRpcServer
} }
func New(caps Caps, usePnp bool) (*Ethereum, error) { func New(caps Caps, usePnp bool) (*Ethereum, error) {
@ -89,6 +94,8 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
serverCaps: caps, serverCaps: caps,
nat: nat, nat: nat,
} }
ethereum.reactor = ethutil.NewReactorEngine()
ethereum.txPool = ethchain.NewTxPool(ethereum) ethereum.txPool = ethchain.NewTxPool(ethereum)
ethereum.blockChain = ethchain.NewBlockChain(ethereum) ethereum.blockChain = ethchain.NewBlockChain(ethereum)
ethereum.stateManager = ethchain.NewStateManager(ethereum) ethereum.stateManager = ethchain.NewStateManager(ethereum)
@ -99,6 +106,10 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
return ethereum, nil return ethereum, nil
} }
func (s *Ethereum) Reactor() *ethutil.ReactorEngine {
return s.reactor
}
func (s *Ethereum) BlockChain() *ethchain.BlockChain { func (s *Ethereum) BlockChain() *ethchain.BlockChain {
return s.blockChain return s.blockChain
} }
@ -328,6 +339,7 @@ func (s *Ethereum) Stop() {
close(s.quit) close(s.quit)
s.RpcServer.Stop()
s.txPool.Stop() s.txPool.Stop()
s.stateManager.Stop() s.stateManager.Stop()
@ -342,7 +354,7 @@ func (s *Ethereum) WaitForShutdown() {
func (s *Ethereum) upnpUpdateThread() { func (s *Ethereum) upnpUpdateThread() {
// Go off immediately to prevent code duplication, thereafter we renew // Go off immediately to prevent code duplication, thereafter we renew
// lease every 15 minutes. // lease every 15 minutes.
timer := time.NewTimer(0 * time.Second) timer := time.NewTimer(5 * time.Minute)
lport, _ := strconv.ParseInt(s.Port, 10, 16) lport, _ := strconv.ParseInt(s.Port, 10, 16)
first := true first := true
out: out:

156
ethminer/miner.go Normal file
View 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: &ethchain.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
View 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
View 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
View 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
View 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,
}
}

View File

@ -12,7 +12,9 @@ var BigTrue *big.Int = big.NewInt(1)
// False // False
var BigFalse *big.Int = big.NewInt(0) 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 { func BigPow(a, b int) *big.Int {
c := new(big.Int) c := new(big.Int)
c.Exp(big.NewInt(int64(a)), big.NewInt(int64(b)), big.NewInt(0)) 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 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 { func Big(num string) *big.Int {
n := new(big.Int) n := new(big.Int)
n.SetString(num, 0) n.SetString(num, 0)
@ -28,7 +32,9 @@ func Big(num string) *big.Int {
return n 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 { func BigD(data []byte) *big.Int {
n := new(big.Int) n := new(big.Int)
n.SetBytes(data) n.SetBytes(data)
@ -36,17 +42,30 @@ func BigD(data []byte) *big.Int {
return n 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 { func BigToBytes(num *big.Int, base int) []byte {
ret := make([]byte, base/8) ret := make([]byte, base/8)
return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...) return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...)
} }
// Functions like the build in "copy" function // Big copy
// but works on big integers //
func BigCopy(src *big.Int) (ret *big.Int) { // Creates a copy of the given big integer
ret = new(big.Int) func BigCopy(src *big.Int) *big.Int {
ret.Add(ret, src) return new(big.Int).Set(src)
}
return
// 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
} }

View File

@ -6,6 +6,9 @@ import (
"fmt" "fmt"
) )
// Number to bytes
//
// Returns the number in bytes with the specified base
func NumberToBytes(num interface{}, bits int) []byte { func NumberToBytes(num interface{}, bits int) []byte {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
err := binary.Write(buf, binary.BigEndian, num) 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):] 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 { func BytesToNumber(b []byte) uint64 {
var number uint64 var number uint64
@ -32,7 +38,9 @@ func BytesToNumber(b []byte) uint64 {
return number 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) { func ReadVarint(reader *bytes.Reader) (ret uint64) {
if reader.Len() == 8 { if reader.Len() == 8 {
var num uint64 var num uint64
@ -55,6 +63,9 @@ func ReadVarint(reader *bytes.Reader) (ret uint64) {
return ret return ret
} }
// Binary length
//
// Returns the true binary length of the given number
func BinaryLength(num int) int { func BinaryLength(num int) int {
if num == 0 { if num == 0 {
return 0 return 0
@ -62,3 +73,18 @@ func BinaryLength(num int) int {
return 1 + BinaryLength(num>>8) 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"
}

View File

@ -5,16 +5,20 @@ import (
"math/big" "math/big"
) )
// The different number of units
var ( var (
Ether = BigPow(10, 18) Ether = BigPow(10, 18)
Finney = BigPow(10, 15) Finney = BigPow(10, 15)
Szabo = BigPow(10, 12) Szabo = BigPow(10, 12)
Vito = BigPow(10, 9) Vita = BigPow(10, 9)
Turing = BigPow(10, 6) Turing = BigPow(10, 6)
Eins = BigPow(10, 3) Eins = BigPow(10, 3)
Wei = big.NewInt(1) Wei = big.NewInt(1)
) )
// Currency to string
//
// Returns a string representing a human readable format
func CurrencyToString(num *big.Int) string { func CurrencyToString(num *big.Int) string {
switch { switch {
case num.Cmp(Ether) >= 0: 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)) return fmt.Sprintf("%v Finney", new(big.Int).Div(num, Finney))
case num.Cmp(Szabo) >= 0: case num.Cmp(Szabo) >= 0:
return fmt.Sprintf("%v Szabo", new(big.Int).Div(num, Szabo)) return fmt.Sprintf("%v Szabo", new(big.Int).Div(num, Szabo))
case num.Cmp(Vito) >= 0: case num.Cmp(Vita) >= 0:
return fmt.Sprintf("%v Vito", new(big.Int).Div(num, Vito)) return fmt.Sprintf("%v Vita", new(big.Int).Div(num, Vita))
case num.Cmp(Turing) >= 0: case num.Cmp(Turing) >= 0:
return fmt.Sprintf("%v Turing", new(big.Int).Div(num, Turing)) return fmt.Sprintf("%v Turing", new(big.Int).Div(num, Turing))
case num.Cmp(Eins) >= 0: case num.Cmp(Eins) >= 0:
@ -34,8 +38,18 @@ func CurrencyToString(num *big.Int) string {
return fmt.Sprintf("%v Wei", num) return fmt.Sprintf("%v Wei", num)
} }
// Common big integers often used
var ( var (
Big1 = big.NewInt(1) Big1 = big.NewInt(1)
Big2 = big.NewInt(1)
Big0 = big.NewInt(0) Big0 = big.NewInt(0)
Big32 = big.NewInt(32)
Big256 = big.NewInt(0xff) 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:]
}

View File

@ -9,6 +9,7 @@ import (
"runtime" "runtime"
) )
// Log types available
type LogType byte type LogType byte
const ( const (
@ -16,7 +17,7 @@ const (
LogTypeFile = 2 LogTypeFile = 2
) )
// Config struct isn't exposed // Config struct
type config struct { type config struct {
Db Database Db Database
@ -31,7 +32,9 @@ type config struct {
var Config *config 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 { func ReadConfig(base string) *config {
if Config == nil { if Config == nil {
usr, _ := user.Current() 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.Log = NewLogger(LogFile|LogStd, LogLevelDebug)
Config.SetClientString("/Ethereum(G)") Config.SetClientString("/Ethereum(G)")
} }
@ -56,6 +59,8 @@ func ReadConfig(base string) *config {
return Config return Config
} }
// Set client string
//
func (c *config) SetClientString(str string) { func (c *config) SetClientString(str string) {
Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, runtime.GOOS) Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, runtime.GOOS)
} }
@ -134,7 +139,7 @@ func (log *Logger) Infoln(v ...interface{}) {
return return
} }
fmt.Println(len(log.logSys)) //fmt.Println(len(log.logSys))
for _, logger := range log.logSys { for _, logger := range log.logSys {
logger.Println(v...) logger.Println(v...)
} }

1690
ethutil/mnemonic.go Normal file

File diff suppressed because it is too large Load Diff

74
ethutil/mnemonic_test.go Normal file
View 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
View 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
}

View File

@ -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
}

View File

@ -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) {
}
*/

View File

@ -26,16 +26,6 @@ func (coder *RlpEncoder) EncodeData(rlpData interface{}) []byte {
return Encode(rlpData) 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 ( const (
RlpEmptyList = 0x80 RlpEmptyList = 0x80
RlpEmptyStr = 0x40 RlpEmptyStr = 0x40
@ -57,7 +47,7 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} {
switch { switch {
case char == 0: case char == 0:
return nil return nil
case char <= 0x7c: case char <= 0x7f:
return char return char
case char <= 0xb7: case char <= 0xb7:
@ -186,7 +176,12 @@ func Encode(object interface{}) []byte {
case byte: case byte:
buff.Write(Encode(big.NewInt(int64(t)))) buff.Write(Encode(big.NewInt(int64(t))))
case *big.Int: case *big.Int:
// Not sure how this is possible while we check for
if t == nil {
buff.WriteByte(0xc0)
} else {
buff.Write(Encode(t.Bytes())) buff.Write(Encode(t.Bytes()))
}
case []byte: case []byte:
if len(t) == 1 && t[0] <= 0x7f { if len(t) == 1 && t[0] <= 0x7f {
buff.Write(t) buff.Write(t)

View File

@ -2,6 +2,7 @@ package ethutil
import ( import (
"bytes" "bytes"
"fmt"
"math/big" "math/big"
"reflect" "reflect"
"testing" "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) { func TestEncode(t *testing.T) {
strRes := "\x83dog" strRes := "\x83dog"
bytes := Encode("dog") 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) { func BenchmarkEncodeDecode(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
bytes := Encode([]interface{}{"dog", "god", "cat"}) bytes := Encode([]interface{}{"dog", "god", "cat"})

41
ethutil/script.go Normal file
View 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
}

View File

@ -119,14 +119,29 @@ type Trie struct {
cache *Cache 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 { 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. // Save the cached value to the database.
func (t *Trie) Sync() { func (t *Trie) Sync() {
t.cache.Commit() t.cache.Commit()
t.prevRoot = t.Root t.prevRoot = copyRoot(t.Root)
} }
func (t *Trie) Undo() { func (t *Trie) Undo() {

View File

@ -1,7 +1,7 @@
package ethutil package ethutil
import ( import (
"fmt" _ "fmt"
"reflect" "reflect"
"testing" "testing"
) )

View File

@ -20,7 +20,12 @@ func (val *Value) String() string {
} }
func NewValue(val interface{}) *Value { 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 { func (val *Value) Type() reflect.Kind {
@ -149,6 +154,15 @@ func (val *Value) IsStr() bool {
return val.Type() == reflect.String 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 { func (val *Value) IsEmpty() bool {
return val.Val == nil || ((val.IsSlice() || val.IsStr()) && val.Len() == 0) return val.Val == nil || ((val.IsSlice() || val.IsStr()) && val.Len() == 0)
} }

View File

@ -32,6 +32,7 @@ const (
MsgBlockTy = 0x13 MsgBlockTy = 0x13
MsgGetChainTy = 0x14 MsgGetChainTy = 0x14
MsgNotInChainTy = 0x15 MsgNotInChainTy = 0x15
MsgGetTxsTy = 0x16
MsgTalkTy = 0xff MsgTalkTy = 0xff
) )
@ -46,6 +47,7 @@ var msgTypeToString = map[MsgType]string{
MsgTxTy: "Transactions", MsgTxTy: "Transactions",
MsgBlockTy: "Blocks", MsgBlockTy: "Blocks",
MsgGetChainTy: "Get chain", MsgGetChainTy: "Get chain",
MsgGetTxsTy: "Get Txs",
MsgNotInChainTy: "Not in chain", MsgNotInChainTy: "Not in chain",
} }

View File

@ -246,6 +246,10 @@ func soapRequest(url, function, message string) (r *http.Response, err error) {
//fmt.Println(fullMessage) //fmt.Println(fullMessage)
r, err = http.DefaultClient.Do(req) r, err = http.DefaultClient.Do(req)
if err != nil {
return
}
if r.Body != nil { if r.Body != nil {
defer r.Body.Close() defer r.Body.Close()
} }

165
peer.go
View File

@ -126,6 +126,7 @@ type Peer struct {
// Indicated whether the node is catching up or not // Indicated whether the node is catching up or not
catchingUp bool catchingUp bool
blocksRequested int
Version string Version string
} }
@ -144,6 +145,7 @@ func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer {
connected: 1, connected: 1,
port: 30303, port: 30303,
pubkey: pubkey, pubkey: pubkey,
blocksRequested: 10,
} }
} }
@ -290,17 +292,69 @@ func (p *Peer) HandleInbound() {
// Get all blocks and process them // Get all blocks and process them
var block, lastBlock *ethchain.Block var block, lastBlock *ethchain.Block
var err error var err error
// 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-- { for i := msg.Data.Len() - 1; i >= 0; i-- {
block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i)) block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i))
p.ethereum.StateManager().PrepareDefault(block) p.ethereum.StateManager().PrepareDefault(block)
err = p.ethereum.StateManager().ProcessBlock(block) err = p.ethereum.StateManager().ProcessBlock(block, false)
if err != nil { if err != nil {
if ethutil.Config.Debug { if ethutil.Config.Debug {
ethutil.Config.Log.Infof("[PEER] Block %x failed\n", block.Hash()) ethutil.Config.Log.Infof("[PEER] Block %x failed\n", block.Hash())
ethutil.Config.Log.Infof("[PEER] %v\n", err) ethutil.Config.Log.Infof("[PEER] %v\n", err)
ethutil.Config.Log.Infoln(block)
} }
break break
} else { } else {
@ -313,7 +367,7 @@ func (p *Peer) HandleInbound() {
if ethchain.IsParentErr(err) { if ethchain.IsParentErr(err) {
ethutil.Config.Log.Infoln("Attempting to catch up") ethutil.Config.Log.Infoln("Attempting to catch up")
p.catchingUp = false p.catchingUp = false
p.CatchupWithPeer() p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
} else if ethchain.IsValidationErr(err) { } else if ethchain.IsValidationErr(err) {
// TODO // 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) ethutil.Config.Log.Infof("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash)
} }
p.catchingUp = false p.catchingUp = false
p.CatchupWithPeer() p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
} }
} }
case ethwire.MsgTxTy: case ethwire.MsgTxTy:
@ -334,7 +388,8 @@ func (p *Peer) HandleInbound() {
// in the TxPool where it will undergo validation and // in the TxPool where it will undergo validation and
// processing when a new block is found // processing when a new block is found
for i := 0; i < msg.Data.Len(); i++ { 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: case ethwire.MsgGetPeersTy:
// Flag this peer as a 'requested of new peers' this to // 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 // Amount of parents in the canonical chain
//amountOfBlocks := msg.Data.Get(l).AsUint() //amountOfBlocks := msg.Data.Get(l).AsUint()
amountOfBlocks := uint64(100) amountOfBlocks := uint64(100)
// Check each SHA block hash from the message and determine whether // Check each SHA block hash from the message and determine whether
// the SHA is in the database // the SHA is in the database
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
if data := if data := msg.Data.Get(i).Bytes(); p.ethereum.StateManager().BlockChain().HasBlock(data) {
msg.Data.Get(i).Bytes(); p.ethereum.StateManager().BlockChain().HasBlock(data) {
parent = p.ethereum.BlockChain().GetBlock(data) parent = p.ethereum.BlockChain().GetBlock(data)
break break
} }
@ -385,9 +440,14 @@ func (p *Peer) HandleInbound() {
// If a parent is found send back a reply // If a parent is found send back a reply
if parent != nil { 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) chain := p.ethereum.BlockChain().GetChainFromHash(parent.Hash(), amountOfBlocks)
if len(chain) > 0 {
ethutil.Config.Log.Debugf("[PEER] Returning %d blocks: %x ", len(chain), parent.Hash())
p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, chain)) p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, chain))
}
} else { } 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 // If no blocks are found we send back a reply with msg not in chain
// and the last hash from get chain // and the last hash from get chain
lastHash := msg.Data.Get(l - 1) lastHash := msg.Data.Get(l - 1)
@ -395,8 +455,18 @@ func (p *Peer) HandleInbound() {
p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()})) p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()}))
} }
case ethwire.MsgNotInChainTy: 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 // 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 // Unofficial but fun nonetheless
case ethwire.MsgTalkTy: case ethwire.MsgTalkTy:
@ -408,29 +478,6 @@ func (p *Peer) HandleInbound() {
p.Stop() 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() { func (p *Peer) Start() {
peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String()) peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String())
servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String()) servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
@ -526,7 +573,8 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
} }
// Catch up with the connected peer // Catch up with the connected peer
p.CatchupWithPeer() // p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
p.SyncWithBlocks()
// Set the peer's caps // Set the peer's caps
p.caps = Caps(c.Get(3).Byte()) 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) return fmt.Sprintf("[%s] (%s) %v %s [%s]", strConnectType, strBoundType, p.conn.RemoteAddr(), p.Version, p.caps)
} }
func (p *Peer) SyncWithBlocks() {
func (p *Peer) CatchupWithPeer() {
if !p.catchingUp { if !p.catchingUp {
p.catchingUp = true 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) p.QueueMessage(msg)
ethutil.Config.Log.Debugf("Requesting blockchain %x...\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4]) 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{} { func (p *Peer) RlpData() []interface{} {
return []interface{}{p.host, p.port, p.pubkey} 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)
}