forked from cerc-io/plugeth
The great merge
This commit is contained in:
parent
c2fb9f06ad
commit
f6d1bfe45b
12
ethchain/.gitignore
vendored
Normal file
12
ethchain/.gitignore
vendored
Normal 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
363
ethchain/block.go
Normal 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
184
ethchain/block_chain.go
Normal 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
627
ethchain/block_manager.go
Normal 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()
|
||||||
|
}
|
75
ethchain/block_manager_test.go
Normal file
75
ethchain/block_manager_test.go
Normal 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
66
ethchain/contract.go
Normal 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
199
ethchain/dagger.go
Normal 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
18
ethchain/dagger_test.go
Normal 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
42
ethchain/error.go
Normal 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
65
ethchain/fees.go
Normal 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
39
ethchain/genesis.go
Normal 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
167
ethchain/stack.go
Normal 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
157
ethchain/transaction.go
Normal 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()
|
||||||
|
}
|
219
ethchain/transaction_pool.go
Normal file
219
ethchain/transaction_pool.go
Normal 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()
|
||||||
|
}
|
54
ethchain/transaction_test.go
Normal file
54
ethchain/transaction_test.go
Normal 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
12
ethdb/.gitignore
vendored
Normal 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
11
ethdb/README.md
Normal 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
64
ethdb/database.go
Normal 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
6
ethdb/database_test.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package ethdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "fmt"
|
||||||
|
_ "testing"
|
||||||
|
)
|
49
ethdb/memory_database.go
Normal file
49
ethdb/memory_database.go
Normal 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
|
||||||
|
}
|
12
ethereum.go
12
ethereum.go
@ -2,10 +2,10 @@ package eth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"github.com/ethereum/ethchain-go"
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
"github.com/ethereum/ethdb-go"
|
"github.com/ethereum/eth-go/ethdb"
|
||||||
"github.com/ethereum/ethutil-go"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"github.com/ethereum/ethwire-go"
|
"github.com/ethereum/eth-go/ethwire"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
@ -60,8 +60,8 @@ type Ethereum struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func New(caps Caps, usePnp bool) (*Ethereum, error) {
|
func New(caps Caps, usePnp bool) (*Ethereum, error) {
|
||||||
db, err := ethdb.NewLDBDatabase()
|
//db, err := ethdb.NewLDBDatabase()
|
||||||
//db, err := ethdb.NewMemDatabase()
|
db, err := ethdb.NewMemDatabase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
12
ethutil/.gitignore
vendored
Normal file
12
ethutil/.gitignore
vendored
Normal 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
3
ethutil/.travil.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.2
|
137
ethutil/README.md
Normal file
137
ethutil/README.md
Normal 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
37
ethutil/big.go
Normal 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
64
ethutil/bytes.go
Normal 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
106
ethutil/config.go
Normal 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
10
ethutil/db.go
Normal 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
62
ethutil/encoding.go
Normal 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
37
ethutil/encoding_test.go
Normal 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
61
ethutil/helpers.go
Normal 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
108
ethutil/parsing.go
Normal 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
32
ethutil/parsing_test.go
Normal 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
24
ethutil/rand.go
Normal 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
418
ethutil/rlp.go
Normal 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
170
ethutil/rlp_test.go
Normal 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
354
ethutil/trie.go
Normal 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
40
ethutil/trie_test.go
Normal 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
204
ethutil/value.go
Normal 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
12
ethwire/.gitignore
vendored
Normal 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
36
ethwire/README.md
Normal 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
180
ethwire/messaging.go
Normal 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
11
peer.go
@ -3,9 +3,9 @@ package eth
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/ethchain-go"
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
"github.com/ethereum/ethutil-go"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"github.com/ethereum/ethwire-go"
|
"github.com/ethereum/eth-go/ethwire"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -293,7 +293,7 @@ func (p *Peer) HandleInbound() {
|
|||||||
err = p.ethereum.BlockManager.ProcessBlock(block)
|
err = p.ethereum.BlockManager.ProcessBlock(block)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println("bckmsg", err)
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
lastBlock = block
|
lastBlock = block
|
||||||
@ -306,8 +306,7 @@ func (p *Peer) HandleInbound() {
|
|||||||
log.Println("Attempting to catch up")
|
log.Println("Attempting to catch up")
|
||||||
p.catchingUp = false
|
p.catchingUp = false
|
||||||
p.CatchupWithPeer()
|
p.CatchupWithPeer()
|
||||||
}
|
} else if ethchain.IsValidationErr(err) {
|
||||||
if ethchain.IsValidationErr(err) {
|
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user