Merge branch 'release/0.3.0'
This commit is contained in:
commit
839bd73fbb
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2013 Geff Obscura
|
Copyright (c) 2013 Jeffrey Wilcke
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
60
ethchain/address.go
Normal file
60
ethchain/address.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package ethchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
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.NewValueFromBytes(data)
|
||||||
|
|
||||||
|
a.Amount = decoder.Get(0).BigInt()
|
||||||
|
a.Nonce = decoder.Get(1).Uint()
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddrStateStore struct {
|
||||||
|
states map[string]*AddressState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAddrStateStore() *AddrStateStore {
|
||||||
|
return &AddrStateStore{states: make(map[string]*AddressState)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AddrStateStore) Add(addr []byte, account *Address) *AddressState {
|
||||||
|
state := &AddressState{Nonce: account.Nonce, Account: account}
|
||||||
|
s.states[string(addr)] = state
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AddrStateStore) Get(addr []byte) *AddressState {
|
||||||
|
return s.states[string(addr)]
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddressState struct {
|
||||||
|
Nonce uint64
|
||||||
|
Account *Address
|
||||||
|
}
|
8
ethchain/address_test.go
Normal file
8
ethchain/address_test.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package ethchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddressState(t *testing.T) {
|
||||||
|
}
|
@ -46,6 +46,8 @@ type Block struct {
|
|||||||
// List of transactions and/or contracts
|
// List of transactions and/or contracts
|
||||||
transactions []*Transaction
|
transactions []*Transaction
|
||||||
TxSha []byte
|
TxSha []byte
|
||||||
|
|
||||||
|
contractStates map[string]*ethutil.Trie
|
||||||
}
|
}
|
||||||
|
|
||||||
// New block takes a raw encoded string
|
// New block takes a raw encoded string
|
||||||
@ -79,14 +81,15 @@ func CreateBlock(root interface{},
|
|||||||
|
|
||||||
block := &Block{
|
block := &Block{
|
||||||
// Slice of transactions to include in this block
|
// Slice of transactions to include in this block
|
||||||
transactions: txes,
|
transactions: txes,
|
||||||
PrevHash: prevHash,
|
PrevHash: prevHash,
|
||||||
Coinbase: base,
|
Coinbase: base,
|
||||||
Difficulty: Difficulty,
|
Difficulty: Difficulty,
|
||||||
Nonce: Nonce,
|
Nonce: Nonce,
|
||||||
Time: time.Now().Unix(),
|
Time: time.Now().Unix(),
|
||||||
Extra: extra,
|
Extra: extra,
|
||||||
UncleSha: EmptyShaList,
|
UncleSha: EmptyShaList,
|
||||||
|
contractStates: make(map[string]*ethutil.Trie),
|
||||||
}
|
}
|
||||||
block.SetTransactions(txes)
|
block.SetTransactions(txes)
|
||||||
block.SetUncles([]*Block{})
|
block.SetUncles([]*Block{})
|
||||||
@ -128,14 +131,26 @@ func (block *Block) GetContract(addr []byte) *Contract {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value := ethutil.NewValueFromBytes([]byte(data))
|
||||||
|
if value.Len() == 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
contract := &Contract{}
|
contract := &Contract{}
|
||||||
contract.RlpDecode([]byte(data))
|
contract.RlpDecode([]byte(data))
|
||||||
|
|
||||||
|
cachedState := block.contractStates[string(addr)]
|
||||||
|
if cachedState != nil {
|
||||||
|
contract.state = cachedState
|
||||||
|
} else {
|
||||||
|
block.contractStates[string(addr)] = contract.state
|
||||||
|
}
|
||||||
|
|
||||||
return contract
|
return contract
|
||||||
}
|
}
|
||||||
func (block *Block) UpdateContract(addr []byte, contract *Contract) {
|
func (block *Block) UpdateContract(addr []byte, contract *Contract) {
|
||||||
// Make sure the state is synced
|
// Make sure the state is synced
|
||||||
contract.State().Sync()
|
//contract.State().Sync()
|
||||||
|
|
||||||
block.state.Update(string(addr), string(contract.RlpEncode()))
|
block.state.Update(string(addr), string(contract.RlpEncode()))
|
||||||
}
|
}
|
||||||
@ -190,18 +205,29 @@ func (block *Block) BlockInfo() BlockInfo {
|
|||||||
return bi
|
return bi
|
||||||
}
|
}
|
||||||
|
|
||||||
func (block *Block) MakeContract(tx *Transaction) {
|
// Sync the block's state and contract respectively
|
||||||
// Create contract if there's no recipient
|
func (block *Block) Sync() {
|
||||||
if tx.IsContract() {
|
// Sync all contracts currently in cache
|
||||||
addr := tx.Hash()
|
for _, val := range block.contractStates {
|
||||||
|
val.Sync()
|
||||||
|
}
|
||||||
|
// Sync the block state itself
|
||||||
|
block.state.Sync()
|
||||||
|
}
|
||||||
|
|
||||||
value := tx.Value
|
func (block *Block) Undo() {
|
||||||
contract := NewContract(value, []byte(""))
|
// Sync all contracts currently in cache
|
||||||
block.state.Update(string(addr), string(contract.RlpEncode()))
|
for _, val := range block.contractStates {
|
||||||
for i, val := range tx.Data {
|
val.Undo()
|
||||||
contract.state.Update(string(ethutil.NumberToBytes(uint64(i), 32)), val)
|
}
|
||||||
}
|
// Sync the block state itself
|
||||||
block.UpdateContract(addr, contract)
|
block.state.Undo()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (block *Block) MakeContract(tx *Transaction) {
|
||||||
|
contract := MakeContract(tx, NewState(block.state))
|
||||||
|
if contract != nil {
|
||||||
|
block.contractStates[string(tx.Hash()[12:])] = contract.state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,6 +314,7 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
|
|||||||
block.Time = int64(header.Get(6).BigInt().Uint64())
|
block.Time = int64(header.Get(6).BigInt().Uint64())
|
||||||
block.Extra = header.Get(7).Str()
|
block.Extra = header.Get(7).Str()
|
||||||
block.Nonce = header.Get(8).Bytes()
|
block.Nonce = header.Get(8).Bytes()
|
||||||
|
block.contractStates = make(map[string]*ethutil.Trie)
|
||||||
|
|
||||||
// Tx list might be empty if this is an uncle. Uncles only have their
|
// Tx list might be empty if this is an uncle. Uncles only have their
|
||||||
// header set.
|
// header set.
|
||||||
@ -298,12 +325,6 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
|
|||||||
tx := NewTransactionFromValue(txes.Get(i))
|
tx := NewTransactionFromValue(txes.Get(i))
|
||||||
|
|
||||||
block.transactions[i] = tx
|
block.transactions[i] = tx
|
||||||
|
|
||||||
/*
|
|
||||||
if ethutil.Config.Debug {
|
|
||||||
ethutil.Config.Db.Put(tx.Hash(), ethutil.Encode(tx))
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -335,7 +356,7 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (block *Block) String() string {
|
func (block *Block) String() string {
|
||||||
return fmt.Sprintf("Block(%x):\nPrevHash:%x\nUncleSha:%x\nCoinbase:%x\nRoot:%x\nTxSha:%x\nDiff:%v\nTime:%d\nNonce:%x", block.Hash(), block.PrevHash, block.UncleSha, block.Coinbase, block.state.Root, block.TxSha, block.Difficulty, block.Time, block.Nonce)
|
return fmt.Sprintf("Block(%x):\nPrevHash:%x\nUncleSha:%x\nCoinbase:%x\nRoot:%x\nTxSha:%x\nDiff:%v\nTime:%d\nNonce:%x\nTxs:%d\n", block.Hash(), block.PrevHash, block.UncleSha, block.Coinbase, block.state.Root, block.TxSha, block.Difficulty, block.Time, block.Nonce, len(block.transactions))
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////// UNEXPORTED /////////////////
|
//////////// UNEXPORTED /////////////////
|
||||||
|
@ -104,7 +104,6 @@ func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
|
|||||||
currentHash = block.PrevHash
|
currentHash = block.PrevHash
|
||||||
|
|
||||||
chain = append(chain, block.Value().Val)
|
chain = append(chain, block.Value().Val)
|
||||||
//chain = append([]interface{}{block.RlpValue().Value}, chain...)
|
|
||||||
|
|
||||||
num--
|
num--
|
||||||
}
|
}
|
||||||
@ -112,6 +111,24 @@ func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
|
|||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bc *BlockChain) GetChain(hash []byte, amount int) []*Block {
|
||||||
|
genHash := bc.genesisBlock.Hash()
|
||||||
|
|
||||||
|
block := bc.GetBlock(hash)
|
||||||
|
var blocks []*Block
|
||||||
|
|
||||||
|
for i := 0; i < amount && block != nil; block = bc.GetBlock(block.PrevHash) {
|
||||||
|
blocks = append([]*Block{block}, blocks...)
|
||||||
|
|
||||||
|
if bytes.Compare(genHash, block.Hash()) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) setLastBlock() {
|
func (bc *BlockChain) setLastBlock() {
|
||||||
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
|
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
|
||||||
if len(data) != 0 {
|
if len(data) != 0 {
|
||||||
@ -141,11 +158,16 @@ func (bc *BlockChain) Add(block *Block) {
|
|||||||
bc.CurrentBlock = block
|
bc.CurrentBlock = block
|
||||||
bc.LastBlockHash = block.Hash()
|
bc.LastBlockHash = block.Hash()
|
||||||
|
|
||||||
ethutil.Config.Db.Put(block.Hash(), block.RlpEncode())
|
encodedBlock := block.RlpEncode()
|
||||||
|
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
|
||||||
|
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) GetBlock(hash []byte) *Block {
|
func (bc *BlockChain) GetBlock(hash []byte) *Block {
|
||||||
data, _ := ethutil.Config.Db.Get(hash)
|
data, _ := ethutil.Config.Db.Get(hash)
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return NewBlockFromData(data)
|
return NewBlockFromData(data)
|
||||||
}
|
}
|
||||||
@ -177,8 +199,6 @@ func (bc *BlockChain) writeBlockInfo(block *Block) {
|
|||||||
|
|
||||||
func (bc *BlockChain) Stop() {
|
func (bc *BlockChain) Stop() {
|
||||||
if bc.CurrentBlock != nil {
|
if bc.CurrentBlock != nil {
|
||||||
ethutil.Config.Db.Put([]byte("LastBlock"), bc.CurrentBlock.RlpEncode())
|
|
||||||
|
|
||||||
log.Println("[CHAIN] Stopped")
|
log.Println("[CHAIN] Stopped")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,9 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"github.com/obscuren/secp256k1-go"
|
_ "github.com/ethereum/eth-go/ethwire"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -18,10 +16,7 @@ type BlockProcessor interface {
|
|||||||
ProcessBlock(block *Block)
|
ProcessBlock(block *Block)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CalculateBlockReward(block *Block, uncleLength int) *big.Int {
|
// TODO rename to state manager
|
||||||
return BlockReward
|
|
||||||
}
|
|
||||||
|
|
||||||
type BlockManager struct {
|
type BlockManager struct {
|
||||||
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
@ -29,6 +24,10 @@ type BlockManager struct {
|
|||||||
// The block chain :)
|
// The block chain :)
|
||||||
bc *BlockChain
|
bc *BlockChain
|
||||||
|
|
||||||
|
// States for addresses. You can watch any address
|
||||||
|
// at any given time
|
||||||
|
addrStateStore *AddrStateStore
|
||||||
|
|
||||||
// Stack for processing contracts
|
// Stack for processing contracts
|
||||||
stack *Stack
|
stack *Stack
|
||||||
// non-persistent key/value memory storage
|
// non-persistent key/value memory storage
|
||||||
@ -46,9 +45,9 @@ type BlockManager struct {
|
|||||||
func AddTestNetFunds(block *Block) {
|
func AddTestNetFunds(block *Block) {
|
||||||
for _, addr := range []string{
|
for _, addr := range []string{
|
||||||
"8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin
|
"8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin
|
||||||
"93658b04240e4bd4046fd2d6d417d20f146f4b43", // Jeffrey
|
"e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey
|
||||||
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
|
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
|
||||||
"80c01a26338f0d905e295fccb71fa9ea849ffa12", // Alex
|
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex
|
||||||
} {
|
} {
|
||||||
//log.Println("2^200 Wei to", addr)
|
//log.Println("2^200 Wei to", addr)
|
||||||
codedAddr, _ := hex.DecodeString(addr)
|
codedAddr, _ := hex.DecodeString(addr)
|
||||||
@ -61,26 +60,47 @@ func AddTestNetFunds(block *Block) {
|
|||||||
func NewBlockManager(speaker PublicSpeaker) *BlockManager {
|
func NewBlockManager(speaker PublicSpeaker) *BlockManager {
|
||||||
bm := &BlockManager{
|
bm := &BlockManager{
|
||||||
//server: s,
|
//server: s,
|
||||||
bc: NewBlockChain(),
|
bc: NewBlockChain(),
|
||||||
stack: NewStack(),
|
stack: NewStack(),
|
||||||
mem: make(map[string]*big.Int),
|
mem: make(map[string]*big.Int),
|
||||||
Pow: &EasyPow{},
|
Pow: &EasyPow{},
|
||||||
Speaker: speaker,
|
Speaker: speaker,
|
||||||
|
addrStateStore: NewAddrStateStore(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if bm.bc.CurrentBlock == nil {
|
if bm.bc.CurrentBlock == nil {
|
||||||
AddTestNetFunds(bm.bc.genesisBlock)
|
AddTestNetFunds(bm.bc.genesisBlock)
|
||||||
|
|
||||||
|
bm.bc.genesisBlock.State().Sync()
|
||||||
// Prepare the genesis block
|
// Prepare the genesis block
|
||||||
bm.bc.Add(bm.bc.genesisBlock)
|
bm.bc.Add(bm.bc.genesisBlock)
|
||||||
|
|
||||||
log.Printf("Genesis: %x\n", bm.bc.genesisBlock.Hash())
|
|
||||||
//log.Printf("root %x\n", bm.bc.genesisBlock.State().Root)
|
//log.Printf("root %x\n", bm.bc.genesisBlock.State().Root)
|
||||||
//bm.bc.genesisBlock.PrintHash()
|
//bm.bc.genesisBlock.PrintHash()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("Last block: %x\n", bm.bc.CurrentBlock.Hash())
|
||||||
|
|
||||||
return bm
|
return bm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Watches any given address and puts it in the address state store
|
||||||
|
func (bm *BlockManager) WatchAddr(addr []byte) *AddressState {
|
||||||
|
account := bm.bc.CurrentBlock.GetAddr(addr)
|
||||||
|
|
||||||
|
return bm.addrStateStore.Add(addr, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bm *BlockManager) GetAddrState(addr []byte) *AddressState {
|
||||||
|
account := bm.addrStateStore.Get(addr)
|
||||||
|
if account == nil {
|
||||||
|
a := bm.bc.CurrentBlock.GetAddr(addr)
|
||||||
|
account = &AddressState{Nonce: a.Nonce, Account: a}
|
||||||
|
}
|
||||||
|
|
||||||
|
return account
|
||||||
|
}
|
||||||
|
|
||||||
func (bm *BlockManager) BlockChain() *BlockChain {
|
func (bm *BlockManager) BlockChain() *BlockChain {
|
||||||
return bm.bc
|
return bm.bc
|
||||||
}
|
}
|
||||||
@ -91,9 +111,15 @@ func (bm *BlockManager) ApplyTransactions(block *Block, txs []*Transaction) {
|
|||||||
// If there's no recipient, it's a contract
|
// If there's no recipient, it's a contract
|
||||||
if tx.IsContract() {
|
if tx.IsContract() {
|
||||||
block.MakeContract(tx)
|
block.MakeContract(tx)
|
||||||
bm.ProcessContract(tx, block)
|
|
||||||
} else {
|
} else {
|
||||||
bm.TransactionPool.ProcessTransaction(tx, block)
|
if contract := block.GetContract(tx.Recipient); contract != nil {
|
||||||
|
bm.ProcessContract(contract, tx, block)
|
||||||
|
} else {
|
||||||
|
err := bm.TransactionPool.ProcessTransaction(tx, block)
|
||||||
|
if err != nil {
|
||||||
|
ethutil.Config.Log.Infoln("[BMGR]", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,6 +129,11 @@ func (bm *BlockManager) ProcessBlock(block *Block) error {
|
|||||||
// Processing a blocks may never happen simultaneously
|
// Processing a blocks may never happen simultaneously
|
||||||
bm.mutex.Lock()
|
bm.mutex.Lock()
|
||||||
defer bm.mutex.Unlock()
|
defer bm.mutex.Unlock()
|
||||||
|
// Defer the Undo on the Trie. If the block processing happened
|
||||||
|
// we don't want to undo but since undo only happens on dirty
|
||||||
|
// nodes this won't happen because Commit would have been called
|
||||||
|
// before that.
|
||||||
|
defer bm.bc.CurrentBlock.Undo()
|
||||||
|
|
||||||
hash := block.Hash()
|
hash := block.Hash()
|
||||||
|
|
||||||
@ -110,12 +141,6 @@ func (bm *BlockManager) ProcessBlock(block *Block) error {
|
|||||||
return nil
|
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
|
// Check if we have the parent hash, if it isn't known we discard it
|
||||||
// Reasons might be catching up or simply an invalid block
|
// Reasons might be catching up or simply an invalid block
|
||||||
if !bm.bc.HasBlock(block.PrevHash) && bm.bc.CurrentBlock != nil {
|
if !bm.bc.HasBlock(block.PrevHash) && bm.bc.CurrentBlock != nil {
|
||||||
@ -137,37 +162,19 @@ func (bm *BlockManager) ProcessBlock(block *Block) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !block.State().Cmp(bm.bc.CurrentBlock.State()) {
|
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)
|
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
|
// Calculate the new total difficulty and sync back to the db
|
||||||
if bm.CalculateTD(block) {
|
if bm.CalculateTD(block) {
|
||||||
// Sync the current block's state to the database
|
// Sync the current block's state to the database and cancelling out the deferred Undo
|
||||||
bm.bc.CurrentBlock.State().Sync()
|
bm.bc.CurrentBlock.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
|
// Broadcast the valid block back to the wire
|
||||||
//bm.Speaker.Broadcast(ethwire.MsgBlockTy, []interface{}{block.RlpValue().Value})
|
//bm.Speaker.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val})
|
||||||
|
|
||||||
|
// Add the block to the chain
|
||||||
|
bm.bc.Add(block)
|
||||||
|
|
||||||
// If there's a block processor present, pass in the block for further
|
// If there's a block processor present, pass in the block for further
|
||||||
// processing
|
// processing
|
||||||
@ -175,7 +182,7 @@ func (bm *BlockManager) ProcessBlock(block *Block) error {
|
|||||||
bm.SecondaryBlockProcessor.ProcessBlock(block)
|
bm.SecondaryBlockProcessor.ProcessBlock(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[BMGR] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash())
|
ethutil.Config.Log.Infof("[BMGR] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash())
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("total diff failed")
|
fmt.Println("total diff failed")
|
||||||
}
|
}
|
||||||
@ -246,6 +253,18 @@ func (bm *BlockManager) ValidateBlock(block *Block) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CalculateBlockReward(block *Block, uncleLength int) *big.Int {
|
||||||
|
base := new(big.Int)
|
||||||
|
for i := 0; i < uncleLength; i++ {
|
||||||
|
base.Add(base, UncleInclusionReward)
|
||||||
|
}
|
||||||
|
return base.Add(base, BlockReward)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CalculateUncleReward(block *Block) *big.Int {
|
||||||
|
return UncleReward
|
||||||
|
}
|
||||||
|
|
||||||
func (bm *BlockManager) AccumelateRewards(processor *Block, block *Block) error {
|
func (bm *BlockManager) AccumelateRewards(processor *Block, block *Block) error {
|
||||||
// Get the coinbase rlp data
|
// Get the coinbase rlp data
|
||||||
addr := processor.GetAddr(block.Coinbase)
|
addr := processor.GetAddr(block.Coinbase)
|
||||||
@ -254,7 +273,12 @@ func (bm *BlockManager) AccumelateRewards(processor *Block, block *Block) error
|
|||||||
|
|
||||||
processor.UpdateAddr(block.Coinbase, addr)
|
processor.UpdateAddr(block.Coinbase, addr)
|
||||||
|
|
||||||
// TODO Reward each uncle
|
for _, uncle := range block.Uncles {
|
||||||
|
uncleAddr := processor.GetAddr(uncle.Coinbase)
|
||||||
|
uncleAddr.AddFee(CalculateUncleReward(uncle))
|
||||||
|
|
||||||
|
processor.UpdateAddr(uncle.Coinbase, uncleAddr)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -263,363 +287,26 @@ func (bm *BlockManager) Stop() {
|
|||||||
bm.bc.Stop()
|
bm.bc.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bm *BlockManager) ProcessContract(tx *Transaction, block *Block) {
|
func (bm *BlockManager) ProcessContract(contract *Contract, tx *Transaction, block *Block) {
|
||||||
// Recovering function in case the VM had any errors
|
// Recovering function in case the VM had any errors
|
||||||
defer func() {
|
/*
|
||||||
if r := recover(); r != nil {
|
defer func() {
|
||||||
fmt.Println("Recovered from VM execution with err =", r)
|
if r := recover(); r != nil {
|
||||||
}
|
fmt.Println("Recovered from VM execution with err =", r)
|
||||||
}()
|
}
|
||||||
|
}()
|
||||||
|
*/
|
||||||
|
|
||||||
// Process contract
|
vm := &Vm{}
|
||||||
bm.ProcContract(tx, block, func(opType OpType) bool {
|
vm.Process(contract, NewState(block.state), RuntimeVars{
|
||||||
// TODO turn on once big ints are in place
|
address: tx.Hash()[12:],
|
||||||
//if !block.PayFee(tx.Hash(), StepFee.Uint64()) {
|
blockNumber: block.BlockInfo().Number,
|
||||||
// return false
|
sender: tx.Sender(),
|
||||||
//}
|
prevHash: block.PrevHash,
|
||||||
|
coinbase: block.Coinbase,
|
||||||
return true // Continue
|
time: block.Time,
|
||||||
|
diff: block.Difficulty,
|
||||||
|
txValue: tx.Value,
|
||||||
|
txData: tx.Data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.NewValueFromBytes([]byte(contract.State().Get(x.String())))
|
|
||||||
if !decoder.IsNil() {
|
|
||||||
bm.stack.Push(decoder.BigInt())
|
|
||||||
} 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.NewValueFromBytes([]byte(val))
|
|
||||||
if decoder.IsNil() {
|
|
||||||
return ethutil.BigFalse
|
|
||||||
}
|
|
||||||
|
|
||||||
return decoder.BigInt()
|
|
||||||
}
|
|
||||||
|
@ -1,75 +1,33 @@
|
|||||||
package ethchain
|
package ethchain
|
||||||
|
|
||||||
/*
|
|
||||||
import (
|
import (
|
||||||
_ "fmt"
|
_ "fmt"
|
||||||
|
"github.com/ethereum/eth-go/ethdb"
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestVm(t *testing.T) {
|
func TestVm(t *testing.T) {
|
||||||
InitFees()
|
InitFees()
|
||||||
|
ethutil.ReadConfig("")
|
||||||
|
|
||||||
db, _ := NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
Db = db
|
ethutil.Config.Db = db
|
||||||
|
bm := NewBlockManager(nil)
|
||||||
|
|
||||||
ctrct := NewTransaction("", 200000000, []string{
|
block := bm.bc.genesisBlock
|
||||||
"PUSH", "1a2f2e",
|
script := Compile([]string{
|
||||||
"PUSH", "hallo",
|
"PUSH",
|
||||||
"POP", // POP hallo
|
"1",
|
||||||
"PUSH", "3",
|
"PUSH",
|
||||||
"LOAD", // Load hallo back on the stack
|
"2",
|
||||||
|
|
||||||
"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{})
|
tx := NewTransaction(ContractAddr, big.NewInt(200000000), script)
|
||||||
|
addr := tx.Hash()[12:]
|
||||||
|
bm.ApplyTransactions(block, []*Transaction{tx})
|
||||||
|
|
||||||
block := CreateBlock("", 0, "", "c014ba53", 0, 0, "", []*Transaction{ctrct, tx})
|
tx2 := NewTransaction(addr, big.NewInt(1e17), nil)
|
||||||
db.Put(block.Hash(), block.RlpEncode())
|
tx2.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
|
||||||
|
bm.ApplyTransactions(block, []*Transaction{tx2})
|
||||||
bm := NewBlockManager()
|
|
||||||
bm.ProcessBlock(block)
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
@ -30,37 +30,42 @@ func (c *Contract) RlpDecode(data []byte) {
|
|||||||
c.state = ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())
|
c.state = ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Contract) Addr(addr []byte) *ethutil.Value {
|
||||||
|
return ethutil.NewValueFromBytes([]byte(c.state.Get(string(addr))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Contract) SetAddr(addr []byte, value interface{}) {
|
||||||
|
c.state.Update(string(addr), string(ethutil.NewValue(value).Encode()))
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Contract) State() *ethutil.Trie {
|
func (c *Contract) State() *ethutil.Trie {
|
||||||
return c.state
|
return c.state
|
||||||
}
|
}
|
||||||
|
|
||||||
type Address struct {
|
func (c *Contract) GetMem(num int) *ethutil.Value {
|
||||||
Amount *big.Int
|
nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256)
|
||||||
Nonce uint64
|
|
||||||
|
return c.Addr(nb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAddress(amount *big.Int) *Address {
|
func MakeContract(tx *Transaction, state *State) *Contract {
|
||||||
return &Address{Amount: amount, Nonce: 0}
|
// Create contract if there's no recipient
|
||||||
}
|
if tx.IsContract() {
|
||||||
|
addr := tx.Hash()[12:]
|
||||||
func NewAddressFromData(data []byte) *Address {
|
|
||||||
address := &Address{}
|
value := tx.Value
|
||||||
address.RlpDecode(data)
|
contract := NewContract(value, []byte(""))
|
||||||
|
state.trie.Update(string(addr), string(contract.RlpEncode()))
|
||||||
return address
|
for i, val := range tx.Data {
|
||||||
}
|
if len(val) > 0 {
|
||||||
|
bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256)
|
||||||
func (a *Address) AddFee(fee *big.Int) {
|
contract.state.Update(string(bytNum), string(ethutil.Encode(val)))
|
||||||
a.Amount.Add(a.Amount, fee)
|
}
|
||||||
}
|
}
|
||||||
|
state.trie.Update(string(addr), string(contract.RlpEncode()))
|
||||||
func (a *Address) RlpEncode() []byte {
|
|
||||||
return ethutil.Encode([]interface{}{a.Amount, a.Nonce})
|
return contract
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Address) RlpDecode(data []byte) {
|
return nil
|
||||||
decoder := ethutil.NewValueFromBytes(data)
|
|
||||||
|
|
||||||
a.Amount = decoder.Get(0).BigInt()
|
|
||||||
a.Nonce = decoder.Get(1).Uint()
|
|
||||||
}
|
}
|
||||||
|
@ -4,22 +4,32 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
var StepFee *big.Int = new(big.Int)
|
|
||||||
var TxFeeRat *big.Int = big.NewInt(100000000000000)
|
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 TxFee *big.Int = big.NewInt(100)
|
||||||
|
var StepFee *big.Int = big.NewInt(1)
|
||||||
|
var StoreFee *big.Int = big.NewInt(5)
|
||||||
|
var DataFee *big.Int = big.NewInt(20)
|
||||||
|
var ExtroFee *big.Int = big.NewInt(40)
|
||||||
|
var CryptoFee *big.Int = big.NewInt(20)
|
||||||
|
var ContractFee *big.Int = big.NewInt(100)
|
||||||
|
|
||||||
|
var BlockReward *big.Int = big.NewInt(1.5e+18)
|
||||||
|
var UncleReward *big.Int = big.NewInt(1.125e+18)
|
||||||
|
var UncleInclusionReward *big.Int = big.NewInt(1.875e+17)
|
||||||
|
|
||||||
var Period1Reward *big.Int = new(big.Int)
|
var Period1Reward *big.Int = new(big.Int)
|
||||||
var Period2Reward *big.Int = new(big.Int)
|
var Period2Reward *big.Int = new(big.Int)
|
||||||
var Period3Reward *big.Int = new(big.Int)
|
var Period3Reward *big.Int = new(big.Int)
|
||||||
var Period4Reward *big.Int = new(big.Int)
|
var Period4Reward *big.Int = new(big.Int)
|
||||||
|
|
||||||
func InitFees() {
|
func InitFees() {
|
||||||
|
StepFee.Mul(StepFee, TxFeeRat)
|
||||||
|
StoreFee.Mul(StoreFee, TxFeeRat)
|
||||||
|
DataFee.Mul(DataFee, TxFeeRat)
|
||||||
|
ExtroFee.Mul(ExtroFee, TxFeeRat)
|
||||||
|
CryptoFee.Mul(CryptoFee, TxFeeRat)
|
||||||
|
ContractFee.Mul(ContractFee, TxFeeRat)
|
||||||
/*
|
/*
|
||||||
// Base for 2**64
|
// Base for 2**64
|
||||||
b60 := new(big.Int)
|
b60 := new(big.Int)
|
||||||
|
@ -13,7 +13,7 @@ var ZeroHash256 = make([]byte, 32)
|
|||||||
var ZeroHash160 = make([]byte, 20)
|
var ZeroHash160 = make([]byte, 20)
|
||||||
var EmptyShaList = ethutil.Sha3Bin(ethutil.Encode([]interface{}{}))
|
var EmptyShaList = ethutil.Sha3Bin(ethutil.Encode([]interface{}{}))
|
||||||
|
|
||||||
var GenisisHeader = []interface{}{
|
var GenesisHeader = []interface{}{
|
||||||
// Previous hash (none)
|
// Previous hash (none)
|
||||||
//"",
|
//"",
|
||||||
ZeroHash256,
|
ZeroHash256,
|
||||||
@ -36,4 +36,4 @@ var GenisisHeader = []interface{}{
|
|||||||
ethutil.Sha3Bin(big.NewInt(42).Bytes()),
|
ethutil.Sha3Bin(big.NewInt(42).Bytes()),
|
||||||
}
|
}
|
||||||
|
|
||||||
var Genesis = []interface{}{GenisisHeader, []interface{}{}, []interface{}{}}
|
var Genesis = []interface{}{GenesisHeader, []interface{}{}, []interface{}{}}
|
||||||
|
@ -9,57 +9,57 @@ type OpCode int
|
|||||||
|
|
||||||
// Op codes
|
// Op codes
|
||||||
const (
|
const (
|
||||||
oSTOP OpCode = iota
|
oSTOP = 0x00
|
||||||
oADD
|
oADD = 0x01
|
||||||
oMUL
|
oMUL = 0x02
|
||||||
oSUB
|
oSUB = 0x03
|
||||||
oDIV
|
oDIV = 0x04
|
||||||
oSDIV
|
oSDIV = 0x05
|
||||||
oMOD
|
oMOD = 0x06
|
||||||
oSMOD
|
oSMOD = 0x07
|
||||||
oEXP
|
oEXP = 0x08
|
||||||
oNEG
|
oNEG = 0x09
|
||||||
oLT
|
oLT = 0x0a
|
||||||
oLE
|
oLE = 0x0b
|
||||||
oGT
|
oGT = 0x0c
|
||||||
oGE
|
oGE = 0x0d
|
||||||
oEQ
|
oEQ = 0x0e
|
||||||
oNOT
|
oNOT = 0x0f
|
||||||
oMYADDRESS
|
oMYADDRESS = 0x10
|
||||||
oTXSENDER
|
oTXSENDER = 0x11
|
||||||
oTXVALUE
|
oTXVALUE = 0x12
|
||||||
oTXFEE
|
oTXDATAN = 0x13
|
||||||
oTXDATAN
|
oTXDATA = 0x14
|
||||||
oTXDATA
|
oBLK_PREVHASH = 0x15
|
||||||
oBLK_PREVHASH
|
oBLK_COINBASE = 0x16
|
||||||
oBLK_COINBASE
|
oBLK_TIMESTAMP = 0x17
|
||||||
oBLK_TIMESTAMP
|
oBLK_NUMBER = 0x18
|
||||||
oBLK_NUMBER
|
oBLK_DIFFICULTY = 0x19
|
||||||
oBLK_DIFFICULTY
|
oBLK_NONCE = 0x1a
|
||||||
oBASEFEE
|
oBASEFEE = 0x1b
|
||||||
oSHA256 OpCode = 32
|
oSHA256 = 0x20
|
||||||
oRIPEMD160 OpCode = 33
|
oRIPEMD160 = 0x21
|
||||||
oECMUL OpCode = 34
|
oECMUL = 0x22
|
||||||
oECADD OpCode = 35
|
oECADD = 0x23
|
||||||
oECSIGN OpCode = 36
|
oECSIGN = 0x24
|
||||||
oECRECOVER OpCode = 37
|
oECRECOVER = 0x25
|
||||||
oECVALID OpCode = 38
|
oECVALID = 0x26
|
||||||
oSHA3 OpCode = 39
|
oSHA3 = 0x27
|
||||||
oPUSH OpCode = 48
|
oPUSH = 0x30
|
||||||
oPOP OpCode = 49
|
oPOP = 0x31
|
||||||
oDUP OpCode = 50
|
oDUP = 0x32
|
||||||
oSWAP OpCode = 51
|
oSWAP = 0x33
|
||||||
oMLOAD OpCode = 52
|
oMLOAD = 0x34
|
||||||
oMSTORE OpCode = 53
|
oMSTORE = 0x35
|
||||||
oSLOAD OpCode = 54
|
oSLOAD = 0x36
|
||||||
oSSTORE OpCode = 55
|
oSSTORE = 0x37
|
||||||
oJMP OpCode = 56
|
oJMP = 0x38
|
||||||
oJMPI OpCode = 57
|
oJMPI = 0x39
|
||||||
oIND OpCode = 58
|
oIND = 0x3a
|
||||||
oEXTRO OpCode = 59
|
oEXTRO = 0x3b
|
||||||
oBALANCE OpCode = 60
|
oBALANCE = 0x3c
|
||||||
oMKTX OpCode = 61
|
oMKTX = 0x3d
|
||||||
oSUICIDE OpCode = 62
|
oSUICIDE = 0x3f
|
||||||
)
|
)
|
||||||
|
|
||||||
// Since the opcodes aren't all in order we can't use a regular slice
|
// Since the opcodes aren't all in order we can't use a regular slice
|
||||||
@ -83,7 +83,6 @@ var opCodeToString = map[OpCode]string{
|
|||||||
oMYADDRESS: "MYADDRESS",
|
oMYADDRESS: "MYADDRESS",
|
||||||
oTXSENDER: "TXSENDER",
|
oTXSENDER: "TXSENDER",
|
||||||
oTXVALUE: "TXVALUE",
|
oTXVALUE: "TXVALUE",
|
||||||
oTXFEE: "TXFEE",
|
|
||||||
oTXDATAN: "TXDATAN",
|
oTXDATAN: "TXDATAN",
|
||||||
oTXDATA: "TXDATA",
|
oTXDATA: "TXDATA",
|
||||||
oBLK_PREVHASH: "BLK_PREVHASH",
|
oBLK_PREVHASH: "BLK_PREVHASH",
|
||||||
@ -159,9 +158,33 @@ func (st *Stack) Popn() (*big.Int, *big.Int) {
|
|||||||
return ints[0], ints[1]
|
return ints[0], ints[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (st *Stack) Peek() *big.Int {
|
||||||
|
s := len(st.data)
|
||||||
|
|
||||||
|
str := st.data[s-1]
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *Stack) Peekn() (*big.Int, *big.Int) {
|
||||||
|
s := len(st.data)
|
||||||
|
|
||||||
|
ints := st.data[s-2:]
|
||||||
|
|
||||||
|
return ints[0], ints[1]
|
||||||
|
}
|
||||||
|
|
||||||
func (st *Stack) Push(d *big.Int) {
|
func (st *Stack) Push(d *big.Int) {
|
||||||
st.data = append(st.data, d)
|
st.data = append(st.data, d)
|
||||||
}
|
}
|
||||||
func (st *Stack) Print() {
|
func (st *Stack) Print() {
|
||||||
fmt.Println(st.data)
|
fmt.Println("### STACK ###")
|
||||||
|
if len(st.data) > 0 {
|
||||||
|
for i, val := range st.data {
|
||||||
|
fmt.Printf("%-3d %v\n", i, val)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("-- empty --")
|
||||||
|
}
|
||||||
|
fmt.Println("#############")
|
||||||
}
|
}
|
||||||
|
56
ethchain/state.go
Normal file
56
ethchain/state.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package ethchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
trie *ethutil.Trie
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewState(trie *ethutil.Trie) *State {
|
||||||
|
return &State{trie: trie}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) GetContract(addr []byte) *Contract {
|
||||||
|
data := s.trie.Get(string(addr))
|
||||||
|
if data == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
contract := &Contract{}
|
||||||
|
contract.RlpDecode([]byte(data))
|
||||||
|
|
||||||
|
return contract
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) UpdateContract(addr []byte, contract *Contract) {
|
||||||
|
s.trie.Update(string(addr), string(contract.RlpEncode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Compile(code []string) (script []string) {
|
||||||
|
script = make([]string, len(code))
|
||||||
|
for i, val := range code {
|
||||||
|
instr, _ := ethutil.CompileInstr(val)
|
||||||
|
|
||||||
|
script[i] = string(instr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) GetAccount(addr []byte) (account *Address) {
|
||||||
|
data := s.trie.Get(string(addr))
|
||||||
|
if data == "" {
|
||||||
|
account = NewAddress(big.NewInt(0))
|
||||||
|
} else {
|
||||||
|
account = NewAddressFromData([]byte(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) UpdateAccount(addr []byte, account *Address) {
|
||||||
|
s.trie.Update(string(addr), string(account.RlpEncode()))
|
||||||
|
}
|
@ -1,11 +1,14 @@
|
|||||||
package ethchain
|
package ethchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"github.com/obscuren/secp256k1-go"
|
"github.com/obscuren/secp256k1-go"
|
||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
Nonce uint64
|
Nonce uint64
|
||||||
Recipient []byte
|
Recipient []byte
|
||||||
@ -21,20 +24,17 @@ func NewTransaction(to []byte, value *big.Int, data []string) *Transaction {
|
|||||||
tx.Nonce = 0
|
tx.Nonce = 0
|
||||||
|
|
||||||
// Serialize the data
|
// Serialize the data
|
||||||
tx.Data = make([]string, len(data))
|
tx.Data = 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
|
return &tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX Deprecated
|
||||||
func NewTransactionFromData(data []byte) *Transaction {
|
func NewTransactionFromData(data []byte) *Transaction {
|
||||||
|
return NewTransactionFromBytes(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTransactionFromBytes(data []byte) *Transaction {
|
||||||
tx := &Transaction{}
|
tx := &Transaction{}
|
||||||
tx.RlpDecode(data)
|
tx.RlpDecode(data)
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ func (tx *Transaction) Hash() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Transaction) IsContract() bool {
|
func (tx *Transaction) IsContract() bool {
|
||||||
return len(tx.Recipient) == 0
|
return bytes.Compare(tx.Recipient, ContractAddr) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Transaction) Signature(key []byte) []byte {
|
func (tx *Transaction) Signature(key []byte) []byte {
|
||||||
|
@ -17,6 +17,17 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TxPoolHook chan *Transaction
|
type TxPoolHook chan *Transaction
|
||||||
|
type TxMsgTy byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
TxPre = iota
|
||||||
|
TxPost
|
||||||
|
)
|
||||||
|
|
||||||
|
type TxMsg struct {
|
||||||
|
Tx *Transaction
|
||||||
|
Type TxMsgTy
|
||||||
|
}
|
||||||
|
|
||||||
func FindTx(pool *list.List, finder func(*Transaction, *list.Element) bool) *Transaction {
|
func FindTx(pool *list.List, finder func(*Transaction, *list.Element) bool) *Transaction {
|
||||||
for e := pool.Front(); e != nil; e = e.Next() {
|
for e := pool.Front(); e != nil; e = e.Next() {
|
||||||
@ -34,6 +45,10 @@ type PublicSpeaker interface {
|
|||||||
Broadcast(msgType ethwire.MsgType, data []interface{})
|
Broadcast(msgType ethwire.MsgType, data []interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TxProcessor interface {
|
||||||
|
ProcessTransaction(tx *Transaction)
|
||||||
|
}
|
||||||
|
|
||||||
// The tx pool a thread safe transaction pool handler. In order to
|
// 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
|
// guarantee a non blocking pool we use a queue channel which can be
|
||||||
// independently read without needing access to the actual pool. If the
|
// independently read without needing access to the actual pool. If the
|
||||||
@ -54,7 +69,9 @@ type TxPool struct {
|
|||||||
|
|
||||||
BlockManager *BlockManager
|
BlockManager *BlockManager
|
||||||
|
|
||||||
Hook TxPoolHook
|
SecondaryProcessor TxProcessor
|
||||||
|
|
||||||
|
subscribers []chan TxMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTxPool() *TxPool {
|
func NewTxPool() *TxPool {
|
||||||
@ -80,8 +97,6 @@ func (pool *TxPool) addTransaction(tx *Transaction) {
|
|||||||
// Process transaction validates the Tx and processes funds from the
|
// Process transaction validates the Tx and processes funds from the
|
||||||
// sender to the recipient.
|
// sender to the recipient.
|
||||||
func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) {
|
func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) {
|
||||||
log.Printf("[TXPL] Processing Tx %x\n", tx.Hash())
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
log.Println(r)
|
log.Println(r)
|
||||||
@ -106,17 +121,30 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subtract the amount from the senders account
|
|
||||||
sender.Amount.Sub(sender.Amount, totAmount)
|
|
||||||
sender.Nonce += 1
|
|
||||||
|
|
||||||
// Get the receiver
|
// Get the receiver
|
||||||
receiver := block.GetAddr(tx.Recipient)
|
receiver := block.GetAddr(tx.Recipient)
|
||||||
// Add the amount to receivers account which should conclude this transaction
|
sender.Nonce += 1
|
||||||
receiver.Amount.Add(receiver.Amount, tx.Value)
|
|
||||||
|
// Send Tx to self
|
||||||
|
if bytes.Compare(tx.Recipient, tx.Sender()) == 0 {
|
||||||
|
// Subtract the fee
|
||||||
|
sender.Amount.Sub(sender.Amount, new(big.Int).Mul(TxFee, TxFeeRat))
|
||||||
|
} else {
|
||||||
|
// Subtract the amount from the senders account
|
||||||
|
sender.Amount.Sub(sender.Amount, totAmount)
|
||||||
|
|
||||||
|
// Add the amount to receivers account which should conclude this transaction
|
||||||
|
receiver.Amount.Add(receiver.Amount, tx.Value)
|
||||||
|
|
||||||
|
block.UpdateAddr(tx.Recipient, receiver)
|
||||||
|
}
|
||||||
|
|
||||||
block.UpdateAddr(tx.Sender(), sender)
|
block.UpdateAddr(tx.Sender(), sender)
|
||||||
block.UpdateAddr(tx.Recipient, receiver)
|
|
||||||
|
log.Printf("[TXPL] Processed Tx %x\n", tx.Hash())
|
||||||
|
|
||||||
|
// Notify the subscribers
|
||||||
|
pool.notifySubscribers(TxPost, tx)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -131,7 +159,8 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the sender
|
// Get the sender
|
||||||
sender := block.GetAddr(tx.Sender())
|
accountState := pool.BlockManager.GetAddrState(tx.Sender())
|
||||||
|
sender := accountState.Account
|
||||||
|
|
||||||
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat))
|
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat))
|
||||||
// Make sure there's enough in the sender's account. Having insufficient
|
// Make sure there's enough in the sender's account. Having insufficient
|
||||||
@ -171,9 +200,8 @@ out:
|
|||||||
// doesn't matter since this is a goroutine
|
// doesn't matter since this is a goroutine
|
||||||
pool.addTransaction(tx)
|
pool.addTransaction(tx)
|
||||||
|
|
||||||
if pool.Hook != nil {
|
// Notify the subscribers
|
||||||
pool.Hook <- tx
|
pool.notifySubscribers(TxPre, tx)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case <-pool.quit:
|
case <-pool.quit:
|
||||||
break out
|
break out
|
||||||
@ -217,3 +245,14 @@ func (pool *TxPool) Stop() {
|
|||||||
|
|
||||||
pool.Flush()
|
pool.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pool *TxPool) Subscribe(channel chan TxMsg) {
|
||||||
|
pool.subscribers = append(pool.subscribers, channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pool *TxPool) notifySubscribers(ty TxMsgTy, tx *Transaction) {
|
||||||
|
msg := TxMsg{Type: ty, Tx: tx}
|
||||||
|
for _, subscriber := range pool.subscribers {
|
||||||
|
subscriber <- msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,8 +2,6 @@ package ethchain
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -42,13 +40,15 @@ func TestAddressRetrieval2(t *testing.T) {
|
|||||||
tx.Sign(key)
|
tx.Sign(key)
|
||||||
//data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7")
|
//data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7")
|
||||||
//tx := NewTransactionFromData(data)
|
//tx := NewTransactionFromData(data)
|
||||||
fmt.Println(tx.RlpValue())
|
/*
|
||||||
|
fmt.Println(tx.RlpValue())
|
||||||
|
|
||||||
fmt.Printf("rlp %x\n", tx.RlpEncode())
|
fmt.Printf("rlp %x\n", tx.RlpEncode())
|
||||||
fmt.Printf("sha rlp %x\n", tx.Hash())
|
fmt.Printf("sha rlp %x\n", tx.Hash())
|
||||||
|
|
||||||
//tx.Sign(key)
|
//tx.Sign(key)
|
||||||
|
|
||||||
fmt.Printf("hex tx key %x\n", tx.PublicKey())
|
fmt.Printf("hex tx key %x\n", tx.PublicKey())
|
||||||
fmt.Printf("seder %x\n", tx.Sender())
|
fmt.Printf("seder %x\n", tx.Sender())
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
436
ethchain/vm.go
Normal file
436
ethchain/vm.go
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
package ethchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"github.com/obscuren/secp256k1-go"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Vm struct {
|
||||||
|
txPool *TxPool
|
||||||
|
// Stack for processing contracts
|
||||||
|
stack *Stack
|
||||||
|
// non-persistent key/value memory storage
|
||||||
|
mem map[string]*big.Int
|
||||||
|
|
||||||
|
vars RuntimeVars
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuntimeVars struct {
|
||||||
|
address []byte
|
||||||
|
blockNumber uint64
|
||||||
|
sender []byte
|
||||||
|
prevHash []byte
|
||||||
|
coinbase []byte
|
||||||
|
time int64
|
||||||
|
diff *big.Int
|
||||||
|
txValue *big.Int
|
||||||
|
txData []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *Vm) Process(contract *Contract, state *State, vars RuntimeVars) {
|
||||||
|
vm.mem = make(map[string]*big.Int)
|
||||||
|
vm.stack = NewStack()
|
||||||
|
|
||||||
|
addr := vars.address // tx.Hash()[12:]
|
||||||
|
// Instruction pointer
|
||||||
|
pc := 0
|
||||||
|
|
||||||
|
if contract == nil {
|
||||||
|
fmt.Println("Contract not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Pow256 := ethutil.BigPow(2, 256)
|
||||||
|
|
||||||
|
if ethutil.Config.Debug {
|
||||||
|
ethutil.Config.Log.Debugf("# op\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
stepcount := 0
|
||||||
|
totalFee := new(big.Int)
|
||||||
|
|
||||||
|
out:
|
||||||
|
for {
|
||||||
|
stepcount++
|
||||||
|
// The base big int for all calculations. Use this for any results.
|
||||||
|
base := new(big.Int)
|
||||||
|
val := contract.GetMem(pc)
|
||||||
|
//fmt.Printf("%x = %d, %v %x\n", r, len(r), v, nb)
|
||||||
|
op := OpCode(val.Uint())
|
||||||
|
|
||||||
|
var fee *big.Int = new(big.Int)
|
||||||
|
var fee2 *big.Int = new(big.Int)
|
||||||
|
if stepcount > 16 {
|
||||||
|
fee.Add(fee, StepFee)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the fees
|
||||||
|
switch op {
|
||||||
|
case oSSTORE:
|
||||||
|
y, x := vm.stack.Peekn()
|
||||||
|
val := contract.Addr(ethutil.BigToBytes(x, 256))
|
||||||
|
if val.IsEmpty() && len(y.Bytes()) > 0 {
|
||||||
|
fee2.Add(DataFee, StoreFee)
|
||||||
|
} else {
|
||||||
|
fee2.Sub(DataFee, StoreFee)
|
||||||
|
}
|
||||||
|
case oSLOAD:
|
||||||
|
fee.Add(fee, StoreFee)
|
||||||
|
case oEXTRO, oBALANCE:
|
||||||
|
fee.Add(fee, ExtroFee)
|
||||||
|
case oSHA256, oRIPEMD160, oECMUL, oECADD, oECSIGN, oECRECOVER, oECVALID:
|
||||||
|
fee.Add(fee, CryptoFee)
|
||||||
|
case oMKTX:
|
||||||
|
fee.Add(fee, ContractFee)
|
||||||
|
}
|
||||||
|
|
||||||
|
tf := new(big.Int).Add(fee, fee2)
|
||||||
|
if contract.Amount.Cmp(tf) < 0 {
|
||||||
|
fmt.Println("Insufficient fees to continue running the contract", tf, contract.Amount)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Add the fee to the total fee. It's subtracted when we're done looping
|
||||||
|
totalFee.Add(totalFee, tf)
|
||||||
|
|
||||||
|
if ethutil.Config.Debug {
|
||||||
|
ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
case oSTOP:
|
||||||
|
fmt.Println("")
|
||||||
|
break out
|
||||||
|
case oADD:
|
||||||
|
x, y := vm.stack.Popn()
|
||||||
|
// (x + y) % 2 ** 256
|
||||||
|
base.Add(x, y)
|
||||||
|
base.Mod(base, Pow256)
|
||||||
|
// Pop result back on the stack
|
||||||
|
vm.stack.Push(base)
|
||||||
|
case oSUB:
|
||||||
|
x, y := vm.stack.Popn()
|
||||||
|
// (x - y) % 2 ** 256
|
||||||
|
base.Sub(x, y)
|
||||||
|
base.Mod(base, Pow256)
|
||||||
|
// Pop result back on the stack
|
||||||
|
vm.stack.Push(base)
|
||||||
|
case oMUL:
|
||||||
|
x, y := vm.stack.Popn()
|
||||||
|
// (x * y) % 2 ** 256
|
||||||
|
base.Mul(x, y)
|
||||||
|
base.Mod(base, Pow256)
|
||||||
|
// Pop result back on the stack
|
||||||
|
vm.stack.Push(base)
|
||||||
|
case oDIV:
|
||||||
|
x, y := vm.stack.Popn()
|
||||||
|
// floor(x / y)
|
||||||
|
base.Div(x, y)
|
||||||
|
// Pop result back on the stack
|
||||||
|
vm.stack.Push(base)
|
||||||
|
case oSDIV:
|
||||||
|
x, y := vm.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
|
||||||
|
vm.stack.Push(z)
|
||||||
|
case oMOD:
|
||||||
|
x, y := vm.stack.Popn()
|
||||||
|
base.Mod(x, y)
|
||||||
|
vm.stack.Push(base)
|
||||||
|
case oSMOD:
|
||||||
|
x, y := vm.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
|
||||||
|
vm.stack.Push(z)
|
||||||
|
case oEXP:
|
||||||
|
x, y := vm.stack.Popn()
|
||||||
|
base.Exp(x, y, Pow256)
|
||||||
|
|
||||||
|
vm.stack.Push(base)
|
||||||
|
case oNEG:
|
||||||
|
base.Sub(Pow256, vm.stack.Pop())
|
||||||
|
vm.stack.Push(base)
|
||||||
|
case oLT:
|
||||||
|
x, y := vm.stack.Popn()
|
||||||
|
// x < y
|
||||||
|
if x.Cmp(y) < 0 {
|
||||||
|
vm.stack.Push(ethutil.BigTrue)
|
||||||
|
} else {
|
||||||
|
vm.stack.Push(ethutil.BigFalse)
|
||||||
|
}
|
||||||
|
case oLE:
|
||||||
|
x, y := vm.stack.Popn()
|
||||||
|
// x <= y
|
||||||
|
if x.Cmp(y) < 1 {
|
||||||
|
vm.stack.Push(ethutil.BigTrue)
|
||||||
|
} else {
|
||||||
|
vm.stack.Push(ethutil.BigFalse)
|
||||||
|
}
|
||||||
|
case oGT:
|
||||||
|
x, y := vm.stack.Popn()
|
||||||
|
// x > y
|
||||||
|
if x.Cmp(y) > 0 {
|
||||||
|
vm.stack.Push(ethutil.BigTrue)
|
||||||
|
} else {
|
||||||
|
vm.stack.Push(ethutil.BigFalse)
|
||||||
|
}
|
||||||
|
case oGE:
|
||||||
|
x, y := vm.stack.Popn()
|
||||||
|
// x >= y
|
||||||
|
if x.Cmp(y) > -1 {
|
||||||
|
vm.stack.Push(ethutil.BigTrue)
|
||||||
|
} else {
|
||||||
|
vm.stack.Push(ethutil.BigFalse)
|
||||||
|
}
|
||||||
|
case oNOT:
|
||||||
|
x, y := vm.stack.Popn()
|
||||||
|
// x != y
|
||||||
|
if x.Cmp(y) != 0 {
|
||||||
|
vm.stack.Push(ethutil.BigTrue)
|
||||||
|
} else {
|
||||||
|
vm.stack.Push(ethutil.BigFalse)
|
||||||
|
}
|
||||||
|
case oMYADDRESS:
|
||||||
|
vm.stack.Push(ethutil.BigD(addr))
|
||||||
|
case oTXSENDER:
|
||||||
|
vm.stack.Push(ethutil.BigD(vars.sender))
|
||||||
|
case oTXVALUE:
|
||||||
|
vm.stack.Push(vars.txValue)
|
||||||
|
case oTXDATAN:
|
||||||
|
vm.stack.Push(big.NewInt(int64(len(vars.txData))))
|
||||||
|
case oTXDATA:
|
||||||
|
v := vm.stack.Pop()
|
||||||
|
// v >= len(data)
|
||||||
|
if v.Cmp(big.NewInt(int64(len(vars.txData)))) >= 0 {
|
||||||
|
vm.stack.Push(ethutil.Big("0"))
|
||||||
|
} else {
|
||||||
|
vm.stack.Push(ethutil.Big(vars.txData[v.Uint64()]))
|
||||||
|
}
|
||||||
|
case oBLK_PREVHASH:
|
||||||
|
vm.stack.Push(ethutil.BigD(vars.prevHash))
|
||||||
|
case oBLK_COINBASE:
|
||||||
|
vm.stack.Push(ethutil.BigD(vars.coinbase))
|
||||||
|
case oBLK_TIMESTAMP:
|
||||||
|
vm.stack.Push(big.NewInt(vars.time))
|
||||||
|
case oBLK_NUMBER:
|
||||||
|
vm.stack.Push(big.NewInt(int64(vars.blockNumber)))
|
||||||
|
case oBLK_DIFFICULTY:
|
||||||
|
vm.stack.Push(vars.diff)
|
||||||
|
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(vars.diff)
|
||||||
|
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))
|
||||||
|
vm.stack.Push(x)
|
||||||
|
case oSHA256, oSHA3, oRIPEMD160:
|
||||||
|
// This is probably save
|
||||||
|
// ceil(pop / 32)
|
||||||
|
length := int(math.Ceil(float64(vm.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(vm.stack.Pop().Bytes(), 256)
|
||||||
|
data.WriteString(string(num))
|
||||||
|
}
|
||||||
|
|
||||||
|
if op == oSHA256 {
|
||||||
|
vm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes())))
|
||||||
|
} else if op == oSHA3 {
|
||||||
|
vm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes())))
|
||||||
|
} else {
|
||||||
|
vm.stack.Push(base.SetBytes(ethutil.Ripemd160(data.Bytes())))
|
||||||
|
}
|
||||||
|
case oECMUL:
|
||||||
|
y := vm.stack.Pop()
|
||||||
|
x := vm.stack.Pop()
|
||||||
|
//n := vm.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
|
||||||
|
vm.stack.Push(ethutil.Big("0"))
|
||||||
|
vm.stack.Push(ethutil.Big("0"))
|
||||||
|
}
|
||||||
|
//} else {
|
||||||
|
// // Invalid, push infinity
|
||||||
|
// vm.stack.Push("0")
|
||||||
|
// vm.stack.Push("0")
|
||||||
|
//}
|
||||||
|
|
||||||
|
case oECADD:
|
||||||
|
case oECSIGN:
|
||||||
|
case oECRECOVER:
|
||||||
|
case oECVALID:
|
||||||
|
case oPUSH:
|
||||||
|
pc++
|
||||||
|
vm.stack.Push(contract.GetMem(pc).BigInt())
|
||||||
|
case oPOP:
|
||||||
|
// Pop current value of the stack
|
||||||
|
vm.stack.Pop()
|
||||||
|
case oDUP:
|
||||||
|
// Dup top stack
|
||||||
|
x := vm.stack.Pop()
|
||||||
|
vm.stack.Push(x)
|
||||||
|
vm.stack.Push(x)
|
||||||
|
case oSWAP:
|
||||||
|
// Swap two top most values
|
||||||
|
x, y := vm.stack.Popn()
|
||||||
|
vm.stack.Push(y)
|
||||||
|
vm.stack.Push(x)
|
||||||
|
case oMLOAD:
|
||||||
|
x := vm.stack.Pop()
|
||||||
|
vm.stack.Push(vm.mem[x.String()])
|
||||||
|
case oMSTORE:
|
||||||
|
x, y := vm.stack.Popn()
|
||||||
|
vm.mem[x.String()] = y
|
||||||
|
case oSLOAD:
|
||||||
|
// Load the value in storage and push it on the stack
|
||||||
|
x := vm.stack.Pop()
|
||||||
|
// decode the object as a big integer
|
||||||
|
decoder := ethutil.NewValueFromBytes([]byte(contract.State().Get(x.String())))
|
||||||
|
if !decoder.IsNil() {
|
||||||
|
vm.stack.Push(decoder.BigInt())
|
||||||
|
} else {
|
||||||
|
vm.stack.Push(ethutil.BigFalse)
|
||||||
|
}
|
||||||
|
case oSSTORE:
|
||||||
|
// Store Y at index X
|
||||||
|
y, x := vm.stack.Popn()
|
||||||
|
addr := ethutil.BigToBytes(x, 256)
|
||||||
|
fmt.Printf(" => %x (%v) @ %v", y.Bytes(), y, ethutil.BigD(addr))
|
||||||
|
contract.SetAddr(addr, y)
|
||||||
|
//contract.State().Update(string(idx), string(y))
|
||||||
|
case oJMP:
|
||||||
|
x := int(vm.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 := vm.stack.Pop()
|
||||||
|
// Set pc to x if it's non zero
|
||||||
|
if x.Cmp(ethutil.BigFalse) != 0 {
|
||||||
|
pc = int(x.Uint64())
|
||||||
|
pc--
|
||||||
|
}
|
||||||
|
case oIND:
|
||||||
|
vm.stack.Push(big.NewInt(int64(pc)))
|
||||||
|
case oEXTRO:
|
||||||
|
memAddr := vm.stack.Pop()
|
||||||
|
contractAddr := vm.stack.Pop().Bytes()
|
||||||
|
|
||||||
|
// Push the contract's memory on to the stack
|
||||||
|
vm.stack.Push(contractMemory(state, contractAddr, memAddr))
|
||||||
|
case oBALANCE:
|
||||||
|
// Pushes the balance of the popped value on to the stack
|
||||||
|
account := state.GetAccount(vm.stack.Pop().Bytes())
|
||||||
|
vm.stack.Push(account.Amount)
|
||||||
|
case oMKTX:
|
||||||
|
addr, value := vm.stack.Popn()
|
||||||
|
from, length := vm.stack.Popn()
|
||||||
|
|
||||||
|
makeInlineTx(addr.Bytes(), value, from, length, contract, state)
|
||||||
|
case oSUICIDE:
|
||||||
|
recAddr := vm.stack.Pop().Bytes()
|
||||||
|
// Purge all memory
|
||||||
|
deletedMemory := contract.state.NewIterator().Purge()
|
||||||
|
// Add refunds to the pop'ed address
|
||||||
|
refund := new(big.Int).Mul(StoreFee, big.NewInt(int64(deletedMemory)))
|
||||||
|
account := state.GetAccount(recAddr)
|
||||||
|
account.Amount.Add(account.Amount, refund)
|
||||||
|
// Update the refunding address
|
||||||
|
state.UpdateAccount(recAddr, account)
|
||||||
|
// Delete the contract
|
||||||
|
state.trie.Update(string(addr), "")
|
||||||
|
|
||||||
|
ethutil.Config.Log.Debugf("(%d) => %x\n", deletedMemory, recAddr)
|
||||||
|
break out
|
||||||
|
default:
|
||||||
|
fmt.Printf("Invalid OPCODE: %x\n", op)
|
||||||
|
}
|
||||||
|
ethutil.Config.Log.Debugln("")
|
||||||
|
//vm.stack.Print()
|
||||||
|
pc++
|
||||||
|
}
|
||||||
|
|
||||||
|
state.UpdateContract(addr, contract)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) {
|
||||||
|
ethutil.Config.Log.Debugf(" => creating inline tx %x %v %v %v", addr, value, from, length)
|
||||||
|
j := 0
|
||||||
|
dataItems := make([]string, int(length.Uint64()))
|
||||||
|
for i := from.Uint64(); i < length.Uint64(); i++ {
|
||||||
|
dataItems[j] = contract.GetMem(j).Str()
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := NewTransaction(addr, value, dataItems)
|
||||||
|
if tx.IsContract() {
|
||||||
|
contract := MakeContract(tx, state)
|
||||||
|
state.UpdateContract(tx.Hash()[12:], contract)
|
||||||
|
} else {
|
||||||
|
account := state.GetAccount(tx.Recipient)
|
||||||
|
account.Amount.Add(account.Amount, tx.Value)
|
||||||
|
state.UpdateAccount(tx.Recipient, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns an address from the specified contract's address
|
||||||
|
func contractMemory(state *State, contractAddr []byte, memAddr *big.Int) *big.Int {
|
||||||
|
contract := state.GetContract(contractAddr)
|
||||||
|
if contract == nil {
|
||||||
|
log.Panicf("invalid contract addr %x", contractAddr)
|
||||||
|
}
|
||||||
|
val := state.trie.Get(memAddr.String())
|
||||||
|
|
||||||
|
// decode the object as a big integer
|
||||||
|
decoder := ethutil.NewValueFromBytes([]byte(val))
|
||||||
|
if decoder.IsNil() {
|
||||||
|
return ethutil.BigFalse
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoder.BigInt()
|
||||||
|
}
|
106
ethchain/vm_test.go
Normal file
106
ethchain/vm_test.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package ethchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/eth-go/ethdb"
|
||||||
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRun(t *testing.T) {
|
||||||
|
InitFees()
|
||||||
|
|
||||||
|
ethutil.ReadConfig("")
|
||||||
|
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
state := NewState(ethutil.NewTrie(db, ""))
|
||||||
|
|
||||||
|
script := Compile([]string{
|
||||||
|
"TXSENDER",
|
||||||
|
"SUICIDE",
|
||||||
|
})
|
||||||
|
|
||||||
|
tx := NewTransaction(ContractAddr, big.NewInt(1e17), script)
|
||||||
|
fmt.Printf("contract addr %x\n", tx.Hash()[12:])
|
||||||
|
contract := MakeContract(tx, state)
|
||||||
|
vm := &Vm{}
|
||||||
|
|
||||||
|
vm.Process(contract, state, RuntimeVars{
|
||||||
|
address: tx.Hash()[12:],
|
||||||
|
blockNumber: 1,
|
||||||
|
sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
|
||||||
|
prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
|
||||||
|
coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
|
||||||
|
time: 1,
|
||||||
|
diff: big.NewInt(256),
|
||||||
|
txValue: tx.Value,
|
||||||
|
txData: tx.Data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRun1(t *testing.T) {
|
||||||
|
ethutil.ReadConfig("")
|
||||||
|
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
state := NewState(ethutil.NewTrie(db, ""))
|
||||||
|
|
||||||
|
script := Compile([]string{
|
||||||
|
"PUSH", "0",
|
||||||
|
"PUSH", "0",
|
||||||
|
"TXSENDER",
|
||||||
|
"PUSH", "10000000",
|
||||||
|
"MKTX",
|
||||||
|
})
|
||||||
|
fmt.Println(ethutil.NewValue(script))
|
||||||
|
|
||||||
|
tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
|
||||||
|
fmt.Printf("contract addr %x\n", tx.Hash()[12:])
|
||||||
|
contract := MakeContract(tx, state)
|
||||||
|
vm := &Vm{}
|
||||||
|
|
||||||
|
vm.Process(contract, state, RuntimeVars{
|
||||||
|
address: tx.Hash()[12:],
|
||||||
|
blockNumber: 1,
|
||||||
|
sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
|
||||||
|
prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
|
||||||
|
coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
|
||||||
|
time: 1,
|
||||||
|
diff: big.NewInt(256),
|
||||||
|
txValue: tx.Value,
|
||||||
|
txData: tx.Data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRun2(t *testing.T) {
|
||||||
|
ethutil.ReadConfig("")
|
||||||
|
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
state := NewState(ethutil.NewTrie(db, ""))
|
||||||
|
|
||||||
|
script := Compile([]string{
|
||||||
|
"PUSH", "0",
|
||||||
|
"PUSH", "0",
|
||||||
|
"TXSENDER",
|
||||||
|
"PUSH", "10000000",
|
||||||
|
"MKTX",
|
||||||
|
})
|
||||||
|
fmt.Println(ethutil.NewValue(script))
|
||||||
|
|
||||||
|
tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
|
||||||
|
fmt.Printf("contract addr %x\n", tx.Hash()[12:])
|
||||||
|
contract := MakeContract(tx, state)
|
||||||
|
vm := &Vm{}
|
||||||
|
|
||||||
|
vm.Process(contract, state, RuntimeVars{
|
||||||
|
address: tx.Hash()[12:],
|
||||||
|
blockNumber: 1,
|
||||||
|
sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
|
||||||
|
prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
|
||||||
|
coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
|
||||||
|
time: 1,
|
||||||
|
diff: big.NewInt(256),
|
||||||
|
txValue: tx.Value,
|
||||||
|
txData: tx.Data,
|
||||||
|
})
|
||||||
|
}
|
@ -11,8 +11,8 @@ type LDBDatabase struct {
|
|||||||
db *leveldb.DB
|
db *leveldb.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLDBDatabase() (*LDBDatabase, error) {
|
func NewLDBDatabase(name string) (*LDBDatabase, error) {
|
||||||
dbPath := path.Join(ethutil.Config.ExecPath, "database")
|
dbPath := path.Join(ethutil.Config.ExecPath, name)
|
||||||
|
|
||||||
// Open the db
|
// Open the db
|
||||||
db, err := leveldb.OpenFile(dbPath, nil)
|
db, err := leveldb.OpenFile(dbPath, nil)
|
||||||
@ -36,6 +36,14 @@ func (db *LDBDatabase) Get(key []byte) ([]byte, error) {
|
|||||||
return db.db.Get(key, nil)
|
return db.db.Get(key, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *LDBDatabase) Delete(key []byte) error {
|
||||||
|
return db.db.Delete(key, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *LDBDatabase) Db() *leveldb.DB {
|
||||||
|
return db.db
|
||||||
|
}
|
||||||
|
|
||||||
func (db *LDBDatabase) LastKnownTD() []byte {
|
func (db *LDBDatabase) LastKnownTD() []byte {
|
||||||
data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil)
|
data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil)
|
||||||
|
|
||||||
@ -46,13 +54,19 @@ func (db *LDBDatabase) LastKnownTD() []byte {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *LDBDatabase) GetKeys() []*ethutil.Key {
|
||||||
|
data, _ := db.Get([]byte("KeyRing"))
|
||||||
|
|
||||||
|
return []*ethutil.Key{ethutil.NewKeyFromBytes(data)}
|
||||||
|
}
|
||||||
|
|
||||||
func (db *LDBDatabase) Close() {
|
func (db *LDBDatabase) Close() {
|
||||||
// Close the leveldb database
|
// Close the leveldb database
|
||||||
db.db.Close()
|
db.db.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *LDBDatabase) Print() {
|
func (db *LDBDatabase) Print() {
|
||||||
iter := db.db.NewIterator(nil)
|
iter := db.db.NewIterator(nil, nil)
|
||||||
for iter.Next() {
|
for iter.Next() {
|
||||||
key := iter.Key()
|
key := iter.Key()
|
||||||
value := iter.Value()
|
value := iter.Value()
|
||||||
|
@ -26,6 +26,18 @@ func (db *MemDatabase) Get(key []byte) ([]byte, error) {
|
|||||||
return db.db[string(key)], nil
|
return db.db[string(key)], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *MemDatabase) GetKeys() []*ethutil.Key {
|
||||||
|
data, _ := db.Get([]byte("KeyRing"))
|
||||||
|
|
||||||
|
return []*ethutil.Key{ethutil.NewKeyFromBytes(data)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *MemDatabase) Delete(key []byte) error {
|
||||||
|
delete(db.db, string(key))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (db *MemDatabase) Print() {
|
func (db *MemDatabase) Print() {
|
||||||
for key, val := range db.db {
|
for key, val := range db.db {
|
||||||
fmt.Printf("%x(%d): ", key, len(key))
|
fmt.Printf("%x(%d): ", key, len(key))
|
||||||
|
70
ethereum.go
70
ethereum.go
@ -47,6 +47,7 @@ type Ethereum struct {
|
|||||||
Nonce uint64
|
Nonce uint64
|
||||||
|
|
||||||
Addr net.Addr
|
Addr net.Addr
|
||||||
|
Port string
|
||||||
|
|
||||||
peerMut sync.Mutex
|
peerMut sync.Mutex
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ 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("database")
|
||||||
//db, err := ethdb.NewMemDatabase()
|
//db, err := ethdb.NewMemDatabase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -70,7 +71,7 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
|
|||||||
if usePnp {
|
if usePnp {
|
||||||
nat, err = Discover()
|
nat, err = Discover()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("UPnP failed", err)
|
ethutil.Config.Log.Debugln("UPnP failed", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +86,6 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
|
|||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
serverCaps: caps,
|
serverCaps: caps,
|
||||||
nat: nat,
|
nat: nat,
|
||||||
MaxPeers: 5,
|
|
||||||
}
|
}
|
||||||
ethereum.TxPool = ethchain.NewTxPool()
|
ethereum.TxPool = ethchain.NewTxPool()
|
||||||
ethereum.TxPool.Speaker = ethereum
|
ethereum.TxPool.Speaker = ethereum
|
||||||
@ -94,6 +94,9 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
|
|||||||
ethereum.TxPool.BlockManager = ethereum.BlockManager
|
ethereum.TxPool.BlockManager = ethereum.BlockManager
|
||||||
ethereum.BlockManager.TransactionPool = ethereum.TxPool
|
ethereum.BlockManager.TransactionPool = ethereum.TxPool
|
||||||
|
|
||||||
|
// Start the tx pool
|
||||||
|
ethereum.TxPool.Start()
|
||||||
|
|
||||||
return ethereum, nil
|
return ethereum, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,29 +117,33 @@ func (s *Ethereum) ProcessPeerList(addrs []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Ethereum) ConnectToPeer(addr string) error {
|
func (s *Ethereum) ConnectToPeer(addr string) error {
|
||||||
var alreadyConnected bool
|
if s.peers.Len() < s.MaxPeers {
|
||||||
|
var alreadyConnected bool
|
||||||
|
|
||||||
eachPeer(s.peers, func(p *Peer, v *list.Element) {
|
eachPeer(s.peers, func(p *Peer, v *list.Element) {
|
||||||
if p.conn == nil {
|
if p.conn == nil {
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
phost, _, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
|
||||||
|
ahost, _, _ := net.SplitHostPort(addr)
|
||||||
|
|
||||||
|
if phost == ahost {
|
||||||
|
alreadyConnected = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if alreadyConnected {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
phost, _, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
|
|
||||||
ahost, _, _ := net.SplitHostPort(addr)
|
|
||||||
|
|
||||||
if phost == ahost {
|
peer := NewOutboundPeer(addr, s, s.serverCaps)
|
||||||
alreadyConnected = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if alreadyConnected {
|
s.peers.PushBack(peer)
|
||||||
return nil
|
|
||||||
|
log.Printf("[SERV] Adding peer %d / %d\n", s.peers.Len(), s.MaxPeers)
|
||||||
}
|
}
|
||||||
|
|
||||||
peer := NewOutboundPeer(addr, s, s.serverCaps)
|
|
||||||
|
|
||||||
s.peers.PushBack(peer)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,12 +233,12 @@ func (s *Ethereum) ReapDeadPeerHandler() {
|
|||||||
// Start the ethereum
|
// Start the ethereum
|
||||||
func (s *Ethereum) Start() {
|
func (s *Ethereum) Start() {
|
||||||
// Bind to addr and port
|
// Bind to addr and port
|
||||||
ln, err := net.Listen("tcp", ":30303")
|
ln, err := net.Listen("tcp", ":"+s.Port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Connection listening disabled. Acting as client")
|
log.Println("Connection listening disabled. Acting as client")
|
||||||
} else {
|
} else {
|
||||||
// Starting accepting connections
|
// Starting accepting connections
|
||||||
log.Println("Ready and accepting connections")
|
ethutil.Config.Log.Infoln("Ready and accepting connections")
|
||||||
// Start the peer handler
|
// Start the peer handler
|
||||||
go s.peerHandler(ln)
|
go s.peerHandler(ln)
|
||||||
}
|
}
|
||||||
@ -243,13 +250,10 @@ func (s *Ethereum) Start() {
|
|||||||
// Start the reaping processes
|
// Start the reaping processes
|
||||||
go s.ReapDeadPeerHandler()
|
go s.ReapDeadPeerHandler()
|
||||||
|
|
||||||
// Start the tx pool
|
|
||||||
s.TxPool.Start()
|
|
||||||
|
|
||||||
if ethutil.Config.Seed {
|
if ethutil.Config.Seed {
|
||||||
log.Println("Seeding")
|
ethutil.Config.Log.Debugln("Seeding")
|
||||||
// Testnet seed bootstrapping
|
// Testnet seed bootstrapping
|
||||||
resp, err := http.Get("http://www.ethereum.org/servers.poc2.txt")
|
resp, err := http.Get("http://www.ethereum.org/servers.poc3.txt")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Fetching seed failed:", err)
|
log.Println("Fetching seed failed:", err)
|
||||||
return
|
return
|
||||||
@ -269,7 +273,7 @@ func (s *Ethereum) peerHandler(listener net.Listener) {
|
|||||||
for {
|
for {
|
||||||
conn, err := listener.Accept()
|
conn, err := listener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
ethutil.Config.Log.Debugln(err)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -303,7 +307,7 @@ func (s *Ethereum) upnpUpdateThread() {
|
|||||||
// Go off immediately to prevent code duplication, thereafter we renew
|
// Go off immediately to prevent code duplication, thereafter we renew
|
||||||
// lease every 15 minutes.
|
// lease every 15 minutes.
|
||||||
timer := time.NewTimer(0 * time.Second)
|
timer := time.NewTimer(0 * time.Second)
|
||||||
lport, _ := strconv.ParseInt("30303", 10, 16)
|
lport, _ := strconv.ParseInt(s.Port, 10, 16)
|
||||||
first := true
|
first := true
|
||||||
out:
|
out:
|
||||||
for {
|
for {
|
||||||
@ -312,13 +316,13 @@ out:
|
|||||||
var err error
|
var err error
|
||||||
_, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60)
|
_, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("can't add UPnP port mapping:", err)
|
ethutil.Config.Log.Debugln("can't add UPnP port mapping:", err)
|
||||||
break out
|
break out
|
||||||
}
|
}
|
||||||
if first && err == nil {
|
if first && err == nil {
|
||||||
_, err = s.nat.GetExternalAddress()
|
_, err = s.nat.GetExternalAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("UPnP can't get external address:", err)
|
ethutil.Config.Log.Debugln("UPnP can't get external address:", err)
|
||||||
continue out
|
continue out
|
||||||
}
|
}
|
||||||
first = false
|
first = false
|
||||||
@ -332,8 +336,8 @@ out:
|
|||||||
timer.Stop()
|
timer.Stop()
|
||||||
|
|
||||||
if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil {
|
if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil {
|
||||||
log.Println("unable to remove UPnP port mapping:", err)
|
ethutil.Config.Log.Debugln("unable to remove UPnP port mapping:", err)
|
||||||
} else {
|
} else {
|
||||||
log.Println("succesfully disestablished UPnP port mapping")
|
ethutil.Config.Log.Debugln("succesfully disestablished UPnP port mapping")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,8 @@ trie.Put("doge", "coin")
|
|||||||
// Look up the key "do" in the trie
|
// Look up the key "do" in the trie
|
||||||
out := trie.Get("do")
|
out := trie.Get("do")
|
||||||
fmt.Println(out) // => verb
|
fmt.Println(out) // => verb
|
||||||
|
|
||||||
|
trie.Delete("puppy")
|
||||||
```
|
```
|
||||||
|
|
||||||
The patricia trie, in combination with RLP, provides a robust,
|
The patricia trie, in combination with RLP, provides a robust,
|
||||||
@ -82,7 +84,7 @@ type (e.g. `Slice()` returns []interface{}, `Uint()` return 0, etc).
|
|||||||
`NewEmptyValue()` returns a new \*Value with it's initial value set to a
|
`NewEmptyValue()` returns a new \*Value with it's initial value set to a
|
||||||
`[]interface{}`
|
`[]interface{}`
|
||||||
|
|
||||||
`AppendLint()` appends a list to the current value.
|
`AppendList()` appends a list to the current value.
|
||||||
|
|
||||||
`Append(v)` appends the value (v) to the current value/list.
|
`Append(v)` appends the value (v) to the current value/list.
|
||||||
|
|
||||||
|
@ -35,3 +35,18 @@ func BigD(data []byte) *big.Int {
|
|||||||
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BigToBytes(num *big.Int, base int) []byte {
|
||||||
|
ret := make([]byte, base/8)
|
||||||
|
|
||||||
|
return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions like the build in "copy" function
|
||||||
|
// but works on big integers
|
||||||
|
func BigCopy(src *big.Int) (ret *big.Int) {
|
||||||
|
ret = new(big.Int)
|
||||||
|
ret.Add(ret, src)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
35
ethutil/common.go
Normal file
35
ethutil/common.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package ethutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Ether = BigPow(10, 18)
|
||||||
|
Finney = BigPow(10, 15)
|
||||||
|
Szabo = BigPow(10, 12)
|
||||||
|
Vito = BigPow(10, 9)
|
||||||
|
Turing = BigPow(10, 6)
|
||||||
|
Eins = BigPow(10, 3)
|
||||||
|
Wei = big.NewInt(1)
|
||||||
|
)
|
||||||
|
|
||||||
|
func CurrencyToString(num *big.Int) string {
|
||||||
|
switch {
|
||||||
|
case num.Cmp(Ether) >= 0:
|
||||||
|
return fmt.Sprintf("%v Ether", new(big.Int).Div(num, Ether))
|
||||||
|
case num.Cmp(Finney) >= 0:
|
||||||
|
return fmt.Sprintf("%v Finney", new(big.Int).Div(num, Finney))
|
||||||
|
case num.Cmp(Szabo) >= 0:
|
||||||
|
return fmt.Sprintf("%v Szabo", new(big.Int).Div(num, Szabo))
|
||||||
|
case num.Cmp(Vito) >= 0:
|
||||||
|
return fmt.Sprintf("%v Vito", new(big.Int).Div(num, Vito))
|
||||||
|
case num.Cmp(Turing) >= 0:
|
||||||
|
return fmt.Sprintf("%v Turing", new(big.Int).Div(num, Turing))
|
||||||
|
case num.Cmp(Eins) >= 0:
|
||||||
|
return fmt.Sprintf("%v Eins", new(big.Int).Div(num, Eins))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%v Wei", num)
|
||||||
|
}
|
17
ethutil/common_test.go
Normal file
17
ethutil/common_test.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package ethutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommon(t *testing.T) {
|
||||||
|
fmt.Println(CurrencyToString(BigPow(10, 19)))
|
||||||
|
fmt.Println(CurrencyToString(BigPow(10, 16)))
|
||||||
|
fmt.Println(CurrencyToString(BigPow(10, 13)))
|
||||||
|
fmt.Println(CurrencyToString(BigPow(10, 10)))
|
||||||
|
fmt.Println(CurrencyToString(BigPow(10, 7)))
|
||||||
|
fmt.Println(CurrencyToString(BigPow(10, 4)))
|
||||||
|
fmt.Println(CurrencyToString(big.NewInt(10)))
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package ethutil
|
package ethutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
@ -18,7 +19,7 @@ const (
|
|||||||
type config struct {
|
type config struct {
|
||||||
Db Database
|
Db Database
|
||||||
|
|
||||||
Log Logger
|
Log *Logger
|
||||||
ExecPath string
|
ExecPath string
|
||||||
Debug bool
|
Debug bool
|
||||||
Ver string
|
Ver string
|
||||||
@ -34,17 +35,19 @@ func ReadConfig(base string) *config {
|
|||||||
usr, _ := user.Current()
|
usr, _ := user.Current()
|
||||||
path := path.Join(usr.HomeDir, base)
|
path := path.Join(usr.HomeDir, base)
|
||||||
|
|
||||||
//Check if the logging directory already exists, create it if not
|
if len(base) > 0 {
|
||||||
_, err := os.Stat(path)
|
//Check if the logging directory already exists, create it if not
|
||||||
if err != nil {
|
_, err := os.Stat(path)
|
||||||
if os.IsNotExist(err) {
|
if err != nil {
|
||||||
log.Printf("Debug logging directory %s doesn't exist, creating it", path)
|
if os.IsNotExist(err) {
|
||||||
os.Mkdir(path, 0777)
|
log.Printf("Debug logging directory %s doesn't exist, creating it\n", path)
|
||||||
|
os.Mkdir(path, 0777)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Config = &config{ExecPath: path, Debug: true, Ver: "0.2.2"}
|
Config = &config{ExecPath: path, Debug: true, Ver: "0.3.0"}
|
||||||
Config.Log = NewLogger(LogFile|LogStd, 0)
|
Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Config
|
return Config
|
||||||
@ -57,15 +60,20 @@ const (
|
|||||||
LogStd = 0x2
|
LogStd = 0x2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type LogSystem interface {
|
||||||
|
Println(v ...interface{})
|
||||||
|
Printf(format string, v ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
logSys []*log.Logger
|
logSys []LogSystem
|
||||||
logLevel int
|
logLevel int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLogger(flag LoggerType, level int) Logger {
|
func NewLogger(flag LoggerType, level int) *Logger {
|
||||||
var loggers []*log.Logger
|
var loggers []LogSystem
|
||||||
|
|
||||||
flags := log.LstdFlags | log.Lshortfile
|
flags := log.LstdFlags
|
||||||
|
|
||||||
if flag&LogFile > 0 {
|
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)
|
file, err := os.OpenFile(path.Join(Config.ExecPath, "debug.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm)
|
||||||
@ -73,20 +81,29 @@ func NewLogger(flag LoggerType, level int) Logger {
|
|||||||
log.Panic("unable to create file logger", err)
|
log.Panic("unable to create file logger", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log := log.New(file, "[ETH]", flags)
|
log := log.New(file, "", flags)
|
||||||
|
|
||||||
loggers = append(loggers, log)
|
loggers = append(loggers, log)
|
||||||
}
|
}
|
||||||
if flag&LogStd > 0 {
|
if flag&LogStd > 0 {
|
||||||
log := log.New(os.Stdout, "[ETH]", flags)
|
log := log.New(os.Stdout, "", flags)
|
||||||
loggers = append(loggers, log)
|
loggers = append(loggers, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Logger{logSys: loggers, logLevel: level}
|
return &Logger{logSys: loggers, logLevel: level}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (log Logger) Debugln(v ...interface{}) {
|
func (log *Logger) AddLogSystem(logger LogSystem) {
|
||||||
if log.logLevel != 0 {
|
log.logSys = append(log.logSys, logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
LogLevelDebug = iota
|
||||||
|
LogLevelInfo
|
||||||
|
)
|
||||||
|
|
||||||
|
func (log *Logger) Debugln(v ...interface{}) {
|
||||||
|
if log.logLevel != LogLevelDebug {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,8 +112,29 @@ func (log Logger) Debugln(v ...interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (log Logger) Debugf(format string, v ...interface{}) {
|
func (log *Logger) Debugf(format string, v ...interface{}) {
|
||||||
if log.logLevel != 0 {
|
if log.logLevel != LogLevelDebug {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, logger := range log.logSys {
|
||||||
|
logger.Printf(format, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (log *Logger) Infoln(v ...interface{}) {
|
||||||
|
if log.logLevel > LogLevelInfo {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(len(log.logSys))
|
||||||
|
for _, logger := range log.logSys {
|
||||||
|
logger.Println(v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (log *Logger) Infof(format string, v ...interface{}) {
|
||||||
|
if log.logLevel > LogLevelInfo {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ package ethutil
|
|||||||
type Database interface {
|
type Database interface {
|
||||||
Put(key []byte, value []byte)
|
Put(key []byte, value []byte)
|
||||||
Get(key []byte) ([]byte, error)
|
Get(key []byte) ([]byte, error)
|
||||||
|
GetKeys() []*Key
|
||||||
|
Delete(key []byte) error
|
||||||
LastKnownTD() []byte
|
LastKnownTD() []byte
|
||||||
Close()
|
Close()
|
||||||
Print()
|
Print()
|
||||||
|
@ -3,7 +3,6 @@ package ethutil
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
_ "fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,7 +35,7 @@ func CompactEncode(hexSlice []int) string {
|
|||||||
func CompactDecode(str string) []int {
|
func CompactDecode(str string) []int {
|
||||||
base := CompactHexDecode(str)
|
base := CompactHexDecode(str)
|
||||||
base = base[:len(base)-1]
|
base = base[:len(base)-1]
|
||||||
if base[0] >= 2 { // && base[len(base)-1] != 16 {
|
if base[0] >= 2 {
|
||||||
base = append(base, 16)
|
base = append(base, 16)
|
||||||
}
|
}
|
||||||
if base[0]%2 == 1 {
|
if base[0]%2 == 1 {
|
||||||
|
@ -35,3 +35,33 @@ func TestCompactHexDecode(t *testing.T) {
|
|||||||
t.Error("Error compact hex decode. Expected", exp, "got", res)
|
t.Error("Error compact hex decode. Expected", exp, "got", res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCompactDecode(t *testing.T) {
|
||||||
|
exp := []int{1, 2, 3, 4, 5}
|
||||||
|
res := CompactDecode("\x11\x23\x45")
|
||||||
|
|
||||||
|
if !CompareIntSlice(res, exp) {
|
||||||
|
t.Error("odd compact decode. Expected", exp, "got", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
exp = []int{0, 1, 2, 3, 4, 5}
|
||||||
|
res = CompactDecode("\x00\x01\x23\x45")
|
||||||
|
|
||||||
|
if !CompareIntSlice(res, exp) {
|
||||||
|
t.Error("even compact decode. Expected", exp, "got", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
exp = []int{0, 15, 1, 12, 11, 8 /*term*/, 16}
|
||||||
|
res = CompactDecode("\x20\x0f\x1c\xb8")
|
||||||
|
|
||||||
|
if !CompareIntSlice(res, exp) {
|
||||||
|
t.Error("even terminated compact decode. Expected", exp, "got", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
exp = []int{15, 1, 12, 11, 8 /*term*/, 16}
|
||||||
|
res = CompactDecode("\x3f\x1c\xb8")
|
||||||
|
|
||||||
|
if !CompareIntSlice(res, exp) {
|
||||||
|
t.Error("even terminated compact decode. Expected", exp, "got", res)
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,6 @@ func Ripemd160(data []byte) []byte {
|
|||||||
|
|
||||||
func Sha3Bin(data []byte) []byte {
|
func Sha3Bin(data []byte) []byte {
|
||||||
d := sha3.NewKeccak256()
|
d := sha3.NewKeccak256()
|
||||||
d.Reset()
|
|
||||||
d.Write(data)
|
d.Write(data)
|
||||||
|
|
||||||
return d.Sum(nil)
|
return d.Sum(nil)
|
||||||
@ -59,3 +58,7 @@ func MatchingNibbleLength(a, b []int) int {
|
|||||||
func Hex(d []byte) string {
|
func Hex(d []byte) string {
|
||||||
return hex.EncodeToString(d)
|
return hex.EncodeToString(d)
|
||||||
}
|
}
|
||||||
|
func FromHex(str string) []byte {
|
||||||
|
h, _ := hex.DecodeString(str)
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
19
ethutil/key.go
Normal file
19
ethutil/key.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package ethutil
|
||||||
|
|
||||||
|
type Key struct {
|
||||||
|
PrivateKey []byte
|
||||||
|
PublicKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeyFromBytes(data []byte) *Key {
|
||||||
|
val := NewValueFromBytes(data)
|
||||||
|
return &Key{val.Get(0).Bytes(), val.Get(1).Bytes()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Key) Address() []byte {
|
||||||
|
return Sha3Bin(k.PublicKey[1:])[12:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Key) RlpEncode() []byte {
|
||||||
|
return EmptyValue().Append(k.PrivateKey).Append(k.PublicKey).Encode()
|
||||||
|
}
|
@ -1,95 +1,88 @@
|
|||||||
package ethutil
|
package ethutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Op codes
|
// Op codes
|
||||||
var OpCodes = map[string]string{
|
var OpCodes = map[string]byte{
|
||||||
"STOP": "0",
|
"STOP": 0x00,
|
||||||
"ADD": "1",
|
"ADD": 0x01,
|
||||||
"MUL": "2",
|
"MUL": 0x02,
|
||||||
"SUB": "3",
|
"SUB": 0x03,
|
||||||
"DIV": "4",
|
"DIV": 0x04,
|
||||||
"SDIV": "5",
|
"SDIV": 0x05,
|
||||||
"MOD": "6",
|
"MOD": 0x06,
|
||||||
"SMOD": "7",
|
"SMOD": 0x07,
|
||||||
"EXP": "8",
|
"EXP": 0x08,
|
||||||
"NEG": "9",
|
"NEG": 0x09,
|
||||||
"LT": "10",
|
"LT": 0x0a,
|
||||||
"LE": "11",
|
"LE": 0x0b,
|
||||||
"GT": "12",
|
"GT": 0x0c,
|
||||||
"GE": "13",
|
"GE": 0x0d,
|
||||||
"EQ": "14",
|
"EQ": 0x0e,
|
||||||
"NOT": "15",
|
"NOT": 0x0f,
|
||||||
"MYADDRESS": "16",
|
"MYADDRESS": 0x10,
|
||||||
"TXSENDER": "17",
|
"TXSENDER": 0x11,
|
||||||
|
"TXVALUE": 0x12,
|
||||||
"PUSH": "48",
|
"TXDATAN": 0x13,
|
||||||
"POP": "49",
|
"TXDATA": 0x14,
|
||||||
"LOAD": "54",
|
"BLK_PREVHASH": 0x15,
|
||||||
|
"BLK_COINBASE": 0x16,
|
||||||
|
"BLK_TIMESTAMP": 0x17,
|
||||||
|
"BLK_NUMBER": 0x18,
|
||||||
|
"BLK_DIFFICULTY": 0x19,
|
||||||
|
"BLK_NONCE": 0x1a,
|
||||||
|
"BASEFEE": 0x1b,
|
||||||
|
"SHA256": 0x20,
|
||||||
|
"RIPEMD160": 0x21,
|
||||||
|
"ECMUL": 0x22,
|
||||||
|
"ECADD": 0x23,
|
||||||
|
"ECSIGN": 0x24,
|
||||||
|
"ECRECOVER": 0x25,
|
||||||
|
"ECVALID": 0x26,
|
||||||
|
"SHA3": 0x27,
|
||||||
|
"PUSH": 0x30,
|
||||||
|
"POP": 0x31,
|
||||||
|
"DUP": 0x32,
|
||||||
|
"SWAP": 0x33,
|
||||||
|
"MLOAD": 0x34,
|
||||||
|
"MSTORE": 0x35,
|
||||||
|
"SLOAD": 0x36,
|
||||||
|
"SSTORE": 0x37,
|
||||||
|
"JMP": 0x38,
|
||||||
|
"JMPI": 0x39,
|
||||||
|
"IND": 0x3a,
|
||||||
|
"EXTRO": 0x3b,
|
||||||
|
"BALANCE": 0x3c,
|
||||||
|
"MKTX": 0x3d,
|
||||||
|
"SUICIDE": 0x3f,
|
||||||
}
|
}
|
||||||
|
|
||||||
func CompileInstr(s string) (string, error) {
|
func IsOpCode(s string) bool {
|
||||||
tokens := strings.Split(s, " ")
|
for key, _ := range OpCodes {
|
||||||
if OpCodes[tokens[0]] == "" {
|
if key == s {
|
||||||
return s, errors.New(fmt.Sprintf("OP not found: %s", tokens[0]))
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompileInstr(s string) ([]byte, error) {
|
||||||
|
isOp := IsOpCode(s)
|
||||||
|
if isOp {
|
||||||
|
return []byte{OpCodes[s]}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
code := OpCodes[tokens[0]] // Replace op codes with the proper numerical equivalent
|
num := new(big.Int)
|
||||||
op := new(big.Int)
|
num.SetString(s, 0)
|
||||||
op.SetString(code, 0)
|
|
||||||
|
|
||||||
args := make([]*big.Int, 6)
|
return num.Bytes(), nil
|
||||||
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) {
|
func Instr(instr string) (int, []string, error) {
|
||||||
|
|
||||||
base := new(big.Int)
|
base := new(big.Int)
|
||||||
base.SetString(instr, 0)
|
base.SetString(instr, 0)
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package ethutil
|
package ethutil
|
||||||
|
|
||||||
|
/*
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
@ -13,20 +14,19 @@ func TestCompile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
calc := (48 + 0*256 + 0*int64(math.Pow(256, 2)))
|
calc := (48 + 0*256 + 0*int64(math.Pow(256, 2)))
|
||||||
if Big(instr).Int64() != calc {
|
if BigD(instr).Int64() != calc {
|
||||||
t.Error("Expected", calc, ", got:", instr)
|
t.Error("Expected", calc, ", got:", instr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidInstr(t *testing.T) {
|
func TestValidInstr(t *testing.T) {
|
||||||
/*
|
|
||||||
op, args, err := Instr("68163")
|
op, args, err := Instr("68163")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("Error decoding instruction")
|
t.Error("Error decoding instruction")
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidInstr(t *testing.T) {
|
func TestInvalidInstr(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
@ -86,13 +86,6 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} {
|
|||||||
// TODO Use a bytes.Buffer instead of a raw byte 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
|
// Cleaner code, and use draining instead of seeking the next bytes to read
|
||||||
func Decode(data []byte, pos uint64) (interface{}, uint64) {
|
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{}
|
var slice []interface{}
|
||||||
char := int(data[pos])
|
char := int(data[pos])
|
||||||
switch {
|
switch {
|
||||||
@ -131,7 +124,6 @@ func Decode(data []byte, pos uint64) (interface{}, uint64) {
|
|||||||
|
|
||||||
case char <= 0xff:
|
case char <= 0xff:
|
||||||
l := uint64(data[pos]) - 0xf7
|
l := uint64(data[pos]) - 0xf7
|
||||||
//b := BigD(data[pos+1 : pos+1+l]).Uint64()
|
|
||||||
b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l]))
|
b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l]))
|
||||||
|
|
||||||
pos = pos + l + 1
|
pos = pos + l + 1
|
||||||
|
@ -2,15 +2,13 @@ package ethutil
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRlpValueEncoding(t *testing.T) {
|
func TestRlpValueEncoding(t *testing.T) {
|
||||||
val := EmptyRlpValue()
|
val := EmptyValue()
|
||||||
val.AppendList().Append(1).Append(2).Append(3)
|
val.AppendList().Append(1).Append(2).Append(3)
|
||||||
val.Append("4").AppendList().Append(5)
|
val.Append("4").AppendList().Append(5)
|
||||||
|
|
||||||
@ -63,7 +61,7 @@ func TestEncode(t *testing.T) {
|
|||||||
|
|
||||||
str := string(bytes)
|
str := string(bytes)
|
||||||
if str != strRes {
|
if str != strRes {
|
||||||
t.Error(fmt.Sprintf("Expected %q, got %q", strRes, str))
|
t.Errorf("Expected %q, got %q", strRes, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
sliceRes := "\xcc\x83dog\x83god\x83cat"
|
sliceRes := "\xcc\x83dog\x83god\x83cat"
|
||||||
@ -71,7 +69,7 @@ func TestEncode(t *testing.T) {
|
|||||||
bytes = Encode(strs)
|
bytes = Encode(strs)
|
||||||
slice := string(bytes)
|
slice := string(bytes)
|
||||||
if slice != sliceRes {
|
if slice != sliceRes {
|
||||||
t.Error(fmt.Sprintf("Expected %q, got %q", sliceRes, slice))
|
t.Error("Expected %q, got %q", sliceRes, slice)
|
||||||
}
|
}
|
||||||
|
|
||||||
intRes := "\x82\x04\x00"
|
intRes := "\x82\x04\x00"
|
||||||
@ -108,13 +106,9 @@ func TestEncodeDecodeBigInt(t *testing.T) {
|
|||||||
encoded := Encode(bigInt)
|
encoded := Encode(bigInt)
|
||||||
|
|
||||||
value := NewValueFromBytes(encoded)
|
value := NewValueFromBytes(encoded)
|
||||||
fmt.Println(value.BigInt(), bigInt)
|
|
||||||
if value.BigInt().Cmp(bigInt) != 0 {
|
if value.BigInt().Cmp(bigInt) != 0 {
|
||||||
t.Errorf("Expected %v, got %v", bigInt, value.BigInt())
|
t.Errorf("Expected %v, got %v", bigInt, value.BigInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
dec, _ := hex.DecodeString("52f4fc1e")
|
|
||||||
fmt.Println(NewValueFromBytes(dec).BigInt())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncodeDecodeBytes(t *testing.T) {
|
func TestEncodeDecodeBytes(t *testing.T) {
|
||||||
@ -125,43 +119,6 @@ func TestEncodeDecodeBytes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
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) {
|
func BenchmarkEncodeDecode(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
bytes := Encode([]interface{}{"dog", "god", "cat"})
|
bytes := Encode([]interface{}{"dog", "god", "cat"})
|
||||||
|
212
ethutil/trie.go
212
ethutil/trie.go
@ -5,6 +5,15 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// A StateObject is an object that has a state root
|
||||||
|
// This is goig to be the object for the second level caching (the caching of object which have a state such as contracts)
|
||||||
|
type StateObject interface {
|
||||||
|
State() *Trie
|
||||||
|
Sync()
|
||||||
|
Undo()
|
||||||
|
}
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
Key []byte
|
Key []byte
|
||||||
Value *Value
|
Value *Value
|
||||||
@ -20,8 +29,9 @@ func (n *Node) Copy() *Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Cache struct {
|
type Cache struct {
|
||||||
nodes map[string]*Node
|
nodes map[string]*Node
|
||||||
db Database
|
db Database
|
||||||
|
IsDirty bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCache(db Database) *Cache {
|
func NewCache(db Database) *Cache {
|
||||||
@ -36,6 +46,7 @@ func (cache *Cache) Put(v interface{}) interface{} {
|
|||||||
sha := Sha3Bin(enc)
|
sha := Sha3Bin(enc)
|
||||||
|
|
||||||
cache.nodes[string(sha)] = NewNode(sha, value, true)
|
cache.nodes[string(sha)] = NewNode(sha, value, true)
|
||||||
|
cache.IsDirty = true
|
||||||
|
|
||||||
return sha
|
return sha
|
||||||
}
|
}
|
||||||
@ -59,13 +70,25 @@ func (cache *Cache) Get(key []byte) *Value {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cache *Cache) Delete(key []byte) {
|
||||||
|
delete(cache.nodes, string(key))
|
||||||
|
|
||||||
|
cache.db.Delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
func (cache *Cache) Commit() {
|
func (cache *Cache) Commit() {
|
||||||
|
// Don't try to commit if it isn't dirty
|
||||||
|
if !cache.IsDirty {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for key, node := range cache.nodes {
|
for key, node := range cache.nodes {
|
||||||
if node.Dirty {
|
if node.Dirty {
|
||||||
cache.db.Put([]byte(key), node.Value.Encode())
|
cache.db.Put([]byte(key), node.Value.Encode())
|
||||||
node.Dirty = false
|
node.Dirty = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cache.IsDirty = false
|
||||||
|
|
||||||
// If the nodes grows beyond the 200 entries we simple empty it
|
// If the nodes grows beyond the 200 entries we simple empty it
|
||||||
// FIXME come up with something better
|
// FIXME come up with something better
|
||||||
@ -80,6 +103,7 @@ func (cache *Cache) Undo() {
|
|||||||
delete(cache.nodes, key)
|
delete(cache.nodes, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cache.IsDirty = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// A (modified) Radix Trie implementation. The Trie implements
|
// A (modified) Radix Trie implementation. The Trie implements
|
||||||
@ -89,18 +113,29 @@ func (cache *Cache) Undo() {
|
|||||||
// Please note that the data isn't persisted unless `Sync` is
|
// Please note that the data isn't persisted unless `Sync` is
|
||||||
// explicitly called.
|
// explicitly called.
|
||||||
type Trie struct {
|
type Trie struct {
|
||||||
Root interface{}
|
prevRoot interface{}
|
||||||
|
Root interface{}
|
||||||
//db Database
|
//db Database
|
||||||
cache *Cache
|
cache *Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTrie(db Database, Root interface{}) *Trie {
|
func NewTrie(db Database, Root interface{}) *Trie {
|
||||||
return &Trie{cache: NewCache(db), Root: Root}
|
return &Trie{cache: NewCache(db), Root: Root, prevRoot: Root}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the cached value to the database.
|
// Save the cached value to the database.
|
||||||
func (t *Trie) Sync() {
|
func (t *Trie) Sync() {
|
||||||
t.cache.Commit()
|
t.cache.Commit()
|
||||||
|
t.prevRoot = t.Root
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Trie) Undo() {
|
||||||
|
t.cache.Undo()
|
||||||
|
t.Root = t.prevRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Trie) Cache() *Cache {
|
||||||
|
return t.cache
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -119,6 +154,10 @@ func (t *Trie) Get(key string) string {
|
|||||||
return c.Str()
|
return c.Str()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Trie) Delete(key string) {
|
||||||
|
t.Update(key, "")
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Trie) GetState(node interface{}, key []int) interface{} {
|
func (t *Trie) GetState(node interface{}, key []int) interface{} {
|
||||||
n := NewValue(node)
|
n := NewValue(node)
|
||||||
// Return the node if key is empty (= found)
|
// Return the node if key is empty (= found)
|
||||||
@ -168,13 +207,15 @@ func (t *Trie) GetNode(node interface{}) *Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trie) UpdateState(node interface{}, key []int, value string) interface{} {
|
func (t *Trie) UpdateState(node interface{}, key []int, value string) interface{} {
|
||||||
|
|
||||||
if value != "" {
|
if value != "" {
|
||||||
return t.InsertState(node, key, value)
|
return t.InsertState(node, key, value)
|
||||||
} else {
|
} else {
|
||||||
// delete it
|
// delete it
|
||||||
|
return t.DeleteState(node, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return t.Root
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trie) Put(node interface{}) interface{} {
|
func (t *Trie) Put(node interface{}) interface{} {
|
||||||
@ -228,6 +269,7 @@ func (t *Trie) InsertState(node interface{}, key []int, value interface{}) inter
|
|||||||
// Check for "special" 2 slice type node
|
// Check for "special" 2 slice type node
|
||||||
if currentNode.Len() == 2 {
|
if currentNode.Len() == 2 {
|
||||||
// Decode the key
|
// Decode the key
|
||||||
|
|
||||||
k := CompactDecode(currentNode.Get(0).Str())
|
k := CompactDecode(currentNode.Get(0).Str())
|
||||||
v := currentNode.Get(1).Raw()
|
v := currentNode.Get(1).Raw()
|
||||||
|
|
||||||
@ -282,6 +324,87 @@ func (t *Trie) InsertState(node interface{}, key []int, value interface{}) inter
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Trie) DeleteState(node interface{}, key []int) interface{} {
|
||||||
|
if len(key) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// New node
|
||||||
|
n := NewValue(node)
|
||||||
|
if node == nil || (n.Type() == reflect.String && (n.Str() == "" || n.Get(0).IsNil())) || n.Len() == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return ""
|
||||||
|
} else if CompareIntSlice(key[:len(k)], k) {
|
||||||
|
hash := t.DeleteState(v, key[len(k):])
|
||||||
|
child := t.GetNode(hash)
|
||||||
|
|
||||||
|
var newNode []interface{}
|
||||||
|
if child.Len() == 2 {
|
||||||
|
newKey := append(k, CompactDecode(child.Get(0).Str())...)
|
||||||
|
newNode = []interface{}{CompactEncode(newKey), child.Get(1).Raw()}
|
||||||
|
} else {
|
||||||
|
newNode = []interface{}{currentNode.Get(0).Str(), hash}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.Put(newNode)
|
||||||
|
} else {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Copy the current node over to the new node and replace the first nibble in the key
|
||||||
|
n := EmptyStringSlice(17)
|
||||||
|
var newNode []interface{}
|
||||||
|
|
||||||
|
for i := 0; i < 17; i++ {
|
||||||
|
cpy := currentNode.Get(i).Raw()
|
||||||
|
if cpy != nil {
|
||||||
|
n[i] = cpy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n[key[0]] = t.DeleteState(n[key[0]], key[1:])
|
||||||
|
amount := -1
|
||||||
|
for i := 0; i < 17; i++ {
|
||||||
|
if n[i] != "" {
|
||||||
|
if amount == -1 {
|
||||||
|
amount = i
|
||||||
|
} else {
|
||||||
|
amount = -2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if amount == 16 {
|
||||||
|
newNode = []interface{}{CompactEncode([]int{16}), n[amount]}
|
||||||
|
} else if amount >= 0 {
|
||||||
|
child := t.GetNode(n[amount])
|
||||||
|
if child.Len() == 17 {
|
||||||
|
newNode = []interface{}{CompactEncode([]int{amount}), n[amount]}
|
||||||
|
} else if child.Len() == 2 {
|
||||||
|
key := append([]int{amount}, CompactDecode(child.Get(0).Str())...)
|
||||||
|
newNode = []interface{}{CompactEncode(key), child.Get(1).Str()}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
newNode = n
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.Put(newNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// Simple compare function which creates a rlp value out of the evaluated objects
|
// Simple compare function which creates a rlp value out of the evaluated objects
|
||||||
func (t *Trie) Cmp(trie *Trie) bool {
|
func (t *Trie) Cmp(trie *Trie) bool {
|
||||||
return NewValue(t.Root).Cmp(NewValue(trie.Root))
|
return NewValue(t.Root).Cmp(NewValue(trie.Root))
|
||||||
@ -296,3 +419,82 @@ func (t *Trie) Copy() *Trie {
|
|||||||
|
|
||||||
return trie
|
return trie
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TrieIterator struct {
|
||||||
|
trie *Trie
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
|
||||||
|
shas [][]byte
|
||||||
|
values []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Trie) NewIterator() *TrieIterator {
|
||||||
|
return &TrieIterator{trie: t}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some time in the near future this will need refactoring :-)
|
||||||
|
// XXX Note to self, IsSlice == inline node. Str == sha3 to node
|
||||||
|
func (it *TrieIterator) workNode(currentNode *Value) {
|
||||||
|
if currentNode.Len() == 2 {
|
||||||
|
k := CompactDecode(currentNode.Get(0).Str())
|
||||||
|
|
||||||
|
if currentNode.Get(1).Str() == "" {
|
||||||
|
it.workNode(currentNode.Get(1))
|
||||||
|
} else {
|
||||||
|
if k[len(k)-1] == 16 {
|
||||||
|
it.values = append(it.values, currentNode.Get(1).Str())
|
||||||
|
} else {
|
||||||
|
it.shas = append(it.shas, currentNode.Get(1).Bytes())
|
||||||
|
it.getNode(currentNode.Get(1).Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := 0; i < currentNode.Len(); i++ {
|
||||||
|
if i == 16 && currentNode.Get(i).Len() != 0 {
|
||||||
|
it.values = append(it.values, currentNode.Get(i).Str())
|
||||||
|
} else {
|
||||||
|
if currentNode.Get(i).Str() == "" {
|
||||||
|
it.workNode(currentNode.Get(i))
|
||||||
|
} else {
|
||||||
|
val := currentNode.Get(i).Str()
|
||||||
|
if val != "" {
|
||||||
|
it.shas = append(it.shas, currentNode.Get(1).Bytes())
|
||||||
|
it.getNode([]byte(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *TrieIterator) getNode(node []byte) {
|
||||||
|
currentNode := it.trie.cache.Get(node)
|
||||||
|
it.workNode(currentNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *TrieIterator) Collect() [][]byte {
|
||||||
|
if it.trie.Root == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
it.getNode(NewValue(it.trie.Root).Bytes())
|
||||||
|
|
||||||
|
return it.shas
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *TrieIterator) Purge() int {
|
||||||
|
shas := it.Collect()
|
||||||
|
for _, sha := range shas {
|
||||||
|
it.trie.cache.Delete(sha)
|
||||||
|
}
|
||||||
|
return len(it.values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *TrieIterator) Key() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *TrieIterator) Value() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package ethutil
|
package ethutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "encoding/hex"
|
"reflect"
|
||||||
_ "fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const LONG_WORD = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
|
||||||
type MemDatabase struct {
|
type MemDatabase struct {
|
||||||
db map[string][]byte
|
db map[string][]byte
|
||||||
}
|
}
|
||||||
@ -20,15 +21,24 @@ func (db *MemDatabase) Put(key []byte, value []byte) {
|
|||||||
func (db *MemDatabase) Get(key []byte) ([]byte, error) {
|
func (db *MemDatabase) Get(key []byte) ([]byte, error) {
|
||||||
return db.db[string(key)], nil
|
return db.db[string(key)], nil
|
||||||
}
|
}
|
||||||
|
func (db *MemDatabase) Delete(key []byte) error {
|
||||||
|
delete(db.db, string(key))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (db *MemDatabase) GetKeys() []*Key { return nil }
|
||||||
func (db *MemDatabase) Print() {}
|
func (db *MemDatabase) Print() {}
|
||||||
func (db *MemDatabase) Close() {}
|
func (db *MemDatabase) Close() {}
|
||||||
func (db *MemDatabase) LastKnownTD() []byte { return nil }
|
func (db *MemDatabase) LastKnownTD() []byte { return nil }
|
||||||
|
|
||||||
func TestTrieSync(t *testing.T) {
|
func New() (*MemDatabase, *Trie) {
|
||||||
db, _ := NewMemDatabase()
|
db, _ := NewMemDatabase()
|
||||||
trie := NewTrie(db, "")
|
return db, NewTrie(db, "")
|
||||||
|
}
|
||||||
|
|
||||||
trie.Update("dog", "kindofalongsentencewhichshouldbeencodedinitsentirety")
|
func TestTrieSync(t *testing.T) {
|
||||||
|
db, trie := New()
|
||||||
|
|
||||||
|
trie.Update("dog", LONG_WORD)
|
||||||
if len(db.db) != 0 {
|
if len(db.db) != 0 {
|
||||||
t.Error("Expected no data in database")
|
t.Error("Expected no data in database")
|
||||||
}
|
}
|
||||||
@ -38,3 +48,125 @@ func TestTrieSync(t *testing.T) {
|
|||||||
t.Error("Expected data to be persisted")
|
t.Error("Expected data to be persisted")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTrieDirtyTracking(t *testing.T) {
|
||||||
|
_, trie := New()
|
||||||
|
trie.Update("dog", LONG_WORD)
|
||||||
|
if !trie.cache.IsDirty {
|
||||||
|
t.Error("Expected trie to be dirty")
|
||||||
|
}
|
||||||
|
|
||||||
|
trie.Sync()
|
||||||
|
if trie.cache.IsDirty {
|
||||||
|
t.Error("Expected trie not to be dirty")
|
||||||
|
}
|
||||||
|
|
||||||
|
trie.Update("test", LONG_WORD)
|
||||||
|
trie.cache.Undo()
|
||||||
|
if trie.cache.IsDirty {
|
||||||
|
t.Error("Expected trie not to be dirty")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrieReset(t *testing.T) {
|
||||||
|
_, trie := New()
|
||||||
|
|
||||||
|
trie.Update("cat", LONG_WORD)
|
||||||
|
if len(trie.cache.nodes) == 0 {
|
||||||
|
t.Error("Expected cached nodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
trie.cache.Undo()
|
||||||
|
|
||||||
|
if len(trie.cache.nodes) != 0 {
|
||||||
|
t.Error("Expected no nodes after undo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrieGet(t *testing.T) {
|
||||||
|
_, trie := New()
|
||||||
|
|
||||||
|
trie.Update("cat", LONG_WORD)
|
||||||
|
x := trie.Get("cat")
|
||||||
|
if x != LONG_WORD {
|
||||||
|
t.Error("expected %s, got %s", LONG_WORD, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrieUpdating(t *testing.T) {
|
||||||
|
_, trie := New()
|
||||||
|
trie.Update("cat", LONG_WORD)
|
||||||
|
trie.Update("cat", LONG_WORD+"1")
|
||||||
|
x := trie.Get("cat")
|
||||||
|
if x != LONG_WORD+"1" {
|
||||||
|
t.Error("expected %S, got %s", LONG_WORD+"1", x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrieCmp(t *testing.T) {
|
||||||
|
_, trie1 := New()
|
||||||
|
_, trie2 := New()
|
||||||
|
|
||||||
|
trie1.Update("doge", LONG_WORD)
|
||||||
|
trie2.Update("doge", LONG_WORD)
|
||||||
|
if !trie1.Cmp(trie2) {
|
||||||
|
t.Error("Expected tries to be equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
trie1.Update("dog", LONG_WORD)
|
||||||
|
trie2.Update("cat", LONG_WORD)
|
||||||
|
if trie1.Cmp(trie2) {
|
||||||
|
t.Errorf("Expected tries not to be equal %x %x", trie1.Root, trie2.Root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrieDelete(t *testing.T) {
|
||||||
|
_, trie := New()
|
||||||
|
trie.Update("cat", LONG_WORD)
|
||||||
|
exp := trie.Root
|
||||||
|
trie.Update("dog", LONG_WORD)
|
||||||
|
trie.Delete("dog")
|
||||||
|
if !reflect.DeepEqual(exp, trie.Root) {
|
||||||
|
t.Errorf("Expected tries to be equal %x : %x", exp, trie.Root)
|
||||||
|
}
|
||||||
|
|
||||||
|
trie.Update("dog", LONG_WORD)
|
||||||
|
exp = trie.Root
|
||||||
|
trie.Update("dude", LONG_WORD)
|
||||||
|
trie.Delete("dude")
|
||||||
|
if !reflect.DeepEqual(exp, trie.Root) {
|
||||||
|
t.Errorf("Expected tries to be equal %x : %x", exp, trie.Root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrieDeleteWithValue(t *testing.T) {
|
||||||
|
_, trie := New()
|
||||||
|
trie.Update("c", LONG_WORD)
|
||||||
|
exp := trie.Root
|
||||||
|
trie.Update("ca", LONG_WORD)
|
||||||
|
trie.Update("cat", LONG_WORD)
|
||||||
|
trie.Delete("ca")
|
||||||
|
trie.Delete("cat")
|
||||||
|
if !reflect.DeepEqual(exp, trie.Root) {
|
||||||
|
t.Errorf("Expected tries to be equal %x : %x", exp, trie.Root)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrieIterator(t *testing.T) {
|
||||||
|
_, trie := New()
|
||||||
|
trie.Update("c", LONG_WORD)
|
||||||
|
trie.Update("ca", LONG_WORD)
|
||||||
|
trie.Update("cat", LONG_WORD)
|
||||||
|
|
||||||
|
lenBefore := len(trie.cache.nodes)
|
||||||
|
it := trie.NewIterator()
|
||||||
|
if num := it.Purge(); num != 3 {
|
||||||
|
t.Errorf("Expected purge to return 3, got %d", num)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lenBefore == len(trie.cache.nodes) {
|
||||||
|
t.Errorf("Expected cached nodes to be deleted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -36,7 +36,8 @@ func (val *Value) Len() int {
|
|||||||
if data, ok := val.Val.([]interface{}); ok {
|
if data, ok := val.Val.([]interface{}); ok {
|
||||||
return len(data)
|
return len(data)
|
||||||
} else if data, ok := val.Val.([]byte); ok {
|
} else if data, ok := val.Val.([]byte); ok {
|
||||||
// FIXME
|
return len(data)
|
||||||
|
} else if data, ok := val.Val.(string); ok {
|
||||||
return len(data)
|
return len(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,6 +61,10 @@ func (val *Value) Uint() uint64 {
|
|||||||
return uint64(Val)
|
return uint64(Val)
|
||||||
} else if Val, ok := val.Val.(uint64); ok {
|
} else if Val, ok := val.Val.(uint64); ok {
|
||||||
return Val
|
return Val
|
||||||
|
} else if Val, ok := val.Val.(int); ok {
|
||||||
|
return uint64(Val)
|
||||||
|
} else if Val, ok := val.Val.(uint); ok {
|
||||||
|
return uint64(Val)
|
||||||
} else if Val, ok := val.Val.([]byte); ok {
|
} else if Val, ok := val.Val.([]byte); ok {
|
||||||
return ReadVarint(bytes.NewReader(Val))
|
return ReadVarint(bytes.NewReader(Val))
|
||||||
}
|
}
|
||||||
@ -80,6 +85,8 @@ func (val *Value) BigInt() *big.Int {
|
|||||||
b := new(big.Int).SetBytes(a)
|
b := new(big.Int).SetBytes(a)
|
||||||
|
|
||||||
return b
|
return b
|
||||||
|
} else if a, ok := val.Val.(*big.Int); ok {
|
||||||
|
return a
|
||||||
} else {
|
} else {
|
||||||
return big.NewInt(int64(val.Uint()))
|
return big.NewInt(int64(val.Uint()))
|
||||||
}
|
}
|
||||||
@ -92,6 +99,8 @@ func (val *Value) Str() string {
|
|||||||
return string(a)
|
return string(a)
|
||||||
} else if a, ok := val.Val.(string); ok {
|
} else if a, ok := val.Val.(string); ok {
|
||||||
return a
|
return a
|
||||||
|
} else if a, ok := val.Val.(byte); ok {
|
||||||
|
return string(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
@ -102,7 +111,7 @@ func (val *Value) Bytes() []byte {
|
|||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
return make([]byte, 0)
|
return []byte{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (val *Value) Slice() []interface{} {
|
func (val *Value) Slice() []interface{} {
|
||||||
@ -131,6 +140,19 @@ func (val *Value) SliceFromTo(from, to int) *Value {
|
|||||||
return NewValue(slice[from:to])
|
return NewValue(slice[from:to])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO More type checking methods
|
||||||
|
func (val *Value) IsSlice() bool {
|
||||||
|
return val.Type() == reflect.Slice
|
||||||
|
}
|
||||||
|
|
||||||
|
func (val *Value) IsStr() bool {
|
||||||
|
return val.Type() == reflect.String
|
||||||
|
}
|
||||||
|
|
||||||
|
func (val *Value) IsEmpty() bool {
|
||||||
|
return val.Val == nil || ((val.IsSlice() || val.IsStr()) && val.Len() == 0)
|
||||||
|
}
|
||||||
|
|
||||||
// Threat the value as a slice
|
// Threat the value as a slice
|
||||||
func (val *Value) Get(idx int) *Value {
|
func (val *Value) Get(idx int) *Value {
|
||||||
if d, ok := val.Val.([]interface{}); ok {
|
if d, ok := val.Val.([]interface{}); ok {
|
||||||
@ -140,7 +162,7 @@ func (val *Value) Get(idx int) *Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if idx < 0 {
|
if idx < 0 {
|
||||||
panic("negative idx for Rlp Get")
|
panic("negative idx for Value Get")
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewValue(d[idx])
|
return NewValue(d[idx])
|
||||||
@ -158,9 +180,9 @@ func (val *Value) Encode() []byte {
|
|||||||
return Encode(val.Val)
|
return Encode(val.Val)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewValueFromBytes(rlpData []byte) *Value {
|
func NewValueFromBytes(data []byte) *Value {
|
||||||
if len(rlpData) != 0 {
|
if len(data) != 0 {
|
||||||
data, _ := Decode(rlpData, 0)
|
data, _ := Decode(data, 0)
|
||||||
return NewValue(data)
|
return NewValue(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
52
ethutil/value_test.go
Normal file
52
ethutil/value_test.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package ethutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValueCmp(t *testing.T) {
|
||||||
|
val1 := NewValue("hello")
|
||||||
|
val2 := NewValue("world")
|
||||||
|
if val1.Cmp(val2) {
|
||||||
|
t.Error("Expected values not to be equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
val3 := NewValue("hello")
|
||||||
|
val4 := NewValue("hello")
|
||||||
|
if !val3.Cmp(val4) {
|
||||||
|
t.Error("Expected values to be equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueTypes(t *testing.T) {
|
||||||
|
str := NewValue("str")
|
||||||
|
num := NewValue(1)
|
||||||
|
inter := NewValue([]interface{}{1})
|
||||||
|
byt := NewValue([]byte{1, 2, 3, 4})
|
||||||
|
bigInt := NewValue(big.NewInt(10))
|
||||||
|
|
||||||
|
if str.Str() != "str" {
|
||||||
|
t.Errorf("expected Str to return 'str', got %s", str.Str())
|
||||||
|
}
|
||||||
|
|
||||||
|
if num.Uint() != 1 {
|
||||||
|
t.Errorf("expected Uint to return '1', got %d", num.Uint())
|
||||||
|
}
|
||||||
|
|
||||||
|
interExp := []interface{}{1}
|
||||||
|
if !NewValue(inter.Interface()).Cmp(NewValue(interExp)) {
|
||||||
|
t.Errorf("expected Interface to return '%v', got %v", interExp, num.Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
bytExp := []byte{1, 2, 3, 4}
|
||||||
|
if bytes.Compare(byt.Bytes(), bytExp) != 0 {
|
||||||
|
t.Errorf("expected Bytes to return '%v', got %v", bytExp, byt.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
bigExp := big.NewInt(10)
|
||||||
|
if bigInt.BigInt().Cmp(bigExp) != 0 {
|
||||||
|
t.Errorf("expected BigInt to return '%v', got %v", bigExp, bigInt.BigInt())
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,9 @@ var MagicToken = []byte{34, 64, 8, 145}
|
|||||||
type MsgType byte
|
type MsgType byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// Values are given explicitly instead of by iota because these values are
|
||||||
|
// defined by the wire protocol spec; it is easier for humans to ensure
|
||||||
|
// correctness when values are explicit.
|
||||||
MsgHandshakeTy = 0x00
|
MsgHandshakeTy = 0x00
|
||||||
MsgDiscTy = 0x01
|
MsgDiscTy = 0x01
|
||||||
MsgPingTy = 0x02
|
MsgPingTy = 0x02
|
||||||
|
46
peer.go
46
peer.go
@ -6,7 +6,6 @@ import (
|
|||||||
"github.com/ethereum/eth-go/ethchain"
|
"github.com/ethereum/eth-go/ethchain"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
"github.com/ethereum/eth-go/ethwire"
|
"github.com/ethereum/eth-go/ethwire"
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -23,6 +22,9 @@ const (
|
|||||||
type DiscReason byte
|
type DiscReason byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// Values are given explicitly instead of by iota because these values are
|
||||||
|
// defined by the wire protocol spec; it is easier for humans to ensure
|
||||||
|
// correctness when values are explicit.
|
||||||
DiscReRequested = 0x00
|
DiscReRequested = 0x00
|
||||||
DiscReTcpSysErr = 0x01
|
DiscReTcpSysErr = 0x01
|
||||||
DiscBadProto = 0x02
|
DiscBadProto = 0x02
|
||||||
@ -56,9 +58,9 @@ func (d DiscReason) String() string {
|
|||||||
type Caps byte
|
type Caps byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CapPeerDiscTy = 0x01
|
CapPeerDiscTy = 1 << iota
|
||||||
CapTxTy = 0x02
|
CapTxTy
|
||||||
CapChainTy = 0x04
|
CapChainTy
|
||||||
|
|
||||||
CapDefault = CapChainTy | CapTxTy | CapPeerDiscTy
|
CapDefault = CapChainTy | CapTxTy | CapPeerDiscTy
|
||||||
)
|
)
|
||||||
@ -162,7 +164,7 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
|
|||||||
conn, err := net.DialTimeout("tcp", addr, 30*time.Second)
|
conn, err := net.DialTimeout("tcp", addr, 30*time.Second)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Connection to peer failed", err)
|
ethutil.Config.Log.Debugln("Connection to peer failed", err)
|
||||||
p.Stop()
|
p.Stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -199,7 +201,7 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) {
|
|||||||
|
|
||||||
err := ethwire.WriteMessage(p.conn, msg)
|
err := ethwire.WriteMessage(p.conn, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Can't send message:", err)
|
ethutil.Config.Log.Debugln("Can't send message:", err)
|
||||||
// Stop the client if there was an error writing to it
|
// Stop the client if there was an error writing to it
|
||||||
p.Stop()
|
p.Stop()
|
||||||
return
|
return
|
||||||
@ -261,7 +263,7 @@ func (p *Peer) HandleInbound() {
|
|||||||
// Wait for a message from the peer
|
// Wait for a message from the peer
|
||||||
msgs, err := ethwire.ReadMessages(p.conn)
|
msgs, err := ethwire.ReadMessages(p.conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
ethutil.Config.Log.Debugln(err)
|
||||||
}
|
}
|
||||||
for _, msg := range msgs {
|
for _, msg := range msgs {
|
||||||
switch msg.Type {
|
switch msg.Type {
|
||||||
@ -274,7 +276,7 @@ func (p *Peer) HandleInbound() {
|
|||||||
}
|
}
|
||||||
case ethwire.MsgDiscTy:
|
case ethwire.MsgDiscTy:
|
||||||
p.Stop()
|
p.Stop()
|
||||||
log.Println("Disconnect peer:", DiscReason(msg.Data.Get(0).Uint()))
|
ethutil.Config.Log.Infoln("Disconnect peer:", DiscReason(msg.Data.Get(0).Uint()))
|
||||||
case ethwire.MsgPingTy:
|
case ethwire.MsgPingTy:
|
||||||
// Respond back with pong
|
// Respond back with pong
|
||||||
p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, ""))
|
p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, ""))
|
||||||
@ -285,7 +287,6 @@ func (p *Peer) HandleInbound() {
|
|||||||
p.lastPong = time.Now().Unix()
|
p.lastPong = time.Now().Unix()
|
||||||
case ethwire.MsgBlockTy:
|
case ethwire.MsgBlockTy:
|
||||||
// Get all blocks and process them
|
// Get all blocks and process them
|
||||||
msg.Data = msg.Data
|
|
||||||
var block, lastBlock *ethchain.Block
|
var block, lastBlock *ethchain.Block
|
||||||
var err error
|
var err error
|
||||||
for i := msg.Data.Len() - 1; i >= 0; i-- {
|
for i := msg.Data.Len() - 1; i >= 0; i-- {
|
||||||
@ -293,7 +294,10 @@ func (p *Peer) HandleInbound() {
|
|||||||
err = p.ethereum.BlockManager.ProcessBlock(block)
|
err = p.ethereum.BlockManager.ProcessBlock(block)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("bckmsg", err)
|
if ethutil.Config.Debug {
|
||||||
|
ethutil.Config.Log.Infof("[PEER] Block %x failed\n", block.Hash())
|
||||||
|
ethutil.Config.Log.Infof("[PEER] %v\n", err)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
lastBlock = block
|
lastBlock = block
|
||||||
@ -303,7 +307,7 @@ func (p *Peer) HandleInbound() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// If the parent is unknown try to catch up with this peer
|
// If the parent is unknown try to catch up with this peer
|
||||||
if ethchain.IsParentErr(err) {
|
if ethchain.IsParentErr(err) {
|
||||||
log.Println("Attempting to catch up")
|
ethutil.Config.Log.Infoln("Attempting to catch up")
|
||||||
p.catchingUp = false
|
p.catchingUp = false
|
||||||
p.CatchupWithPeer()
|
p.CatchupWithPeer()
|
||||||
} else if ethchain.IsValidationErr(err) {
|
} else if ethchain.IsValidationErr(err) {
|
||||||
@ -315,7 +319,7 @@ func (p *Peer) HandleInbound() {
|
|||||||
if p.catchingUp && msg.Data.Len() > 1 {
|
if p.catchingUp && msg.Data.Len() > 1 {
|
||||||
if ethutil.Config.Debug && lastBlock != nil {
|
if ethutil.Config.Debug && lastBlock != nil {
|
||||||
blockInfo := lastBlock.BlockInfo()
|
blockInfo := lastBlock.BlockInfo()
|
||||||
log.Printf("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash)
|
ethutil.Config.Log.Infof("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash)
|
||||||
}
|
}
|
||||||
p.catchingUp = false
|
p.catchingUp = false
|
||||||
p.CatchupWithPeer()
|
p.CatchupWithPeer()
|
||||||
@ -386,12 +390,12 @@ func (p *Peer) HandleInbound() {
|
|||||||
p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()}))
|
p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()}))
|
||||||
}
|
}
|
||||||
case ethwire.MsgNotInChainTy:
|
case ethwire.MsgNotInChainTy:
|
||||||
log.Printf("Not in chain %x\n", msg.Data)
|
ethutil.Config.Log.Infof("Not in chain %x\n", msg.Data)
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
// Unofficial but fun nonetheless
|
// Unofficial but fun nonetheless
|
||||||
case ethwire.MsgTalkTy:
|
case ethwire.MsgTalkTy:
|
||||||
log.Printf("%v says: %s\n", p.conn.RemoteAddr(), msg.Data.Str())
|
ethutil.Config.Log.Infoln("%v says: %s\n", p.conn.RemoteAddr(), msg.Data.Str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -434,7 +438,7 @@ func (p *Peer) Start() {
|
|||||||
|
|
||||||
err := p.pushHandshake()
|
err := p.pushHandshake()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Peer can't send outbound version ack", err)
|
ethutil.Config.Log.Debugln("Peer can't send outbound version ack", err)
|
||||||
|
|
||||||
p.Stop()
|
p.Stop()
|
||||||
|
|
||||||
@ -465,7 +469,7 @@ func (p *Peer) pushHandshake() error {
|
|||||||
pubkey := ethutil.NewValueFromBytes(data).Get(2).Bytes()
|
pubkey := ethutil.NewValueFromBytes(data).Get(2).Bytes()
|
||||||
|
|
||||||
msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{
|
msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{
|
||||||
uint32(4), uint32(0), p.Version, byte(p.caps), p.port, pubkey,
|
uint32(5), uint32(0), p.Version, byte(p.caps), p.port, pubkey,
|
||||||
})
|
})
|
||||||
|
|
||||||
p.QueueMessage(msg)
|
p.QueueMessage(msg)
|
||||||
@ -492,8 +496,8 @@ func (p *Peer) pushPeers() {
|
|||||||
func (p *Peer) handleHandshake(msg *ethwire.Msg) {
|
func (p *Peer) handleHandshake(msg *ethwire.Msg) {
|
||||||
c := msg.Data
|
c := msg.Data
|
||||||
|
|
||||||
if c.Get(0).Uint() != 4 {
|
if c.Get(0).Uint() != 5 {
|
||||||
log.Println("Invalid peer version. Require protocol v4")
|
ethutil.Config.Log.Debugln("Invalid peer version. Require protocol v5")
|
||||||
p.Stop()
|
p.Stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -525,7 +529,7 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
|
|||||||
// Get a reference to the peers version
|
// Get a reference to the peers version
|
||||||
p.Version = c.Get(2).Str()
|
p.Version = c.Get(2).Str()
|
||||||
|
|
||||||
log.Println(p)
|
ethutil.Config.Log.Debugln("[PEER]", p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Peer) String() string {
|
func (p *Peer) String() string {
|
||||||
@ -542,7 +546,7 @@ func (p *Peer) String() string {
|
|||||||
strConnectType = "disconnected"
|
strConnectType = "disconnected"
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("peer [%s] (%s) %v %s [%s]", strConnectType, strBoundType, p.conn.RemoteAddr(), p.Version, p.caps)
|
return fmt.Sprintf("[%s] (%s) %v %s [%s]", strConnectType, strBoundType, p.conn.RemoteAddr(), p.Version, p.caps)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,7 +556,7 @@ func (p *Peer) CatchupWithPeer() {
|
|||||||
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash(), uint64(50)})
|
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash(), uint64(50)})
|
||||||
p.QueueMessage(msg)
|
p.QueueMessage(msg)
|
||||||
|
|
||||||
log.Printf("Requesting blockchain %x...\n", p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash()[:4])
|
ethutil.Config.Log.Debugf("Requesting blockchain %x...\n", p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash()[:4])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user