The great merge
This commit is contained in:
12
ethchain/.gitignore
vendored
Normal file
12
ethchain/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
#
|
||||
# If you find yourself ignoring temporary files generated by your text editor
|
||||
# or operating system, you probably want to add a global ignore instead:
|
||||
# git config --global core.excludesfile ~/.gitignore_global
|
||||
|
||||
/tmp
|
||||
*/**/*un~
|
||||
*un~
|
||||
.DS_Store
|
||||
*/**/.DS_Store
|
||||
|
363
ethchain/block.go
Normal file
363
ethchain/block.go
Normal file
@ -0,0 +1,363 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BlockInfo struct {
|
||||
Number uint64
|
||||
Hash []byte
|
||||
Parent []byte
|
||||
}
|
||||
|
||||
func (bi *BlockInfo) RlpDecode(data []byte) {
|
||||
decoder := ethutil.NewValueFromBytes(data)
|
||||
|
||||
bi.Number = decoder.Get(0).Uint()
|
||||
bi.Hash = decoder.Get(1).Bytes()
|
||||
bi.Parent = decoder.Get(2).Bytes()
|
||||
}
|
||||
|
||||
func (bi *BlockInfo) RlpEncode() []byte {
|
||||
return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent})
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
// Hash to the previous block
|
||||
PrevHash []byte
|
||||
// Uncles of this block
|
||||
Uncles []*Block
|
||||
UncleSha []byte
|
||||
// The coin base address
|
||||
Coinbase []byte
|
||||
// Block Trie state
|
||||
state *ethutil.Trie
|
||||
// Difficulty for the current block
|
||||
Difficulty *big.Int
|
||||
// Creation time
|
||||
Time int64
|
||||
// Extra data
|
||||
Extra string
|
||||
// Block Nonce for verification
|
||||
Nonce []byte
|
||||
// List of transactions and/or contracts
|
||||
transactions []*Transaction
|
||||
TxSha []byte
|
||||
}
|
||||
|
||||
// New block takes a raw encoded string
|
||||
// XXX DEPRICATED
|
||||
func NewBlockFromData(raw []byte) *Block {
|
||||
return NewBlockFromBytes(raw)
|
||||
}
|
||||
|
||||
func NewBlockFromBytes(raw []byte) *Block {
|
||||
block := &Block{}
|
||||
block.RlpDecode(raw)
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
// New block takes a raw encoded string
|
||||
func NewBlockFromRlpValue(rlpValue *ethutil.Value) *Block {
|
||||
block := &Block{}
|
||||
block.RlpValueDecode(rlpValue)
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func CreateBlock(root interface{},
|
||||
prevHash []byte,
|
||||
base []byte,
|
||||
Difficulty *big.Int,
|
||||
Nonce []byte,
|
||||
extra string,
|
||||
txes []*Transaction) *Block {
|
||||
|
||||
block := &Block{
|
||||
// Slice of transactions to include in this block
|
||||
transactions: txes,
|
||||
PrevHash: prevHash,
|
||||
Coinbase: base,
|
||||
Difficulty: Difficulty,
|
||||
Nonce: Nonce,
|
||||
Time: time.Now().Unix(),
|
||||
Extra: extra,
|
||||
UncleSha: EmptyShaList,
|
||||
}
|
||||
block.SetTransactions(txes)
|
||||
block.SetUncles([]*Block{})
|
||||
|
||||
block.state = ethutil.NewTrie(ethutil.Config.Db, root)
|
||||
|
||||
for _, tx := range txes {
|
||||
block.MakeContract(tx)
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
// Returns a hash of the block
|
||||
func (block *Block) Hash() []byte {
|
||||
return ethutil.Sha3Bin(block.RlpValue().Encode())
|
||||
}
|
||||
|
||||
func (block *Block) HashNoNonce() []byte {
|
||||
return ethutil.Sha3Bin(ethutil.Encode([]interface{}{block.PrevHash, block.UncleSha, block.Coinbase, block.state.Root, block.TxSha, block.Difficulty, block.Time, block.Extra}))
|
||||
}
|
||||
|
||||
func (block *Block) PrintHash() {
|
||||
fmt.Println(block)
|
||||
fmt.Println(ethutil.NewValue(ethutil.Encode([]interface{}{block.PrevHash, block.UncleSha, block.Coinbase, block.state.Root, block.TxSha, block.Difficulty, block.Time, block.Extra, block.Nonce})))
|
||||
}
|
||||
|
||||
func (block *Block) State() *ethutil.Trie {
|
||||
return block.state
|
||||
}
|
||||
|
||||
func (block *Block) Transactions() []*Transaction {
|
||||
return block.transactions
|
||||
}
|
||||
|
||||
func (block *Block) GetContract(addr []byte) *Contract {
|
||||
data := block.state.Get(string(addr))
|
||||
if data == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
contract := &Contract{}
|
||||
contract.RlpDecode([]byte(data))
|
||||
|
||||
return contract
|
||||
}
|
||||
func (block *Block) UpdateContract(addr []byte, contract *Contract) {
|
||||
// Make sure the state is synced
|
||||
contract.State().Sync()
|
||||
|
||||
block.state.Update(string(addr), string(contract.RlpEncode()))
|
||||
}
|
||||
|
||||
func (block *Block) GetAddr(addr []byte) *Address {
|
||||
var address *Address
|
||||
|
||||
data := block.State().Get(string(addr))
|
||||
if data == "" {
|
||||
address = NewAddress(big.NewInt(0))
|
||||
} else {
|
||||
address = NewAddressFromData([]byte(data))
|
||||
}
|
||||
|
||||
return address
|
||||
}
|
||||
func (block *Block) UpdateAddr(addr []byte, address *Address) {
|
||||
block.state.Update(string(addr), string(address.RlpEncode()))
|
||||
}
|
||||
|
||||
func (block *Block) PayFee(addr []byte, fee *big.Int) bool {
|
||||
contract := block.GetContract(addr)
|
||||
// If we can't pay the fee return
|
||||
if contract == nil || contract.Amount.Cmp(fee) < 0 /* amount < fee */ {
|
||||
fmt.Println("Contract has insufficient funds", contract.Amount, fee)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
base := new(big.Int)
|
||||
contract.Amount = base.Sub(contract.Amount, fee)
|
||||
block.state.Update(string(addr), string(contract.RlpEncode()))
|
||||
|
||||
data := block.state.Get(string(block.Coinbase))
|
||||
|
||||
// Get the ether (Coinbase) and add the fee (gief fee to miner)
|
||||
ether := NewAddressFromData([]byte(data))
|
||||
|
||||
base = new(big.Int)
|
||||
ether.Amount = base.Add(ether.Amount, fee)
|
||||
|
||||
block.state.Update(string(block.Coinbase), string(ether.RlpEncode()))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (block *Block) BlockInfo() BlockInfo {
|
||||
bi := BlockInfo{}
|
||||
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
|
||||
bi.RlpDecode(data)
|
||||
|
||||
return bi
|
||||
}
|
||||
|
||||
func (block *Block) MakeContract(tx *Transaction) {
|
||||
// Create contract if there's no recipient
|
||||
if tx.IsContract() {
|
||||
addr := tx.Hash()
|
||||
|
||||
value := tx.Value
|
||||
contract := NewContract(value, []byte(""))
|
||||
block.state.Update(string(addr), string(contract.RlpEncode()))
|
||||
for i, val := range tx.Data {
|
||||
contract.state.Update(string(ethutil.NumberToBytes(uint64(i), 32)), val)
|
||||
}
|
||||
block.UpdateContract(addr, contract)
|
||||
}
|
||||
}
|
||||
|
||||
/////// Block Encoding
|
||||
func (block *Block) encodedUncles() interface{} {
|
||||
uncles := make([]interface{}, len(block.Uncles))
|
||||
for i, uncle := range block.Uncles {
|
||||
uncles[i] = uncle.RlpEncode()
|
||||
}
|
||||
|
||||
return uncles
|
||||
}
|
||||
|
||||
func (block *Block) encodedTxs() interface{} {
|
||||
// Marshal the transactions of this block
|
||||
encTx := make([]interface{}, len(block.transactions))
|
||||
for i, tx := range block.transactions {
|
||||
// Cast it to a string (safe)
|
||||
encTx[i] = tx.RlpData()
|
||||
}
|
||||
|
||||
return encTx
|
||||
}
|
||||
|
||||
func (block *Block) rlpTxs() interface{} {
|
||||
// Marshal the transactions of this block
|
||||
encTx := make([]interface{}, len(block.transactions))
|
||||
for i, tx := range block.transactions {
|
||||
// Cast it to a string (safe)
|
||||
encTx[i] = tx.RlpData()
|
||||
}
|
||||
|
||||
return encTx
|
||||
}
|
||||
|
||||
func (block *Block) rlpUncles() interface{} {
|
||||
// Marshal the transactions of this block
|
||||
uncles := make([]interface{}, len(block.Uncles))
|
||||
for i, uncle := range block.Uncles {
|
||||
// Cast it to a string (safe)
|
||||
uncles[i] = uncle.header()
|
||||
}
|
||||
|
||||
return uncles
|
||||
}
|
||||
|
||||
func (block *Block) SetUncles(uncles []*Block) {
|
||||
block.Uncles = uncles
|
||||
|
||||
// Sha of the concatenated uncles
|
||||
block.UncleSha = ethutil.Sha3Bin(ethutil.Encode(block.rlpUncles()))
|
||||
}
|
||||
|
||||
func (block *Block) SetTransactions(txs []*Transaction) {
|
||||
block.transactions = txs
|
||||
|
||||
block.TxSha = ethutil.Sha3Bin(ethutil.Encode(block.rlpTxs()))
|
||||
}
|
||||
|
||||
func (block *Block) RlpValue() *ethutil.RlpValue {
|
||||
return ethutil.NewRlpValue([]interface{}{block.header(), block.rlpTxs(), block.rlpUncles()})
|
||||
}
|
||||
|
||||
func (block *Block) RlpEncode() []byte {
|
||||
// Encode a slice interface which contains the header and the list of
|
||||
// transactions.
|
||||
return block.RlpValue().Encode()
|
||||
}
|
||||
|
||||
func (block *Block) RlpDecode(data []byte) {
|
||||
rlpValue := ethutil.NewValueFromBytes(data)
|
||||
block.RlpValueDecode(rlpValue)
|
||||
}
|
||||
|
||||
func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
|
||||
header := decoder.Get(0)
|
||||
|
||||
block.PrevHash = header.Get(0).Bytes()
|
||||
block.UncleSha = header.Get(1).Bytes()
|
||||
block.Coinbase = header.Get(2).Bytes()
|
||||
block.state = ethutil.NewTrie(ethutil.Config.Db, header.Get(3).Val)
|
||||
block.TxSha = header.Get(4).Bytes()
|
||||
block.Difficulty = header.Get(5).BigInt()
|
||||
block.Time = int64(header.Get(6).BigInt().Uint64())
|
||||
block.Extra = header.Get(7).Str()
|
||||
block.Nonce = header.Get(8).Bytes()
|
||||
|
||||
// Tx list might be empty if this is an uncle. Uncles only have their
|
||||
// header set.
|
||||
if decoder.Get(1).IsNil() == false { // Yes explicitness
|
||||
txes := decoder.Get(1)
|
||||
block.transactions = make([]*Transaction, txes.Len())
|
||||
for i := 0; i < txes.Len(); i++ {
|
||||
tx := NewTransactionFromValue(txes.Get(i))
|
||||
|
||||
block.transactions[i] = tx
|
||||
|
||||
/*
|
||||
if ethutil.Config.Debug {
|
||||
ethutil.Config.Db.Put(tx.Hash(), ethutil.Encode(tx))
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if decoder.Get(2).IsNil() == false { // Yes explicitness
|
||||
uncles := decoder.Get(2)
|
||||
block.Uncles = make([]*Block, uncles.Len())
|
||||
for i := 0; i < uncles.Len(); i++ {
|
||||
block.Uncles[i] = NewUncleBlockFromValue(uncles.Get(i))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func NewUncleBlockFromValue(header *ethutil.Value) *Block {
|
||||
block := &Block{}
|
||||
|
||||
block.PrevHash = header.Get(0).Bytes()
|
||||
block.UncleSha = header.Get(1).Bytes()
|
||||
block.Coinbase = header.Get(2).Bytes()
|
||||
block.state = ethutil.NewTrie(ethutil.Config.Db, header.Get(3).Val)
|
||||
block.TxSha = header.Get(4).Bytes()
|
||||
block.Difficulty = header.Get(5).BigInt()
|
||||
block.Time = int64(header.Get(6).BigInt().Uint64())
|
||||
block.Extra = header.Get(7).Str()
|
||||
block.Nonce = header.Get(8).Bytes()
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func (block *Block) String() string {
|
||||
return fmt.Sprintf("Block(%x):\nPrevHash:%x\nUncleSha:%x\nCoinbase:%x\nRoot:%x\nTxSha:%x\nDiff:%v\nTime:%d\nNonce:%x", block.Hash(), block.PrevHash, block.UncleSha, block.Coinbase, block.state.Root, block.TxSha, block.Difficulty, block.Time, block.Nonce)
|
||||
}
|
||||
|
||||
//////////// UNEXPORTED /////////////////
|
||||
func (block *Block) header() []interface{} {
|
||||
return []interface{}{
|
||||
// Sha of the previous block
|
||||
block.PrevHash,
|
||||
// Sha of uncles
|
||||
block.UncleSha,
|
||||
// Coinbase address
|
||||
block.Coinbase,
|
||||
// root state
|
||||
block.state.Root,
|
||||
// Sha of tx
|
||||
block.TxSha,
|
||||
// Current block Difficulty
|
||||
block.Difficulty,
|
||||
// Time the block was found?
|
||||
block.Time,
|
||||
// Extra data
|
||||
block.Extra,
|
||||
// Block's Nonce for validation
|
||||
block.Nonce,
|
||||
}
|
||||
}
|
184
ethchain/block_chain.go
Normal file
184
ethchain/block_chain.go
Normal file
@ -0,0 +1,184 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"log"
|
||||
"math"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type BlockChain struct {
|
||||
// The famous, the fabulous Mister GENESIIIIIIS (block)
|
||||
genesisBlock *Block
|
||||
// Last known total difficulty
|
||||
TD *big.Int
|
||||
|
||||
LastBlockNumber uint64
|
||||
|
||||
CurrentBlock *Block
|
||||
LastBlockHash []byte
|
||||
}
|
||||
|
||||
func NewBlockChain() *BlockChain {
|
||||
bc := &BlockChain{}
|
||||
bc.genesisBlock = NewBlockFromData(ethutil.Encode(Genesis))
|
||||
|
||||
bc.setLastBlock()
|
||||
|
||||
return bc
|
||||
}
|
||||
|
||||
func (bc *BlockChain) Genesis() *Block {
|
||||
return bc.genesisBlock
|
||||
}
|
||||
|
||||
func (bc *BlockChain) NewBlock(coinbase []byte, txs []*Transaction) *Block {
|
||||
var root interface{}
|
||||
var lastBlockTime int64
|
||||
hash := ZeroHash256
|
||||
|
||||
if bc.CurrentBlock != nil {
|
||||
root = bc.CurrentBlock.State().Root
|
||||
hash = bc.LastBlockHash
|
||||
lastBlockTime = bc.CurrentBlock.Time
|
||||
}
|
||||
|
||||
block := CreateBlock(
|
||||
root,
|
||||
hash,
|
||||
coinbase,
|
||||
ethutil.BigPow(2, 32),
|
||||
nil,
|
||||
"",
|
||||
txs)
|
||||
|
||||
if bc.CurrentBlock != nil {
|
||||
var mul *big.Int
|
||||
if block.Time < lastBlockTime+42 {
|
||||
mul = big.NewInt(1)
|
||||
} else {
|
||||
mul = big.NewInt(-1)
|
||||
}
|
||||
|
||||
diff := new(big.Int)
|
||||
diff.Add(diff, bc.CurrentBlock.Difficulty)
|
||||
diff.Div(diff, big.NewInt(1024))
|
||||
diff.Mul(diff, mul)
|
||||
diff.Add(diff, bc.CurrentBlock.Difficulty)
|
||||
block.Difficulty = diff
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func (bc *BlockChain) HasBlock(hash []byte) bool {
|
||||
data, _ := ethutil.Config.Db.Get(hash)
|
||||
return len(data) != 0
|
||||
}
|
||||
|
||||
func (bc *BlockChain) GenesisBlock() *Block {
|
||||
return bc.genesisBlock
|
||||
}
|
||||
|
||||
// Get chain return blocks from hash up to max in RLP format
|
||||
func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
|
||||
var chain []interface{}
|
||||
// Get the current hash to start with
|
||||
currentHash := bc.CurrentBlock.Hash()
|
||||
// Get the last number on the block chain
|
||||
lastNumber := bc.BlockInfo(bc.CurrentBlock).Number
|
||||
// Get the parents number
|
||||
parentNumber := bc.BlockInfoByHash(hash).Number
|
||||
// Get the min amount. We might not have max amount of blocks
|
||||
count := uint64(math.Min(float64(lastNumber-parentNumber), float64(max)))
|
||||
startNumber := parentNumber + count
|
||||
|
||||
num := lastNumber
|
||||
for ; num > startNumber; currentHash = bc.GetBlock(currentHash).PrevHash {
|
||||
num--
|
||||
}
|
||||
for i := uint64(0); bytes.Compare(currentHash, hash) != 0 && num >= parentNumber && i < count; i++ {
|
||||
// Get the block of the chain
|
||||
block := bc.GetBlock(currentHash)
|
||||
currentHash = block.PrevHash
|
||||
|
||||
chain = append(chain, block.RlpValue().Value)
|
||||
//chain = append([]interface{}{block.RlpValue().Value}, chain...)
|
||||
|
||||
num--
|
||||
}
|
||||
|
||||
return chain
|
||||
}
|
||||
|
||||
func (bc *BlockChain) setLastBlock() {
|
||||
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
|
||||
if len(data) != 0 {
|
||||
block := NewBlockFromBytes(data)
|
||||
info := bc.BlockInfo(block)
|
||||
bc.CurrentBlock = block
|
||||
bc.LastBlockHash = block.Hash()
|
||||
bc.LastBlockNumber = info.Number
|
||||
|
||||
log.Printf("[CHAIN] Last known block height #%d\n", bc.LastBlockNumber)
|
||||
}
|
||||
|
||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
||||
}
|
||||
|
||||
func (bc *BlockChain) SetTotalDifficulty(td *big.Int) {
|
||||
ethutil.Config.Db.Put([]byte("LastKnownTotalDifficulty"), td.Bytes())
|
||||
bc.TD = td
|
||||
}
|
||||
|
||||
// Add a block to the chain and record addition information
|
||||
func (bc *BlockChain) Add(block *Block) {
|
||||
bc.writeBlockInfo(block)
|
||||
|
||||
// Prepare the genesis block
|
||||
bc.CurrentBlock = block
|
||||
bc.LastBlockHash = block.Hash()
|
||||
|
||||
ethutil.Config.Db.Put(block.Hash(), block.RlpEncode())
|
||||
}
|
||||
|
||||
func (bc *BlockChain) GetBlock(hash []byte) *Block {
|
||||
data, _ := ethutil.Config.Db.Get(hash)
|
||||
|
||||
return NewBlockFromData(data)
|
||||
}
|
||||
|
||||
func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo {
|
||||
bi := BlockInfo{}
|
||||
data, _ := ethutil.Config.Db.Get(append(hash, []byte("Info")...))
|
||||
bi.RlpDecode(data)
|
||||
|
||||
return bi
|
||||
}
|
||||
|
||||
func (bc *BlockChain) BlockInfo(block *Block) BlockInfo {
|
||||
bi := BlockInfo{}
|
||||
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
|
||||
bi.RlpDecode(data)
|
||||
|
||||
return bi
|
||||
}
|
||||
|
||||
// Unexported method for writing extra non-essential block info to the db
|
||||
func (bc *BlockChain) writeBlockInfo(block *Block) {
|
||||
bc.LastBlockNumber++
|
||||
bi := BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash}
|
||||
|
||||
// For now we use the block hash with the words "info" appended as key
|
||||
ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
|
||||
}
|
||||
|
||||
func (bc *BlockChain) Stop() {
|
||||
if bc.CurrentBlock != nil {
|
||||
ethutil.Config.Db.Put([]byte("LastBlock"), bc.CurrentBlock.RlpEncode())
|
||||
|
||||
log.Println("[CHAIN] Stopped")
|
||||
}
|
||||
}
|
627
ethchain/block_manager.go
Normal file
627
ethchain/block_manager.go
Normal file
@ -0,0 +1,627 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/obscuren/secp256k1-go"
|
||||
"log"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BlockProcessor interface {
|
||||
ProcessBlock(block *Block)
|
||||
}
|
||||
|
||||
func CalculateBlockReward(block *Block, uncleLength int) *big.Int {
|
||||
return BlockReward
|
||||
}
|
||||
|
||||
type BlockManager struct {
|
||||
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
||||
mutex sync.Mutex
|
||||
|
||||
// The block chain :)
|
||||
bc *BlockChain
|
||||
|
||||
// Stack for processing contracts
|
||||
stack *Stack
|
||||
// non-persistent key/value memory storage
|
||||
mem map[string]*big.Int
|
||||
|
||||
TransactionPool *TxPool
|
||||
|
||||
Pow PoW
|
||||
|
||||
Speaker PublicSpeaker
|
||||
|
||||
SecondaryBlockProcessor BlockProcessor
|
||||
}
|
||||
|
||||
func AddTestNetFunds(block *Block) {
|
||||
for _, addr := range []string{
|
||||
"8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin
|
||||
"93658b04240e4bd4046fd2d6d417d20f146f4b43", // Jeffrey
|
||||
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
|
||||
"80c01a26338f0d905e295fccb71fa9ea849ffa12", // Alex
|
||||
} {
|
||||
//log.Println("2^200 Wei to", addr)
|
||||
codedAddr, _ := hex.DecodeString(addr)
|
||||
addr := block.GetAddr(codedAddr)
|
||||
addr.Amount = ethutil.BigPow(2, 200)
|
||||
block.UpdateAddr(codedAddr, addr)
|
||||
}
|
||||
}
|
||||
|
||||
func NewBlockManager(speaker PublicSpeaker) *BlockManager {
|
||||
bm := &BlockManager{
|
||||
//server: s,
|
||||
bc: NewBlockChain(),
|
||||
stack: NewStack(),
|
||||
mem: make(map[string]*big.Int),
|
||||
Pow: &EasyPow{},
|
||||
Speaker: speaker,
|
||||
}
|
||||
|
||||
if bm.bc.CurrentBlock == nil {
|
||||
AddTestNetFunds(bm.bc.genesisBlock)
|
||||
// Prepare the genesis block
|
||||
//bm.bc.genesisBlock.State().Sync()
|
||||
bm.bc.Add(bm.bc.genesisBlock)
|
||||
log.Println(bm.bc.genesisBlock)
|
||||
|
||||
log.Printf("Genesis: %x\n", bm.bc.genesisBlock.Hash())
|
||||
//log.Printf("root %x\n", bm.bc.genesisBlock.State().Root)
|
||||
//bm.bc.genesisBlock.PrintHash()
|
||||
}
|
||||
|
||||
return bm
|
||||
}
|
||||
|
||||
func (bm *BlockManager) BlockChain() *BlockChain {
|
||||
return bm.bc
|
||||
}
|
||||
|
||||
func (bm *BlockManager) ApplyTransactions(block *Block, txs []*Transaction) {
|
||||
// Process each transaction/contract
|
||||
for _, tx := range txs {
|
||||
// If there's no recipient, it's a contract
|
||||
if tx.IsContract() {
|
||||
block.MakeContract(tx)
|
||||
bm.ProcessContract(tx, block)
|
||||
} else {
|
||||
bm.TransactionPool.ProcessTransaction(tx, block)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Block processing and validating with a given (temporarily) state
|
||||
func (bm *BlockManager) ProcessBlock(block *Block) error {
|
||||
// Processing a blocks may never happen simultaneously
|
||||
bm.mutex.Lock()
|
||||
defer bm.mutex.Unlock()
|
||||
|
||||
hash := block.Hash()
|
||||
|
||||
if bm.bc.HasBlock(hash) {
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
if ethutil.Config.Debug {
|
||||
log.Printf("[BMGR] Processing block(%x)\n", hash)
|
||||
}
|
||||
*/
|
||||
|
||||
// Check if we have the parent hash, if it isn't known we discard it
|
||||
// Reasons might be catching up or simply an invalid block
|
||||
if !bm.bc.HasBlock(block.PrevHash) && bm.bc.CurrentBlock != nil {
|
||||
return ParentError(block.PrevHash)
|
||||
}
|
||||
|
||||
// Process the transactions on to current block
|
||||
bm.ApplyTransactions(bm.bc.CurrentBlock, block.Transactions())
|
||||
|
||||
// Block validation
|
||||
if err := bm.ValidateBlock(block); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// I'm not sure, but I don't know if there should be thrown
|
||||
// any errors at this time.
|
||||
if err := bm.AccumelateRewards(bm.bc.CurrentBlock, block); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !block.State().Cmp(bm.bc.CurrentBlock.State()) {
|
||||
//if block.State().Root != state.Root {
|
||||
return fmt.Errorf("Invalid merkle root. Expected %x, got %x", block.State().Root, bm.bc.CurrentBlock.State().Root)
|
||||
}
|
||||
|
||||
// Calculate the new total difficulty and sync back to the db
|
||||
if bm.CalculateTD(block) {
|
||||
// Sync the current block's state to the database
|
||||
bm.bc.CurrentBlock.State().Sync()
|
||||
// Add the block to the chain
|
||||
bm.bc.Add(block)
|
||||
|
||||
/*
|
||||
ethutil.Config.Db.Put(block.Hash(), block.RlpEncode())
|
||||
bm.bc.CurrentBlock = block
|
||||
bm.LastBlockHash = block.Hash()
|
||||
bm.writeBlockInfo(block)
|
||||
*/
|
||||
|
||||
/*
|
||||
txs := bm.TransactionPool.Flush()
|
||||
var coded = []interface{}{}
|
||||
for _, tx := range txs {
|
||||
err := bm.TransactionPool.ValidateTransaction(tx)
|
||||
if err == nil {
|
||||
coded = append(coded, tx.RlpEncode())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Broadcast the valid block back to the wire
|
||||
//bm.Speaker.Broadcast(ethwire.MsgBlockTy, []interface{}{block.RlpValue().Value})
|
||||
|
||||
// If there's a block processor present, pass in the block for further
|
||||
// processing
|
||||
if bm.SecondaryBlockProcessor != nil {
|
||||
bm.SecondaryBlockProcessor.ProcessBlock(block)
|
||||
}
|
||||
|
||||
log.Printf("[BMGR] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash())
|
||||
} else {
|
||||
fmt.Println("total diff failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bm *BlockManager) CalculateTD(block *Block) bool {
|
||||
uncleDiff := new(big.Int)
|
||||
for _, uncle := range block.Uncles {
|
||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
||||
}
|
||||
|
||||
// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
|
||||
td := new(big.Int)
|
||||
td = td.Add(bm.bc.TD, uncleDiff)
|
||||
td = td.Add(td, block.Difficulty)
|
||||
|
||||
// The new TD will only be accepted if the new difficulty is
|
||||
// is greater than the previous.
|
||||
if td.Cmp(bm.bc.TD) > 0 {
|
||||
// Set the new total difficulty back to the block chain
|
||||
bm.bc.SetTotalDifficulty(td)
|
||||
|
||||
/*
|
||||
if ethutil.Config.Debug {
|
||||
log.Println("[BMGR] TD(block) =", td)
|
||||
}
|
||||
*/
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Validates the current block. Returns an error if the block was invalid,
|
||||
// an uncle or anything that isn't on the current block chain.
|
||||
// Validation validates easy over difficult (dagger takes longer time = difficult)
|
||||
func (bm *BlockManager) ValidateBlock(block *Block) error {
|
||||
// TODO
|
||||
// 2. Check if the difficulty is correct
|
||||
|
||||
// Check each uncle's previous hash. In order for it to be valid
|
||||
// is if it has the same block hash as the current
|
||||
previousBlock := bm.bc.GetBlock(block.PrevHash)
|
||||
for _, uncle := range block.Uncles {
|
||||
if bytes.Compare(uncle.PrevHash, previousBlock.PrevHash) != 0 {
|
||||
return ValidationError("Mismatch uncle's previous hash. Expected %x, got %x", previousBlock.PrevHash, uncle.PrevHash)
|
||||
}
|
||||
}
|
||||
|
||||
diff := block.Time - bm.bc.CurrentBlock.Time
|
||||
if diff < 0 {
|
||||
return ValidationError("Block timestamp less then prev block %v", diff)
|
||||
}
|
||||
|
||||
// New blocks must be within the 15 minute range of the last block.
|
||||
if diff > int64(15*time.Minute) {
|
||||
return ValidationError("Block is too far in the future of last block (> 15 minutes)")
|
||||
}
|
||||
|
||||
// Verify the nonce of the block. Return an error if it's not valid
|
||||
if !bm.Pow.Verify(block.HashNoNonce(), block.Difficulty, block.Nonce) {
|
||||
return ValidationError("Block's nonce is invalid (= %v)", block.Nonce)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bm *BlockManager) AccumelateRewards(processor *Block, block *Block) error {
|
||||
// Get the coinbase rlp data
|
||||
addr := processor.GetAddr(block.Coinbase)
|
||||
// Reward amount of ether to the coinbase address
|
||||
addr.AddFee(CalculateBlockReward(block, len(block.Uncles)))
|
||||
|
||||
processor.UpdateAddr(block.Coinbase, addr)
|
||||
|
||||
// TODO Reward each uncle
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bm *BlockManager) Stop() {
|
||||
bm.bc.Stop()
|
||||
}
|
||||
|
||||
func (bm *BlockManager) ProcessContract(tx *Transaction, block *Block) {
|
||||
// Recovering function in case the VM had any errors
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("Recovered from VM execution with err =", r)
|
||||
}
|
||||
}()
|
||||
|
||||
// Process contract
|
||||
bm.ProcContract(tx, block, func(opType OpType) bool {
|
||||
// TODO turn on once big ints are in place
|
||||
//if !block.PayFee(tx.Hash(), StepFee.Uint64()) {
|
||||
// return false
|
||||
//}
|
||||
|
||||
return true // Continue
|
||||
})
|
||||
}
|
||||
|
||||
// Contract evaluation is done here.
|
||||
func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallback) {
|
||||
|
||||
// Instruction pointer
|
||||
pc := 0
|
||||
blockInfo := bm.bc.BlockInfo(block)
|
||||
|
||||
contract := block.GetContract(tx.Hash())
|
||||
if contract == nil {
|
||||
fmt.Println("Contract not found")
|
||||
return
|
||||
}
|
||||
|
||||
Pow256 := ethutil.BigPow(2, 256)
|
||||
|
||||
if ethutil.Config.Debug {
|
||||
fmt.Printf("# op arg\n")
|
||||
}
|
||||
out:
|
||||
for {
|
||||
// The base big int for all calculations. Use this for any results.
|
||||
base := new(big.Int)
|
||||
// XXX Should Instr return big int slice instead of string slice?
|
||||
// Get the next instruction from the contract
|
||||
//op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc)))))
|
||||
nb := ethutil.NumberToBytes(uint64(pc), 32)
|
||||
o, _, _ := ethutil.Instr(contract.State().Get(string(nb)))
|
||||
op := OpCode(o)
|
||||
|
||||
if !cb(0) {
|
||||
break
|
||||
}
|
||||
|
||||
if ethutil.Config.Debug {
|
||||
fmt.Printf("%-3d %-4s\n", pc, op.String())
|
||||
}
|
||||
|
||||
switch op {
|
||||
case oSTOP:
|
||||
break out
|
||||
case oADD:
|
||||
x, y := bm.stack.Popn()
|
||||
// (x + y) % 2 ** 256
|
||||
base.Add(x, y)
|
||||
base.Mod(base, Pow256)
|
||||
// Pop result back on the stack
|
||||
bm.stack.Push(base)
|
||||
case oSUB:
|
||||
x, y := bm.stack.Popn()
|
||||
// (x - y) % 2 ** 256
|
||||
base.Sub(x, y)
|
||||
base.Mod(base, Pow256)
|
||||
// Pop result back on the stack
|
||||
bm.stack.Push(base)
|
||||
case oMUL:
|
||||
x, y := bm.stack.Popn()
|
||||
// (x * y) % 2 ** 256
|
||||
base.Mul(x, y)
|
||||
base.Mod(base, Pow256)
|
||||
// Pop result back on the stack
|
||||
bm.stack.Push(base)
|
||||
case oDIV:
|
||||
x, y := bm.stack.Popn()
|
||||
// floor(x / y)
|
||||
base.Div(x, y)
|
||||
// Pop result back on the stack
|
||||
bm.stack.Push(base)
|
||||
case oSDIV:
|
||||
x, y := bm.stack.Popn()
|
||||
// n > 2**255
|
||||
if x.Cmp(Pow256) > 0 {
|
||||
x.Sub(Pow256, x)
|
||||
}
|
||||
if y.Cmp(Pow256) > 0 {
|
||||
y.Sub(Pow256, y)
|
||||
}
|
||||
z := new(big.Int)
|
||||
z.Div(x, y)
|
||||
if z.Cmp(Pow256) > 0 {
|
||||
z.Sub(Pow256, z)
|
||||
}
|
||||
// Push result on to the stack
|
||||
bm.stack.Push(z)
|
||||
case oMOD:
|
||||
x, y := bm.stack.Popn()
|
||||
base.Mod(x, y)
|
||||
bm.stack.Push(base)
|
||||
case oSMOD:
|
||||
x, y := bm.stack.Popn()
|
||||
// n > 2**255
|
||||
if x.Cmp(Pow256) > 0 {
|
||||
x.Sub(Pow256, x)
|
||||
}
|
||||
if y.Cmp(Pow256) > 0 {
|
||||
y.Sub(Pow256, y)
|
||||
}
|
||||
z := new(big.Int)
|
||||
z.Mod(x, y)
|
||||
if z.Cmp(Pow256) > 0 {
|
||||
z.Sub(Pow256, z)
|
||||
}
|
||||
// Push result on to the stack
|
||||
bm.stack.Push(z)
|
||||
case oEXP:
|
||||
x, y := bm.stack.Popn()
|
||||
base.Exp(x, y, Pow256)
|
||||
|
||||
bm.stack.Push(base)
|
||||
case oNEG:
|
||||
base.Sub(Pow256, bm.stack.Pop())
|
||||
bm.stack.Push(base)
|
||||
case oLT:
|
||||
x, y := bm.stack.Popn()
|
||||
// x < y
|
||||
if x.Cmp(y) < 0 {
|
||||
bm.stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
bm.stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oLE:
|
||||
x, y := bm.stack.Popn()
|
||||
// x <= y
|
||||
if x.Cmp(y) < 1 {
|
||||
bm.stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
bm.stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oGT:
|
||||
x, y := bm.stack.Popn()
|
||||
// x > y
|
||||
if x.Cmp(y) > 0 {
|
||||
bm.stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
bm.stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oGE:
|
||||
x, y := bm.stack.Popn()
|
||||
// x >= y
|
||||
if x.Cmp(y) > -1 {
|
||||
bm.stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
bm.stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oNOT:
|
||||
x, y := bm.stack.Popn()
|
||||
// x != y
|
||||
if x.Cmp(y) != 0 {
|
||||
bm.stack.Push(ethutil.BigTrue)
|
||||
} else {
|
||||
bm.stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
|
||||
// Please note that the following code contains some
|
||||
// ugly string casting. This will have to change to big
|
||||
// ints. TODO :)
|
||||
case oMYADDRESS:
|
||||
bm.stack.Push(ethutil.BigD(tx.Hash()))
|
||||
case oTXSENDER:
|
||||
bm.stack.Push(ethutil.BigD(tx.Sender()))
|
||||
case oTXVALUE:
|
||||
bm.stack.Push(tx.Value)
|
||||
case oTXDATAN:
|
||||
bm.stack.Push(big.NewInt(int64(len(tx.Data))))
|
||||
case oTXDATA:
|
||||
v := bm.stack.Pop()
|
||||
// v >= len(data)
|
||||
if v.Cmp(big.NewInt(int64(len(tx.Data)))) >= 0 {
|
||||
bm.stack.Push(ethutil.Big("0"))
|
||||
} else {
|
||||
bm.stack.Push(ethutil.Big(tx.Data[v.Uint64()]))
|
||||
}
|
||||
case oBLK_PREVHASH:
|
||||
bm.stack.Push(ethutil.BigD(block.PrevHash))
|
||||
case oBLK_COINBASE:
|
||||
bm.stack.Push(ethutil.BigD(block.Coinbase))
|
||||
case oBLK_TIMESTAMP:
|
||||
bm.stack.Push(big.NewInt(block.Time))
|
||||
case oBLK_NUMBER:
|
||||
bm.stack.Push(big.NewInt(int64(blockInfo.Number)))
|
||||
case oBLK_DIFFICULTY:
|
||||
bm.stack.Push(block.Difficulty)
|
||||
case oBASEFEE:
|
||||
// e = 10^21
|
||||
e := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(21), big.NewInt(0))
|
||||
d := new(big.Rat)
|
||||
d.SetInt(block.Difficulty)
|
||||
c := new(big.Rat)
|
||||
c.SetFloat64(0.5)
|
||||
// d = diff / 0.5
|
||||
d.Quo(d, c)
|
||||
// base = floor(d)
|
||||
base.Div(d.Num(), d.Denom())
|
||||
|
||||
x := new(big.Int)
|
||||
x.Div(e, base)
|
||||
|
||||
// x = floor(10^21 / floor(diff^0.5))
|
||||
bm.stack.Push(x)
|
||||
case oSHA256, oSHA3, oRIPEMD160:
|
||||
// This is probably save
|
||||
// ceil(pop / 32)
|
||||
length := int(math.Ceil(float64(bm.stack.Pop().Uint64()) / 32.0))
|
||||
// New buffer which will contain the concatenated popped items
|
||||
data := new(bytes.Buffer)
|
||||
for i := 0; i < length; i++ {
|
||||
// Encode the number to bytes and have it 32bytes long
|
||||
num := ethutil.NumberToBytes(bm.stack.Pop().Bytes(), 256)
|
||||
data.WriteString(string(num))
|
||||
}
|
||||
|
||||
if op == oSHA256 {
|
||||
bm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes())))
|
||||
} else if op == oSHA3 {
|
||||
bm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes())))
|
||||
} else {
|
||||
bm.stack.Push(base.SetBytes(ethutil.Ripemd160(data.Bytes())))
|
||||
}
|
||||
case oECMUL:
|
||||
y := bm.stack.Pop()
|
||||
x := bm.stack.Pop()
|
||||
//n := bm.stack.Pop()
|
||||
|
||||
//if ethutil.Big(x).Cmp(ethutil.Big(y)) {
|
||||
data := new(bytes.Buffer)
|
||||
data.WriteString(x.String())
|
||||
data.WriteString(y.String())
|
||||
if secp256k1.VerifyPubkeyValidity(data.Bytes()) == 1 {
|
||||
// TODO
|
||||
} else {
|
||||
// Invalid, push infinity
|
||||
bm.stack.Push(ethutil.Big("0"))
|
||||
bm.stack.Push(ethutil.Big("0"))
|
||||
}
|
||||
//} else {
|
||||
// // Invalid, push infinity
|
||||
// bm.stack.Push("0")
|
||||
// bm.stack.Push("0")
|
||||
//}
|
||||
|
||||
case oECADD:
|
||||
case oECSIGN:
|
||||
case oECRECOVER:
|
||||
case oECVALID:
|
||||
case oPUSH:
|
||||
pc++
|
||||
bm.stack.Push(bm.mem[strconv.Itoa(pc)])
|
||||
case oPOP:
|
||||
// Pop current value of the stack
|
||||
bm.stack.Pop()
|
||||
case oDUP:
|
||||
// Dup top stack
|
||||
x := bm.stack.Pop()
|
||||
bm.stack.Push(x)
|
||||
bm.stack.Push(x)
|
||||
case oSWAP:
|
||||
// Swap two top most values
|
||||
x, y := bm.stack.Popn()
|
||||
bm.stack.Push(y)
|
||||
bm.stack.Push(x)
|
||||
case oMLOAD:
|
||||
x := bm.stack.Pop()
|
||||
bm.stack.Push(bm.mem[x.String()])
|
||||
case oMSTORE:
|
||||
x, y := bm.stack.Popn()
|
||||
bm.mem[x.String()] = y
|
||||
case oSLOAD:
|
||||
// Load the value in storage and push it on the stack
|
||||
x := bm.stack.Pop()
|
||||
// decode the object as a big integer
|
||||
decoder := ethutil.NewRlpValueFromBytes([]byte(contract.State().Get(x.String())))
|
||||
if !decoder.IsNil() {
|
||||
bm.stack.Push(decoder.AsBigInt())
|
||||
} else {
|
||||
bm.stack.Push(ethutil.BigFalse)
|
||||
}
|
||||
case oSSTORE:
|
||||
// Store Y at index X
|
||||
x, y := bm.stack.Popn()
|
||||
contract.State().Update(x.String(), string(ethutil.Encode(y)))
|
||||
case oJMP:
|
||||
x := int(bm.stack.Pop().Uint64())
|
||||
// Set pc to x - 1 (minus one so the incrementing at the end won't effect it)
|
||||
pc = x
|
||||
pc--
|
||||
case oJMPI:
|
||||
x := bm.stack.Pop()
|
||||
// Set pc to x if it's non zero
|
||||
if x.Cmp(ethutil.BigFalse) != 0 {
|
||||
pc = int(x.Uint64())
|
||||
pc--
|
||||
}
|
||||
case oIND:
|
||||
bm.stack.Push(big.NewInt(int64(pc)))
|
||||
case oEXTRO:
|
||||
memAddr := bm.stack.Pop()
|
||||
contractAddr := bm.stack.Pop().Bytes()
|
||||
|
||||
// Push the contract's memory on to the stack
|
||||
bm.stack.Push(getContractMemory(block, contractAddr, memAddr))
|
||||
case oBALANCE:
|
||||
// Pushes the balance of the popped value on to the stack
|
||||
d := block.State().Get(bm.stack.Pop().String())
|
||||
ether := NewAddressFromData([]byte(d))
|
||||
bm.stack.Push(ether.Amount)
|
||||
case oMKTX:
|
||||
value, addr := bm.stack.Popn()
|
||||
from, length := bm.stack.Popn()
|
||||
|
||||
j := 0
|
||||
dataItems := make([]string, int(length.Uint64()))
|
||||
for i := from.Uint64(); i < length.Uint64(); i++ {
|
||||
dataItems[j] = string(bm.mem[strconv.Itoa(int(i))].Bytes())
|
||||
j++
|
||||
}
|
||||
// TODO sign it?
|
||||
tx := NewTransaction(addr.Bytes(), value, dataItems)
|
||||
// Add the transaction to the tx pool
|
||||
bm.TransactionPool.QueueTransaction(tx)
|
||||
case oSUICIDE:
|
||||
//addr := bm.stack.Pop()
|
||||
}
|
||||
pc++
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an address from the specified contract's address
|
||||
func getContractMemory(block *Block, contractAddr []byte, memAddr *big.Int) *big.Int {
|
||||
contract := block.GetContract(contractAddr)
|
||||
if contract == nil {
|
||||
log.Panicf("invalid contract addr %x", contractAddr)
|
||||
}
|
||||
val := contract.State().Get(memAddr.String())
|
||||
|
||||
// decode the object as a big integer
|
||||
decoder := ethutil.NewRlpValueFromBytes([]byte(val))
|
||||
if decoder.IsNil() {
|
||||
return ethutil.BigFalse
|
||||
}
|
||||
|
||||
return decoder.AsBigInt()
|
||||
}
|
75
ethchain/block_manager_test.go
Normal file
75
ethchain/block_manager_test.go
Normal file
@ -0,0 +1,75 @@
|
||||
package ethchain
|
||||
|
||||
/*
|
||||
import (
|
||||
_ "fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVm(t *testing.T) {
|
||||
InitFees()
|
||||
|
||||
db, _ := NewMemDatabase()
|
||||
Db = db
|
||||
|
||||
ctrct := NewTransaction("", 200000000, []string{
|
||||
"PUSH", "1a2f2e",
|
||||
"PUSH", "hallo",
|
||||
"POP", // POP hallo
|
||||
"PUSH", "3",
|
||||
"LOAD", // Load hallo back on the stack
|
||||
|
||||
"PUSH", "1",
|
||||
"PUSH", "2",
|
||||
"ADD",
|
||||
|
||||
"PUSH", "2",
|
||||
"PUSH", "1",
|
||||
"SUB",
|
||||
|
||||
"PUSH", "100000000000000000000000",
|
||||
"PUSH", "10000000000000",
|
||||
"SDIV",
|
||||
|
||||
"PUSH", "105",
|
||||
"PUSH", "200",
|
||||
"MOD",
|
||||
|
||||
"PUSH", "100000000000000000000000",
|
||||
"PUSH", "10000000000000",
|
||||
"SMOD",
|
||||
|
||||
"PUSH", "5",
|
||||
"PUSH", "10",
|
||||
"LT",
|
||||
|
||||
"PUSH", "5",
|
||||
"PUSH", "5",
|
||||
"LE",
|
||||
|
||||
"PUSH", "50",
|
||||
"PUSH", "5",
|
||||
"GT",
|
||||
|
||||
"PUSH", "5",
|
||||
"PUSH", "5",
|
||||
"GE",
|
||||
|
||||
"PUSH", "10",
|
||||
"PUSH", "10",
|
||||
"NOT",
|
||||
|
||||
"MYADDRESS",
|
||||
"TXSENDER",
|
||||
|
||||
"STOP",
|
||||
})
|
||||
tx := NewTransaction("1e8a42ea8cce13", 100, []string{})
|
||||
|
||||
block := CreateBlock("", 0, "", "c014ba53", 0, 0, "", []*Transaction{ctrct, tx})
|
||||
db.Put(block.Hash(), block.RlpEncode())
|
||||
|
||||
bm := NewBlockManager()
|
||||
bm.ProcessBlock(block)
|
||||
}
|
||||
*/
|
66
ethchain/contract.go
Normal file
66
ethchain/contract.go
Normal file
@ -0,0 +1,66 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type Contract struct {
|
||||
Amount *big.Int
|
||||
Nonce uint64
|
||||
state *ethutil.Trie
|
||||
}
|
||||
|
||||
func NewContract(Amount *big.Int, root []byte) *Contract {
|
||||
contract := &Contract{Amount: Amount, Nonce: 0}
|
||||
contract.state = ethutil.NewTrie(ethutil.Config.Db, string(root))
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
func (c *Contract) RlpEncode() []byte {
|
||||
return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.Root})
|
||||
}
|
||||
|
||||
func (c *Contract) RlpDecode(data []byte) {
|
||||
decoder := ethutil.NewRlpValueFromBytes(data)
|
||||
|
||||
c.Amount = decoder.Get(0).AsBigInt()
|
||||
c.Nonce = decoder.Get(1).AsUint()
|
||||
c.state = ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).AsRaw())
|
||||
}
|
||||
|
||||
func (c *Contract) State() *ethutil.Trie {
|
||||
return c.state
|
||||
}
|
||||
|
||||
type Address struct {
|
||||
Amount *big.Int
|
||||
Nonce uint64
|
||||
}
|
||||
|
||||
func NewAddress(amount *big.Int) *Address {
|
||||
return &Address{Amount: amount, Nonce: 0}
|
||||
}
|
||||
|
||||
func NewAddressFromData(data []byte) *Address {
|
||||
address := &Address{}
|
||||
address.RlpDecode(data)
|
||||
|
||||
return address
|
||||
}
|
||||
|
||||
func (a *Address) AddFee(fee *big.Int) {
|
||||
a.Amount.Add(a.Amount, fee)
|
||||
}
|
||||
|
||||
func (a *Address) RlpEncode() []byte {
|
||||
return ethutil.Encode([]interface{}{a.Amount, a.Nonce})
|
||||
}
|
||||
|
||||
func (a *Address) RlpDecode(data []byte) {
|
||||
decoder := ethutil.NewRlpValueFromBytes(data)
|
||||
|
||||
a.Amount = decoder.Get(0).AsBigInt()
|
||||
a.Nonce = decoder.Get(1).AsUint()
|
||||
}
|
199
ethchain/dagger.go
Normal file
199
ethchain/dagger.go
Normal file
@ -0,0 +1,199 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/obscuren/sha3"
|
||||
"hash"
|
||||
"log"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PoW interface {
|
||||
Search(block *Block) []byte
|
||||
Verify(hash []byte, diff *big.Int, nonce []byte) bool
|
||||
}
|
||||
|
||||
type EasyPow struct {
|
||||
hash *big.Int
|
||||
}
|
||||
|
||||
func (pow *EasyPow) Search(block *Block) []byte {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
hash := block.HashNoNonce()
|
||||
diff := block.Difficulty
|
||||
for {
|
||||
sha := ethutil.Sha3Bin(big.NewInt(r.Int63()).Bytes())
|
||||
if pow.Verify(hash, diff, sha) {
|
||||
return sha
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pow *EasyPow) Verify(hash []byte, diff *big.Int, nonce []byte) bool {
|
||||
sha := sha3.NewKeccak256()
|
||||
|
||||
d := append(hash, nonce...)
|
||||
sha.Write(d)
|
||||
|
||||
v := ethutil.BigPow(2, 256)
|
||||
ret := new(big.Int).Div(v, diff)
|
||||
|
||||
res := new(big.Int)
|
||||
res.SetBytes(sha.Sum(nil))
|
||||
|
||||
return res.Cmp(ret) == -1
|
||||
}
|
||||
|
||||
func (pow *EasyPow) SetHash(hash *big.Int) {
|
||||
}
|
||||
|
||||
type Dagger struct {
|
||||
hash *big.Int
|
||||
xn *big.Int
|
||||
}
|
||||
|
||||
var Found bool
|
||||
|
||||
func (dag *Dagger) Find(obj *big.Int, resChan chan int64) {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
rnd := r.Int63()
|
||||
|
||||
res := dag.Eval(big.NewInt(rnd))
|
||||
log.Printf("rnd %v\nres %v\nobj %v\n", rnd, res, obj)
|
||||
if res.Cmp(obj) < 0 {
|
||||
// Post back result on the channel
|
||||
resChan <- rnd
|
||||
// Notify other threads we've found a valid nonce
|
||||
Found = true
|
||||
}
|
||||
|
||||
// Break out if found
|
||||
if Found {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
resChan <- 0
|
||||
}
|
||||
|
||||
func (dag *Dagger) Search(hash, diff *big.Int) *big.Int {
|
||||
// TODO fix multi threading. Somehow it results in the wrong nonce
|
||||
amountOfRoutines := 1
|
||||
|
||||
dag.hash = hash
|
||||
|
||||
obj := ethutil.BigPow(2, 256)
|
||||
obj = obj.Div(obj, diff)
|
||||
|
||||
Found = false
|
||||
resChan := make(chan int64, 3)
|
||||
var res int64
|
||||
|
||||
for k := 0; k < amountOfRoutines; k++ {
|
||||
go dag.Find(obj, resChan)
|
||||
}
|
||||
|
||||
// Wait for each go routine to finish
|
||||
for k := 0; k < amountOfRoutines; k++ {
|
||||
// Get the result from the channel. 0 = quit
|
||||
if r := <-resChan; r != 0 {
|
||||
res = r
|
||||
}
|
||||
}
|
||||
|
||||
return big.NewInt(res)
|
||||
}
|
||||
|
||||
func (dag *Dagger) Verify(hash, diff, nonce *big.Int) bool {
|
||||
dag.hash = hash
|
||||
|
||||
obj := ethutil.BigPow(2, 256)
|
||||
obj = obj.Div(obj, diff)
|
||||
|
||||
return dag.Eval(nonce).Cmp(obj) < 0
|
||||
}
|
||||
|
||||
func DaggerVerify(hash, diff, nonce *big.Int) bool {
|
||||
dagger := &Dagger{}
|
||||
dagger.hash = hash
|
||||
|
||||
obj := ethutil.BigPow(2, 256)
|
||||
obj = obj.Div(obj, diff)
|
||||
|
||||
return dagger.Eval(nonce).Cmp(obj) < 0
|
||||
}
|
||||
|
||||
func (dag *Dagger) Node(L uint64, i uint64) *big.Int {
|
||||
if L == i {
|
||||
return dag.hash
|
||||
}
|
||||
|
||||
var m *big.Int
|
||||
if L == 9 {
|
||||
m = big.NewInt(16)
|
||||
} else {
|
||||
m = big.NewInt(3)
|
||||
}
|
||||
|
||||
sha := sha3.NewKeccak256()
|
||||
sha.Reset()
|
||||
d := sha3.NewKeccak256()
|
||||
b := new(big.Int)
|
||||
ret := new(big.Int)
|
||||
|
||||
for k := 0; k < int(m.Uint64()); k++ {
|
||||
d.Reset()
|
||||
d.Write(dag.hash.Bytes())
|
||||
d.Write(dag.xn.Bytes())
|
||||
d.Write(big.NewInt(int64(L)).Bytes())
|
||||
d.Write(big.NewInt(int64(i)).Bytes())
|
||||
d.Write(big.NewInt(int64(k)).Bytes())
|
||||
|
||||
b.SetBytes(Sum(d))
|
||||
pk := b.Uint64() & ((1 << ((L - 1) * 3)) - 1)
|
||||
sha.Write(dag.Node(L-1, pk).Bytes())
|
||||
}
|
||||
|
||||
ret.SetBytes(Sum(sha))
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func Sum(sha hash.Hash) []byte {
|
||||
//in := make([]byte, 32)
|
||||
return sha.Sum(nil)
|
||||
}
|
||||
|
||||
func (dag *Dagger) Eval(N *big.Int) *big.Int {
|
||||
pow := ethutil.BigPow(2, 26)
|
||||
dag.xn = pow.Div(N, pow)
|
||||
|
||||
sha := sha3.NewKeccak256()
|
||||
sha.Reset()
|
||||
ret := new(big.Int)
|
||||
|
||||
for k := 0; k < 4; k++ {
|
||||
d := sha3.NewKeccak256()
|
||||
b := new(big.Int)
|
||||
|
||||
d.Reset()
|
||||
d.Write(dag.hash.Bytes())
|
||||
d.Write(dag.xn.Bytes())
|
||||
d.Write(N.Bytes())
|
||||
d.Write(big.NewInt(int64(k)).Bytes())
|
||||
|
||||
b.SetBytes(Sum(d))
|
||||
pk := (b.Uint64() & 0x1ffffff)
|
||||
|
||||
sha.Write(dag.Node(9, pk).Bytes())
|
||||
}
|
||||
|
||||
return ret.SetBytes(Sum(sha))
|
||||
}
|
18
ethchain/dagger_test.go
Normal file
18
ethchain/dagger_test.go
Normal file
@ -0,0 +1,18 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkDaggerSearch(b *testing.B) {
|
||||
hash := big.NewInt(0)
|
||||
diff := ethutil.BigPow(2, 36)
|
||||
o := big.NewInt(0) // nonce doesn't matter. We're only testing against speed, not validity
|
||||
|
||||
// Reset timer so the big generation isn't included in the benchmark
|
||||
b.ResetTimer()
|
||||
// Validate
|
||||
DaggerVerify(hash, diff, o)
|
||||
}
|
42
ethchain/error.go
Normal file
42
ethchain/error.go
Normal file
@ -0,0 +1,42 @@
|
||||
package ethchain
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Parent error. In case a parent is unknown this error will be thrown
|
||||
// by the block manager
|
||||
type ParentErr struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (err *ParentErr) Error() string {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
func ParentError(hash []byte) error {
|
||||
return &ParentErr{Message: fmt.Sprintf("Block's parent unkown %x", hash)}
|
||||
}
|
||||
|
||||
func IsParentErr(err error) bool {
|
||||
_, ok := err.(*ParentErr)
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// Block validation error. If any validation fails, this error will be thrown
|
||||
type ValidationErr struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (err *ValidationErr) Error() string {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
func ValidationError(format string, v ...interface{}) *ValidationErr {
|
||||
return &ValidationErr{Message: fmt.Sprintf(format, v...)}
|
||||
}
|
||||
|
||||
func IsValidationErr(err error) bool {
|
||||
_, ok := err.(*ValidationErr)
|
||||
|
||||
return ok
|
||||
}
|
65
ethchain/fees.go
Normal file
65
ethchain/fees.go
Normal file
@ -0,0 +1,65 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var StepFee *big.Int = new(big.Int)
|
||||
var TxFeeRat *big.Int = big.NewInt(100000000000000)
|
||||
var TxFee *big.Int = big.NewInt(100)
|
||||
var ContractFee *big.Int = new(big.Int)
|
||||
var MemFee *big.Int = new(big.Int)
|
||||
var DataFee *big.Int = new(big.Int)
|
||||
var CryptoFee *big.Int = new(big.Int)
|
||||
var ExtroFee *big.Int = new(big.Int)
|
||||
|
||||
var BlockReward *big.Int = big.NewInt(1500000000000000000)
|
||||
var Period1Reward *big.Int = new(big.Int)
|
||||
var Period2Reward *big.Int = new(big.Int)
|
||||
var Period3Reward *big.Int = new(big.Int)
|
||||
var Period4Reward *big.Int = new(big.Int)
|
||||
|
||||
func InitFees() {
|
||||
/*
|
||||
// Base for 2**64
|
||||
b60 := new(big.Int)
|
||||
b60.Exp(big.NewInt(2), big.NewInt(64), big.NewInt(0))
|
||||
// Base for 2**80
|
||||
b80 := new(big.Int)
|
||||
b80.Exp(big.NewInt(2), big.NewInt(80), big.NewInt(0))
|
||||
|
||||
StepFee.Exp(big.NewInt(10), big.NewInt(16), big.NewInt(0))
|
||||
//StepFee.Div(b60, big.NewInt(64))
|
||||
//fmt.Println("StepFee:", StepFee)
|
||||
|
||||
TxFee.Exp(big.NewInt(2), big.NewInt(64), big.NewInt(0))
|
||||
//fmt.Println("TxFee:", TxFee)
|
||||
|
||||
ContractFee.Exp(big.NewInt(2), big.NewInt(64), big.NewInt(0))
|
||||
//fmt.Println("ContractFee:", ContractFee)
|
||||
|
||||
MemFee.Div(b60, big.NewInt(4))
|
||||
//fmt.Println("MemFee:", MemFee)
|
||||
|
||||
DataFee.Div(b60, big.NewInt(16))
|
||||
//fmt.Println("DataFee:", DataFee)
|
||||
|
||||
CryptoFee.Div(b60, big.NewInt(16))
|
||||
//fmt.Println("CrytoFee:", CryptoFee)
|
||||
|
||||
ExtroFee.Div(b60, big.NewInt(16))
|
||||
//fmt.Println("ExtroFee:", ExtroFee)
|
||||
|
||||
Period1Reward.Mul(b80, big.NewInt(1024))
|
||||
//fmt.Println("Period1Reward:", Period1Reward)
|
||||
|
||||
Period2Reward.Mul(b80, big.NewInt(512))
|
||||
//fmt.Println("Period2Reward:", Period2Reward)
|
||||
|
||||
Period3Reward.Mul(b80, big.NewInt(256))
|
||||
//fmt.Println("Period3Reward:", Period3Reward)
|
||||
|
||||
Period4Reward.Mul(b80, big.NewInt(128))
|
||||
//fmt.Println("Period4Reward:", Period4Reward)
|
||||
*/
|
||||
}
|
39
ethchain/genesis.go
Normal file
39
ethchain/genesis.go
Normal file
@ -0,0 +1,39 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
/*
|
||||
* This is the special genesis block.
|
||||
*/
|
||||
|
||||
var ZeroHash256 = make([]byte, 32)
|
||||
var ZeroHash160 = make([]byte, 20)
|
||||
var EmptyShaList = ethutil.Sha3Bin(ethutil.Encode([]interface{}{}))
|
||||
|
||||
var GenisisHeader = []interface{}{
|
||||
// Previous hash (none)
|
||||
//"",
|
||||
ZeroHash256,
|
||||
// Sha of uncles
|
||||
ethutil.Sha3Bin(ethutil.Encode([]interface{}{})),
|
||||
// Coinbase
|
||||
ZeroHash160,
|
||||
// Root state
|
||||
"",
|
||||
// Sha of transactions
|
||||
//EmptyShaList,
|
||||
ethutil.Sha3Bin(ethutil.Encode([]interface{}{})),
|
||||
// Difficulty
|
||||
ethutil.BigPow(2, 22),
|
||||
// Time
|
||||
int64(0),
|
||||
// Extra
|
||||
"",
|
||||
// Nonce
|
||||
ethutil.Sha3Bin(big.NewInt(42).Bytes()),
|
||||
}
|
||||
|
||||
var Genesis = []interface{}{GenisisHeader, []interface{}{}, []interface{}{}}
|
167
ethchain/stack.go
Normal file
167
ethchain/stack.go
Normal file
@ -0,0 +1,167 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type OpCode int
|
||||
|
||||
// Op codes
|
||||
const (
|
||||
oSTOP OpCode = iota
|
||||
oADD
|
||||
oMUL
|
||||
oSUB
|
||||
oDIV
|
||||
oSDIV
|
||||
oMOD
|
||||
oSMOD
|
||||
oEXP
|
||||
oNEG
|
||||
oLT
|
||||
oLE
|
||||
oGT
|
||||
oGE
|
||||
oEQ
|
||||
oNOT
|
||||
oMYADDRESS
|
||||
oTXSENDER
|
||||
oTXVALUE
|
||||
oTXFEE
|
||||
oTXDATAN
|
||||
oTXDATA
|
||||
oBLK_PREVHASH
|
||||
oBLK_COINBASE
|
||||
oBLK_TIMESTAMP
|
||||
oBLK_NUMBER
|
||||
oBLK_DIFFICULTY
|
||||
oBASEFEE
|
||||
oSHA256 OpCode = 32
|
||||
oRIPEMD160 OpCode = 33
|
||||
oECMUL OpCode = 34
|
||||
oECADD OpCode = 35
|
||||
oECSIGN OpCode = 36
|
||||
oECRECOVER OpCode = 37
|
||||
oECVALID OpCode = 38
|
||||
oSHA3 OpCode = 39
|
||||
oPUSH OpCode = 48
|
||||
oPOP OpCode = 49
|
||||
oDUP OpCode = 50
|
||||
oSWAP OpCode = 51
|
||||
oMLOAD OpCode = 52
|
||||
oMSTORE OpCode = 53
|
||||
oSLOAD OpCode = 54
|
||||
oSSTORE OpCode = 55
|
||||
oJMP OpCode = 56
|
||||
oJMPI OpCode = 57
|
||||
oIND OpCode = 58
|
||||
oEXTRO OpCode = 59
|
||||
oBALANCE OpCode = 60
|
||||
oMKTX OpCode = 61
|
||||
oSUICIDE OpCode = 62
|
||||
)
|
||||
|
||||
// Since the opcodes aren't all in order we can't use a regular slice
|
||||
var opCodeToString = map[OpCode]string{
|
||||
oSTOP: "STOP",
|
||||
oADD: "ADD",
|
||||
oMUL: "MUL",
|
||||
oSUB: "SUB",
|
||||
oDIV: "DIV",
|
||||
oSDIV: "SDIV",
|
||||
oMOD: "MOD",
|
||||
oSMOD: "SMOD",
|
||||
oEXP: "EXP",
|
||||
oNEG: "NEG",
|
||||
oLT: "LT",
|
||||
oLE: "LE",
|
||||
oGT: "GT",
|
||||
oGE: "GE",
|
||||
oEQ: "EQ",
|
||||
oNOT: "NOT",
|
||||
oMYADDRESS: "MYADDRESS",
|
||||
oTXSENDER: "TXSENDER",
|
||||
oTXVALUE: "TXVALUE",
|
||||
oTXFEE: "TXFEE",
|
||||
oTXDATAN: "TXDATAN",
|
||||
oTXDATA: "TXDATA",
|
||||
oBLK_PREVHASH: "BLK_PREVHASH",
|
||||
oBLK_COINBASE: "BLK_COINBASE",
|
||||
oBLK_TIMESTAMP: "BLK_TIMESTAMP",
|
||||
oBLK_NUMBER: "BLK_NUMBER",
|
||||
oBLK_DIFFICULTY: "BLK_DIFFICULTY",
|
||||
oBASEFEE: "BASEFEE",
|
||||
oSHA256: "SHA256",
|
||||
oRIPEMD160: "RIPEMD160",
|
||||
oECMUL: "ECMUL",
|
||||
oECADD: "ECADD",
|
||||
oECSIGN: "ECSIGN",
|
||||
oECRECOVER: "ECRECOVER",
|
||||
oECVALID: "ECVALID",
|
||||
oSHA3: "SHA3",
|
||||
oPUSH: "PUSH",
|
||||
oPOP: "POP",
|
||||
oDUP: "DUP",
|
||||
oSWAP: "SWAP",
|
||||
oMLOAD: "MLOAD",
|
||||
oMSTORE: "MSTORE",
|
||||
oSLOAD: "SLOAD",
|
||||
oSSTORE: "SSTORE",
|
||||
oJMP: "JMP",
|
||||
oJMPI: "JMPI",
|
||||
oIND: "IND",
|
||||
oEXTRO: "EXTRO",
|
||||
oBALANCE: "BALANCE",
|
||||
oMKTX: "MKTX",
|
||||
oSUICIDE: "SUICIDE",
|
||||
}
|
||||
|
||||
func (o OpCode) String() string {
|
||||
return opCodeToString[o]
|
||||
}
|
||||
|
||||
type OpType int
|
||||
|
||||
const (
|
||||
tNorm = iota
|
||||
tData
|
||||
tExtro
|
||||
tCrypto
|
||||
)
|
||||
|
||||
type TxCallback func(opType OpType) bool
|
||||
|
||||
// Simple push/pop stack mechanism
|
||||
type Stack struct {
|
||||
data []*big.Int
|
||||
}
|
||||
|
||||
func NewStack() *Stack {
|
||||
return &Stack{}
|
||||
}
|
||||
|
||||
func (st *Stack) Pop() *big.Int {
|
||||
s := len(st.data)
|
||||
|
||||
str := st.data[s-1]
|
||||
st.data = st.data[:s-1]
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func (st *Stack) Popn() (*big.Int, *big.Int) {
|
||||
s := len(st.data)
|
||||
|
||||
ints := st.data[s-2:]
|
||||
st.data = st.data[:s-2]
|
||||
|
||||
return ints[0], ints[1]
|
||||
}
|
||||
|
||||
func (st *Stack) Push(d *big.Int) {
|
||||
st.data = append(st.data, d)
|
||||
}
|
||||
func (st *Stack) Print() {
|
||||
fmt.Println(st.data)
|
||||
}
|
157
ethchain/transaction.go
Normal file
157
ethchain/transaction.go
Normal file
@ -0,0 +1,157 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/obscuren/secp256k1-go"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type Transaction struct {
|
||||
Nonce uint64
|
||||
Recipient []byte
|
||||
Value *big.Int
|
||||
Data []string
|
||||
Memory []int
|
||||
v byte
|
||||
r, s []byte
|
||||
}
|
||||
|
||||
func NewTransaction(to []byte, value *big.Int, data []string) *Transaction {
|
||||
tx := Transaction{Recipient: to, Value: value}
|
||||
tx.Nonce = 0
|
||||
|
||||
// Serialize the data
|
||||
tx.Data = make([]string, len(data))
|
||||
for i, val := range data {
|
||||
instr, err := ethutil.CompileInstr(val)
|
||||
if err != nil {
|
||||
//fmt.Printf("compile error:%d %v\n", i+1, err)
|
||||
}
|
||||
|
||||
tx.Data[i] = instr
|
||||
}
|
||||
|
||||
return &tx
|
||||
}
|
||||
|
||||
func NewTransactionFromData(data []byte) *Transaction {
|
||||
tx := &Transaction{}
|
||||
tx.RlpDecode(data)
|
||||
|
||||
return tx
|
||||
}
|
||||
|
||||
func NewTransactionFromValue(val *ethutil.Value) *Transaction {
|
||||
tx := &Transaction{}
|
||||
tx.RlpValueDecode(val)
|
||||
|
||||
return tx
|
||||
}
|
||||
|
||||
func (tx *Transaction) Hash() []byte {
|
||||
data := make([]interface{}, len(tx.Data))
|
||||
for i, val := range tx.Data {
|
||||
data[i] = val
|
||||
}
|
||||
|
||||
preEnc := []interface{}{
|
||||
tx.Nonce,
|
||||
tx.Recipient,
|
||||
tx.Value,
|
||||
data,
|
||||
}
|
||||
|
||||
return ethutil.Sha3Bin(ethutil.Encode(preEnc))
|
||||
}
|
||||
|
||||
func (tx *Transaction) IsContract() bool {
|
||||
return len(tx.Recipient) == 0
|
||||
}
|
||||
|
||||
func (tx *Transaction) Signature(key []byte) []byte {
|
||||
hash := tx.Hash()
|
||||
|
||||
sig, _ := secp256k1.Sign(hash, key)
|
||||
|
||||
return sig
|
||||
}
|
||||
|
||||
func (tx *Transaction) PublicKey() []byte {
|
||||
hash := tx.Hash()
|
||||
|
||||
// If we don't make a copy we will overwrite the existing underlying array
|
||||
dst := make([]byte, len(tx.r))
|
||||
copy(dst, tx.r)
|
||||
|
||||
sig := append(dst, tx.s...)
|
||||
sig = append(sig, tx.v-27)
|
||||
|
||||
pubkey, _ := secp256k1.RecoverPubkey(hash, sig)
|
||||
|
||||
return pubkey
|
||||
}
|
||||
|
||||
func (tx *Transaction) Sender() []byte {
|
||||
pubkey := tx.PublicKey()
|
||||
|
||||
// Validate the returned key.
|
||||
// Return nil if public key isn't in full format
|
||||
if pubkey[0] != 4 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ethutil.Sha3Bin(pubkey[1:])[12:]
|
||||
}
|
||||
|
||||
func (tx *Transaction) Sign(privk []byte) error {
|
||||
|
||||
sig := tx.Signature(privk)
|
||||
|
||||
tx.r = sig[:32]
|
||||
tx.s = sig[32:64]
|
||||
tx.v = sig[64] + 27
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpData() interface{} {
|
||||
// Prepare the transaction for serialization
|
||||
return []interface{}{
|
||||
tx.Nonce,
|
||||
tx.Recipient,
|
||||
tx.Value,
|
||||
ethutil.NewSliceValue(tx.Data).Slice(),
|
||||
tx.v,
|
||||
tx.r,
|
||||
tx.s,
|
||||
}
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpValue() *ethutil.Value {
|
||||
return ethutil.NewValue(tx.RlpData())
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpEncode() []byte {
|
||||
return tx.RlpValue().Encode()
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpDecode(data []byte) {
|
||||
tx.RlpValueDecode(ethutil.NewValueFromBytes(data))
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
|
||||
tx.Nonce = decoder.Get(0).Uint()
|
||||
tx.Recipient = decoder.Get(1).Bytes()
|
||||
tx.Value = decoder.Get(2).BigInt()
|
||||
|
||||
d := decoder.Get(3)
|
||||
tx.Data = make([]string, d.Len())
|
||||
for i := 0; i < d.Len(); i++ {
|
||||
tx.Data[i] = d.Get(i).Str()
|
||||
}
|
||||
|
||||
// TODO something going wrong here
|
||||
tx.v = byte(decoder.Get(4).Uint())
|
||||
tx.r = decoder.Get(5).Bytes()
|
||||
tx.s = decoder.Get(6).Bytes()
|
||||
}
|
219
ethchain/transaction_pool.go
Normal file
219
ethchain/transaction_pool.go
Normal file
@ -0,0 +1,219 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"github.com/ethereum/eth-go/ethwire"
|
||||
"log"
|
||||
"math/big"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
txPoolQueueSize = 50
|
||||
)
|
||||
|
||||
type TxPoolHook chan *Transaction
|
||||
|
||||
func FindTx(pool *list.List, finder func(*Transaction, *list.Element) bool) *Transaction {
|
||||
for e := pool.Front(); e != nil; e = e.Next() {
|
||||
if tx, ok := e.Value.(*Transaction); ok {
|
||||
if finder(tx, e) {
|
||||
return tx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PublicSpeaker interface {
|
||||
Broadcast(msgType ethwire.MsgType, data []interface{})
|
||||
}
|
||||
|
||||
// The tx pool a thread safe transaction pool handler. In order to
|
||||
// guarantee a non blocking pool we use a queue channel which can be
|
||||
// independently read without needing access to the actual pool. If the
|
||||
// pool is being drained or synced for whatever reason the transactions
|
||||
// will simple queue up and handled when the mutex is freed.
|
||||
type TxPool struct {
|
||||
//server *Server
|
||||
Speaker PublicSpeaker
|
||||
// The mutex for accessing the Tx pool.
|
||||
mutex sync.Mutex
|
||||
// Queueing channel for reading and writing incoming
|
||||
// transactions to
|
||||
queueChan chan *Transaction
|
||||
// Quiting channel
|
||||
quit chan bool
|
||||
// The actual pool
|
||||
pool *list.List
|
||||
|
||||
BlockManager *BlockManager
|
||||
|
||||
Hook TxPoolHook
|
||||
}
|
||||
|
||||
func NewTxPool() *TxPool {
|
||||
return &TxPool{
|
||||
//server: s,
|
||||
mutex: sync.Mutex{},
|
||||
pool: list.New(),
|
||||
queueChan: make(chan *Transaction, txPoolQueueSize),
|
||||
quit: make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
// Blocking function. Don't use directly. Use QueueTransaction instead
|
||||
func (pool *TxPool) addTransaction(tx *Transaction) {
|
||||
pool.mutex.Lock()
|
||||
pool.pool.PushBack(tx)
|
||||
pool.mutex.Unlock()
|
||||
|
||||
// Broadcast the transaction to the rest of the peers
|
||||
pool.Speaker.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()})
|
||||
}
|
||||
|
||||
// Process transaction validates the Tx and processes funds from the
|
||||
// sender to the recipient.
|
||||
func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) {
|
||||
log.Printf("[TXPL] Processing Tx %x\n", tx.Hash())
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Println(r)
|
||||
err = fmt.Errorf("%v", r)
|
||||
}
|
||||
}()
|
||||
// Get the sender
|
||||
sender := block.GetAddr(tx.Sender())
|
||||
|
||||
// Make sure there's enough in the sender's account. Having insufficient
|
||||
// funds won't invalidate this transaction but simple ignores it.
|
||||
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat))
|
||||
if sender.Amount.Cmp(totAmount) < 0 {
|
||||
return errors.New("Insufficient amount in sender's account")
|
||||
}
|
||||
|
||||
if sender.Nonce != tx.Nonce {
|
||||
if ethutil.Config.Debug {
|
||||
return fmt.Errorf("Invalid nonce %d(%d) continueing anyway", tx.Nonce, sender.Nonce)
|
||||
} else {
|
||||
return fmt.Errorf("Invalid nonce %d(%d)", tx.Nonce, sender.Nonce)
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract the amount from the senders account
|
||||
sender.Amount.Sub(sender.Amount, totAmount)
|
||||
sender.Nonce += 1
|
||||
|
||||
// Get the receiver
|
||||
receiver := block.GetAddr(tx.Recipient)
|
||||
// Add the amount to receivers account which should conclude this transaction
|
||||
receiver.Amount.Add(receiver.Amount, tx.Value)
|
||||
|
||||
block.UpdateAddr(tx.Sender(), sender)
|
||||
block.UpdateAddr(tx.Recipient, receiver)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
|
||||
// Get the last block so we can retrieve the sender and receiver from
|
||||
// the merkle trie
|
||||
block := pool.BlockManager.BlockChain().CurrentBlock
|
||||
// Something has gone horribly wrong if this happens
|
||||
if block == nil {
|
||||
return errors.New("No last block on the block chain")
|
||||
}
|
||||
|
||||
// Get the sender
|
||||
sender := block.GetAddr(tx.Sender())
|
||||
|
||||
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat))
|
||||
// Make sure there's enough in the sender's account. Having insufficient
|
||||
// funds won't invalidate this transaction but simple ignores it.
|
||||
if sender.Amount.Cmp(totAmount) < 0 {
|
||||
return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.Sender())
|
||||
}
|
||||
|
||||
// Increment the nonce making each tx valid only once to prevent replay
|
||||
// attacks
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pool *TxPool) queueHandler() {
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case tx := <-pool.queueChan:
|
||||
hash := tx.Hash()
|
||||
foundTx := FindTx(pool.pool, func(tx *Transaction, e *list.Element) bool {
|
||||
return bytes.Compare(tx.Hash(), hash) == 0
|
||||
})
|
||||
|
||||
if foundTx != nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Validate the transaction
|
||||
err := pool.ValidateTransaction(tx)
|
||||
if err != nil {
|
||||
if ethutil.Config.Debug {
|
||||
log.Println("Validating Tx failed", err)
|
||||
}
|
||||
} else {
|
||||
// Call blocking version. At this point it
|
||||
// doesn't matter since this is a goroutine
|
||||
pool.addTransaction(tx)
|
||||
|
||||
if pool.Hook != nil {
|
||||
pool.Hook <- tx
|
||||
}
|
||||
}
|
||||
case <-pool.quit:
|
||||
break out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pool *TxPool) QueueTransaction(tx *Transaction) {
|
||||
pool.queueChan <- tx
|
||||
}
|
||||
|
||||
func (pool *TxPool) Flush() []*Transaction {
|
||||
pool.mutex.Lock()
|
||||
defer pool.mutex.Unlock()
|
||||
|
||||
txList := make([]*Transaction, pool.pool.Len())
|
||||
i := 0
|
||||
for e := pool.pool.Front(); e != nil; e = e.Next() {
|
||||
if tx, ok := e.Value.(*Transaction); ok {
|
||||
txList[i] = tx
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
// Recreate a new list all together
|
||||
// XXX Is this the fastest way?
|
||||
pool.pool = list.New()
|
||||
|
||||
return txList
|
||||
}
|
||||
|
||||
func (pool *TxPool) Start() {
|
||||
go pool.queueHandler()
|
||||
}
|
||||
|
||||
func (pool *TxPool) Stop() {
|
||||
log.Println("[TXP] Stopping...")
|
||||
|
||||
close(pool.quit)
|
||||
|
||||
pool.Flush()
|
||||
}
|
54
ethchain/transaction_test.go
Normal file
54
ethchain/transaction_test.go
Normal file
@ -0,0 +1,54 @@
|
||||
package ethchain
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/ethereum/eth-go/ethutil"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddressRetrieval(t *testing.T) {
|
||||
// TODO
|
||||
// 88f9b82462f6c4bf4a0fb15e5c3971559a316e7f
|
||||
key, _ := hex.DecodeString("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4")
|
||||
|
||||
tx := &Transaction{
|
||||
Nonce: 0,
|
||||
Recipient: ZeroHash160,
|
||||
Value: big.NewInt(0),
|
||||
Data: nil,
|
||||
}
|
||||
//fmt.Printf("rlp %x\n", tx.RlpEncode())
|
||||
//fmt.Printf("sha rlp %x\n", tx.Hash())
|
||||
|
||||
tx.Sign(key)
|
||||
|
||||
//fmt.Printf("hex tx key %x\n", tx.PublicKey())
|
||||
//fmt.Printf("seder %x\n", tx.Sender())
|
||||
}
|
||||
|
||||
func TestAddressRetrieval2(t *testing.T) {
|
||||
// TODO
|
||||
// 88f9b82462f6c4bf4a0fb15e5c3971559a316e7f
|
||||
key, _ := hex.DecodeString("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4")
|
||||
addr, _ := hex.DecodeString("944400f4b88ac9589a0f17ed4671da26bddb668b")
|
||||
tx := &Transaction{
|
||||
Nonce: 0,
|
||||
Recipient: addr,
|
||||
Value: big.NewInt(1000),
|
||||
Data: nil,
|
||||
}
|
||||
tx.Sign(key)
|
||||
//data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7")
|
||||
//tx := NewTransactionFromData(data)
|
||||
fmt.Println(tx.RlpValue())
|
||||
|
||||
fmt.Printf("rlp %x\n", tx.RlpEncode())
|
||||
fmt.Printf("sha rlp %x\n", tx.Hash())
|
||||
|
||||
//tx.Sign(key)
|
||||
|
||||
fmt.Printf("hex tx key %x\n", tx.PublicKey())
|
||||
fmt.Printf("seder %x\n", tx.Sender())
|
||||
}
|
Reference in New Issue
Block a user