The great merge

This commit is contained in:
obscuren 2014-02-14 23:56:09 +01:00
parent c2fb9f06ad
commit f6d1bfe45b
43 changed files with 4547 additions and 12 deletions

12
ethchain/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile ~/.gitignore_global
/tmp
*/**/*un~
*un~
.DS_Store
*/**/.DS_Store

363
ethchain/block.go Normal file
View File

@ -0,0 +1,363 @@
package ethchain
import (
"fmt"
"github.com/ethereum/eth-go/ethutil"
"math/big"
"time"
)
type BlockInfo struct {
Number uint64
Hash []byte
Parent []byte
}
func (bi *BlockInfo) RlpDecode(data []byte) {
decoder := ethutil.NewValueFromBytes(data)
bi.Number = decoder.Get(0).Uint()
bi.Hash = decoder.Get(1).Bytes()
bi.Parent = decoder.Get(2).Bytes()
}
func (bi *BlockInfo) RlpEncode() []byte {
return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent})
}
type Block struct {
// Hash to the previous block
PrevHash []byte
// Uncles of this block
Uncles []*Block
UncleSha []byte
// The coin base address
Coinbase []byte
// Block Trie state
state *ethutil.Trie
// Difficulty for the current block
Difficulty *big.Int
// Creation time
Time int64
// Extra data
Extra string
// Block Nonce for verification
Nonce []byte
// List of transactions and/or contracts
transactions []*Transaction
TxSha []byte
}
// New block takes a raw encoded string
// XXX DEPRICATED
func NewBlockFromData(raw []byte) *Block {
return NewBlockFromBytes(raw)
}
func NewBlockFromBytes(raw []byte) *Block {
block := &Block{}
block.RlpDecode(raw)
return block
}
// New block takes a raw encoded string
func NewBlockFromRlpValue(rlpValue *ethutil.Value) *Block {
block := &Block{}
block.RlpValueDecode(rlpValue)
return block
}
func CreateBlock(root interface{},
prevHash []byte,
base []byte,
Difficulty *big.Int,
Nonce []byte,
extra string,
txes []*Transaction) *Block {
block := &Block{
// Slice of transactions to include in this block
transactions: txes,
PrevHash: prevHash,
Coinbase: base,
Difficulty: Difficulty,
Nonce: Nonce,
Time: time.Now().Unix(),
Extra: extra,
UncleSha: EmptyShaList,
}
block.SetTransactions(txes)
block.SetUncles([]*Block{})
block.state = ethutil.NewTrie(ethutil.Config.Db, root)
for _, tx := range txes {
block.MakeContract(tx)
}
return block
}
// Returns a hash of the block
func (block *Block) Hash() []byte {
return ethutil.Sha3Bin(block.RlpValue().Encode())
}
func (block *Block) HashNoNonce() []byte {
return ethutil.Sha3Bin(ethutil.Encode([]interface{}{block.PrevHash, block.UncleSha, block.Coinbase, block.state.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.Root, block.TxSha, block.Difficulty, block.Time, block.Extra, block.Nonce})))
}
func (block *Block) State() *ethutil.Trie {
return block.state
}
func (block *Block) Transactions() []*Transaction {
return block.transactions
}
func (block *Block) GetContract(addr []byte) *Contract {
data := block.state.Get(string(addr))
if data == "" {
return nil
}
contract := &Contract{}
contract.RlpDecode([]byte(data))
return contract
}
func (block *Block) UpdateContract(addr []byte, contract *Contract) {
// Make sure the state is synced
contract.State().Sync()
block.state.Update(string(addr), string(contract.RlpEncode()))
}
func (block *Block) GetAddr(addr []byte) *Address {
var address *Address
data := block.State().Get(string(addr))
if data == "" {
address = NewAddress(big.NewInt(0))
} else {
address = NewAddressFromData([]byte(data))
}
return address
}
func (block *Block) UpdateAddr(addr []byte, address *Address) {
block.state.Update(string(addr), string(address.RlpEncode()))
}
func (block *Block) PayFee(addr []byte, fee *big.Int) bool {
contract := block.GetContract(addr)
// If we can't pay the fee return
if contract == nil || contract.Amount.Cmp(fee) < 0 /* amount < fee */ {
fmt.Println("Contract has insufficient funds", contract.Amount, fee)
return false
}
base := new(big.Int)
contract.Amount = base.Sub(contract.Amount, fee)
block.state.Update(string(addr), string(contract.RlpEncode()))
data := block.state.Get(string(block.Coinbase))
// Get the ether (Coinbase) and add the fee (gief fee to miner)
ether := NewAddressFromData([]byte(data))
base = new(big.Int)
ether.Amount = base.Add(ether.Amount, fee)
block.state.Update(string(block.Coinbase), string(ether.RlpEncode()))
return true
}
func (block *Block) BlockInfo() BlockInfo {
bi := BlockInfo{}
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
bi.RlpDecode(data)
return bi
}
func (block *Block) MakeContract(tx *Transaction) {
// Create contract if there's no recipient
if tx.IsContract() {
addr := tx.Hash()
value := tx.Value
contract := NewContract(value, []byte(""))
block.state.Update(string(addr), string(contract.RlpEncode()))
for i, val := range tx.Data {
contract.state.Update(string(ethutil.NumberToBytes(uint64(i), 32)), val)
}
block.UpdateContract(addr, contract)
}
}
/////// Block Encoding
func (block *Block) encodedUncles() interface{} {
uncles := make([]interface{}, len(block.Uncles))
for i, uncle := range block.Uncles {
uncles[i] = uncle.RlpEncode()
}
return uncles
}
func (block *Block) encodedTxs() interface{} {
// Marshal the transactions of this block
encTx := make([]interface{}, len(block.transactions))
for i, tx := range block.transactions {
// Cast it to a string (safe)
encTx[i] = tx.RlpData()
}
return encTx
}
func (block *Block) rlpTxs() interface{} {
// Marshal the transactions of this block
encTx := make([]interface{}, len(block.transactions))
for i, tx := range block.transactions {
// Cast it to a string (safe)
encTx[i] = tx.RlpData()
}
return encTx
}
func (block *Block) rlpUncles() interface{} {
// Marshal the transactions of this block
uncles := make([]interface{}, len(block.Uncles))
for i, uncle := range block.Uncles {
// Cast it to a string (safe)
uncles[i] = uncle.header()
}
return uncles
}
func (block *Block) SetUncles(uncles []*Block) {
block.Uncles = uncles
// Sha of the concatenated uncles
block.UncleSha = ethutil.Sha3Bin(ethutil.Encode(block.rlpUncles()))
}
func (block *Block) SetTransactions(txs []*Transaction) {
block.transactions = txs
block.TxSha = ethutil.Sha3Bin(ethutil.Encode(block.rlpTxs()))
}
func (block *Block) RlpValue() *ethutil.RlpValue {
return ethutil.NewRlpValue([]interface{}{block.header(), block.rlpTxs(), block.rlpUncles()})
}
func (block *Block) RlpEncode() []byte {
// Encode a slice interface which contains the header and the list of
// transactions.
return block.RlpValue().Encode()
}
func (block *Block) RlpDecode(data []byte) {
rlpValue := ethutil.NewValueFromBytes(data)
block.RlpValueDecode(rlpValue)
}
func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
header := decoder.Get(0)
block.PrevHash = header.Get(0).Bytes()
block.UncleSha = header.Get(1).Bytes()
block.Coinbase = header.Get(2).Bytes()
block.state = ethutil.NewTrie(ethutil.Config.Db, header.Get(3).Val)
block.TxSha = header.Get(4).Bytes()
block.Difficulty = header.Get(5).BigInt()
block.Time = int64(header.Get(6).BigInt().Uint64())
block.Extra = header.Get(7).Str()
block.Nonce = header.Get(8).Bytes()
// Tx list might be empty if this is an uncle. Uncles only have their
// header set.
if decoder.Get(1).IsNil() == false { // Yes explicitness
txes := decoder.Get(1)
block.transactions = make([]*Transaction, txes.Len())
for i := 0; i < txes.Len(); i++ {
tx := NewTransactionFromValue(txes.Get(i))
block.transactions[i] = tx
/*
if ethutil.Config.Debug {
ethutil.Config.Db.Put(tx.Hash(), ethutil.Encode(tx))
}
*/
}
}
if decoder.Get(2).IsNil() == false { // Yes explicitness
uncles := decoder.Get(2)
block.Uncles = make([]*Block, uncles.Len())
for i := 0; i < uncles.Len(); i++ {
block.Uncles[i] = NewUncleBlockFromValue(uncles.Get(i))
}
}
}
func NewUncleBlockFromValue(header *ethutil.Value) *Block {
block := &Block{}
block.PrevHash = header.Get(0).Bytes()
block.UncleSha = header.Get(1).Bytes()
block.Coinbase = header.Get(2).Bytes()
block.state = ethutil.NewTrie(ethutil.Config.Db, header.Get(3).Val)
block.TxSha = header.Get(4).Bytes()
block.Difficulty = header.Get(5).BigInt()
block.Time = int64(header.Get(6).BigInt().Uint64())
block.Extra = header.Get(7).Str()
block.Nonce = header.Get(8).Bytes()
return block
}
func (block *Block) String() string {
return fmt.Sprintf("Block(%x):\nPrevHash:%x\nUncleSha:%x\nCoinbase:%x\nRoot:%x\nTxSha:%x\nDiff:%v\nTime:%d\nNonce:%x", block.Hash(), block.PrevHash, block.UncleSha, block.Coinbase, block.state.Root, block.TxSha, block.Difficulty, block.Time, block.Nonce)
}
//////////// UNEXPORTED /////////////////
func (block *Block) header() []interface{} {
return []interface{}{
// Sha of the previous block
block.PrevHash,
// Sha of uncles
block.UncleSha,
// Coinbase address
block.Coinbase,
// root state
block.state.Root,
// Sha of tx
block.TxSha,
// Current block Difficulty
block.Difficulty,
// Time the block was found?
block.Time,
// Extra data
block.Extra,
// Block's Nonce for validation
block.Nonce,
}
}

184
ethchain/block_chain.go Normal file
View File

@ -0,0 +1,184 @@
package ethchain
import (
"bytes"
"github.com/ethereum/eth-go/ethutil"
"log"
"math"
"math/big"
)
type BlockChain struct {
// The famous, the fabulous Mister GENESIIIIIIS (block)
genesisBlock *Block
// Last known total difficulty
TD *big.Int
LastBlockNumber uint64
CurrentBlock *Block
LastBlockHash []byte
}
func NewBlockChain() *BlockChain {
bc := &BlockChain{}
bc.genesisBlock = NewBlockFromData(ethutil.Encode(Genesis))
bc.setLastBlock()
return bc
}
func (bc *BlockChain) Genesis() *Block {
return bc.genesisBlock
}
func (bc *BlockChain) NewBlock(coinbase []byte, txs []*Transaction) *Block {
var root interface{}
var lastBlockTime int64
hash := ZeroHash256
if bc.CurrentBlock != nil {
root = bc.CurrentBlock.State().Root
hash = bc.LastBlockHash
lastBlockTime = bc.CurrentBlock.Time
}
block := CreateBlock(
root,
hash,
coinbase,
ethutil.BigPow(2, 32),
nil,
"",
txs)
if bc.CurrentBlock != nil {
var mul *big.Int
if block.Time < lastBlockTime+42 {
mul = big.NewInt(1)
} else {
mul = big.NewInt(-1)
}
diff := new(big.Int)
diff.Add(diff, bc.CurrentBlock.Difficulty)
diff.Div(diff, big.NewInt(1024))
diff.Mul(diff, mul)
diff.Add(diff, bc.CurrentBlock.Difficulty)
block.Difficulty = diff
}
return block
}
func (bc *BlockChain) HasBlock(hash []byte) bool {
data, _ := ethutil.Config.Db.Get(hash)
return len(data) != 0
}
func (bc *BlockChain) GenesisBlock() *Block {
return bc.genesisBlock
}
// Get chain return blocks from hash up to max in RLP format
func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
var chain []interface{}
// Get the current hash to start with
currentHash := bc.CurrentBlock.Hash()
// Get the last number on the block chain
lastNumber := bc.BlockInfo(bc.CurrentBlock).Number
// Get the parents number
parentNumber := bc.BlockInfoByHash(hash).Number
// Get the min amount. We might not have max amount of blocks
count := uint64(math.Min(float64(lastNumber-parentNumber), float64(max)))
startNumber := parentNumber + count
num := lastNumber
for ; num > startNumber; currentHash = bc.GetBlock(currentHash).PrevHash {
num--
}
for i := uint64(0); bytes.Compare(currentHash, hash) != 0 && num >= parentNumber && i < count; i++ {
// Get the block of the chain
block := bc.GetBlock(currentHash)
currentHash = block.PrevHash
chain = append(chain, block.RlpValue().Value)
//chain = append([]interface{}{block.RlpValue().Value}, chain...)
num--
}
return chain
}
func (bc *BlockChain) setLastBlock() {
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
if len(data) != 0 {
block := NewBlockFromBytes(data)
info := bc.BlockInfo(block)
bc.CurrentBlock = block
bc.LastBlockHash = block.Hash()
bc.LastBlockNumber = info.Number
log.Printf("[CHAIN] Last known block height #%d\n", bc.LastBlockNumber)
}
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
}
func (bc *BlockChain) SetTotalDifficulty(td *big.Int) {
ethutil.Config.Db.Put([]byte("LastKnownTotalDifficulty"), td.Bytes())
bc.TD = td
}
// Add a block to the chain and record addition information
func (bc *BlockChain) Add(block *Block) {
bc.writeBlockInfo(block)
// Prepare the genesis block
bc.CurrentBlock = block
bc.LastBlockHash = block.Hash()
ethutil.Config.Db.Put(block.Hash(), block.RlpEncode())
}
func (bc *BlockChain) GetBlock(hash []byte) *Block {
data, _ := ethutil.Config.Db.Get(hash)
return NewBlockFromData(data)
}
func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo {
bi := BlockInfo{}
data, _ := ethutil.Config.Db.Get(append(hash, []byte("Info")...))
bi.RlpDecode(data)
return bi
}
func (bc *BlockChain) BlockInfo(block *Block) BlockInfo {
bi := BlockInfo{}
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
bi.RlpDecode(data)
return bi
}
// Unexported method for writing extra non-essential block info to the db
func (bc *BlockChain) writeBlockInfo(block *Block) {
bc.LastBlockNumber++
bi := BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash}
// For now we use the block hash with the words "info" appended as key
ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
}
func (bc *BlockChain) Stop() {
if bc.CurrentBlock != nil {
ethutil.Config.Db.Put([]byte("LastBlock"), bc.CurrentBlock.RlpEncode())
log.Println("[CHAIN] Stopped")
}
}

627
ethchain/block_manager.go Normal file
View File

@ -0,0 +1,627 @@
package ethchain
import (
"bytes"
"encoding/hex"
"fmt"
"github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go"
"log"
"math"
"math/big"
"strconv"
"sync"
"time"
)
type BlockProcessor interface {
ProcessBlock(block *Block)
}
func CalculateBlockReward(block *Block, uncleLength int) *big.Int {
return BlockReward
}
type BlockManager struct {
// Mutex for locking the block processor. Blocks can only be handled one at a time
mutex sync.Mutex
// The block chain :)
bc *BlockChain
// Stack for processing contracts
stack *Stack
// non-persistent key/value memory storage
mem map[string]*big.Int
TransactionPool *TxPool
Pow PoW
Speaker PublicSpeaker
SecondaryBlockProcessor BlockProcessor
}
func AddTestNetFunds(block *Block) {
for _, addr := range []string{
"8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin
"93658b04240e4bd4046fd2d6d417d20f146f4b43", // Jeffrey
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
"80c01a26338f0d905e295fccb71fa9ea849ffa12", // Alex
} {
//log.Println("2^200 Wei to", addr)
codedAddr, _ := hex.DecodeString(addr)
addr := block.GetAddr(codedAddr)
addr.Amount = ethutil.BigPow(2, 200)
block.UpdateAddr(codedAddr, addr)
}
}
func NewBlockManager(speaker PublicSpeaker) *BlockManager {
bm := &BlockManager{
//server: s,
bc: NewBlockChain(),
stack: NewStack(),
mem: make(map[string]*big.Int),
Pow: &EasyPow{},
Speaker: speaker,
}
if bm.bc.CurrentBlock == nil {
AddTestNetFunds(bm.bc.genesisBlock)
// Prepare the genesis block
//bm.bc.genesisBlock.State().Sync()
bm.bc.Add(bm.bc.genesisBlock)
log.Println(bm.bc.genesisBlock)
log.Printf("Genesis: %x\n", bm.bc.genesisBlock.Hash())
//log.Printf("root %x\n", bm.bc.genesisBlock.State().Root)
//bm.bc.genesisBlock.PrintHash()
}
return bm
}
func (bm *BlockManager) BlockChain() *BlockChain {
return bm.bc
}
func (bm *BlockManager) ApplyTransactions(block *Block, txs []*Transaction) {
// Process each transaction/contract
for _, tx := range txs {
// If there's no recipient, it's a contract
if tx.IsContract() {
block.MakeContract(tx)
bm.ProcessContract(tx, block)
} else {
bm.TransactionPool.ProcessTransaction(tx, block)
}
}
}
// Block processing and validating with a given (temporarily) state
func (bm *BlockManager) ProcessBlock(block *Block) error {
// Processing a blocks may never happen simultaneously
bm.mutex.Lock()
defer bm.mutex.Unlock()
hash := block.Hash()
if bm.bc.HasBlock(hash) {
return nil
}
/*
if ethutil.Config.Debug {
log.Printf("[BMGR] Processing block(%x)\n", hash)
}
*/
// Check if we have the parent hash, if it isn't known we discard it
// Reasons might be catching up or simply an invalid block
if !bm.bc.HasBlock(block.PrevHash) && bm.bc.CurrentBlock != nil {
return ParentError(block.PrevHash)
}
// Process the transactions on to current block
bm.ApplyTransactions(bm.bc.CurrentBlock, block.Transactions())
// Block validation
if err := bm.ValidateBlock(block); err != nil {
return err
}
// I'm not sure, but I don't know if there should be thrown
// any errors at this time.
if err := bm.AccumelateRewards(bm.bc.CurrentBlock, block); err != nil {
return err
}
if !block.State().Cmp(bm.bc.CurrentBlock.State()) {
//if block.State().Root != state.Root {
return fmt.Errorf("Invalid merkle root. Expected %x, got %x", block.State().Root, bm.bc.CurrentBlock.State().Root)
}
// Calculate the new total difficulty and sync back to the db
if bm.CalculateTD(block) {
// Sync the current block's state to the database
bm.bc.CurrentBlock.State().Sync()
// Add the block to the chain
bm.bc.Add(block)
/*
ethutil.Config.Db.Put(block.Hash(), block.RlpEncode())
bm.bc.CurrentBlock = block
bm.LastBlockHash = block.Hash()
bm.writeBlockInfo(block)
*/
/*
txs := bm.TransactionPool.Flush()
var coded = []interface{}{}
for _, tx := range txs {
err := bm.TransactionPool.ValidateTransaction(tx)
if err == nil {
coded = append(coded, tx.RlpEncode())
}
}
*/
// Broadcast the valid block back to the wire
//bm.Speaker.Broadcast(ethwire.MsgBlockTy, []interface{}{block.RlpValue().Value})
// If there's a block processor present, pass in the block for further
// processing
if bm.SecondaryBlockProcessor != nil {
bm.SecondaryBlockProcessor.ProcessBlock(block)
}
log.Printf("[BMGR] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash())
} else {
fmt.Println("total diff failed")
}
return nil
}
func (bm *BlockManager) CalculateTD(block *Block) bool {
uncleDiff := new(big.Int)
for _, uncle := range block.Uncles {
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
}
// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
td := new(big.Int)
td = td.Add(bm.bc.TD, uncleDiff)
td = td.Add(td, block.Difficulty)
// The new TD will only be accepted if the new difficulty is
// is greater than the previous.
if td.Cmp(bm.bc.TD) > 0 {
// Set the new total difficulty back to the block chain
bm.bc.SetTotalDifficulty(td)
/*
if ethutil.Config.Debug {
log.Println("[BMGR] TD(block) =", td)
}
*/
return true
}
return false
}
// Validates the current block. Returns an error if the block was invalid,
// an uncle or anything that isn't on the current block chain.
// Validation validates easy over difficult (dagger takes longer time = difficult)
func (bm *BlockManager) ValidateBlock(block *Block) error {
// TODO
// 2. Check if the difficulty is correct
// Check each uncle's previous hash. In order for it to be valid
// is if it has the same block hash as the current
previousBlock := bm.bc.GetBlock(block.PrevHash)
for _, uncle := range block.Uncles {
if bytes.Compare(uncle.PrevHash, previousBlock.PrevHash) != 0 {
return ValidationError("Mismatch uncle's previous hash. Expected %x, got %x", previousBlock.PrevHash, uncle.PrevHash)
}
}
diff := block.Time - bm.bc.CurrentBlock.Time
if diff < 0 {
return ValidationError("Block timestamp less then prev block %v", diff)
}
// New blocks must be within the 15 minute range of the last block.
if diff > int64(15*time.Minute) {
return ValidationError("Block is too far in the future of last block (> 15 minutes)")
}
// Verify the nonce of the block. Return an error if it's not valid
if !bm.Pow.Verify(block.HashNoNonce(), block.Difficulty, block.Nonce) {
return ValidationError("Block's nonce is invalid (= %v)", block.Nonce)
}
return nil
}
func (bm *BlockManager) AccumelateRewards(processor *Block, block *Block) error {
// Get the coinbase rlp data
addr := processor.GetAddr(block.Coinbase)
// Reward amount of ether to the coinbase address
addr.AddFee(CalculateBlockReward(block, len(block.Uncles)))
processor.UpdateAddr(block.Coinbase, addr)
// TODO Reward each uncle
return nil
}
func (bm *BlockManager) Stop() {
bm.bc.Stop()
}
func (bm *BlockManager) ProcessContract(tx *Transaction, block *Block) {
// Recovering function in case the VM had any errors
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from VM execution with err =", r)
}
}()
// Process contract
bm.ProcContract(tx, block, func(opType OpType) bool {
// TODO turn on once big ints are in place
//if !block.PayFee(tx.Hash(), StepFee.Uint64()) {
// return false
//}
return true // Continue
})
}
// Contract evaluation is done here.
func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallback) {
// Instruction pointer
pc := 0
blockInfo := bm.bc.BlockInfo(block)
contract := block.GetContract(tx.Hash())
if contract == nil {
fmt.Println("Contract not found")
return
}
Pow256 := ethutil.BigPow(2, 256)
if ethutil.Config.Debug {
fmt.Printf("# op arg\n")
}
out:
for {
// The base big int for all calculations. Use this for any results.
base := new(big.Int)
// XXX Should Instr return big int slice instead of string slice?
// Get the next instruction from the contract
//op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc)))))
nb := ethutil.NumberToBytes(uint64(pc), 32)
o, _, _ := ethutil.Instr(contract.State().Get(string(nb)))
op := OpCode(o)
if !cb(0) {
break
}
if ethutil.Config.Debug {
fmt.Printf("%-3d %-4s\n", pc, op.String())
}
switch op {
case oSTOP:
break out
case oADD:
x, y := bm.stack.Popn()
// (x + y) % 2 ** 256
base.Add(x, y)
base.Mod(base, Pow256)
// Pop result back on the stack
bm.stack.Push(base)
case oSUB:
x, y := bm.stack.Popn()
// (x - y) % 2 ** 256
base.Sub(x, y)
base.Mod(base, Pow256)
// Pop result back on the stack
bm.stack.Push(base)
case oMUL:
x, y := bm.stack.Popn()
// (x * y) % 2 ** 256
base.Mul(x, y)
base.Mod(base, Pow256)
// Pop result back on the stack
bm.stack.Push(base)
case oDIV:
x, y := bm.stack.Popn()
// floor(x / y)
base.Div(x, y)
// Pop result back on the stack
bm.stack.Push(base)
case oSDIV:
x, y := bm.stack.Popn()
// n > 2**255
if x.Cmp(Pow256) > 0 {
x.Sub(Pow256, x)
}
if y.Cmp(Pow256) > 0 {
y.Sub(Pow256, y)
}
z := new(big.Int)
z.Div(x, y)
if z.Cmp(Pow256) > 0 {
z.Sub(Pow256, z)
}
// Push result on to the stack
bm.stack.Push(z)
case oMOD:
x, y := bm.stack.Popn()
base.Mod(x, y)
bm.stack.Push(base)
case oSMOD:
x, y := bm.stack.Popn()
// n > 2**255
if x.Cmp(Pow256) > 0 {
x.Sub(Pow256, x)
}
if y.Cmp(Pow256) > 0 {
y.Sub(Pow256, y)
}
z := new(big.Int)
z.Mod(x, y)
if z.Cmp(Pow256) > 0 {
z.Sub(Pow256, z)
}
// Push result on to the stack
bm.stack.Push(z)
case oEXP:
x, y := bm.stack.Popn()
base.Exp(x, y, Pow256)
bm.stack.Push(base)
case oNEG:
base.Sub(Pow256, bm.stack.Pop())
bm.stack.Push(base)
case oLT:
x, y := bm.stack.Popn()
// x < y
if x.Cmp(y) < 0 {
bm.stack.Push(ethutil.BigTrue)
} else {
bm.stack.Push(ethutil.BigFalse)
}
case oLE:
x, y := bm.stack.Popn()
// x <= y
if x.Cmp(y) < 1 {
bm.stack.Push(ethutil.BigTrue)
} else {
bm.stack.Push(ethutil.BigFalse)
}
case oGT:
x, y := bm.stack.Popn()
// x > y
if x.Cmp(y) > 0 {
bm.stack.Push(ethutil.BigTrue)
} else {
bm.stack.Push(ethutil.BigFalse)
}
case oGE:
x, y := bm.stack.Popn()
// x >= y
if x.Cmp(y) > -1 {
bm.stack.Push(ethutil.BigTrue)
} else {
bm.stack.Push(ethutil.BigFalse)
}
case oNOT:
x, y := bm.stack.Popn()
// x != y
if x.Cmp(y) != 0 {
bm.stack.Push(ethutil.BigTrue)
} else {
bm.stack.Push(ethutil.BigFalse)
}
// Please note that the following code contains some
// ugly string casting. This will have to change to big
// ints. TODO :)
case oMYADDRESS:
bm.stack.Push(ethutil.BigD(tx.Hash()))
case oTXSENDER:
bm.stack.Push(ethutil.BigD(tx.Sender()))
case oTXVALUE:
bm.stack.Push(tx.Value)
case oTXDATAN:
bm.stack.Push(big.NewInt(int64(len(tx.Data))))
case oTXDATA:
v := bm.stack.Pop()
// v >= len(data)
if v.Cmp(big.NewInt(int64(len(tx.Data)))) >= 0 {
bm.stack.Push(ethutil.Big("0"))
} else {
bm.stack.Push(ethutil.Big(tx.Data[v.Uint64()]))
}
case oBLK_PREVHASH:
bm.stack.Push(ethutil.BigD(block.PrevHash))
case oBLK_COINBASE:
bm.stack.Push(ethutil.BigD(block.Coinbase))
case oBLK_TIMESTAMP:
bm.stack.Push(big.NewInt(block.Time))
case oBLK_NUMBER:
bm.stack.Push(big.NewInt(int64(blockInfo.Number)))
case oBLK_DIFFICULTY:
bm.stack.Push(block.Difficulty)
case oBASEFEE:
// e = 10^21
e := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(21), big.NewInt(0))
d := new(big.Rat)
d.SetInt(block.Difficulty)
c := new(big.Rat)
c.SetFloat64(0.5)
// d = diff / 0.5
d.Quo(d, c)
// base = floor(d)
base.Div(d.Num(), d.Denom())
x := new(big.Int)
x.Div(e, base)
// x = floor(10^21 / floor(diff^0.5))
bm.stack.Push(x)
case oSHA256, oSHA3, oRIPEMD160:
// This is probably save
// ceil(pop / 32)
length := int(math.Ceil(float64(bm.stack.Pop().Uint64()) / 32.0))
// New buffer which will contain the concatenated popped items
data := new(bytes.Buffer)
for i := 0; i < length; i++ {
// Encode the number to bytes and have it 32bytes long
num := ethutil.NumberToBytes(bm.stack.Pop().Bytes(), 256)
data.WriteString(string(num))
}
if op == oSHA256 {
bm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes())))
} else if op == oSHA3 {
bm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes())))
} else {
bm.stack.Push(base.SetBytes(ethutil.Ripemd160(data.Bytes())))
}
case oECMUL:
y := bm.stack.Pop()
x := bm.stack.Pop()
//n := bm.stack.Pop()
//if ethutil.Big(x).Cmp(ethutil.Big(y)) {
data := new(bytes.Buffer)
data.WriteString(x.String())
data.WriteString(y.String())
if secp256k1.VerifyPubkeyValidity(data.Bytes()) == 1 {
// TODO
} else {
// Invalid, push infinity
bm.stack.Push(ethutil.Big("0"))
bm.stack.Push(ethutil.Big("0"))
}
//} else {
// // Invalid, push infinity
// bm.stack.Push("0")
// bm.stack.Push("0")
//}
case oECADD:
case oECSIGN:
case oECRECOVER:
case oECVALID:
case oPUSH:
pc++
bm.stack.Push(bm.mem[strconv.Itoa(pc)])
case oPOP:
// Pop current value of the stack
bm.stack.Pop()
case oDUP:
// Dup top stack
x := bm.stack.Pop()
bm.stack.Push(x)
bm.stack.Push(x)
case oSWAP:
// Swap two top most values
x, y := bm.stack.Popn()
bm.stack.Push(y)
bm.stack.Push(x)
case oMLOAD:
x := bm.stack.Pop()
bm.stack.Push(bm.mem[x.String()])
case oMSTORE:
x, y := bm.stack.Popn()
bm.mem[x.String()] = y
case oSLOAD:
// Load the value in storage and push it on the stack
x := bm.stack.Pop()
// decode the object as a big integer
decoder := ethutil.NewRlpValueFromBytes([]byte(contract.State().Get(x.String())))
if !decoder.IsNil() {
bm.stack.Push(decoder.AsBigInt())
} else {
bm.stack.Push(ethutil.BigFalse)
}
case oSSTORE:
// Store Y at index X
x, y := bm.stack.Popn()
contract.State().Update(x.String(), string(ethutil.Encode(y)))
case oJMP:
x := int(bm.stack.Pop().Uint64())
// Set pc to x - 1 (minus one so the incrementing at the end won't effect it)
pc = x
pc--
case oJMPI:
x := bm.stack.Pop()
// Set pc to x if it's non zero
if x.Cmp(ethutil.BigFalse) != 0 {
pc = int(x.Uint64())
pc--
}
case oIND:
bm.stack.Push(big.NewInt(int64(pc)))
case oEXTRO:
memAddr := bm.stack.Pop()
contractAddr := bm.stack.Pop().Bytes()
// Push the contract's memory on to the stack
bm.stack.Push(getContractMemory(block, contractAddr, memAddr))
case oBALANCE:
// Pushes the balance of the popped value on to the stack
d := block.State().Get(bm.stack.Pop().String())
ether := NewAddressFromData([]byte(d))
bm.stack.Push(ether.Amount)
case oMKTX:
value, addr := bm.stack.Popn()
from, length := bm.stack.Popn()
j := 0
dataItems := make([]string, int(length.Uint64()))
for i := from.Uint64(); i < length.Uint64(); i++ {
dataItems[j] = string(bm.mem[strconv.Itoa(int(i))].Bytes())
j++
}
// TODO sign it?
tx := NewTransaction(addr.Bytes(), value, dataItems)
// Add the transaction to the tx pool
bm.TransactionPool.QueueTransaction(tx)
case oSUICIDE:
//addr := bm.stack.Pop()
}
pc++
}
}
// Returns an address from the specified contract's address
func getContractMemory(block *Block, contractAddr []byte, memAddr *big.Int) *big.Int {
contract := block.GetContract(contractAddr)
if contract == nil {
log.Panicf("invalid contract addr %x", contractAddr)
}
val := contract.State().Get(memAddr.String())
// decode the object as a big integer
decoder := ethutil.NewRlpValueFromBytes([]byte(val))
if decoder.IsNil() {
return ethutil.BigFalse
}
return decoder.AsBigInt()
}

View File

@ -0,0 +1,75 @@
package ethchain
/*
import (
_ "fmt"
"testing"
)
func TestVm(t *testing.T) {
InitFees()
db, _ := NewMemDatabase()
Db = db
ctrct := NewTransaction("", 200000000, []string{
"PUSH", "1a2f2e",
"PUSH", "hallo",
"POP", // POP hallo
"PUSH", "3",
"LOAD", // Load hallo back on the stack
"PUSH", "1",
"PUSH", "2",
"ADD",
"PUSH", "2",
"PUSH", "1",
"SUB",
"PUSH", "100000000000000000000000",
"PUSH", "10000000000000",
"SDIV",
"PUSH", "105",
"PUSH", "200",
"MOD",
"PUSH", "100000000000000000000000",
"PUSH", "10000000000000",
"SMOD",
"PUSH", "5",
"PUSH", "10",
"LT",
"PUSH", "5",
"PUSH", "5",
"LE",
"PUSH", "50",
"PUSH", "5",
"GT",
"PUSH", "5",
"PUSH", "5",
"GE",
"PUSH", "10",
"PUSH", "10",
"NOT",
"MYADDRESS",
"TXSENDER",
"STOP",
})
tx := NewTransaction("1e8a42ea8cce13", 100, []string{})
block := CreateBlock("", 0, "", "c014ba53", 0, 0, "", []*Transaction{ctrct, tx})
db.Put(block.Hash(), block.RlpEncode())
bm := NewBlockManager()
bm.ProcessBlock(block)
}
*/

66
ethchain/contract.go Normal file
View File

@ -0,0 +1,66 @@
package ethchain
import (
"github.com/ethereum/eth-go/ethutil"
"math/big"
)
type Contract struct {
Amount *big.Int
Nonce uint64
state *ethutil.Trie
}
func NewContract(Amount *big.Int, root []byte) *Contract {
contract := &Contract{Amount: Amount, Nonce: 0}
contract.state = ethutil.NewTrie(ethutil.Config.Db, string(root))
return contract
}
func (c *Contract) RlpEncode() []byte {
return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.Root})
}
func (c *Contract) RlpDecode(data []byte) {
decoder := ethutil.NewRlpValueFromBytes(data)
c.Amount = decoder.Get(0).AsBigInt()
c.Nonce = decoder.Get(1).AsUint()
c.state = ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).AsRaw())
}
func (c *Contract) State() *ethutil.Trie {
return c.state
}
type Address struct {
Amount *big.Int
Nonce uint64
}
func NewAddress(amount *big.Int) *Address {
return &Address{Amount: amount, Nonce: 0}
}
func NewAddressFromData(data []byte) *Address {
address := &Address{}
address.RlpDecode(data)
return address
}
func (a *Address) AddFee(fee *big.Int) {
a.Amount.Add(a.Amount, fee)
}
func (a *Address) RlpEncode() []byte {
return ethutil.Encode([]interface{}{a.Amount, a.Nonce})
}
func (a *Address) RlpDecode(data []byte) {
decoder := ethutil.NewRlpValueFromBytes(data)
a.Amount = decoder.Get(0).AsBigInt()
a.Nonce = decoder.Get(1).AsUint()
}

199
ethchain/dagger.go Normal file
View File

@ -0,0 +1,199 @@
package ethchain
import (
"github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/sha3"
"hash"
"log"
"math/big"
"math/rand"
"time"
)
type PoW interface {
Search(block *Block) []byte
Verify(hash []byte, diff *big.Int, nonce []byte) bool
}
type EasyPow struct {
hash *big.Int
}
func (pow *EasyPow) Search(block *Block) []byte {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
hash := block.HashNoNonce()
diff := block.Difficulty
for {
sha := ethutil.Sha3Bin(big.NewInt(r.Int63()).Bytes())
if pow.Verify(hash, diff, sha) {
return sha
}
}
return nil
}
func (pow *EasyPow) Verify(hash []byte, diff *big.Int, nonce []byte) bool {
sha := sha3.NewKeccak256()
d := append(hash, nonce...)
sha.Write(d)
v := ethutil.BigPow(2, 256)
ret := new(big.Int).Div(v, diff)
res := new(big.Int)
res.SetBytes(sha.Sum(nil))
return res.Cmp(ret) == -1
}
func (pow *EasyPow) SetHash(hash *big.Int) {
}
type Dagger struct {
hash *big.Int
xn *big.Int
}
var Found bool
func (dag *Dagger) Find(obj *big.Int, resChan chan int64) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < 1000; i++ {
rnd := r.Int63()
res := dag.Eval(big.NewInt(rnd))
log.Printf("rnd %v\nres %v\nobj %v\n", rnd, res, obj)
if res.Cmp(obj) < 0 {
// Post back result on the channel
resChan <- rnd
// Notify other threads we've found a valid nonce
Found = true
}
// Break out if found
if Found {
break
}
}
resChan <- 0
}
func (dag *Dagger) Search(hash, diff *big.Int) *big.Int {
// TODO fix multi threading. Somehow it results in the wrong nonce
amountOfRoutines := 1
dag.hash = hash
obj := ethutil.BigPow(2, 256)
obj = obj.Div(obj, diff)
Found = false
resChan := make(chan int64, 3)
var res int64
for k := 0; k < amountOfRoutines; k++ {
go dag.Find(obj, resChan)
}
// Wait for each go routine to finish
for k := 0; k < amountOfRoutines; k++ {
// Get the result from the channel. 0 = quit
if r := <-resChan; r != 0 {
res = r
}
}
return big.NewInt(res)
}
func (dag *Dagger) Verify(hash, diff, nonce *big.Int) bool {
dag.hash = hash
obj := ethutil.BigPow(2, 256)
obj = obj.Div(obj, diff)
return dag.Eval(nonce).Cmp(obj) < 0
}
func DaggerVerify(hash, diff, nonce *big.Int) bool {
dagger := &Dagger{}
dagger.hash = hash
obj := ethutil.BigPow(2, 256)
obj = obj.Div(obj, diff)
return dagger.Eval(nonce).Cmp(obj) < 0
}
func (dag *Dagger) Node(L uint64, i uint64) *big.Int {
if L == i {
return dag.hash
}
var m *big.Int
if L == 9 {
m = big.NewInt(16)
} else {
m = big.NewInt(3)
}
sha := sha3.NewKeccak256()
sha.Reset()
d := sha3.NewKeccak256()
b := new(big.Int)
ret := new(big.Int)
for k := 0; k < int(m.Uint64()); k++ {
d.Reset()
d.Write(dag.hash.Bytes())
d.Write(dag.xn.Bytes())
d.Write(big.NewInt(int64(L)).Bytes())
d.Write(big.NewInt(int64(i)).Bytes())
d.Write(big.NewInt(int64(k)).Bytes())
b.SetBytes(Sum(d))
pk := b.Uint64() & ((1 << ((L - 1) * 3)) - 1)
sha.Write(dag.Node(L-1, pk).Bytes())
}
ret.SetBytes(Sum(sha))
return ret
}
func Sum(sha hash.Hash) []byte {
//in := make([]byte, 32)
return sha.Sum(nil)
}
func (dag *Dagger) Eval(N *big.Int) *big.Int {
pow := ethutil.BigPow(2, 26)
dag.xn = pow.Div(N, pow)
sha := sha3.NewKeccak256()
sha.Reset()
ret := new(big.Int)
for k := 0; k < 4; k++ {
d := sha3.NewKeccak256()
b := new(big.Int)
d.Reset()
d.Write(dag.hash.Bytes())
d.Write(dag.xn.Bytes())
d.Write(N.Bytes())
d.Write(big.NewInt(int64(k)).Bytes())
b.SetBytes(Sum(d))
pk := (b.Uint64() & 0x1ffffff)
sha.Write(dag.Node(9, pk).Bytes())
}
return ret.SetBytes(Sum(sha))
}

18
ethchain/dagger_test.go Normal file
View File

@ -0,0 +1,18 @@
package ethchain
import (
"github.com/ethereum/eth-go/ethutil"
"math/big"
"testing"
)
func BenchmarkDaggerSearch(b *testing.B) {
hash := big.NewInt(0)
diff := ethutil.BigPow(2, 36)
o := big.NewInt(0) // nonce doesn't matter. We're only testing against speed, not validity
// Reset timer so the big generation isn't included in the benchmark
b.ResetTimer()
// Validate
DaggerVerify(hash, diff, o)
}

42
ethchain/error.go Normal file
View File

@ -0,0 +1,42 @@
package ethchain
import "fmt"
// Parent error. In case a parent is unknown this error will be thrown
// by the block manager
type ParentErr struct {
Message string
}
func (err *ParentErr) Error() string {
return err.Message
}
func ParentError(hash []byte) error {
return &ParentErr{Message: fmt.Sprintf("Block's parent unkown %x", hash)}
}
func IsParentErr(err error) bool {
_, ok := err.(*ParentErr)
return ok
}
// Block validation error. If any validation fails, this error will be thrown
type ValidationErr struct {
Message string
}
func (err *ValidationErr) Error() string {
return err.Message
}
func ValidationError(format string, v ...interface{}) *ValidationErr {
return &ValidationErr{Message: fmt.Sprintf(format, v...)}
}
func IsValidationErr(err error) bool {
_, ok := err.(*ValidationErr)
return ok
}

65
ethchain/fees.go Normal file
View File

@ -0,0 +1,65 @@
package ethchain
import (
"math/big"
)
var StepFee *big.Int = new(big.Int)
var TxFeeRat *big.Int = big.NewInt(100000000000000)
var TxFee *big.Int = big.NewInt(100)
var ContractFee *big.Int = new(big.Int)
var MemFee *big.Int = new(big.Int)
var DataFee *big.Int = new(big.Int)
var CryptoFee *big.Int = new(big.Int)
var ExtroFee *big.Int = new(big.Int)
var BlockReward *big.Int = big.NewInt(1500000000000000000)
var Period1Reward *big.Int = new(big.Int)
var Period2Reward *big.Int = new(big.Int)
var Period3Reward *big.Int = new(big.Int)
var Period4Reward *big.Int = new(big.Int)
func InitFees() {
/*
// Base for 2**64
b60 := new(big.Int)
b60.Exp(big.NewInt(2), big.NewInt(64), big.NewInt(0))
// Base for 2**80
b80 := new(big.Int)
b80.Exp(big.NewInt(2), big.NewInt(80), big.NewInt(0))
StepFee.Exp(big.NewInt(10), big.NewInt(16), big.NewInt(0))
//StepFee.Div(b60, big.NewInt(64))
//fmt.Println("StepFee:", StepFee)
TxFee.Exp(big.NewInt(2), big.NewInt(64), big.NewInt(0))
//fmt.Println("TxFee:", TxFee)
ContractFee.Exp(big.NewInt(2), big.NewInt(64), big.NewInt(0))
//fmt.Println("ContractFee:", ContractFee)
MemFee.Div(b60, big.NewInt(4))
//fmt.Println("MemFee:", MemFee)
DataFee.Div(b60, big.NewInt(16))
//fmt.Println("DataFee:", DataFee)
CryptoFee.Div(b60, big.NewInt(16))
//fmt.Println("CrytoFee:", CryptoFee)
ExtroFee.Div(b60, big.NewInt(16))
//fmt.Println("ExtroFee:", ExtroFee)
Period1Reward.Mul(b80, big.NewInt(1024))
//fmt.Println("Period1Reward:", Period1Reward)
Period2Reward.Mul(b80, big.NewInt(512))
//fmt.Println("Period2Reward:", Period2Reward)
Period3Reward.Mul(b80, big.NewInt(256))
//fmt.Println("Period3Reward:", Period3Reward)
Period4Reward.Mul(b80, big.NewInt(128))
//fmt.Println("Period4Reward:", Period4Reward)
*/
}

39
ethchain/genesis.go Normal file
View File

@ -0,0 +1,39 @@
package ethchain
import (
"github.com/ethereum/eth-go/ethutil"
"math/big"
)
/*
* This is the special genesis block.
*/
var ZeroHash256 = make([]byte, 32)
var ZeroHash160 = make([]byte, 20)
var EmptyShaList = ethutil.Sha3Bin(ethutil.Encode([]interface{}{}))
var GenisisHeader = []interface{}{
// Previous hash (none)
//"",
ZeroHash256,
// Sha of uncles
ethutil.Sha3Bin(ethutil.Encode([]interface{}{})),
// Coinbase
ZeroHash160,
// Root state
"",
// Sha of transactions
//EmptyShaList,
ethutil.Sha3Bin(ethutil.Encode([]interface{}{})),
// Difficulty
ethutil.BigPow(2, 22),
// Time
int64(0),
// Extra
"",
// Nonce
ethutil.Sha3Bin(big.NewInt(42).Bytes()),
}
var Genesis = []interface{}{GenisisHeader, []interface{}{}, []interface{}{}}

167
ethchain/stack.go Normal file
View File

@ -0,0 +1,167 @@
package ethchain
import (
"fmt"
"math/big"
)
type OpCode int
// Op codes
const (
oSTOP OpCode = iota
oADD
oMUL
oSUB
oDIV
oSDIV
oMOD
oSMOD
oEXP
oNEG
oLT
oLE
oGT
oGE
oEQ
oNOT
oMYADDRESS
oTXSENDER
oTXVALUE
oTXFEE
oTXDATAN
oTXDATA
oBLK_PREVHASH
oBLK_COINBASE
oBLK_TIMESTAMP
oBLK_NUMBER
oBLK_DIFFICULTY
oBASEFEE
oSHA256 OpCode = 32
oRIPEMD160 OpCode = 33
oECMUL OpCode = 34
oECADD OpCode = 35
oECSIGN OpCode = 36
oECRECOVER OpCode = 37
oECVALID OpCode = 38
oSHA3 OpCode = 39
oPUSH OpCode = 48
oPOP OpCode = 49
oDUP OpCode = 50
oSWAP OpCode = 51
oMLOAD OpCode = 52
oMSTORE OpCode = 53
oSLOAD OpCode = 54
oSSTORE OpCode = 55
oJMP OpCode = 56
oJMPI OpCode = 57
oIND OpCode = 58
oEXTRO OpCode = 59
oBALANCE OpCode = 60
oMKTX OpCode = 61
oSUICIDE OpCode = 62
)
// Since the opcodes aren't all in order we can't use a regular slice
var opCodeToString = map[OpCode]string{
oSTOP: "STOP",
oADD: "ADD",
oMUL: "MUL",
oSUB: "SUB",
oDIV: "DIV",
oSDIV: "SDIV",
oMOD: "MOD",
oSMOD: "SMOD",
oEXP: "EXP",
oNEG: "NEG",
oLT: "LT",
oLE: "LE",
oGT: "GT",
oGE: "GE",
oEQ: "EQ",
oNOT: "NOT",
oMYADDRESS: "MYADDRESS",
oTXSENDER: "TXSENDER",
oTXVALUE: "TXVALUE",
oTXFEE: "TXFEE",
oTXDATAN: "TXDATAN",
oTXDATA: "TXDATA",
oBLK_PREVHASH: "BLK_PREVHASH",
oBLK_COINBASE: "BLK_COINBASE",
oBLK_TIMESTAMP: "BLK_TIMESTAMP",
oBLK_NUMBER: "BLK_NUMBER",
oBLK_DIFFICULTY: "BLK_DIFFICULTY",
oBASEFEE: "BASEFEE",
oSHA256: "SHA256",
oRIPEMD160: "RIPEMD160",
oECMUL: "ECMUL",
oECADD: "ECADD",
oECSIGN: "ECSIGN",
oECRECOVER: "ECRECOVER",
oECVALID: "ECVALID",
oSHA3: "SHA3",
oPUSH: "PUSH",
oPOP: "POP",
oDUP: "DUP",
oSWAP: "SWAP",
oMLOAD: "MLOAD",
oMSTORE: "MSTORE",
oSLOAD: "SLOAD",
oSSTORE: "SSTORE",
oJMP: "JMP",
oJMPI: "JMPI",
oIND: "IND",
oEXTRO: "EXTRO",
oBALANCE: "BALANCE",
oMKTX: "MKTX",
oSUICIDE: "SUICIDE",
}
func (o OpCode) String() string {
return opCodeToString[o]
}
type OpType int
const (
tNorm = iota
tData
tExtro
tCrypto
)
type TxCallback func(opType OpType) bool
// Simple push/pop stack mechanism
type Stack struct {
data []*big.Int
}
func NewStack() *Stack {
return &Stack{}
}
func (st *Stack) Pop() *big.Int {
s := len(st.data)
str := st.data[s-1]
st.data = st.data[:s-1]
return str
}
func (st *Stack) Popn() (*big.Int, *big.Int) {
s := len(st.data)
ints := st.data[s-2:]
st.data = st.data[:s-2]
return ints[0], ints[1]
}
func (st *Stack) Push(d *big.Int) {
st.data = append(st.data, d)
}
func (st *Stack) Print() {
fmt.Println(st.data)
}

157
ethchain/transaction.go Normal file
View File

@ -0,0 +1,157 @@
package ethchain
import (
"github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go"
"math/big"
)
type Transaction struct {
Nonce uint64
Recipient []byte
Value *big.Int
Data []string
Memory []int
v byte
r, s []byte
}
func NewTransaction(to []byte, value *big.Int, data []string) *Transaction {
tx := Transaction{Recipient: to, Value: value}
tx.Nonce = 0
// Serialize the data
tx.Data = make([]string, len(data))
for i, val := range data {
instr, err := ethutil.CompileInstr(val)
if err != nil {
//fmt.Printf("compile error:%d %v\n", i+1, err)
}
tx.Data[i] = instr
}
return &tx
}
func NewTransactionFromData(data []byte) *Transaction {
tx := &Transaction{}
tx.RlpDecode(data)
return tx
}
func NewTransactionFromValue(val *ethutil.Value) *Transaction {
tx := &Transaction{}
tx.RlpValueDecode(val)
return tx
}
func (tx *Transaction) Hash() []byte {
data := make([]interface{}, len(tx.Data))
for i, val := range tx.Data {
data[i] = val
}
preEnc := []interface{}{
tx.Nonce,
tx.Recipient,
tx.Value,
data,
}
return ethutil.Sha3Bin(ethutil.Encode(preEnc))
}
func (tx *Transaction) IsContract() bool {
return len(tx.Recipient) == 0
}
func (tx *Transaction) Signature(key []byte) []byte {
hash := tx.Hash()
sig, _ := secp256k1.Sign(hash, key)
return sig
}
func (tx *Transaction) PublicKey() []byte {
hash := tx.Hash()
// If we don't make a copy we will overwrite the existing underlying array
dst := make([]byte, len(tx.r))
copy(dst, tx.r)
sig := append(dst, tx.s...)
sig = append(sig, tx.v-27)
pubkey, _ := secp256k1.RecoverPubkey(hash, sig)
return pubkey
}
func (tx *Transaction) Sender() []byte {
pubkey := tx.PublicKey()
// Validate the returned key.
// Return nil if public key isn't in full format
if pubkey[0] != 4 {
return nil
}
return ethutil.Sha3Bin(pubkey[1:])[12:]
}
func (tx *Transaction) Sign(privk []byte) error {
sig := tx.Signature(privk)
tx.r = sig[:32]
tx.s = sig[32:64]
tx.v = sig[64] + 27
return nil
}
func (tx *Transaction) RlpData() interface{} {
// Prepare the transaction for serialization
return []interface{}{
tx.Nonce,
tx.Recipient,
tx.Value,
ethutil.NewSliceValue(tx.Data).Slice(),
tx.v,
tx.r,
tx.s,
}
}
func (tx *Transaction) RlpValue() *ethutil.Value {
return ethutil.NewValue(tx.RlpData())
}
func (tx *Transaction) RlpEncode() []byte {
return tx.RlpValue().Encode()
}
func (tx *Transaction) RlpDecode(data []byte) {
tx.RlpValueDecode(ethutil.NewValueFromBytes(data))
}
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
tx.Nonce = decoder.Get(0).Uint()
tx.Recipient = decoder.Get(1).Bytes()
tx.Value = decoder.Get(2).BigInt()
d := decoder.Get(3)
tx.Data = make([]string, d.Len())
for i := 0; i < d.Len(); i++ {
tx.Data[i] = d.Get(i).Str()
}
// TODO something going wrong here
tx.v = byte(decoder.Get(4).Uint())
tx.r = decoder.Get(5).Bytes()
tx.s = decoder.Get(6).Bytes()
}

View File

@ -0,0 +1,219 @@
package ethchain
import (
"bytes"
"container/list"
"errors"
"fmt"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"log"
"math/big"
"sync"
)
const (
txPoolQueueSize = 50
)
type TxPoolHook chan *Transaction
func FindTx(pool *list.List, finder func(*Transaction, *list.Element) bool) *Transaction {
for e := pool.Front(); e != nil; e = e.Next() {
if tx, ok := e.Value.(*Transaction); ok {
if finder(tx, e) {
return tx
}
}
}
return nil
}
type PublicSpeaker interface {
Broadcast(msgType ethwire.MsgType, data []interface{})
}
// The tx pool a thread safe transaction pool handler. In order to
// guarantee a non blocking pool we use a queue channel which can be
// independently read without needing access to the actual pool. If the
// pool is being drained or synced for whatever reason the transactions
// will simple queue up and handled when the mutex is freed.
type TxPool struct {
//server *Server
Speaker PublicSpeaker
// The mutex for accessing the Tx pool.
mutex sync.Mutex
// Queueing channel for reading and writing incoming
// transactions to
queueChan chan *Transaction
// Quiting channel
quit chan bool
// The actual pool
pool *list.List
BlockManager *BlockManager
Hook TxPoolHook
}
func NewTxPool() *TxPool {
return &TxPool{
//server: s,
mutex: sync.Mutex{},
pool: list.New(),
queueChan: make(chan *Transaction, txPoolQueueSize),
quit: make(chan bool),
}
}
// Blocking function. Don't use directly. Use QueueTransaction instead
func (pool *TxPool) addTransaction(tx *Transaction) {
pool.mutex.Lock()
pool.pool.PushBack(tx)
pool.mutex.Unlock()
// Broadcast the transaction to the rest of the peers
pool.Speaker.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()})
}
// Process transaction validates the Tx and processes funds from the
// sender to the recipient.
func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) {
log.Printf("[TXPL] Processing Tx %x\n", tx.Hash())
defer func() {
if r := recover(); r != nil {
log.Println(r)
err = fmt.Errorf("%v", r)
}
}()
// Get the sender
sender := block.GetAddr(tx.Sender())
// Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it.
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat))
if sender.Amount.Cmp(totAmount) < 0 {
return errors.New("Insufficient amount in sender's account")
}
if sender.Nonce != tx.Nonce {
if ethutil.Config.Debug {
return fmt.Errorf("Invalid nonce %d(%d) continueing anyway", tx.Nonce, sender.Nonce)
} else {
return fmt.Errorf("Invalid nonce %d(%d)", tx.Nonce, sender.Nonce)
}
}
// Subtract the amount from the senders account
sender.Amount.Sub(sender.Amount, totAmount)
sender.Nonce += 1
// Get the receiver
receiver := block.GetAddr(tx.Recipient)
// Add the amount to receivers account which should conclude this transaction
receiver.Amount.Add(receiver.Amount, tx.Value)
block.UpdateAddr(tx.Sender(), sender)
block.UpdateAddr(tx.Recipient, receiver)
return
}
func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
// Get the last block so we can retrieve the sender and receiver from
// the merkle trie
block := pool.BlockManager.BlockChain().CurrentBlock
// Something has gone horribly wrong if this happens
if block == nil {
return errors.New("No last block on the block chain")
}
// Get the sender
sender := block.GetAddr(tx.Sender())
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat))
// Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it.
if sender.Amount.Cmp(totAmount) < 0 {
return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.Sender())
}
// Increment the nonce making each tx valid only once to prevent replay
// attacks
return nil
}
func (pool *TxPool) queueHandler() {
out:
for {
select {
case tx := <-pool.queueChan:
hash := tx.Hash()
foundTx := FindTx(pool.pool, func(tx *Transaction, e *list.Element) bool {
return bytes.Compare(tx.Hash(), hash) == 0
})
if foundTx != nil {
break
}
// Validate the transaction
err := pool.ValidateTransaction(tx)
if err != nil {
if ethutil.Config.Debug {
log.Println("Validating Tx failed", err)
}
} else {
// Call blocking version. At this point it
// doesn't matter since this is a goroutine
pool.addTransaction(tx)
if pool.Hook != nil {
pool.Hook <- tx
}
}
case <-pool.quit:
break out
}
}
}
func (pool *TxPool) QueueTransaction(tx *Transaction) {
pool.queueChan <- tx
}
func (pool *TxPool) Flush() []*Transaction {
pool.mutex.Lock()
defer pool.mutex.Unlock()
txList := make([]*Transaction, pool.pool.Len())
i := 0
for e := pool.pool.Front(); e != nil; e = e.Next() {
if tx, ok := e.Value.(*Transaction); ok {
txList[i] = tx
}
i++
}
// Recreate a new list all together
// XXX Is this the fastest way?
pool.pool = list.New()
return txList
}
func (pool *TxPool) Start() {
go pool.queueHandler()
}
func (pool *TxPool) Stop() {
log.Println("[TXP] Stopping...")
close(pool.quit)
pool.Flush()
}

View File

@ -0,0 +1,54 @@
package ethchain
import (
"encoding/hex"
"fmt"
"github.com/ethereum/eth-go/ethutil"
"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())
}

12
ethdb/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile ~/.gitignore_global
/tmp
*/**/*un~
*un~
.DS_Store
*/**/.DS_Store

11
ethdb/README.md Normal file
View File

@ -0,0 +1,11 @@
# ethdb
The ethdb package contains the ethereum database interfaces
# Installation
`go get github.com/ethereum/ethdb-go`
# Usage
Todo :-)

64
ethdb/database.go Normal file
View File

@ -0,0 +1,64 @@
package ethdb
import (
"fmt"
"github.com/ethereum/eth-go/ethutil"
"github.com/syndtr/goleveldb/leveldb"
"path"
)
type LDBDatabase struct {
db *leveldb.DB
}
func NewLDBDatabase() (*LDBDatabase, error) {
dbPath := path.Join(ethutil.Config.ExecPath, "database")
// Open the db
db, err := leveldb.OpenFile(dbPath, nil)
if err != nil {
return nil, err
}
database := &LDBDatabase{db: db}
return database, nil
}
func (db *LDBDatabase) Put(key []byte, value []byte) {
err := db.db.Put(key, value, nil)
if err != nil {
fmt.Println("Error put", err)
}
}
func (db *LDBDatabase) Get(key []byte) ([]byte, error) {
return db.db.Get(key, nil)
}
func (db *LDBDatabase) LastKnownTD() []byte {
data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil)
if len(data) == 0 {
data = []byte{0x0}
}
return data
}
func (db *LDBDatabase) Close() {
// Close the leveldb database
db.db.Close()
}
func (db *LDBDatabase) Print() {
iter := db.db.NewIterator(nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Printf("%x(%d): ", key, len(key))
node := ethutil.NewValueFromBytes(value)
fmt.Printf("%v\n", node)
}
}

6
ethdb/database_test.go Normal file
View File

@ -0,0 +1,6 @@
package ethdb
import (
_ "fmt"
_ "testing"
)

49
ethdb/memory_database.go Normal file
View File

@ -0,0 +1,49 @@
package ethdb
import (
"fmt"
"github.com/ethereum/eth-go/ethutil"
)
/*
* This is a test memory database. Do not use for any production it does not get persisted
*/
type MemDatabase struct {
db map[string][]byte
}
func NewMemDatabase() (*MemDatabase, error) {
db := &MemDatabase{db: make(map[string][]byte)}
return db, nil
}
func (db *MemDatabase) Put(key []byte, value []byte) {
db.db[string(key)] = value
}
func (db *MemDatabase) Get(key []byte) ([]byte, error) {
return db.db[string(key)], nil
}
func (db *MemDatabase) Print() {
for key, val := range db.db {
fmt.Printf("%x(%d): ", key, len(key))
dec, _ := ethutil.Decode(val, 0)
node := ethutil.Conv(dec)
fmt.Printf("%q\n", node.AsRaw())
}
}
func (db *MemDatabase) Close() {
}
func (db *MemDatabase) LastKnownTD() []byte {
data, _ := db.Get([]byte("LastKnownTotalDifficulty"))
if len(data) == 0 || data == nil {
data = []byte{0x0}
}
return data
}

View File

@ -2,10 +2,10 @@ package eth
import (
"container/list"
"github.com/ethereum/ethchain-go"
"github.com/ethereum/ethdb-go"
"github.com/ethereum/ethutil-go"
"github.com/ethereum/ethwire-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"io/ioutil"
"log"
"net"
@ -60,8 +60,8 @@ type Ethereum struct {
}
func New(caps Caps, usePnp bool) (*Ethereum, error) {
db, err := ethdb.NewLDBDatabase()
//db, err := ethdb.NewMemDatabase()
//db, err := ethdb.NewLDBDatabase()
db, err := ethdb.NewMemDatabase()
if err != nil {
return nil, err
}

12
ethutil/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile ~/.gitignore_global
/tmp
*/**/*un~
*un~
.DS_Store
*/**/.DS_Store

3
ethutil/.travil.yml Normal file
View File

@ -0,0 +1,3 @@
language: go
go:
- 1.2

137
ethutil/README.md Normal file
View File

@ -0,0 +1,137 @@
# ethutil
[![Build
Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
The ethutil package contains the ethereum utility library.
# Installation
`go get github.com/ethereum/ethutil-go`
# Usage
## RLP (Recursive Linear Prefix) Encoding
RLP Encoding is an encoding scheme utilized by the Ethereum project. It
encodes any native value or list to string.
More in depth information about the Encoding scheme see the [Wiki](http://wiki.ethereum.org/index.php/RLP)
article.
```go
rlp := ethutil.Encode("doge")
fmt.Printf("%q\n", rlp) // => "\0x83dog"
rlp = ethutil.Encode([]interface{}{"dog", "cat"})
fmt.Printf("%q\n", rlp) // => "\0xc8\0x83dog\0x83cat"
decoded := ethutil.Decode(rlp)
fmt.Println(decoded) // => ["dog" "cat"]
```
## Patricia Trie
Patricie Tree is a merkle trie utilized by the Ethereum project.
More in depth information about the (modified) Patricia Trie can be
found on the [Wiki](http://wiki.ethereum.org/index.php/Patricia_Tree).
The patricia trie uses a db as backend and could be anything as long as
it satisfies the Database interface found in `ethutil/db.go`.
```go
db := NewDatabase()
// db, root
trie := ethutil.NewTrie(db, "")
trie.Put("puppy", "dog")
trie.Put("horse", "stallion")
trie.Put("do", "verb")
trie.Put("doge", "coin")
// Look up the key "do" in the trie
out := trie.Get("do")
fmt.Println(out) // => verb
```
The patricia trie, in combination with RLP, provides a robust,
cryptographically authenticated data structure that can be used to store
all (key, value) bindings.
```go
// ... Create db/trie
// Note that RLP uses interface slices as list
value := ethutil.Encode([]interface{}{"one", 2, "three", []interface{}{42}})
// Store the RLP encoded value of the list
trie.Put("mykey", value)
```
## Value
Value is a Generic Value which is used in combination with RLP data or
`([])interface{}` structures. It may serve as a bridge between RLP data
and actual real values and takes care of all the type checking and
casting. Unlike Go's `reflect.Value` it does not panic if it's unable to
cast to the requested value. It simple returns the base value of that
type (e.g. `Slice()` returns []interface{}, `Uint()` return 0, etc).
### Creating a new Value
`NewEmptyValue()` returns a new \*Value with it's initial value set to a
`[]interface{}`
`AppendLint()` appends a list to the current value.
`Append(v)` appends the value (v) to the current value/list.
```go
val := ethutil.NewEmptyValue().Append(1).Append("2")
val.AppendList().Append(3)
```
### Retrieving values
`Get(i)` returns the `i` item in the list.
`Uint()` returns the value as an unsigned int64.
`Slice()` returns the value as a interface slice.
`Str()` returns the value as a string.
`Bytes()` returns the value as a byte slice.
`Len()` assumes current to be a slice and returns its length.
`Byte()` returns the value as a single byte.
```go
val := ethutil.NewValue([]interface{}{1,"2",[]interface{}{3}})
val.Get(0).Uint() // => 1
val.Get(1).Str() // => "2"
s := val.Get(2) // => Value([]interface{}{3})
s.Get(0).Uint() // => 3
```
## Decoding
Decoding streams of RLP data is simplified
```go
val := ethutil.NewValueFromBytes(rlpData)
val.Get(0).Uint()
```
## Encoding
Encoding from Value to RLP is done with the `Encode` method. The
underlying value can be anything RLP can encode (int, str, lists, bytes)
```go
val := ethutil.NewValue([]interface{}{1,"2",[]interface{}{3}})
rlp := val.Encode()
// Store the rlp data
Store(rlp)
```

37
ethutil/big.go Normal file
View File

@ -0,0 +1,37 @@
package ethutil
import (
"math/big"
)
var BigInt0 *big.Int = big.NewInt(0)
// True
var BigTrue *big.Int = big.NewInt(1)
// False
var BigFalse *big.Int = big.NewInt(0)
// Returns the power of two integers
func BigPow(a, b int) *big.Int {
c := new(big.Int)
c.Exp(big.NewInt(int64(a)), big.NewInt(int64(b)), big.NewInt(0))
return c
}
// Like big.NewInt(uint64); this takes a string instead.
func Big(num string) *big.Int {
n := new(big.Int)
n.SetString(num, 0)
return n
}
// Like big.NewInt(uint64); this takes a byte buffer instead.
func BigD(data []byte) *big.Int {
n := new(big.Int)
n.SetBytes(data)
return n
}

64
ethutil/bytes.go Normal file
View File

@ -0,0 +1,64 @@
package ethutil
import (
"bytes"
"encoding/binary"
"fmt"
)
func NumberToBytes(num interface{}, bits int) []byte {
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.BigEndian, num)
if err != nil {
fmt.Println("NumberToBytes failed:", err)
}
return buf.Bytes()[buf.Len()-(bits/8):]
}
func BytesToNumber(b []byte) uint64 {
var number uint64
// Make sure the buffer is 64bits
data := make([]byte, 8)
data = append(data[:len(b)], b...)
buf := bytes.NewReader(data)
err := binary.Read(buf, binary.BigEndian, &number)
if err != nil {
fmt.Println("BytesToNumber failed:", err)
}
return number
}
// Read variable integer in big endian
func ReadVarint(reader *bytes.Reader) (ret uint64) {
if reader.Len() == 8 {
var num uint64
binary.Read(reader, binary.BigEndian, &num)
ret = uint64(num)
} else if reader.Len() == 4 {
var num uint32
binary.Read(reader, binary.BigEndian, &num)
ret = uint64(num)
} else if reader.Len() == 2 {
var num uint16
binary.Read(reader, binary.BigEndian, &num)
ret = uint64(num)
} else {
var num uint8
binary.Read(reader, binary.BigEndian, &num)
ret = uint64(num)
}
return ret
}
func BinaryLength(num int) int {
if num == 0 {
return 0
}
return 1 + BinaryLength(num>>8)
}

106
ethutil/config.go Normal file
View File

@ -0,0 +1,106 @@
package ethutil
import (
"log"
"os"
"os/user"
"path"
)
type LogType byte
const (
LogTypeStdIn = 1
LogTypeFile = 2
)
// Config struct isn't exposed
type config struct {
Db Database
Log Logger
ExecPath string
Debug bool
Ver string
Pubkey []byte
Seed bool
}
var Config *config
// Read config doesn't read anything yet.
func ReadConfig(base string) *config {
if Config == nil {
usr, _ := user.Current()
path := path.Join(usr.HomeDir, base)
//Check if the logging directory already exists, create it if not
_, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
log.Printf("Debug logging directory %s doesn't exist, creating it", path)
os.Mkdir(path, 0777)
}
}
Config = &config{ExecPath: path, Debug: true, Ver: "0.2.1"}
Config.Log = NewLogger(LogFile|LogStd, 0)
}
return Config
}
type LoggerType byte
const (
LogFile = 0x1
LogStd = 0x2
)
type Logger struct {
logSys []*log.Logger
logLevel int
}
func NewLogger(flag LoggerType, level int) Logger {
var loggers []*log.Logger
flags := log.LstdFlags | log.Lshortfile
if flag&LogFile > 0 {
file, err := os.OpenFile(path.Join(Config.ExecPath, "debug.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm)
if err != nil {
log.Panic("unable to create file logger", err)
}
log := log.New(file, "[ETH]", flags)
loggers = append(loggers, log)
}
if flag&LogStd > 0 {
log := log.New(os.Stdout, "[ETH]", flags)
loggers = append(loggers, log)
}
return Logger{logSys: loggers, logLevel: level}
}
func (log Logger) Debugln(v ...interface{}) {
if log.logLevel != 0 {
return
}
for _, logger := range log.logSys {
logger.Println(v...)
}
}
func (log Logger) Debugf(format string, v ...interface{}) {
if log.logLevel != 0 {
return
}
for _, logger := range log.logSys {
logger.Printf(format, v...)
}
}

10
ethutil/db.go Normal file
View File

@ -0,0 +1,10 @@
package ethutil
// Database interface
type Database interface {
Put(key []byte, value []byte)
Get(key []byte) ([]byte, error)
LastKnownTD() []byte
Close()
Print()
}

62
ethutil/encoding.go Normal file
View File

@ -0,0 +1,62 @@
package ethutil
import (
"bytes"
"encoding/hex"
_ "fmt"
"strings"
)
func CompactEncode(hexSlice []int) string {
terminator := 0
if hexSlice[len(hexSlice)-1] == 16 {
terminator = 1
}
if terminator == 1 {
hexSlice = hexSlice[:len(hexSlice)-1]
}
oddlen := len(hexSlice) % 2
flags := 2*terminator + oddlen
if oddlen != 0 {
hexSlice = append([]int{flags}, hexSlice...)
} else {
hexSlice = append([]int{flags, 0}, hexSlice...)
}
var buff bytes.Buffer
for i := 0; i < len(hexSlice); i += 2 {
buff.WriteByte(byte(16*hexSlice[i] + hexSlice[i+1]))
}
return buff.String()
}
func CompactDecode(str string) []int {
base := CompactHexDecode(str)
base = base[:len(base)-1]
if base[0] >= 2 { // && base[len(base)-1] != 16 {
base = append(base, 16)
}
if base[0]%2 == 1 {
base = base[1:]
} else {
base = base[2:]
}
return base
}
func CompactHexDecode(str string) []int {
base := "0123456789abcdef"
hexSlice := make([]int, 0)
enc := hex.EncodeToString([]byte(str))
for _, v := range enc {
hexSlice = append(hexSlice, strings.IndexByte(base, byte(v)))
}
hexSlice = append(hexSlice, 16)
return hexSlice
}

37
ethutil/encoding_test.go Normal file
View File

@ -0,0 +1,37 @@
package ethutil
import (
"fmt"
"testing"
)
func TestCompactEncode(t *testing.T) {
test1 := []int{1, 2, 3, 4, 5}
if res := CompactEncode(test1); res != "\x11\x23\x45" {
t.Error(fmt.Sprintf("even compact encode failed. Got: %q", res))
}
test2 := []int{0, 1, 2, 3, 4, 5}
if res := CompactEncode(test2); res != "\x00\x01\x23\x45" {
t.Error(fmt.Sprintf("odd compact encode failed. Got: %q", res))
}
test3 := []int{0, 15, 1, 12, 11, 8 /*term*/, 16}
if res := CompactEncode(test3); res != "\x20\x0f\x1c\xb8" {
t.Error(fmt.Sprintf("odd terminated compact encode failed. Got: %q", res))
}
test4 := []int{15, 1, 12, 11, 8 /*term*/, 16}
if res := CompactEncode(test4); res != "\x3f\x1c\xb8" {
t.Error(fmt.Sprintf("even terminated compact encode failed. Got: %q", res))
}
}
func TestCompactHexDecode(t *testing.T) {
exp := []int{7, 6, 6, 5, 7, 2, 6, 2, 16}
res := CompactHexDecode("verb")
if !CompareIntSlice(res, exp) {
t.Error("Error compact hex decode. Expected", exp, "got", res)
}
}

61
ethutil/helpers.go Normal file
View File

@ -0,0 +1,61 @@
package ethutil
import (
"code.google.com/p/go.crypto/ripemd160"
"crypto/sha256"
"encoding/hex"
"github.com/obscuren/sha3"
"strconv"
)
func Uitoa(i uint32) string {
return strconv.FormatUint(uint64(i), 10)
}
func Sha256Bin(data []byte) []byte {
hash := sha256.Sum256(data)
return hash[:]
}
func Ripemd160(data []byte) []byte {
ripemd := ripemd160.New()
ripemd.Write(data)
return ripemd.Sum(nil)
}
func Sha3Bin(data []byte) []byte {
d := sha3.NewKeccak256()
d.Reset()
d.Write(data)
return d.Sum(nil)
}
// Helper function for comparing slices
func CompareIntSlice(a, b []int) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
// Returns the amount of nibbles that match each other from 0 ...
func MatchingNibbleLength(a, b []int) int {
i := 0
for CompareIntSlice(a[:i+1], b[:i+1]) && i < len(b) {
i += 1
}
return i
}
func Hex(d []byte) string {
return hex.EncodeToString(d)
}

108
ethutil/parsing.go Normal file
View File

@ -0,0 +1,108 @@
package ethutil
import (
"errors"
"fmt"
"math/big"
"strconv"
"strings"
)
// Op codes
var OpCodes = map[string]string{
"STOP": "0",
"ADD": "1",
"MUL": "2",
"SUB": "3",
"DIV": "4",
"SDIV": "5",
"MOD": "6",
"SMOD": "7",
"EXP": "8",
"NEG": "9",
"LT": "10",
"LE": "11",
"GT": "12",
"GE": "13",
"EQ": "14",
"NOT": "15",
"MYADDRESS": "16",
"TXSENDER": "17",
"PUSH": "48",
"POP": "49",
"LOAD": "54",
}
func CompileInstr(s string) (string, error) {
tokens := strings.Split(s, " ")
if OpCodes[tokens[0]] == "" {
return s, errors.New(fmt.Sprintf("OP not found: %s", tokens[0]))
}
code := OpCodes[tokens[0]] // Replace op codes with the proper numerical equivalent
op := new(big.Int)
op.SetString(code, 0)
args := make([]*big.Int, 6)
for i, val := range tokens[1:len(tokens)] {
num := new(big.Int)
num.SetString(val, 0)
args[i] = num
}
// Big int equation = op + x * 256 + y * 256**2 + z * 256**3 + a * 256**4 + b * 256**5 + c * 256**6
base := new(big.Int)
x := new(big.Int)
y := new(big.Int)
z := new(big.Int)
a := new(big.Int)
b := new(big.Int)
c := new(big.Int)
if args[0] != nil {
x.Mul(args[0], big.NewInt(256))
}
if args[1] != nil {
y.Mul(args[1], BigPow(256, 2))
}
if args[2] != nil {
z.Mul(args[2], BigPow(256, 3))
}
if args[3] != nil {
a.Mul(args[3], BigPow(256, 4))
}
if args[4] != nil {
b.Mul(args[4], BigPow(256, 5))
}
if args[5] != nil {
c.Mul(args[5], BigPow(256, 6))
}
base.Add(op, x)
base.Add(base, y)
base.Add(base, z)
base.Add(base, a)
base.Add(base, b)
base.Add(base, c)
return base.String(), 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
}

32
ethutil/parsing_test.go Normal file
View File

@ -0,0 +1,32 @@
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 Big(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) {
}

24
ethutil/rand.go Normal file
View File

@ -0,0 +1,24 @@
package ethutil
import (
"crypto/rand"
"encoding/binary"
"io"
)
func randomUint64(r io.Reader) (uint64, error) {
b := make([]byte, 8)
n, err := r.Read(b)
if n != len(b) {
return 0, io.ErrShortBuffer
}
if err != nil {
return 0, err
}
return binary.BigEndian.Uint64(b), nil
}
// RandomUint64 returns a cryptographically random uint64 value.
func RandomUint64() (uint64, error) {
return randomUint64(rand.Reader)
}

418
ethutil/rlp.go Normal file
View File

@ -0,0 +1,418 @@
package ethutil
import (
"bytes"
_ "encoding/binary"
"fmt"
_ "log"
_ "math"
"math/big"
"reflect"
)
///////////////////////////////////////
type EthEncoder interface {
EncodeData(rlpData interface{}) []byte
}
type EthDecoder interface {
Get(idx int) *RlpValue
}
//////////////////////////////////////
type RlpEncoder struct {
rlpData []byte
}
func NewRlpEncoder() *RlpEncoder {
encoder := &RlpEncoder{}
return encoder
}
func (coder *RlpEncoder) EncodeData(rlpData interface{}) []byte {
return Encode(rlpData)
}
// Data rlpValueutes are returned by the rlp decoder. The data rlpValueutes represents
// one item within the rlp data structure. It's responsible for all the casting
// It always returns something rlpValueid
type RlpValue struct {
Value interface{}
kind reflect.Value
}
func (rlpValue *RlpValue) String() string {
return fmt.Sprintf("%q", rlpValue.Value)
}
func Conv(rlpValue interface{}) *RlpValue {
return &RlpValue{Value: rlpValue, kind: reflect.ValueOf(rlpValue)}
}
func NewRlpValue(rlpValue interface{}) *RlpValue {
return &RlpValue{Value: rlpValue}
}
func (rlpValue *RlpValue) Type() reflect.Kind {
return reflect.TypeOf(rlpValue.Value).Kind()
}
func (rlpValue *RlpValue) IsNil() bool {
return rlpValue.Value == nil
}
func (rlpValue *RlpValue) Length() int {
//return rlpValue.kind.Len()
if data, ok := rlpValue.Value.([]interface{}); ok {
return len(data)
}
return 0
}
func (rlpValue *RlpValue) AsRaw() interface{} {
return rlpValue.Value
}
func (rlpValue *RlpValue) AsUint() uint64 {
if Value, ok := rlpValue.Value.(uint8); ok {
return uint64(Value)
} else if Value, ok := rlpValue.Value.(uint16); ok {
return uint64(Value)
} else if Value, ok := rlpValue.Value.(uint32); ok {
return uint64(Value)
} else if Value, ok := rlpValue.Value.(uint64); ok {
return Value
}
return 0
}
func (rlpValue *RlpValue) AsByte() byte {
if Value, ok := rlpValue.Value.(byte); ok {
return Value
}
return 0x0
}
func (rlpValue *RlpValue) AsBigInt() *big.Int {
if a, ok := rlpValue.Value.([]byte); ok {
b := new(big.Int)
b.SetBytes(a)
return b
}
return big.NewInt(0)
}
func (rlpValue *RlpValue) AsString() string {
if a, ok := rlpValue.Value.([]byte); ok {
return string(a)
} else if a, ok := rlpValue.Value.(string); ok {
return a
} else {
//panic(fmt.Sprintf("not string %T: %v", rlpValue.Value, rlpValue.Value))
}
return ""
}
func (rlpValue *RlpValue) AsBytes() []byte {
if a, ok := rlpValue.Value.([]byte); ok {
return a
}
return make([]byte, 0)
}
func (rlpValue *RlpValue) AsSlice() []interface{} {
if d, ok := rlpValue.Value.([]interface{}); ok {
return d
}
return []interface{}{}
}
func (rlpValue *RlpValue) AsSliceFrom(from int) *RlpValue {
slice := rlpValue.AsSlice()
return NewRlpValue(slice[from:])
}
func (rlpValue *RlpValue) AsSliceTo(to int) *RlpValue {
slice := rlpValue.AsSlice()
return NewRlpValue(slice[:to])
}
func (rlpValue *RlpValue) AsSliceFromTo(from, to int) *RlpValue {
slice := rlpValue.AsSlice()
return NewRlpValue(slice[from:to])
}
// Threat the rlpValueute as a slice
func (rlpValue *RlpValue) Get(idx int) *RlpValue {
if d, ok := rlpValue.Value.([]interface{}); ok {
// Guard for oob
if len(d) <= idx {
return NewRlpValue(nil)
}
if idx < 0 {
panic("negative idx for Rlp Get")
}
return NewRlpValue(d[idx])
}
// If this wasn't a slice you probably shouldn't be using this function
return NewRlpValue(nil)
}
func (rlpValue *RlpValue) Cmp(o *RlpValue) bool {
return reflect.DeepEqual(rlpValue.Value, o.Value)
}
func (rlpValue *RlpValue) Encode() []byte {
return Encode(rlpValue.Value)
}
func NewRlpValueFromBytes(rlpData []byte) *RlpValue {
if len(rlpData) != 0 {
data, _ := Decode(rlpData, 0)
return NewRlpValue(data)
}
return NewRlpValue(nil)
}
// RlpValue value setters
// An empty rlp value is always a list
func EmptyRlpValue() *RlpValue {
return NewRlpValue([]interface{}{})
}
func (rlpValue *RlpValue) AppendList() *RlpValue {
list := EmptyRlpValue()
rlpValue.Value = append(rlpValue.AsSlice(), list)
return list
}
func (rlpValue *RlpValue) Append(v interface{}) *RlpValue {
rlpValue.Value = append(rlpValue.AsSlice(), v)
return rlpValue
}
/*
func FromBin(data []byte) uint64 {
if len(data) == 0 {
return 0
}
return FromBin(data[:len(data)-1])*256 + uint64(data[len(data)-1])
}
*/
const (
RlpEmptyList = 0x80
RlpEmptyStr = 0x40
)
func Char(c []byte) int {
if len(c) > 0 {
return int(c[0])
}
return 0
}
func DecodeWithReader(reader *bytes.Buffer) interface{} {
var slice []interface{}
// Read the next byte
char := Char(reader.Next(1))
switch {
case char == 0:
return nil
case char <= 0x7c:
return char
case char <= 0xb7:
return reader.Next(int(char - 0x80))
case char <= 0xbf:
buff := bytes.NewReader(reader.Next(int(char - 0xb8)))
length := ReadVarint(buff)
return reader.Next(int(length))
case char <= 0xf7:
length := int(char - 0xc0)
for i := 0; i < length; i++ {
obj := DecodeWithReader(reader)
if obj != nil {
slice = append(slice, obj)
} else {
break
}
}
return slice
}
return slice
}
// TODO Use a bytes.Buffer instead of a raw byte slice.
// Cleaner code, and use draining instead of seeking the next bytes to read
func Decode(data []byte, pos uint64) (interface{}, uint64) {
/*
if pos > uint64(len(data)-1) {
log.Println(data)
log.Panicf("index out of range %d for data %q, l = %d", pos, data, len(data))
}
*/
var slice []interface{}
char := int(data[pos])
switch {
case char <= 0x7f:
return data[pos], pos + 1
case char <= 0xb7:
b := uint64(data[pos]) - 0x80
return data[pos+1 : pos+1+b], pos + 1 + b
case char <= 0xbf:
b := uint64(data[pos]) - 0xb7
b2 := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+b]))
return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2
case char <= 0xf7:
b := uint64(data[pos]) - 0xc0
prevPos := pos
pos++
for i := uint64(0); i < b; {
var obj interface{}
// Get the next item in the data list and append it
obj, prevPos = Decode(data, pos)
slice = append(slice, obj)
// Increment i by the amount bytes read in the previous
// read
i += (prevPos - pos)
pos = prevPos
}
return slice, pos
case char <= 0xff:
l := uint64(data[pos]) - 0xf7
//b := BigD(data[pos+1 : pos+1+l]).Uint64()
b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l]))
pos = pos + l + 1
prevPos := b
for i := uint64(0); i < uint64(b); {
var obj interface{}
obj, prevPos = Decode(data, pos)
slice = append(slice, obj)
i += (prevPos - pos)
pos = prevPos
}
return slice, pos
default:
panic(fmt.Sprintf("byte not supported: %q", char))
}
return slice, 0
}
var (
directRlp = big.NewInt(0x7f)
numberRlp = big.NewInt(0xb7)
zeroRlp = big.NewInt(0x0)
)
func Encode(object interface{}) []byte {
var buff bytes.Buffer
if object != nil {
switch t := object.(type) {
case *RlpValue:
buff.Write(Encode(t.AsRaw()))
// Code dup :-/
case int:
buff.Write(Encode(big.NewInt(int64(t))))
case uint:
buff.Write(Encode(big.NewInt(int64(t))))
case int8:
buff.Write(Encode(big.NewInt(int64(t))))
case int16:
buff.Write(Encode(big.NewInt(int64(t))))
case int32:
buff.Write(Encode(big.NewInt(int64(t))))
case int64:
buff.Write(Encode(big.NewInt(t)))
case uint16:
buff.Write(Encode(big.NewInt(int64(t))))
case uint32:
buff.Write(Encode(big.NewInt(int64(t))))
case uint64:
buff.Write(Encode(big.NewInt(int64(t))))
case byte:
buff.Write(Encode(big.NewInt(int64(t))))
case *big.Int:
buff.Write(Encode(t.Bytes()))
case []byte:
if len(t) == 1 && t[0] <= 0x7f {
buff.Write(t)
} else if len(t) < 56 {
buff.WriteByte(byte(len(t) + 0x80))
buff.Write(t)
} else {
b := big.NewInt(int64(len(t)))
buff.WriteByte(byte(len(b.Bytes()) + 0xb7))
buff.Write(b.Bytes())
buff.Write(t)
}
case string:
buff.Write(Encode([]byte(t)))
case []interface{}:
// Inline function for writing the slice header
WriteSliceHeader := func(length int) {
if length < 56 {
buff.WriteByte(byte(length + 0xc0))
} else {
b := big.NewInt(int64(length))
buff.WriteByte(byte(len(b.Bytes()) + 0xf7))
buff.Write(b.Bytes())
}
}
var b bytes.Buffer
for _, val := range t {
b.Write(Encode(val))
}
WriteSliceHeader(len(b.Bytes()))
buff.Write(b.Bytes())
}
} else {
// Empty list for nil
buff.WriteByte(0xc0)
}
return buff.Bytes()
}

170
ethutil/rlp_test.go Normal file
View File

@ -0,0 +1,170 @@
package ethutil
import (
"bytes"
"encoding/hex"
"fmt"
"math/big"
"reflect"
"testing"
)
func TestRlpValueEncoding(t *testing.T) {
val := EmptyRlpValue()
val.AppendList().Append(1).Append(2).Append(3)
val.Append("4").AppendList().Append(5)
res := val.Encode()
exp := Encode([]interface{}{[]interface{}{1, 2, 3}, "4", []interface{}{5}})
if bytes.Compare(res, exp) != 0 {
t.Errorf("expected %q, got %q", res, exp)
}
}
func TestValueSlice(t *testing.T) {
val := []interface{}{
"value1",
"valeu2",
"value3",
}
value := NewValue(val)
splitVal := value.SliceFrom(1)
if splitVal.Len() != 2 {
t.Error("SliceFrom: Expected len", 2, "got", splitVal.Len())
}
splitVal = value.SliceTo(2)
if splitVal.Len() != 2 {
t.Error("SliceTo: Expected len", 2, "got", splitVal.Len())
}
splitVal = value.SliceFromTo(1, 3)
if splitVal.Len() != 2 {
t.Error("SliceFromTo: Expected len", 2, "got", splitVal.Len())
}
}
func TestValue(t *testing.T) {
value := NewValueFromBytes([]byte("\xcd\x83dog\x83god\x83cat\x01"))
if value.Get(0).Str() != "dog" {
t.Errorf("expected '%v', got '%v'", value.Get(0).Str(), "dog")
}
if value.Get(3).Uint() != 1 {
t.Errorf("expected '%v', got '%v'", value.Get(3).Uint(), 1)
}
}
func TestEncode(t *testing.T) {
strRes := "\x83dog"
bytes := Encode("dog")
str := string(bytes)
if str != strRes {
t.Error(fmt.Sprintf("Expected %q, got %q", strRes, str))
}
sliceRes := "\xcc\x83dog\x83god\x83cat"
strs := []interface{}{"dog", "god", "cat"}
bytes = Encode(strs)
slice := string(bytes)
if slice != sliceRes {
t.Error(fmt.Sprintf("Expected %q, got %q", sliceRes, slice))
}
intRes := "\x82\x04\x00"
bytes = Encode(1024)
if string(bytes) != intRes {
t.Errorf("Expected %q, got %q", intRes, bytes)
}
}
func TestDecode(t *testing.T) {
single := []byte("\x01")
b, _ := Decode(single, 0)
if b.(uint8) != 1 {
t.Errorf("Expected 1, got %q", b)
}
str := []byte("\x83dog")
b, _ = Decode(str, 0)
if bytes.Compare(b.([]byte), []byte("dog")) != 0 {
t.Errorf("Expected dog, got %q", b)
}
slice := []byte("\xcc\x83dog\x83god\x83cat")
res := []interface{}{"dog", "god", "cat"}
b, _ = Decode(slice, 0)
if reflect.DeepEqual(b, res) {
t.Errorf("Expected %q, got %q", res, b)
}
}
func TestEncodeDecodeBigInt(t *testing.T) {
bigInt := big.NewInt(1391787038)
encoded := Encode(bigInt)
value := NewValueFromBytes(encoded)
fmt.Println(value.BigInt(), bigInt)
if value.BigInt().Cmp(bigInt) != 0 {
t.Errorf("Expected %v, got %v", bigInt, value.BigInt())
}
dec, _ := hex.DecodeString("52f4fc1e")
fmt.Println(NewValueFromBytes(dec).BigInt())
}
func TestEncodeDecodeBytes(t *testing.T) {
b := NewValue([]interface{}{[]byte{1, 2, 3, 4, 5}, byte(6)})
val := NewValueFromBytes(b.Encode())
if !b.Cmp(val) {
t.Errorf("Expected %v, got %v", val, b)
}
}
/*
var ZeroHash256 = make([]byte, 32)
var ZeroHash160 = make([]byte, 20)
var EmptyShaList = Sha3Bin(Encode([]interface{}{}))
var GenisisHeader = []interface{}{
// Previous hash (none)
//"",
ZeroHash256,
// Sha of uncles
Sha3Bin(Encode([]interface{}{})),
// Coinbase
ZeroHash160,
// Root state
"",
// Sha of transactions
//EmptyShaList,
Sha3Bin(Encode([]interface{}{})),
// Difficulty
BigPow(2, 22),
// Time
//big.NewInt(0),
int64(0),
// extra
"",
// Nonce
big.NewInt(42),
}
func TestEnc(t *testing.T) {
//enc := Encode(GenisisHeader)
//fmt.Printf("%x (%d)\n", enc, len(enc))
h, _ := hex.DecodeString("f8a0a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a06d076baa9c4074fb2df222dd16a96b0155a1e6686b3e5748b4e9ca0a208a425ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493478340000080802a")
fmt.Printf("%x\n", Sha3Bin(h))
}
*/
func BenchmarkEncodeDecode(b *testing.B) {
for i := 0; i < b.N; i++ {
bytes := Encode([]interface{}{"dog", "god", "cat"})
Decode(bytes, 0)
}
}

354
ethutil/trie.go Normal file
View File

@ -0,0 +1,354 @@
package ethutil
import (
"fmt"
"reflect"
)
type Node struct {
Key []byte
Value *Value
Dirty bool
}
func NewNode(key []byte, val *Value, dirty bool) *Node {
return &Node{Key: key, Value: val, Dirty: dirty}
}
func (n *Node) Copy() *Node {
return NewNode(n.Key, n.Value, n.Dirty)
}
type Cache struct {
nodes map[string]*Node
db Database
}
func NewCache(db Database) *Cache {
return &Cache{db: db, nodes: make(map[string]*Node)}
}
func (cache *Cache) Put(v interface{}) interface{} {
value := NewValue(v)
enc := value.Encode()
if len(enc) >= 32 {
sha := Sha3Bin(enc)
cache.nodes[string(sha)] = NewNode(sha, value, true)
return sha
}
return v
}
func (cache *Cache) Get(key []byte) *Value {
// First check if the key is the cache
if cache.nodes[string(key)] != nil {
return cache.nodes[string(key)].Value
}
// Get the key of the database instead and cache it
data, _ := cache.db.Get(key)
// Create the cached value
value := NewValueFromBytes(data)
// Create caching node
cache.nodes[string(key)] = NewNode(key, value, false)
return value
}
func (cache *Cache) Commit() {
for key, node := range cache.nodes {
if node.Dirty {
cache.db.Put([]byte(key), node.Value.Encode())
node.Dirty = false
}
}
// If the nodes grows beyond the 200 entries we simple empty it
// FIXME come up with something better
if len(cache.nodes) > 200 {
cache.nodes = make(map[string]*Node)
}
}
func (cache *Cache) Undo() {
for key, node := range cache.nodes {
if node.Dirty {
delete(cache.nodes, key)
}
}
}
// A (modified) Radix Trie implementation
type Trie struct {
Root interface{}
//db Database
cache *Cache
}
func NewTrie(db Database, Root interface{}) *Trie {
return &Trie{cache: NewCache(db), Root: Root}
}
func (t *Trie) Sync() {
t.cache.Commit()
}
/*
* Public (query) interface functions
*/
func (t *Trie) Update(key string, value string) {
k := CompactHexDecode(key)
t.Root = t.UpdateState(t.Root, k, value)
}
func (t *Trie) Get(key string) string {
k := CompactHexDecode(key)
c := NewValue(t.GetState(t.Root, k))
return c.Str()
}
func (t *Trie) GetState(node interface{}, key []int) interface{} {
n := NewValue(node)
// Return the node if key is empty (= found)
if len(key) == 0 || n.IsNil() || n.Len() == 0 {
return node
}
currentNode := t.GetNode(node)
length := currentNode.Len()
if length == 0 {
return ""
} else if length == 2 {
// Decode the key
k := CompactDecode(currentNode.Get(0).Str())
v := currentNode.Get(1).Raw()
if len(key) >= len(k) && CompareIntSlice(k, key[:len(k)]) {
return t.GetState(v, key[len(k):])
} else {
return ""
}
} else if length == 17 {
return t.GetState(currentNode.Get(key[0]).Raw(), key[1:])
}
// It shouldn't come this far
fmt.Println("GetState unexpected return")
return ""
}
func (t *Trie) GetNode(node interface{}) *Value {
n := NewValue(node)
if !n.Get(0).IsNil() {
return n
}
str := n.Str()
if len(str) == 0 {
return n
} else if len(str) < 32 {
return NewValueFromBytes([]byte(str))
}
/*
else {
// Fetch the encoded node from the db
o, err := t.db.Get(n.Bytes())
if err != nil {
fmt.Println("Error InsertState", err)
return NewValue("")
}
return NewValueFromBytes(o)
}
*/
return t.cache.Get(n.Bytes())
}
func (t *Trie) UpdateState(node interface{}, key []int, value string) interface{} {
if value != "" {
return t.InsertState(node, key, value)
} else {
// delete it
}
return ""
}
func (t *Trie) Put(node interface{}) interface{} {
/*
enc := Encode(node)
if len(enc) >= 32 {
var sha []byte
sha = Sha3Bin(enc)
//t.db.Put([]byte(sha), enc)
return sha
}
return node
*/
/*
TODO?
c := Conv(t.Root)
fmt.Println(c.Type(), c.Length())
if c.Type() == reflect.String && c.AsString() == "" {
return enc
}
*/
return t.cache.Put(node)
}
func EmptyStringSlice(l int) []interface{} {
slice := make([]interface{}, l)
for i := 0; i < l; i++ {
slice[i] = ""
}
return slice
}
func (t *Trie) InsertState(node interface{}, key []int, value interface{}) interface{} {
if len(key) == 0 {
return value
}
// New node
n := NewValue(node)
if node == nil || (n.Type() == reflect.String && (n.Str() == "" || n.Get(0).IsNil())) || n.Len() == 0 {
newNode := []interface{}{CompactEncode(key), value}
return t.Put(newNode)
}
currentNode := t.GetNode(node)
// Check for "special" 2 slice type node
if currentNode.Len() == 2 {
// Decode the key
k := CompactDecode(currentNode.Get(0).Str())
v := currentNode.Get(1).Raw()
// Matching key pair (ie. there's already an object with this key)
if CompareIntSlice(k, key) {
newNode := []interface{}{CompactEncode(key), value}
return t.Put(newNode)
}
var newHash interface{}
matchingLength := MatchingNibbleLength(key, k)
if matchingLength == len(k) {
// Insert the hash, creating a new node
newHash = t.InsertState(v, key[matchingLength:], value)
} else {
// Expand the 2 length slice to a 17 length slice
oldNode := t.InsertState("", k[matchingLength+1:], v)
newNode := t.InsertState("", key[matchingLength+1:], value)
// Create an expanded slice
scaledSlice := EmptyStringSlice(17)
// Set the copied and new node
scaledSlice[k[matchingLength]] = oldNode
scaledSlice[key[matchingLength]] = newNode
newHash = t.Put(scaledSlice)
}
if matchingLength == 0 {
// End of the chain, return
return newHash
} else {
newNode := []interface{}{CompactEncode(key[:matchingLength]), newHash}
return t.Put(newNode)
}
} else {
// Copy the current node over to the new node and replace the first nibble in the key
newNode := EmptyStringSlice(17)
for i := 0; i < 17; i++ {
cpy := currentNode.Get(i).Raw()
if cpy != nil {
newNode[i] = cpy
}
}
newNode[key[0]] = t.InsertState(currentNode.Get(key[0]).Raw(), key[1:], value)
return t.Put(newNode)
}
return ""
}
// Simple compare function which creates a rlp value out of the evaluated objects
func (t *Trie) Cmp(trie *Trie) bool {
return NewValue(t.Root).Cmp(NewValue(trie.Root))
}
// Returns a copy of this trie
func (t *Trie) Copy() *Trie {
trie := NewTrie(t.cache.db, t.Root)
for key, node := range t.cache.nodes {
trie.cache.nodes[key] = node.Copy()
}
return trie
}
/*
* Trie helper functions
*/
// Helper function for printing out the raw contents of a slice
func PrintSlice(slice []string) {
fmt.Printf("[")
for i, val := range slice {
fmt.Printf("%q", val)
if i != len(slice)-1 {
fmt.Printf(",")
}
}
fmt.Printf("]\n")
}
func PrintSliceT(slice interface{}) {
c := Conv(slice)
for i := 0; i < c.Length(); i++ {
val := c.Get(i)
if val.Type() == reflect.Slice {
PrintSliceT(val.AsRaw())
} else {
fmt.Printf("%q", val)
if i != c.Length()-1 {
fmt.Printf(",")
}
}
}
}
// RLP Decodes a node in to a [2] or [17] string slice
func DecodeNode(data []byte) []string {
dec, _ := Decode(data, 0)
if slice, ok := dec.([]interface{}); ok {
strSlice := make([]string, len(slice))
for i, s := range slice {
if str, ok := s.([]byte); ok {
strSlice[i] = string(str)
}
}
return strSlice
} else {
fmt.Printf("It wasn't a []. It's a %T\n", dec)
}
return nil
}

40
ethutil/trie_test.go Normal file
View File

@ -0,0 +1,40 @@
package ethutil
import (
_ "encoding/hex"
_ "fmt"
"testing"
)
type MemDatabase struct {
db map[string][]byte
}
func NewMemDatabase() (*MemDatabase, error) {
db := &MemDatabase{db: make(map[string][]byte)}
return db, nil
}
func (db *MemDatabase) Put(key []byte, value []byte) {
db.db[string(key)] = value
}
func (db *MemDatabase) Get(key []byte) ([]byte, error) {
return db.db[string(key)], nil
}
func (db *MemDatabase) Print() {}
func (db *MemDatabase) Close() {}
func (db *MemDatabase) LastKnownTD() []byte { return nil }
func TestTrieSync(t *testing.T) {
db, _ := NewMemDatabase()
trie := NewTrie(db, "")
trie.Update("dog", "kindofalongsentencewhichshouldbeencodedinitsentirety")
if len(db.db) != 0 {
t.Error("Expected no data in database")
}
trie.Sync()
if len(db.db) == 0 {
t.Error("Expected data to be persisted")
}
}

204
ethutil/value.go Normal file
View File

@ -0,0 +1,204 @@
package ethutil
import (
"bytes"
"fmt"
"math/big"
"reflect"
)
// Data values are returned by the rlp decoder. The data values represents
// one item within the rlp data structure. It's responsible for all the casting
// It always returns something valid
type Value struct {
Val interface{}
kind reflect.Value
}
func (val *Value) String() string {
return fmt.Sprintf("%q", val.Val)
}
func NewValue(val interface{}) *Value {
return &Value{Val: val}
}
func (val *Value) Type() reflect.Kind {
return reflect.TypeOf(val.Val).Kind()
}
func (val *Value) IsNil() bool {
return val.Val == nil
}
func (val *Value) Len() int {
//return val.kind.Len()
if data, ok := val.Val.([]interface{}); ok {
return len(data)
} else if data, ok := val.Val.([]byte); ok {
// FIXME
return len(data)
}
return 0
}
func (val *Value) Raw() interface{} {
return val.Val
}
func (val *Value) Interface() interface{} {
return val.Val
}
func (val *Value) Uint() uint64 {
if Val, ok := val.Val.(uint8); ok {
return uint64(Val)
} else if Val, ok := val.Val.(uint16); ok {
return uint64(Val)
} else if Val, ok := val.Val.(uint32); ok {
return uint64(Val)
} else if Val, ok := val.Val.(uint64); ok {
return Val
} else if Val, ok := val.Val.([]byte); ok {
return ReadVarint(bytes.NewReader(Val))
}
return 0
}
func (val *Value) Byte() byte {
if Val, ok := val.Val.(byte); ok {
return Val
}
return 0x0
}
func (val *Value) BigInt() *big.Int {
if a, ok := val.Val.([]byte); ok {
b := new(big.Int).SetBytes(a)
return b
} else {
return big.NewInt(int64(val.Uint()))
}
return big.NewInt(0)
}
func (val *Value) Str() string {
if a, ok := val.Val.([]byte); ok {
return string(a)
} else if a, ok := val.Val.(string); ok {
return a
}
return ""
}
func (val *Value) Bytes() []byte {
if a, ok := val.Val.([]byte); ok {
return a
}
return make([]byte, 0)
}
func (val *Value) Slice() []interface{} {
if d, ok := val.Val.([]interface{}); ok {
return d
}
return []interface{}{}
}
func (val *Value) SliceFrom(from int) *Value {
slice := val.Slice()
return NewValue(slice[from:])
}
func (val *Value) SliceTo(to int) *Value {
slice := val.Slice()
return NewValue(slice[:to])
}
func (val *Value) SliceFromTo(from, to int) *Value {
slice := val.Slice()
return NewValue(slice[from:to])
}
// Threat the value as a slice
func (val *Value) Get(idx int) *Value {
if d, ok := val.Val.([]interface{}); ok {
// Guard for oob
if len(d) <= idx {
return NewValue(nil)
}
if idx < 0 {
panic("negative idx for Rlp Get")
}
return NewValue(d[idx])
}
// If this wasn't a slice you probably shouldn't be using this function
return NewValue(nil)
}
func (val *Value) Cmp(o *Value) bool {
return reflect.DeepEqual(val.Val, o.Val)
}
func (val *Value) Encode() []byte {
return Encode(val.Val)
}
func NewValueFromBytes(rlpData []byte) *Value {
if len(rlpData) != 0 {
data, _ := Decode(rlpData, 0)
return NewValue(data)
}
return NewValue(nil)
}
// Value setters
func NewSliceValue(s interface{}) *Value {
list := EmptyValue()
if s != nil {
if slice, ok := s.([]interface{}); ok {
for _, val := range slice {
list.Append(val)
}
} else if slice, ok := s.([]string); ok {
for _, val := range slice {
list.Append(val)
}
}
}
return list
}
func EmptyValue() *Value {
return NewValue([]interface{}{})
}
func (val *Value) AppendList() *Value {
list := EmptyValue()
val.Val = append(val.Slice(), list)
return list
}
func (val *Value) Append(v interface{}) *Value {
val.Val = append(val.Slice(), v)
return val
}

12
ethwire/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile ~/.gitignore_global
/tmp
*/**/*un~
*un~
.DS_Store
*/**/.DS_Store

36
ethwire/README.md Normal file
View File

@ -0,0 +1,36 @@
# ethwire
The ethwire package contains the ethereum wire protocol. The ethwire
package is required to write and read from the ethereum network.
# Installation
`go get github.com/ethereum/ethwire-go`
# Messaging overview
The Ethereum Wire protocol defines the communication between the nodes
running Ethereum. Further reader reading can be done on the
[Wiki](http://wiki.ethereum.org/index.php/Wire_Protocol).
# Reading Messages
```go
// Read and validate the next eth message from the provided connection.
// returns a error message with the details.
msg, err := ethwire.ReadMessage(conn)
if err != nil {
// Handle error
}
```
# Writing Messages
```go
// Constructs a message which can be interpreted by the eth network.
// Write the inventory to network
err := ethwire.WriteMessage(conn, &Msg{
Type: ethwire.MsgInvTy,
Data : []interface{}{...},
})
```

180
ethwire/messaging.go Normal file
View File

@ -0,0 +1,180 @@
package ethwire
import (
"bytes"
"errors"
"fmt"
"github.com/ethereum/eth-go/ethutil"
"net"
"time"
)
// Message:
// [4 bytes token] RLP([TYPE, DATA])
// Refer to http://wiki.ethereum.org/index.php/Wire_Protocol
// The magic token which should be the first 4 bytes of every message.
var MagicToken = []byte{34, 64, 8, 145}
type MsgType byte
const (
MsgHandshakeTy = 0x00
MsgDiscTy = 0x01
MsgPingTy = 0x02
MsgPongTy = 0x03
MsgGetPeersTy = 0x10
MsgPeersTy = 0x11
MsgTxTy = 0x12
MsgBlockTy = 0x13
MsgGetChainTy = 0x14
MsgNotInChainTy = 0x15
MsgTalkTy = 0xff
)
var msgTypeToString = map[MsgType]string{
MsgHandshakeTy: "Handshake",
MsgDiscTy: "Disconnect",
MsgPingTy: "Ping",
MsgPongTy: "Pong",
MsgGetPeersTy: "Get peers",
MsgPeersTy: "Peers",
MsgTxTy: "Transactions",
MsgBlockTy: "Blocks",
MsgGetChainTy: "Get chain",
MsgNotInChainTy: "Not in chain",
}
func (mt MsgType) String() string {
return msgTypeToString[mt]
}
type Msg struct {
Type MsgType // Specifies how the encoded data should be interpreted
//Data []byte
Data *ethutil.Value
}
func NewMessage(msgType MsgType, data interface{}) *Msg {
return &Msg{
Type: msgType,
Data: ethutil.NewValue(data),
}
}
func ReadMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) {
if len(data) == 0 {
return nil, nil, true, nil
}
if len(data) <= 8 {
return nil, remaining, false, errors.New("Invalid message")
}
// Check if the received 4 first bytes are the magic token
if bytes.Compare(MagicToken, data[:4]) != 0 {
return nil, nil, false, fmt.Errorf("MagicToken mismatch. Received %v", data[:4])
}
messageLength := ethutil.BytesToNumber(data[4:8])
remaining = data[8+messageLength:]
if int(messageLength) > len(data[8:]) {
return nil, nil, false, fmt.Errorf("message length %d, expected %d", len(data[8:]), messageLength)
}
message := data[8 : 8+messageLength]
decoder := ethutil.NewValueFromBytes(message)
// Type of message
t := decoder.Get(0).Uint()
// Actual data
d := decoder.SliceFrom(1)
msg = &Msg{
Type: MsgType(t),
Data: d,
}
return
}
func bufferedRead(conn net.Conn) ([]byte, error) {
return nil, nil
}
// The basic message reader waits for data on the given connection, decoding
// and doing a few sanity checks such as if there's a data type and
// unmarhals the given data
func ReadMessages(conn net.Conn) (msgs []*Msg, err error) {
// The recovering function in case anything goes horribly wrong
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("ethwire.ReadMessage error: %v", r)
}
}()
// Buff for writing network message to
//buff := make([]byte, 1440)
var buff []byte
var totalBytes int
for {
// Give buffering some time
conn.SetReadDeadline(time.Now().Add(20 * time.Millisecond))
// Create a new temporarily buffer
b := make([]byte, 1440)
// Wait for a message from this peer
n, _ := conn.Read(b)
if err != nil && n == 0 {
if err.Error() != "EOF" {
fmt.Println("err now", err)
return nil, err
} else {
fmt.Println("IOF NOW")
break
}
// Messages can't be empty
} else if n == 0 {
break
}
buff = append(buff, b[:n]...)
totalBytes += n
}
// Reslice buffer
buff = buff[:totalBytes]
msg, remaining, done, err := ReadMessage(buff)
for ; done != true; msg, remaining, done, err = ReadMessage(remaining) {
//log.Println("rx", msg)
if msg != nil {
msgs = append(msgs, msg)
}
}
return
}
// The basic message writer takes care of writing data over the given
// connection and does some basic error checking
func WriteMessage(conn net.Conn, msg *Msg) error {
var pack []byte
// Encode the type and the (RLP encoded) data for sending over the wire
encoded := ethutil.NewValue(append([]interface{}{byte(msg.Type)}, msg.Data.Slice()...)).Encode()
payloadLength := ethutil.NumberToBytes(uint32(len(encoded)), 32)
// Write magic token and payload length (first 8 bytes)
pack = append(MagicToken, payloadLength...)
pack = append(pack, encoded...)
//fmt.Printf("payload %v (%v) %q\n", msg.Type, conn.RemoteAddr(), encoded)
// Write to the connection
_, err := conn.Write(pack)
if err != nil {
return err
}
return nil
}

11
peer.go
View File

@ -3,9 +3,9 @@ package eth
import (
"bytes"
"fmt"
"github.com/ethereum/ethchain-go"
"github.com/ethereum/ethutil-go"
"github.com/ethereum/ethwire-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
"log"
"net"
"runtime"
@ -293,7 +293,7 @@ func (p *Peer) HandleInbound() {
err = p.ethereum.BlockManager.ProcessBlock(block)
if err != nil {
log.Println(err)
log.Println("bckmsg", err)
break
} else {
lastBlock = block
@ -306,8 +306,7 @@ func (p *Peer) HandleInbound() {
log.Println("Attempting to catch up")
p.catchingUp = false
p.CatchupWithPeer()
}
if ethchain.IsValidationErr(err) {
} else if ethchain.IsValidationErr(err) {
// TODO
}
} else {