diff --git a/LICENSE b/LICENSE index 803f2ef4e3..b77f7909a6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ 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 of this software and associated documentation files (the "Software"), to deal diff --git a/ethchain/address.go b/ethchain/address.go new file mode 100644 index 0000000000..a228c7566f --- /dev/null +++ b/ethchain/address.go @@ -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 +} diff --git a/ethchain/address_test.go b/ethchain/address_test.go new file mode 100644 index 0000000000..161e1b251e --- /dev/null +++ b/ethchain/address_test.go @@ -0,0 +1,8 @@ +package ethchain + +import ( + "testing" +) + +func TestAddressState(t *testing.T) { +} diff --git a/ethchain/block.go b/ethchain/block.go index 0678f64e21..7ca44a47df 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -46,6 +46,8 @@ type Block struct { // List of transactions and/or contracts transactions []*Transaction TxSha []byte + + contractStates map[string]*ethutil.Trie } // New block takes a raw encoded string @@ -79,14 +81,15 @@ func CreateBlock(root interface{}, 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, + transactions: txes, + PrevHash: prevHash, + Coinbase: base, + Difficulty: Difficulty, + Nonce: Nonce, + Time: time.Now().Unix(), + Extra: extra, + UncleSha: EmptyShaList, + contractStates: make(map[string]*ethutil.Trie), } block.SetTransactions(txes) block.SetUncles([]*Block{}) @@ -128,14 +131,26 @@ func (block *Block) GetContract(addr []byte) *Contract { return nil } + value := ethutil.NewValueFromBytes([]byte(data)) + if value.Len() == 2 { + return nil + } + contract := &Contract{} contract.RlpDecode([]byte(data)) + cachedState := block.contractStates[string(addr)] + if cachedState != nil { + contract.state = cachedState + } else { + block.contractStates[string(addr)] = contract.state + } + return contract } func (block *Block) UpdateContract(addr []byte, contract *Contract) { // Make sure the state is synced - contract.State().Sync() + //contract.State().Sync() block.state.Update(string(addr), string(contract.RlpEncode())) } @@ -190,18 +205,29 @@ func (block *Block) BlockInfo() BlockInfo { return bi } -func (block *Block) MakeContract(tx *Transaction) { - // Create contract if there's no recipient - if tx.IsContract() { - addr := tx.Hash() +// Sync the block's state and contract respectively +func (block *Block) Sync() { + // Sync all contracts currently in cache + for _, val := range block.contractStates { + val.Sync() + } + // Sync the block state itself + block.state.Sync() +} - 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) +func (block *Block) Undo() { + // Sync all contracts currently in cache + for _, val := range block.contractStates { + val.Undo() + } + // Sync the block state itself + 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.Extra = header.Get(7).Str() 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 // header set. @@ -298,12 +325,6 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) { tx := NewTransactionFromValue(txes.Get(i)) 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 { - 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 ///////////////// diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 54f48bc601..96d22366d6 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -104,7 +104,6 @@ func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} { currentHash = block.PrevHash chain = append(chain, block.Value().Val) - //chain = append([]interface{}{block.RlpValue().Value}, chain...) num-- } @@ -112,6 +111,24 @@ func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} { 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() { data, _ := ethutil.Config.Db.Get([]byte("LastBlock")) if len(data) != 0 { @@ -141,11 +158,16 @@ func (bc *BlockChain) Add(block *Block) { bc.CurrentBlock = block 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 { data, _ := ethutil.Config.Db.Get(hash) + if len(data) == 0 { + return nil + } return NewBlockFromData(data) } @@ -177,8 +199,6 @@ func (bc *BlockChain) writeBlockInfo(block *Block) { func (bc *BlockChain) Stop() { if bc.CurrentBlock != nil { - ethutil.Config.Db.Put([]byte("LastBlock"), bc.CurrentBlock.RlpEncode()) - log.Println("[CHAIN] Stopped") } } diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go index 8b237a29a9..364a061583 100644 --- a/ethchain/block_manager.go +++ b/ethchain/block_manager.go @@ -5,11 +5,9 @@ import ( "encoding/hex" "fmt" "github.com/ethereum/eth-go/ethutil" - "github.com/obscuren/secp256k1-go" + _ "github.com/ethereum/eth-go/ethwire" "log" - "math" "math/big" - "strconv" "sync" "time" ) @@ -18,10 +16,7 @@ type BlockProcessor interface { ProcessBlock(block *Block) } -func CalculateBlockReward(block *Block, uncleLength int) *big.Int { - return BlockReward -} - +// TODO rename to state manager type BlockManager struct { // Mutex for locking the block processor. Blocks can only be handled one at a time mutex sync.Mutex @@ -29,6 +24,10 @@ type BlockManager struct { // The block chain :) bc *BlockChain + // States for addresses. You can watch any address + // at any given time + addrStateStore *AddrStateStore + // Stack for processing contracts stack *Stack // non-persistent key/value memory storage @@ -46,9 +45,9 @@ type BlockManager struct { func AddTestNetFunds(block *Block) { for _, addr := range []string{ "8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin - "93658b04240e4bd4046fd2d6d417d20f146f4b43", // Jeffrey + "e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey "1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit - "80c01a26338f0d905e295fccb71fa9ea849ffa12", // Alex + "1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex } { //log.Println("2^200 Wei to", addr) codedAddr, _ := hex.DecodeString(addr) @@ -61,26 +60,47 @@ func AddTestNetFunds(block *Block) { func NewBlockManager(speaker PublicSpeaker) *BlockManager { bm := &BlockManager{ //server: s, - bc: NewBlockChain(), - stack: NewStack(), - mem: make(map[string]*big.Int), - Pow: &EasyPow{}, - Speaker: speaker, + bc: NewBlockChain(), + stack: NewStack(), + mem: make(map[string]*big.Int), + Pow: &EasyPow{}, + Speaker: speaker, + addrStateStore: NewAddrStateStore(), } if bm.bc.CurrentBlock == nil { AddTestNetFunds(bm.bc.genesisBlock) + + bm.bc.genesisBlock.State().Sync() // Prepare the genesis block 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) //bm.bc.genesisBlock.PrintHash() } + log.Printf("Last block: %x\n", bm.bc.CurrentBlock.Hash()) + 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 { 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 tx.IsContract() { block.MakeContract(tx) - bm.ProcessContract(tx, block) } 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 bm.mutex.Lock() 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() @@ -110,12 +141,6 @@ func (bm *BlockManager) ProcessBlock(block *Block) error { 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 { @@ -137,37 +162,19 @@ func (bm *BlockManager) ProcessBlock(block *Block) error { } 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()) - } - } - */ + // Sync the current block's state to the database and cancelling out the deferred Undo + bm.bc.CurrentBlock.Sync() // 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 // processing @@ -175,7 +182,7 @@ func (bm *BlockManager) ProcessBlock(block *Block) error { 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 { fmt.Println("total diff failed") } @@ -246,6 +253,18 @@ func (bm *BlockManager) ValidateBlock(block *Block) error { 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 { // Get the coinbase rlp data addr := processor.GetAddr(block.Coinbase) @@ -254,7 +273,12 @@ func (bm *BlockManager) AccumelateRewards(processor *Block, block *Block) error 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 } @@ -263,363 +287,26 @@ func (bm *BlockManager) 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 - defer func() { - if r := recover(); r != nil { - fmt.Println("Recovered from VM execution with err =", r) - } - }() + /* + 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 + vm := &Vm{} + vm.Process(contract, NewState(block.state), RuntimeVars{ + address: tx.Hash()[12:], + blockNumber: block.BlockInfo().Number, + sender: tx.Sender(), + prevHash: block.PrevHash, + coinbase: block.Coinbase, + 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() -} diff --git a/ethchain/block_manager_test.go b/ethchain/block_manager_test.go index 502c50b97b..ec4fbe8c5c 100644 --- a/ethchain/block_manager_test.go +++ b/ethchain/block_manager_test.go @@ -1,75 +1,33 @@ package ethchain -/* import ( _ "fmt" + "github.com/ethereum/eth-go/ethdb" + "github.com/ethereum/eth-go/ethutil" + "math/big" "testing" ) func TestVm(t *testing.T) { InitFees() + ethutil.ReadConfig("") - db, _ := NewMemDatabase() - Db = db + db, _ := ethdb.NewMemDatabase() + ethutil.Config.Db = db + bm := NewBlockManager(nil) - 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", + block := bm.bc.genesisBlock + script := Compile([]string{ + "PUSH", + "1", + "PUSH", + "2", }) - 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}) - db.Put(block.Hash(), block.RlpEncode()) - - bm := NewBlockManager() - bm.ProcessBlock(block) + tx2 := NewTransaction(addr, big.NewInt(1e17), nil) + tx2.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + bm.ApplyTransactions(block, []*Transaction{tx2}) } -*/ diff --git a/ethchain/contract.go b/ethchain/contract.go index 70189593b6..dbcbb3697c 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -30,37 +30,42 @@ func (c *Contract) RlpDecode(data []byte) { 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 { return c.state } -type Address struct { - Amount *big.Int - Nonce uint64 +func (c *Contract) GetMem(num int) *ethutil.Value { + nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256) + + return c.Addr(nb) } -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() +func MakeContract(tx *Transaction, state *State) *Contract { + // Create contract if there's no recipient + if tx.IsContract() { + addr := tx.Hash()[12:] + + value := tx.Value + contract := NewContract(value, []byte("")) + state.trie.Update(string(addr), string(contract.RlpEncode())) + for i, val := range tx.Data { + if len(val) > 0 { + bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256) + contract.state.Update(string(bytNum), string(ethutil.Encode(val))) + } + } + state.trie.Update(string(addr), string(contract.RlpEncode())) + + return contract + } + + return nil } diff --git a/ethchain/fees.go b/ethchain/fees.go index 8f1646ab4c..02f09fa04e 100644 --- a/ethchain/fees.go +++ b/ethchain/fees.go @@ -4,22 +4,32 @@ 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 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 Period2Reward *big.Int = new(big.Int) var Period3Reward *big.Int = new(big.Int) var Period4Reward *big.Int = new(big.Int) 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 b60 := new(big.Int) diff --git a/ethchain/genesis.go b/ethchain/genesis.go index 060d347e42..935978a69e 100644 --- a/ethchain/genesis.go +++ b/ethchain/genesis.go @@ -13,7 +13,7 @@ var ZeroHash256 = make([]byte, 32) var ZeroHash160 = make([]byte, 20) var EmptyShaList = ethutil.Sha3Bin(ethutil.Encode([]interface{}{})) -var GenisisHeader = []interface{}{ +var GenesisHeader = []interface{}{ // Previous hash (none) //"", ZeroHash256, @@ -36,4 +36,4 @@ var GenisisHeader = []interface{}{ ethutil.Sha3Bin(big.NewInt(42).Bytes()), } -var Genesis = []interface{}{GenisisHeader, []interface{}{}, []interface{}{}} +var Genesis = []interface{}{GenesisHeader, []interface{}{}, []interface{}{}} diff --git a/ethchain/stack.go b/ethchain/stack.go index c80d01e5c2..13b0f247b6 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -9,57 +9,57 @@ 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 + oSTOP = 0x00 + oADD = 0x01 + oMUL = 0x02 + oSUB = 0x03 + oDIV = 0x04 + oSDIV = 0x05 + oMOD = 0x06 + oSMOD = 0x07 + oEXP = 0x08 + oNEG = 0x09 + oLT = 0x0a + oLE = 0x0b + oGT = 0x0c + oGE = 0x0d + oEQ = 0x0e + oNOT = 0x0f + oMYADDRESS = 0x10 + oTXSENDER = 0x11 + oTXVALUE = 0x12 + oTXDATAN = 0x13 + oTXDATA = 0x14 + oBLK_PREVHASH = 0x15 + oBLK_COINBASE = 0x16 + oBLK_TIMESTAMP = 0x17 + oBLK_NUMBER = 0x18 + oBLK_DIFFICULTY = 0x19 + oBLK_NONCE = 0x1a + oBASEFEE = 0x1b + oSHA256 = 0x20 + oRIPEMD160 = 0x21 + oECMUL = 0x22 + oECADD = 0x23 + oECSIGN = 0x24 + oECRECOVER = 0x25 + oECVALID = 0x26 + oSHA3 = 0x27 + oPUSH = 0x30 + oPOP = 0x31 + oDUP = 0x32 + oSWAP = 0x33 + oMLOAD = 0x34 + oMSTORE = 0x35 + oSLOAD = 0x36 + oSSTORE = 0x37 + oJMP = 0x38 + oJMPI = 0x39 + oIND = 0x3a + oEXTRO = 0x3b + oBALANCE = 0x3c + oMKTX = 0x3d + oSUICIDE = 0x3f ) // 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", oTXSENDER: "TXSENDER", oTXVALUE: "TXVALUE", - oTXFEE: "TXFEE", oTXDATAN: "TXDATAN", oTXDATA: "TXDATA", oBLK_PREVHASH: "BLK_PREVHASH", @@ -159,9 +158,33 @@ func (st *Stack) Popn() (*big.Int, *big.Int) { 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) { st.data = append(st.data, d) } 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("#############") } diff --git a/ethchain/state.go b/ethchain/state.go new file mode 100644 index 0000000000..1a18ea1d79 --- /dev/null +++ b/ethchain/state.go @@ -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())) +} diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 1a92582010..57df9cdc4d 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -1,11 +1,14 @@ package ethchain import ( + "bytes" "github.com/ethereum/eth-go/ethutil" "github.com/obscuren/secp256k1-go" "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 { Nonce uint64 Recipient []byte @@ -21,20 +24,17 @@ func NewTransaction(to []byte, value *big.Int, data []string) *Transaction { 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 - } + tx.Data = data return &tx } +// XXX Deprecated func NewTransactionFromData(data []byte) *Transaction { + return NewTransactionFromBytes(data) +} + +func NewTransactionFromBytes(data []byte) *Transaction { tx := &Transaction{} tx.RlpDecode(data) @@ -65,7 +65,7 @@ func (tx *Transaction) Hash() []byte { } func (tx *Transaction) IsContract() bool { - return len(tx.Recipient) == 0 + return bytes.Compare(tx.Recipient, ContractAddr) == 0 } func (tx *Transaction) Signature(key []byte) []byte { diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index c2d65a2a7b..cd09bf02ed 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -17,6 +17,17 @@ const ( ) 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 { for e := pool.Front(); e != nil; e = e.Next() { @@ -34,6 +45,10 @@ type PublicSpeaker 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 // guarantee a non blocking pool we use a queue channel which can be // independently read without needing access to the actual pool. If the @@ -54,7 +69,9 @@ type TxPool struct { BlockManager *BlockManager - Hook TxPoolHook + SecondaryProcessor TxProcessor + + subscribers []chan TxMsg } func NewTxPool() *TxPool { @@ -80,8 +97,6 @@ func (pool *TxPool) addTransaction(tx *Transaction) { // 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) @@ -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 receiver := block.GetAddr(tx.Recipient) - // Add the amount to receivers account which should conclude this transaction - receiver.Amount.Add(receiver.Amount, tx.Value) + sender.Nonce += 1 + + // 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.Recipient, receiver) + + log.Printf("[TXPL] Processed Tx %x\n", tx.Hash()) + + // Notify the subscribers + pool.notifySubscribers(TxPost, tx) return } @@ -131,7 +159,8 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error { } // 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)) // 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 pool.addTransaction(tx) - if pool.Hook != nil { - pool.Hook <- tx - } + // Notify the subscribers + pool.notifySubscribers(TxPre, tx) } case <-pool.quit: break out @@ -217,3 +245,14 @@ func (pool *TxPool) Stop() { 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 + } +} diff --git a/ethchain/transaction_test.go b/ethchain/transaction_test.go index c9090b83df..a49768aea2 100644 --- a/ethchain/transaction_test.go +++ b/ethchain/transaction_test.go @@ -2,8 +2,6 @@ package ethchain import ( "encoding/hex" - "fmt" - "github.com/ethereum/eth-go/ethutil" "math/big" "testing" ) @@ -42,13 +40,15 @@ func TestAddressRetrieval2(t *testing.T) { tx.Sign(key) //data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7") //tx := NewTransactionFromData(data) - fmt.Println(tx.RlpValue()) + /* + fmt.Println(tx.RlpValue()) - fmt.Printf("rlp %x\n", tx.RlpEncode()) - fmt.Printf("sha rlp %x\n", tx.Hash()) + fmt.Printf("rlp %x\n", tx.RlpEncode()) + 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("seder %x\n", tx.Sender()) + fmt.Printf("hex tx key %x\n", tx.PublicKey()) + fmt.Printf("seder %x\n", tx.Sender()) + */ } diff --git a/ethchain/vm.go b/ethchain/vm.go new file mode 100644 index 0000000000..c7a91a9c52 --- /dev/null +++ b/ethchain/vm.go @@ -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() +} diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go new file mode 100644 index 0000000000..6ceb0ff150 --- /dev/null +++ b/ethchain/vm_test.go @@ -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, + }) +} diff --git a/ethdb/database.go b/ethdb/database.go index 76e4b4e4df..3dbff36dea 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -11,8 +11,8 @@ type LDBDatabase struct { db *leveldb.DB } -func NewLDBDatabase() (*LDBDatabase, error) { - dbPath := path.Join(ethutil.Config.ExecPath, "database") +func NewLDBDatabase(name string) (*LDBDatabase, error) { + dbPath := path.Join(ethutil.Config.ExecPath, name) // Open the db db, err := leveldb.OpenFile(dbPath, nil) @@ -36,6 +36,14 @@ func (db *LDBDatabase) Get(key []byte) ([]byte, error) { 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 { data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil) @@ -46,13 +54,19 @@ func (db *LDBDatabase) LastKnownTD() []byte { return data } +func (db *LDBDatabase) GetKeys() []*ethutil.Key { + data, _ := db.Get([]byte("KeyRing")) + + return []*ethutil.Key{ethutil.NewKeyFromBytes(data)} +} + func (db *LDBDatabase) Close() { // Close the leveldb database db.db.Close() } func (db *LDBDatabase) Print() { - iter := db.db.NewIterator(nil) + iter := db.db.NewIterator(nil, nil) for iter.Next() { key := iter.Key() value := iter.Value() diff --git a/ethdb/memory_database.go b/ethdb/memory_database.go index cd9f240005..b0fa64ed78 100644 --- a/ethdb/memory_database.go +++ b/ethdb/memory_database.go @@ -26,6 +26,18 @@ func (db *MemDatabase) Get(key []byte) ([]byte, error) { 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() { for key, val := range db.db { fmt.Printf("%x(%d): ", key, len(key)) diff --git a/ethereum.go b/ethereum.go index bd6caac089..90682b396d 100644 --- a/ethereum.go +++ b/ethereum.go @@ -47,6 +47,7 @@ type Ethereum struct { Nonce uint64 Addr net.Addr + Port string peerMut sync.Mutex @@ -60,7 +61,7 @@ type Ethereum struct { } func New(caps Caps, usePnp bool) (*Ethereum, error) { - db, err := ethdb.NewLDBDatabase() + db, err := ethdb.NewLDBDatabase("database") //db, err := ethdb.NewMemDatabase() if err != nil { return nil, err @@ -70,7 +71,7 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) { if usePnp { nat, err = Discover() 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, serverCaps: caps, nat: nat, - MaxPeers: 5, } ethereum.TxPool = ethchain.NewTxPool() ethereum.TxPool.Speaker = ethereum @@ -94,6 +94,9 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) { ethereum.TxPool.BlockManager = ethereum.BlockManager ethereum.BlockManager.TransactionPool = ethereum.TxPool + // Start the tx pool + ethereum.TxPool.Start() + return ethereum, nil } @@ -114,29 +117,33 @@ func (s *Ethereum) ProcessPeerList(addrs []string) { } 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) { - if p.conn == nil { - return + eachPeer(s.peers, func(p *Peer, v *list.Element) { + if p.conn == nil { + 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 { - alreadyConnected = true - return - } - }) + peer := NewOutboundPeer(addr, s, s.serverCaps) - if alreadyConnected { - return nil + s.peers.PushBack(peer) + + 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 } @@ -226,12 +233,12 @@ func (s *Ethereum) ReapDeadPeerHandler() { // Start the ethereum func (s *Ethereum) Start() { // Bind to addr and port - ln, err := net.Listen("tcp", ":30303") + ln, err := net.Listen("tcp", ":"+s.Port) if err != nil { log.Println("Connection listening disabled. Acting as client") } else { // Starting accepting connections - log.Println("Ready and accepting connections") + ethutil.Config.Log.Infoln("Ready and accepting connections") // Start the peer handler go s.peerHandler(ln) } @@ -243,13 +250,10 @@ func (s *Ethereum) Start() { // Start the reaping processes go s.ReapDeadPeerHandler() - // Start the tx pool - s.TxPool.Start() - if ethutil.Config.Seed { - log.Println("Seeding") + ethutil.Config.Log.Debugln("Seeding") // 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 { log.Println("Fetching seed failed:", err) return @@ -269,7 +273,7 @@ func (s *Ethereum) peerHandler(listener net.Listener) { for { conn, err := listener.Accept() if err != nil { - log.Println(err) + ethutil.Config.Log.Debugln(err) continue } @@ -303,7 +307,7 @@ func (s *Ethereum) upnpUpdateThread() { // Go off immediately to prevent code duplication, thereafter we renew // lease every 15 minutes. timer := time.NewTimer(0 * time.Second) - lport, _ := strconv.ParseInt("30303", 10, 16) + lport, _ := strconv.ParseInt(s.Port, 10, 16) first := true out: for { @@ -312,13 +316,13 @@ out: var err error _, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60) 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 } if first && err == nil { _, err = s.nat.GetExternalAddress() 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 } first = false @@ -332,8 +336,8 @@ out: timer.Stop() 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 { - log.Println("succesfully disestablished UPnP port mapping") + ethutil.Config.Log.Debugln("succesfully disestablished UPnP port mapping") } } diff --git a/ethutil/README.md b/ethutil/README.md index c98612e1e3..1ed56b71ba 100644 --- a/ethutil/README.md +++ b/ethutil/README.md @@ -53,6 +53,8 @@ trie.Put("doge", "coin") // Look up the key "do" in the trie out := trie.Get("do") fmt.Println(out) // => verb + +trie.Delete("puppy") ``` 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 `[]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. diff --git a/ethutil/big.go b/ethutil/big.go index 979078bef9..1a3902fa3e 100644 --- a/ethutil/big.go +++ b/ethutil/big.go @@ -35,3 +35,18 @@ func BigD(data []byte) *big.Int { 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 +} diff --git a/ethutil/common.go b/ethutil/common.go new file mode 100644 index 0000000000..07df6bb138 --- /dev/null +++ b/ethutil/common.go @@ -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) +} diff --git a/ethutil/common_test.go b/ethutil/common_test.go new file mode 100644 index 0000000000..3a6a37ff57 --- /dev/null +++ b/ethutil/common_test.go @@ -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))) +} diff --git a/ethutil/config.go b/ethutil/config.go index 2a239f8e25..5bf56134d1 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -1,6 +1,7 @@ package ethutil import ( + "fmt" "log" "os" "os/user" @@ -18,7 +19,7 @@ const ( type config struct { Db Database - Log Logger + Log *Logger ExecPath string Debug bool Ver string @@ -34,17 +35,19 @@ func ReadConfig(base string) *config { usr, _ := user.Current() path := path.Join(usr.HomeDir, base) - //Check if the logging directory already exists, create it if not - _, err := os.Stat(path) - if err != nil { - if os.IsNotExist(err) { - log.Printf("Debug logging directory %s doesn't exist, creating it", path) - os.Mkdir(path, 0777) + if len(base) > 0 { + //Check if the logging directory already exists, create it if not + _, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + log.Printf("Debug logging directory %s doesn't exist, creating it\n", path) + os.Mkdir(path, 0777) + } } } - Config = &config{ExecPath: path, Debug: true, Ver: "0.2.2"} - Config.Log = NewLogger(LogFile|LogStd, 0) + Config = &config{ExecPath: path, Debug: true, Ver: "0.3.0"} + Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug) } return Config @@ -57,15 +60,20 @@ const ( LogStd = 0x2 ) +type LogSystem interface { + Println(v ...interface{}) + Printf(format string, v ...interface{}) +} + type Logger struct { - logSys []*log.Logger + logSys []LogSystem logLevel int } -func NewLogger(flag LoggerType, level int) Logger { - var loggers []*log.Logger +func NewLogger(flag LoggerType, level int) *Logger { + var loggers []LogSystem - flags := log.LstdFlags | log.Lshortfile + flags := log.LstdFlags 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) @@ -73,20 +81,29 @@ func NewLogger(flag LoggerType, level int) Logger { log.Panic("unable to create file logger", err) } - log := log.New(file, "[ETH]", flags) + log := log.New(file, "", flags) loggers = append(loggers, log) } if flag&LogStd > 0 { - log := log.New(os.Stdout, "[ETH]", flags) + log := log.New(os.Stdout, "", flags) loggers = append(loggers, log) } - return Logger{logSys: loggers, logLevel: level} + return &Logger{logSys: loggers, logLevel: level} } -func (log Logger) Debugln(v ...interface{}) { - if log.logLevel != 0 { +func (log *Logger) AddLogSystem(logger LogSystem) { + log.logSys = append(log.logSys, logger) +} + +const ( + LogLevelDebug = iota + LogLevelInfo +) + +func (log *Logger) Debugln(v ...interface{}) { + if log.logLevel != LogLevelDebug { return } @@ -95,8 +112,29 @@ func (log Logger) Debugln(v ...interface{}) { } } -func (log Logger) Debugf(format string, v ...interface{}) { - if log.logLevel != 0 { +func (log *Logger) Debugf(format string, v ...interface{}) { + 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 } diff --git a/ethutil/db.go b/ethutil/db.go index 3681c4b058..abbf4a2b09 100644 --- a/ethutil/db.go +++ b/ethutil/db.go @@ -4,6 +4,8 @@ package ethutil type Database interface { Put(key []byte, value []byte) Get(key []byte) ([]byte, error) + GetKeys() []*Key + Delete(key []byte) error LastKnownTD() []byte Close() Print() diff --git a/ethutil/encoding.go b/ethutil/encoding.go index 207548c933..1f661947a7 100644 --- a/ethutil/encoding.go +++ b/ethutil/encoding.go @@ -3,7 +3,6 @@ package ethutil import ( "bytes" "encoding/hex" - _ "fmt" "strings" ) @@ -36,7 +35,7 @@ func CompactEncode(hexSlice []int) string { func CompactDecode(str string) []int { base := CompactHexDecode(str) base = base[:len(base)-1] - if base[0] >= 2 { // && base[len(base)-1] != 16 { + if base[0] >= 2 { base = append(base, 16) } if base[0]%2 == 1 { diff --git a/ethutil/encoding_test.go b/ethutil/encoding_test.go index bcabab0b19..cbfbc0eafa 100644 --- a/ethutil/encoding_test.go +++ b/ethutil/encoding_test.go @@ -35,3 +35,33 @@ func TestCompactHexDecode(t *testing.T) { 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) + } +} \ No newline at end of file diff --git a/ethutil/helpers.go b/ethutil/helpers.go index 1c6adf2569..aa0f79a044 100644 --- a/ethutil/helpers.go +++ b/ethutil/helpers.go @@ -27,7 +27,6 @@ func Ripemd160(data []byte) []byte { func Sha3Bin(data []byte) []byte { d := sha3.NewKeccak256() - d.Reset() d.Write(data) return d.Sum(nil) @@ -59,3 +58,7 @@ func MatchingNibbleLength(a, b []int) int { func Hex(d []byte) string { return hex.EncodeToString(d) } +func FromHex(str string) []byte { + h, _ := hex.DecodeString(str) + return h +} diff --git a/ethutil/key.go b/ethutil/key.go new file mode 100644 index 0000000000..ec195f2136 --- /dev/null +++ b/ethutil/key.go @@ -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() +} diff --git a/ethutil/parsing.go b/ethutil/parsing.go index 2c41fb4df2..553bb97178 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -1,95 +1,88 @@ package ethutil import ( - "errors" - "fmt" "math/big" "strconv" - "strings" ) // Op codes -var OpCodes = map[string]string{ - "STOP": "0", - "ADD": "1", - "MUL": "2", - "SUB": "3", - "DIV": "4", - "SDIV": "5", - "MOD": "6", - "SMOD": "7", - "EXP": "8", - "NEG": "9", - "LT": "10", - "LE": "11", - "GT": "12", - "GE": "13", - "EQ": "14", - "NOT": "15", - "MYADDRESS": "16", - "TXSENDER": "17", - - "PUSH": "48", - "POP": "49", - "LOAD": "54", +var OpCodes = map[string]byte{ + "STOP": 0x00, + "ADD": 0x01, + "MUL": 0x02, + "SUB": 0x03, + "DIV": 0x04, + "SDIV": 0x05, + "MOD": 0x06, + "SMOD": 0x07, + "EXP": 0x08, + "NEG": 0x09, + "LT": 0x0a, + "LE": 0x0b, + "GT": 0x0c, + "GE": 0x0d, + "EQ": 0x0e, + "NOT": 0x0f, + "MYADDRESS": 0x10, + "TXSENDER": 0x11, + "TXVALUE": 0x12, + "TXDATAN": 0x13, + "TXDATA": 0x14, + "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) { - tokens := strings.Split(s, " ") - if OpCodes[tokens[0]] == "" { - return s, errors.New(fmt.Sprintf("OP not found: %s", tokens[0])) +func IsOpCode(s string) bool { + for key, _ := range OpCodes { + if key == s { + 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 - op := new(big.Int) - op.SetString(code, 0) + num := new(big.Int) + num.SetString(s, 0) - args := make([]*big.Int, 6) - for i, val := range tokens[1:len(tokens)] { - num := new(big.Int) - num.SetString(val, 0) - args[i] = num - } - - // Big int equation = op + x * 256 + y * 256**2 + z * 256**3 + a * 256**4 + b * 256**5 + c * 256**6 - base := new(big.Int) - x := new(big.Int) - y := new(big.Int) - z := new(big.Int) - a := new(big.Int) - b := new(big.Int) - c := new(big.Int) - - if args[0] != nil { - x.Mul(args[0], big.NewInt(256)) - } - if args[1] != nil { - y.Mul(args[1], BigPow(256, 2)) - } - if args[2] != nil { - z.Mul(args[2], BigPow(256, 3)) - } - if args[3] != nil { - a.Mul(args[3], BigPow(256, 4)) - } - if args[4] != nil { - b.Mul(args[4], BigPow(256, 5)) - } - if args[5] != nil { - c.Mul(args[5], BigPow(256, 6)) - } - - base.Add(op, x) - base.Add(base, y) - base.Add(base, z) - base.Add(base, a) - base.Add(base, b) - base.Add(base, c) - - return base.String(), nil + return num.Bytes(), nil } func Instr(instr string) (int, []string, error) { + base := new(big.Int) base.SetString(instr, 0) diff --git a/ethutil/parsing_test.go b/ethutil/parsing_test.go index 482eef3ee8..6b59777e69 100644 --- a/ethutil/parsing_test.go +++ b/ethutil/parsing_test.go @@ -1,5 +1,6 @@ package ethutil +/* import ( "math" "testing" @@ -13,20 +14,19 @@ func TestCompile(t *testing.T) { } 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) } } func TestValidInstr(t *testing.T) { - /* op, args, err := Instr("68163") if err != nil { t.Error("Error decoding instruction") } - */ } func TestInvalidInstr(t *testing.T) { } +*/ diff --git a/ethutil/rlp.go b/ethutil/rlp.go index 025d269a0d..e633f5f1dc 100644 --- a/ethutil/rlp.go +++ b/ethutil/rlp.go @@ -86,13 +86,6 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} { // TODO Use a bytes.Buffer instead of a raw byte slice. // Cleaner code, and use draining instead of seeking the next bytes to read func Decode(data []byte, pos uint64) (interface{}, uint64) { - /* - if pos > uint64(len(data)-1) { - log.Println(data) - log.Panicf("index out of range %d for data %q, l = %d", pos, data, len(data)) - } - */ - var slice []interface{} char := int(data[pos]) switch { @@ -131,7 +124,6 @@ func Decode(data []byte, pos uint64) (interface{}, uint64) { case char <= 0xff: l := uint64(data[pos]) - 0xf7 - //b := BigD(data[pos+1 : pos+1+l]).Uint64() b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l])) pos = pos + l + 1 diff --git a/ethutil/rlp_test.go b/ethutil/rlp_test.go index 32bcbdce1d..2a58bfc0fa 100644 --- a/ethutil/rlp_test.go +++ b/ethutil/rlp_test.go @@ -2,15 +2,13 @@ package ethutil import ( "bytes" - "encoding/hex" - "fmt" "math/big" "reflect" "testing" ) func TestRlpValueEncoding(t *testing.T) { - val := EmptyRlpValue() + val := EmptyValue() val.AppendList().Append(1).Append(2).Append(3) val.Append("4").AppendList().Append(5) @@ -63,7 +61,7 @@ func TestEncode(t *testing.T) { str := string(bytes) 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" @@ -71,7 +69,7 @@ func TestEncode(t *testing.T) { bytes = Encode(strs) slice := string(bytes) 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" @@ -108,13 +106,9 @@ func TestEncodeDecodeBigInt(t *testing.T) { encoded := Encode(bigInt) value := NewValueFromBytes(encoded) - fmt.Println(value.BigInt(), bigInt) if value.BigInt().Cmp(bigInt) != 0 { t.Errorf("Expected %v, got %v", bigInt, value.BigInt()) } - - dec, _ := hex.DecodeString("52f4fc1e") - fmt.Println(NewValueFromBytes(dec).BigInt()) } func TestEncodeDecodeBytes(t *testing.T) { @@ -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) { for i := 0; i < b.N; i++ { bytes := Encode([]interface{}{"dog", "god", "cat"}) diff --git a/ethutil/trie.go b/ethutil/trie.go index 0a3f731362..a17dc37ad9 100644 --- a/ethutil/trie.go +++ b/ethutil/trie.go @@ -5,6 +5,15 @@ import ( "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 { Key []byte Value *Value @@ -20,8 +29,9 @@ func (n *Node) Copy() *Node { } type Cache struct { - nodes map[string]*Node - db Database + nodes map[string]*Node + db Database + IsDirty bool } func NewCache(db Database) *Cache { @@ -36,6 +46,7 @@ func (cache *Cache) Put(v interface{}) interface{} { sha := Sha3Bin(enc) cache.nodes[string(sha)] = NewNode(sha, value, true) + cache.IsDirty = true return sha } @@ -59,13 +70,25 @@ func (cache *Cache) Get(key []byte) *Value { return value } +func (cache *Cache) Delete(key []byte) { + delete(cache.nodes, string(key)) + + cache.db.Delete(key) +} + func (cache *Cache) Commit() { + // Don't try to commit if it isn't dirty + if !cache.IsDirty { + return + } + for key, node := range cache.nodes { if node.Dirty { cache.db.Put([]byte(key), node.Value.Encode()) node.Dirty = false } } + cache.IsDirty = false // If the nodes grows beyond the 200 entries we simple empty it // FIXME come up with something better @@ -80,6 +103,7 @@ func (cache *Cache) Undo() { delete(cache.nodes, key) } } + cache.IsDirty = false } // 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 // explicitly called. type Trie struct { - Root interface{} + prevRoot interface{} + Root interface{} //db Database cache *Cache } 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. func (t *Trie) Sync() { 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() } +func (t *Trie) Delete(key string) { + t.Update(key, "") +} + func (t *Trie) GetState(node interface{}, key []int) interface{} { n := NewValue(node) // 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{} { + if value != "" { return t.InsertState(node, key, value) } else { // delete it + return t.DeleteState(node, key) } - return "" + return t.Root } 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 if currentNode.Len() == 2 { // Decode the key + k := CompactDecode(currentNode.Get(0).Str()) v := currentNode.Get(1).Raw() @@ -282,6 +324,87 @@ func (t *Trie) InsertState(node interface{}, key []int, value interface{}) inter 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 func (t *Trie) Cmp(trie *Trie) bool { return NewValue(t.Root).Cmp(NewValue(trie.Root)) @@ -296,3 +419,82 @@ func (t *Trie) Copy() *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 "" +} diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go index b87d35e1ab..7c398f1de9 100644 --- a/ethutil/trie_test.go +++ b/ethutil/trie_test.go @@ -1,11 +1,12 @@ package ethutil import ( - _ "encoding/hex" - _ "fmt" + "reflect" "testing" ) +const LONG_WORD = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ" + type MemDatabase struct { db map[string][]byte } @@ -20,15 +21,24 @@ func (db *MemDatabase) Put(key []byte, value []byte) { func (db *MemDatabase) Get(key []byte) ([]byte, error) { 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) Close() {} func (db *MemDatabase) LastKnownTD() []byte { return nil } -func TestTrieSync(t *testing.T) { +func New() (*MemDatabase, *Trie) { 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 { t.Error("Expected no data in database") } @@ -38,3 +48,125 @@ func TestTrieSync(t *testing.T) { 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") + } +} diff --git a/ethutil/value.go b/ethutil/value.go index 2a990783e0..3dd84d12dd 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -36,7 +36,8 @@ func (val *Value) Len() int { if data, ok := val.Val.([]interface{}); ok { return len(data) } else if data, ok := val.Val.([]byte); ok { - // FIXME + return len(data) + } else if data, ok := val.Val.(string); ok { return len(data) } @@ -60,6 +61,10 @@ func (val *Value) Uint() uint64 { return uint64(Val) } else if Val, ok := val.Val.(uint64); ok { 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 { return ReadVarint(bytes.NewReader(Val)) } @@ -80,6 +85,8 @@ func (val *Value) BigInt() *big.Int { b := new(big.Int).SetBytes(a) return b + } else if a, ok := val.Val.(*big.Int); ok { + return a } else { return big.NewInt(int64(val.Uint())) } @@ -92,6 +99,8 @@ func (val *Value) Str() string { return string(a) } else if a, ok := val.Val.(string); ok { return a + } else if a, ok := val.Val.(byte); ok { + return string(a) } return "" @@ -102,7 +111,7 @@ func (val *Value) Bytes() []byte { return a } - return make([]byte, 0) + return []byte{} } func (val *Value) Slice() []interface{} { @@ -131,6 +140,19 @@ func (val *Value) SliceFromTo(from, to int) *Value { 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 func (val *Value) Get(idx int) *Value { if d, ok := val.Val.([]interface{}); ok { @@ -140,7 +162,7 @@ func (val *Value) Get(idx int) *Value { } if idx < 0 { - panic("negative idx for Rlp Get") + panic("negative idx for Value Get") } return NewValue(d[idx]) @@ -158,9 +180,9 @@ func (val *Value) Encode() []byte { return Encode(val.Val) } -func NewValueFromBytes(rlpData []byte) *Value { - if len(rlpData) != 0 { - data, _ := Decode(rlpData, 0) +func NewValueFromBytes(data []byte) *Value { + if len(data) != 0 { + data, _ := Decode(data, 0) return NewValue(data) } diff --git a/ethutil/value_test.go b/ethutil/value_test.go new file mode 100644 index 0000000000..0e2da53287 --- /dev/null +++ b/ethutil/value_test.go @@ -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()) + } +} diff --git a/ethwire/messaging.go b/ethwire/messaging.go index 651bf4710c..185faa341a 100644 --- a/ethwire/messaging.go +++ b/ethwire/messaging.go @@ -19,6 +19,9 @@ var MagicToken = []byte{34, 64, 8, 145} type MsgType byte 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 MsgDiscTy = 0x01 MsgPingTy = 0x02 diff --git a/peer.go b/peer.go index 7b17b8b09d..a8708206f3 100644 --- a/peer.go +++ b/peer.go @@ -6,7 +6,6 @@ import ( "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" - "log" "net" "runtime" "strconv" @@ -23,6 +22,9 @@ const ( type DiscReason byte 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 DiscReTcpSysErr = 0x01 DiscBadProto = 0x02 @@ -56,9 +58,9 @@ func (d DiscReason) String() string { type Caps byte const ( - CapPeerDiscTy = 0x01 - CapTxTy = 0x02 - CapChainTy = 0x04 + CapPeerDiscTy = 1 << iota + CapTxTy + CapChainTy 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) if err != nil { - log.Println("Connection to peer failed", err) + ethutil.Config.Log.Debugln("Connection to peer failed", err) p.Stop() return } @@ -199,7 +201,7 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) { err := ethwire.WriteMessage(p.conn, msg) 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 p.Stop() return @@ -261,7 +263,7 @@ func (p *Peer) HandleInbound() { // Wait for a message from the peer msgs, err := ethwire.ReadMessages(p.conn) if err != nil { - log.Println(err) + ethutil.Config.Log.Debugln(err) } for _, msg := range msgs { switch msg.Type { @@ -274,7 +276,7 @@ func (p *Peer) HandleInbound() { } case ethwire.MsgDiscTy: 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: // Respond back with pong p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, "")) @@ -285,7 +287,6 @@ func (p *Peer) HandleInbound() { p.lastPong = time.Now().Unix() case ethwire.MsgBlockTy: // Get all blocks and process them - msg.Data = msg.Data var block, lastBlock *ethchain.Block var err error for i := msg.Data.Len() - 1; i >= 0; i-- { @@ -293,7 +294,10 @@ func (p *Peer) HandleInbound() { err = p.ethereum.BlockManager.ProcessBlock(block) 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 } else { lastBlock = block @@ -303,7 +307,7 @@ func (p *Peer) HandleInbound() { if err != nil { // If the parent is unknown try to catch up with this peer if ethchain.IsParentErr(err) { - log.Println("Attempting to catch up") + ethutil.Config.Log.Infoln("Attempting to catch up") p.catchingUp = false p.CatchupWithPeer() } else if ethchain.IsValidationErr(err) { @@ -315,7 +319,7 @@ func (p *Peer) HandleInbound() { if p.catchingUp && msg.Data.Len() > 1 { if ethutil.Config.Debug && lastBlock != nil { 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.CatchupWithPeer() @@ -386,12 +390,12 @@ func (p *Peer) HandleInbound() { p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()})) } case ethwire.MsgNotInChainTy: - log.Printf("Not in chain %x\n", msg.Data) + ethutil.Config.Log.Infof("Not in chain %x\n", msg.Data) // TODO // Unofficial but fun nonetheless 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() 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() @@ -465,7 +469,7 @@ func (p *Peer) pushHandshake() error { pubkey := ethutil.NewValueFromBytes(data).Get(2).Bytes() 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) @@ -492,8 +496,8 @@ func (p *Peer) pushPeers() { func (p *Peer) handleHandshake(msg *ethwire.Msg) { c := msg.Data - if c.Get(0).Uint() != 4 { - log.Println("Invalid peer version. Require protocol v4") + if c.Get(0).Uint() != 5 { + ethutil.Config.Log.Debugln("Invalid peer version. Require protocol v5") p.Stop() return } @@ -525,7 +529,7 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { // Get a reference to the peers version p.Version = c.Get(2).Str() - log.Println(p) + ethutil.Config.Log.Debugln("[PEER]", p) } func (p *Peer) String() string { @@ -542,7 +546,7 @@ func (p *Peer) String() string { 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)}) 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]) } }