diff options
39 files changed, 481 insertions, 156 deletions
diff --git a/cmd/evm/main.go b/cmd/evm/main.go index be6546c95..6639069b9 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -102,7 +102,7 @@ func init() { func run(ctx *cli.Context) { vm.Debug = ctx.GlobalBool(DebugFlag.Name) vm.ForceJit = ctx.GlobalBool(ForceJitFlag.Name) - vm.DisableJit = ctx.GlobalBool(DisableJitFlag.Name) + vm.EnableJit = !ctx.GlobalBool(DisableJitFlag.Name) glog.SetToStderr(true) diff --git a/cmd/geth/js.go b/cmd/geth/js.go index 485e17697..1ca59e2f4 100644 --- a/cmd/geth/js.go +++ b/cmd/geth/js.go @@ -145,19 +145,15 @@ func apiWordCompleter(line string, pos int) (head string, completions []string, return begin, completionWords, end } -func newLightweightJSRE(libPath string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre { +func newLightweightJSRE(libPath string, client comms.EthereumClient, interactive bool) *jsre { js := &jsre{ps1: "> "} js.wait = make(chan *big.Int) js.client = client js.ds = docserver.New("/") - if f == nil { - f = js - } - // update state in separare forever blocks js.re = re.New(libPath) - if err := js.apiBindings(f); err != nil { + if err := js.apiBindings(js); err != nil { utils.Fatalf("Unable to initialize console - %v", err) } @@ -291,7 +287,7 @@ func (js *jsre) apiBindings(f xeth.Frontend) error { utils.Fatalf("Unable to determine supported api's: %v", err) } - jeth := rpc.NewJeth(api.Merge(apiImpl...), js.re, js.client) + jeth := rpc.NewJeth(api.Merge(apiImpl...), js.re, js.client, f) js.re.Set("jeth", struct{}{}) t, _ := js.re.Get("jeth") jethObj := t.Object() diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 0bdcddf50..2dc3c438f 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -48,7 +48,7 @@ import ( ) const ( - ClientIdentifier = "Geth " + ClientIdentifier = "Geth" Version = "1.0.1" VersionMajor = 1 VersionMinor = 0 @@ -414,7 +414,7 @@ func attach(ctx *cli.Context) { ctx.GlobalString(utils.JSpathFlag.Name), client, true, - nil) + ) if ctx.GlobalString(utils.ExecFlag.Name) != "" { repl.batch(ctx.GlobalString(utils.ExecFlag.Name)) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 462da9305..af2929d10 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -21,30 +21,32 @@ import ( "fmt" "log" "math/big" + "net" "net/http" "os" "path/filepath" "runtime" "strconv" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/metrics" - "github.com/codegangsta/cli" "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/rpc/api" "github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/comms" + "github.com/ethereum/go-ethereum/rpc/shared" + "github.com/ethereum/go-ethereum/rpc/useragent" "github.com/ethereum/go-ethereum/xeth" ) @@ -452,7 +454,7 @@ func SetupLogger(ctx *cli.Context) { // SetupVM configured the VM package's global settings func SetupVM(ctx *cli.Context) { - vm.DisableJit = !ctx.GlobalBool(VMEnableJitFlag.Name) + vm.EnableJit = ctx.GlobalBool(VMEnableJitFlag.Name) vm.ForceJit = ctx.GlobalBool(VMForceJitFlag.Name) vm.SetJITCacheSize(ctx.GlobalInt(VMJitCacheFlag.Name)) } @@ -518,15 +520,20 @@ func StartIPC(eth *eth.Ethereum, ctx *cli.Context) error { Endpoint: IpcSocketPath(ctx), } - xeth := xeth.New(eth, nil) - codec := codec.JSON + initializer := func(conn net.Conn) (shared.EthereumApi, error) { + fe := useragent.NewRemoteFrontend(conn, eth.AccountManager()) + xeth := xeth.New(eth, fe) + codec := codec.JSON - apis, err := api.ParseApiString(ctx.GlobalString(IPCApiFlag.Name), codec, xeth, eth) - if err != nil { - return err + apis, err := api.ParseApiString(ctx.GlobalString(IPCApiFlag.Name), codec, xeth, eth) + if err != nil { + return nil, err + } + + return api.Merge(apis...), nil } - return comms.StartIpc(config, codec, api.Merge(apis...)) + return comms.StartIpc(config, codec.JSON, initializer) } func StartRPC(eth *eth.Ethereum, ctx *cli.Context) error { diff --git a/core/block_processor.go b/core/block_processor.go index 477215356..829e4314c 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -354,18 +354,8 @@ func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err erro for _, receipt := range receipts { logs = append(logs, receipt.Logs()...) } - return } - - // TODO: remove backward compatibility - var ( - parent = sm.bc.GetBlock(block.ParentHash()) - state = state.New(parent.Root(), sm.chainDb) - ) - - sm.TransitionState(state, parent, block, true) - - return state.Logs(), nil + return logs, nil } // See YP section 4.3.4. "Block Header Validity" diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 6b7b41220..2de35a443 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -341,19 +341,19 @@ func opCoinbase(instr instruction, env Environment, context *Context, memory *Me } func opTimestamp(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - stack.push(new(big.Int).SetUint64(env.Time())) + stack.push(U256(new(big.Int).SetUint64(env.Time()))) } func opNumber(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - stack.push(U256(env.BlockNumber())) + stack.push(U256(new(big.Int).Set(env.BlockNumber()))) } func opDifficulty(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - stack.push(new(big.Int).Set(env.Difficulty())) + stack.push(U256(new(big.Int).Set(env.Difficulty()))) } func opGasLimit(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - stack.push(new(big.Int).Set(env.GasLimit())) + stack.push(U256(new(big.Int).Set(env.GasLimit()))) } func opPop(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { @@ -415,15 +415,12 @@ func opSstore(instr instruction, env Environment, context *Context, memory *Memo env.State().SetState(context.Address(), loc, common.BigToHash(val)) } -func opJump(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { -} -func opJumpi(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { -} -func opJumpdest(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { -} +func opJump(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {} +func opJumpi(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {} +func opJumpdest(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {} func opPc(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { - stack.push(instr.data) + stack.push(new(big.Int).Set(instr.data)) } func opMsize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { diff --git a/core/vm/jit.go b/core/vm/jit.go index d5c2d7830..084d2a3f3 100644 --- a/core/vm/jit.go +++ b/core/vm/jit.go @@ -83,6 +83,7 @@ type Program struct { code []byte } +// NewProgram returns a new JIT program func NewProgram(code []byte) *Program { program := &Program{ Id: crypto.Sha3Hash(code), @@ -113,6 +114,7 @@ func (p *Program) addInstr(op OpCode, pc uint64, fn instrFn, data *big.Int) { p.mapping[pc] = len(p.instructions) - 1 } +// CompileProgram compiles the given program and return an error when it fails func CompileProgram(program *Program) (err error) { if progStatus(atomic.LoadInt32(&program.status)) == progCompile { return nil @@ -272,6 +274,8 @@ func CompileProgram(program *Program) (err error) { return nil } +// RunProgram runs the program given the enviroment and context and returns an +// error if the execution failed (non-consensus) func RunProgram(program *Program, env Environment, context *Context, input []byte) ([]byte, error) { return runProgram(program, 0, NewMemory(), newstack(), env, context, input) } @@ -352,6 +356,8 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env pc++ } + context.Input = nil + return context.Return(nil), nil } diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go index 5b3feea99..b9e2c6999 100644 --- a/core/vm/jit_test.go +++ b/core/vm/jit_test.go @@ -46,7 +46,7 @@ func runVmBench(test vmBench, b *testing.B) { } env := NewEnv() - DisableJit = test.nojit + EnableJit = !test.nojit ForceJit = test.forcejit b.ResetTimer() diff --git a/core/vm/settings.go b/core/vm/settings.go index 0cd931b6a..f9296f6c8 100644 --- a/core/vm/settings.go +++ b/core/vm/settings.go @@ -17,9 +17,9 @@ package vm var ( - DisableJit bool = true // Disable the JIT VM - ForceJit bool // Force the JIT, skip byte VM - MaxProgSize int // Max cache size for JIT Programs + EnableJit bool // Enables the JIT VM + ForceJit bool // Force the JIT, skip byte VM + MaxProgSize int // Max cache size for JIT Programs ) const defaultJitMaxCache int = 64 diff --git a/core/vm/vm.go b/core/vm/vm.go index c292b45d1..da764004a 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -64,7 +64,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { codehash = crypto.Sha3Hash(context.Code) // codehash is used when doing jump dest caching program *Program ) - if !DisableJit { + if EnableJit { // Fetch program status. // * If ready run using JIT // * If unknown, compile in a seperate goroutine diff --git a/eth/backend.go b/eth/backend.go index c9b71803f..953a150ad 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -45,7 +45,6 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/nat" - "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/whisper" ) @@ -738,48 +737,53 @@ func mergeDatabases(datadir string, newdb func(path string) (common.Database, er } defer database.Close() - glog.Infoln("Merging blockchain database...") + // Migrate blocks chainDb, err := newdb(chainPath) if err != nil { return fmt.Errorf("state db err: %v", err) } defer chainDb.Close() - if db, ok := chainDb.(*ethdb.LDBDatabase); ok { - it := db.NewIterator() + if chain, ok := chainDb.(*ethdb.LDBDatabase); ok { + glog.Infoln("Merging blockchain database...") + it := chain.NewIterator() for it.Next() { database.Put(it.Key(), it.Value()) } + it.Release() } - glog.Infoln("Merging state database...") - state := filepath.Join(datadir, "state") - stateDb, err := newdb(state) + // Migrate state + stateDb, err := newdb(filepath.Join(datadir, "state")) if err != nil { return fmt.Errorf("state db err: %v", err) } defer stateDb.Close() - if db, ok := chainDb.(*ethdb.LDBDatabase); ok { - it := db.NewIterator() + if state, ok := stateDb.(*ethdb.LDBDatabase); ok { + glog.Infoln("Merging state database...") + it := state.NewIterator() for it.Next() { - database.Put(append(trie.StatePre, it.Key()...), it.Value()) + database.Put(it.Key(), it.Value()) } + it.Release() } - glog.Infoln("Merging transaction database...") - extra := filepath.Join(datadir, "extra") - extraDb, err := newdb(extra) + // Migrate transaction / receipts + extraDb, err := newdb(filepath.Join(datadir, "extra")) if err != nil { return fmt.Errorf("state db err: %v", err) } defer extraDb.Close() - if db, ok := chainDb.(*ethdb.LDBDatabase); ok { - it := db.NewIterator() + if extra, ok := extraDb.(*ethdb.LDBDatabase); ok { + glog.Infoln("Merging transaction database...") + + it := extra.NewIterator() for it.Next() { database.Put(it.Key(), it.Value()) } + it.Release() } return nil diff --git a/p2p/peer_error.go b/p2p/peer_error.go index b1762a6ee..62c7b665d 100644 --- a/p2p/peer_error.go +++ b/p2p/peer_error.go @@ -66,7 +66,7 @@ const ( DiscUnexpectedIdentity DiscSelf DiscReadTimeout - DiscSubprotocolError + DiscSubprotocolError = 0x10 ) var discReasonToString = [...]string{ diff --git a/p2p/rlpx.go b/p2p/rlpx.go index fd43f565e..aaa733854 100644 --- a/p2p/rlpx.go +++ b/p2p/rlpx.go @@ -267,6 +267,10 @@ func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID d } func newInitiatorHandshake(remoteID discover.NodeID) (*encHandshake, error) { + rpub, err := remoteID.Pubkey() + if err != nil { + return nil, fmt.Errorf("bad remoteID: %v", err) + } // generate random initiator nonce n := make([]byte, shaLen) if _, err := rand.Read(n); err != nil { @@ -277,10 +281,6 @@ func newInitiatorHandshake(remoteID discover.NodeID) (*encHandshake, error) { if err != nil { return nil, err } - rpub, err := remoteID.Pubkey() - if err != nil { - return nil, fmt.Errorf("bad remoteID: %v", err) - } h := &encHandshake{ initiator: true, remoteID: remoteID, @@ -417,6 +417,14 @@ func decodeAuthMsg(prv *ecdsa.PrivateKey, token []byte, auth []byte) (*encHandsh if err != nil { return nil, err } + + // validate the sha3 of recovered pubkey + remoteRandomPubMAC := msg[sigLen : sigLen+shaLen] + shaRemoteRandomPub := crypto.Sha3(remoteRandomPub[1:]) + if !bytes.Equal(remoteRandomPubMAC, shaRemoteRandomPub) { + return nil, fmt.Errorf("sha3 of recovered ephemeral pubkey does not match checksum in auth message") + } + h.remoteRandomPub, _ = importPublicKey(remoteRandomPub) return h, nil } diff --git a/rlp/decode.go b/rlp/decode.go index c0b5f0699..1381f5274 100644 --- a/rlp/decode.go +++ b/rlp/decode.go @@ -183,6 +183,8 @@ func makeDecoder(typ reflect.Type, tags tags) (dec decoder, err error) { return decodeBigIntNoPtr, nil case isUint(kind): return decodeUint, nil + case kind == reflect.Bool: + return decodeBool, nil case kind == reflect.String: return decodeString, nil case kind == reflect.Slice || kind == reflect.Array: @@ -211,6 +213,15 @@ func decodeUint(s *Stream, val reflect.Value) error { return nil } +func decodeBool(s *Stream, val reflect.Value) error { + b, err := s.Bool() + if err != nil { + return wrapStreamError(err, val.Type()) + } + val.SetBool(b) + return nil +} + func decodeString(s *Stream, val reflect.Value) error { b, err := s.Bytes() if err != nil { @@ -697,6 +708,24 @@ func (s *Stream) uint(maxbits int) (uint64, error) { } } +// Bool reads an RLP string of up to 1 byte and returns its contents +// as an boolean. If the input does not contain an RLP string, the +// returned error will be ErrExpectedString. +func (s *Stream) Bool() (bool, error) { + num, err := s.uint(8) + if err != nil { + return false, err + } + switch num { + case 0: + return false, nil + case 1: + return true, nil + default: + return false, fmt.Errorf("rlp: invalid boolean value: %d", num) + } +} + // List starts decoding an RLP list. If the input does not contain a // list, the returned error will be ErrExpectedList. When the list's // end has been reached, any Stream operation will return EOL. diff --git a/rlp/decode_test.go b/rlp/decode_test.go index 331faa9d8..d6b0611dd 100644 --- a/rlp/decode_test.go +++ b/rlp/decode_test.go @@ -19,6 +19,7 @@ package rlp import ( "bytes" "encoding/hex" + "errors" "fmt" "io" "math/big" @@ -116,6 +117,9 @@ func TestStreamErrors(t *testing.T) { {"817F", calls{"Uint"}, nil, ErrCanonSize}, {"8180", calls{"Uint"}, nil, nil}, + // Non-valid boolean + {"02", calls{"Bool"}, nil, errors.New("rlp: invalid boolean value: 2")}, + // Size tags must use the smallest possible encoding. // Leading zero bytes in the size tag are also rejected. {"8100", calls{"Uint"}, nil, ErrCanonSize}, @@ -315,6 +319,11 @@ var ( ) var decodeTests = []decodeTest{ + // booleans + {input: "01", ptr: new(bool), value: true}, + {input: "80", ptr: new(bool), value: false}, + {input: "02", ptr: new(bool), error: "rlp: invalid boolean value: 2"}, + // integers {input: "05", ptr: new(uint32), value: uint32(5)}, {input: "80", ptr: new(uint32), value: uint32(0)}, diff --git a/rlp/encode.go b/rlp/encode.go index 0ddef7bbb..b525ae4e7 100644 --- a/rlp/encode.go +++ b/rlp/encode.go @@ -361,6 +361,8 @@ func makeWriter(typ reflect.Type) (writer, error) { return writeBigIntNoPtr, nil case isUint(kind): return writeUint, nil + case kind == reflect.Bool: + return writeBool, nil case kind == reflect.String: return writeString, nil case kind == reflect.Slice && isByte(typ.Elem()): @@ -398,6 +400,15 @@ func writeUint(val reflect.Value, w *encbuf) error { return nil } +func writeBool(val reflect.Value, w *encbuf) error { + if val.Bool() { + w.str = append(w.str, 0x01) + } else { + w.str = append(w.str, 0x80) + } + return nil +} + func writeBigIntPtr(val reflect.Value, w *encbuf) error { ptr := val.Interface().(*big.Int) if ptr == nil { diff --git a/rlp/encode_test.go b/rlp/encode_test.go index e83c76b51..60bd95692 100644 --- a/rlp/encode_test.go +++ b/rlp/encode_test.go @@ -71,6 +71,10 @@ type encTest struct { } var encTests = []encTest{ + // booleans + {val: true, output: "01"}, + {val: false, output: "80"}, + // integers {val: uint32(0), output: "80"}, {val: uint32(127), output: "7F"}, diff --git a/rpc/api/admin.go b/rpc/api/admin.go index 29f342ab6..5e392ae32 100644 --- a/rpc/api/admin.go +++ b/rpc/api/admin.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/comms" "github.com/ethereum/go-ethereum/rpc/shared" + "github.com/ethereum/go-ethereum/rpc/useragent" "github.com/ethereum/go-ethereum/xeth" ) @@ -71,6 +72,7 @@ var ( "admin_httpGet": (*adminApi).HttpGet, "admin_sleepBlocks": (*adminApi).SleepBlocks, "admin_sleep": (*adminApi).Sleep, + "admin_enableUserAgent": (*adminApi).EnableUserAgent, } ) @@ -474,3 +476,10 @@ func (self *adminApi) HttpGet(req *shared.Request) (interface{}, error) { return string(resp), nil } + +func (self *adminApi) EnableUserAgent(req *shared.Request) (interface{}, error) { + if fe, ok := self.xeth.Frontend().(*useragent.RemoteFrontend); ok { + fe.Enable() + } + return true, nil +} diff --git a/rpc/api/net.go b/rpc/api/net.go index 39c230e14..9c6369615 100644 --- a/rpc/api/net.go +++ b/rpc/api/net.go @@ -32,7 +32,7 @@ var ( netMapping = map[string]nethandler{ "net_peerCount": (*netApi).PeerCount, "net_listening": (*netApi).IsListening, - "net_version": (*netApi).Version, + "net_version": (*netApi).Version, } ) @@ -97,4 +97,3 @@ func (self *netApi) IsListening(req *shared.Request) (interface{}, error) { func (self *netApi) Version(req *shared.Request) (interface{}, error) { return self.xeth.NetworkVersion(), nil } - diff --git a/rpc/api/personal.go b/rpc/api/personal.go index e9942c1e5..6c73ac83d 100644 --- a/rpc/api/personal.go +++ b/rpc/api/personal.go @@ -17,6 +17,7 @@ package api import ( + "fmt" "time" "github.com/ethereum/go-ethereum/common" @@ -125,18 +126,17 @@ func (self *personalApi) UnlockAccount(req *shared.Request) (interface{}, error) return nil, shared.NewDecodeParamError(err.Error()) } - var err error + if len(args.Passphrase) == 0 { + fe := self.xeth.Frontend() + if fe == nil { + return false, fmt.Errorf("No password provided") + } + return fe.UnlockAccount(common.HexToAddress(args.Address).Bytes()), nil + } + am := self.ethereum.AccountManager() addr := common.HexToAddress(args.Address) - if args.Duration == -1 { - err = am.Unlock(addr, args.Passphrase) - } else { - err = am.TimedUnlock(addr, args.Passphrase, time.Duration(args.Duration)*time.Second) - } - - if err == nil { - return true, nil - } - return false, err + err := am.TimedUnlock(addr, args.Passphrase, time.Duration(args.Duration)*time.Second) + return err == nil, err } diff --git a/rpc/api/personal_args.go b/rpc/api/personal_args.go index 7f00701e3..5a584fb0c 100644 --- a/rpc/api/personal_args.go +++ b/rpc/api/personal_args.go @@ -86,10 +86,10 @@ func (args *UnlockAccountArgs) UnmarshalJSON(b []byte) (err error) { return shared.NewDecodeParamError(err.Error()) } - args.Duration = -1 + args.Duration = 0 - if len(obj) < 2 { - return shared.NewInsufficientParamsError(len(obj), 2) + if len(obj) < 1 { + return shared.NewInsufficientParamsError(len(obj), 1) } if addrstr, ok := obj[0].(string); ok { @@ -98,10 +98,18 @@ func (args *UnlockAccountArgs) UnmarshalJSON(b []byte) (err error) { return shared.NewInvalidTypeError("address", "not a string") } - if passphrasestr, ok := obj[1].(string); ok { - args.Passphrase = passphrasestr - } else { - return shared.NewInvalidTypeError("passphrase", "not a string") + if len(obj) >= 2 && obj[1] != nil { + if passphrasestr, ok := obj[1].(string); ok { + args.Passphrase = passphrasestr + } else { + return shared.NewInvalidTypeError("passphrase", "not a string") + } + } + + if len(obj) >= 3 && obj[2] != nil { + if duration, ok := obj[2].(float64); ok { + args.Duration = int(duration) + } } return nil diff --git a/rpc/api/ssh_js.go b/rpc/api/shh_js.go index a92ad1644..a92ad1644 100644 --- a/rpc/api/ssh_js.go +++ b/rpc/api/shh_js.go diff --git a/rpc/codec/codec.go b/rpc/codec/codec.go index 2fdb0d8f3..786080b44 100644 --- a/rpc/codec/codec.go +++ b/rpc/codec/codec.go @@ -31,6 +31,8 @@ type ApiCoder interface { ReadRequest() ([]*shared.Request, bool, error) // Parse response message from underlying stream ReadResponse() (interface{}, error) + // Read raw message from underlying stream + Recv() (interface{}, error) // Encode response to encoded form in underlying stream WriteResponse(interface{}) error // Decode single message from data diff --git a/rpc/codec/json.go b/rpc/codec/json.go index d811b2096..cfc449143 100644 --- a/rpc/codec/json.go +++ b/rpc/codec/json.go @@ -21,6 +21,7 @@ import ( "fmt" "net" "time" + "strings" "github.com/ethereum/go-ethereum/rpc/shared" ) @@ -73,35 +74,41 @@ func (self *JsonCodec) ReadRequest() (requests []*shared.Request, isBatch bool, return nil, false, err } -func (self *JsonCodec) ReadResponse() (interface{}, error) { - bytesInBuffer := 0 - buf := make([]byte, MAX_RESPONSE_SIZE) +func (self *JsonCodec) Recv() (interface{}, error) { + var msg json.RawMessage + err := self.d.Decode(&msg) + if err != nil { + self.c.Close() + return nil, err + } - deadline := time.Now().Add(READ_TIMEOUT * time.Second) - if err := self.c.SetDeadline(deadline); err != nil { + return msg, err +} + +func (self *JsonCodec) ReadResponse() (interface{}, error) { + in, err := self.Recv() + if err != nil { return nil, err } - for { - n, err := self.c.Read(buf[bytesInBuffer:]) - if err != nil { - return nil, err + if msg, ok := in.(json.RawMessage); ok { + var req *shared.Request + if err = json.Unmarshal(msg, &req); err == nil && strings.HasPrefix(req.Method, "agent_") { + return req, nil } - bytesInBuffer += n - var failure shared.ErrorResponse - if err = json.Unmarshal(buf[:bytesInBuffer], &failure); err == nil && failure.Error != nil { + var failure *shared.ErrorResponse + if err = json.Unmarshal(msg, &failure); err == nil && failure.Error != nil { return failure, fmt.Errorf(failure.Error.Message) } - var success shared.SuccessResponse - if err = json.Unmarshal(buf[:bytesInBuffer], &success); err == nil { + var success *shared.SuccessResponse + if err = json.Unmarshal(msg, &success); err == nil { return success, nil } } - self.c.Close() - return nil, fmt.Errorf("Unable to read response") + return in, err } // Decode data diff --git a/rpc/comms/comms.go b/rpc/comms/comms.go index f5eeae84f..731b2f62e 100644 --- a/rpc/comms/comms.go +++ b/rpc/comms/comms.go @@ -49,7 +49,7 @@ var ( ) type EthereumClient interface { - // Close underlaying connection + // Close underlying connection Close() // Send request Send(interface{}) error diff --git a/rpc/comms/inproc.go b/rpc/comms/inproc.go index f279f0163..e8058e32b 100644 --- a/rpc/comms/inproc.go +++ b/rpc/comms/inproc.go @@ -60,7 +60,7 @@ func (self *InProcClient) Send(req interface{}) error { } func (self *InProcClient) Recv() (interface{}, error) { - return self.lastRes, self.lastErr + return *shared.NewRpcResponse(self.lastId, self.lastJsonrpc, self.lastRes, self.lastErr), nil } func (self *InProcClient) SupportedModules() (map[string]string, error) { diff --git a/rpc/comms/ipc.go b/rpc/comms/ipc.go index 0250aa01e..e982ada13 100644 --- a/rpc/comms/ipc.go +++ b/rpc/comms/ipc.go @@ -44,35 +44,18 @@ func (self *ipcClient) Close() { func (self *ipcClient) Send(req interface{}) error { var err error - if r, ok := req.(*shared.Request); ok { - if err = self.coder.WriteResponse(r); err != nil { - if _, ok := err.(*net.OpError); ok { // connection lost, retry once - if err = self.reconnect(); err == nil { - err = self.coder.WriteResponse(r) - } + if err = self.coder.WriteResponse(req); err != nil { + if _, ok := err.(*net.OpError); ok { // connection lost, retry once + if err = self.reconnect(); err == nil { + err = self.coder.WriteResponse(req) } } - return err } - - return fmt.Errorf("Invalid request (%T)", req) + return err } func (self *ipcClient) Recv() (interface{}, error) { - res, err := self.coder.ReadResponse() - if err != nil { - return nil, err - } - - if r, ok := res.(shared.SuccessResponse); ok { - return r.Result, nil - } - - if r, ok := res.(shared.ErrorResponse); ok { - return r.Error, nil - } - - return res, err + return self.coder.ReadResponse() } func (self *ipcClient) SupportedModules() (map[string]string, error) { @@ -91,7 +74,7 @@ func (self *ipcClient) SupportedModules() (map[string]string, error) { return nil, err } - if sucRes, ok := res.(shared.SuccessResponse); ok { + if sucRes, ok := res.(*shared.SuccessResponse); ok { data, _ := json.Marshal(sucRes.Result) modules := make(map[string]string) err = json.Unmarshal(data, &modules) @@ -109,8 +92,8 @@ func NewIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { } // Start IPC server -func StartIpc(cfg IpcConfig, codec codec.Codec, offeredApi shared.EthereumApi) error { - return startIpc(cfg, codec, offeredApi) +func StartIpc(cfg IpcConfig, codec codec.Codec, initializer func(conn net.Conn) (shared.EthereumApi, error)) error { + return startIpc(cfg, codec, initializer) } func newIpcConnId() int { diff --git a/rpc/comms/ipc_unix.go b/rpc/comms/ipc_unix.go index 432bf93b5..6968fa844 100644 --- a/rpc/comms/ipc_unix.go +++ b/rpc/comms/ipc_unix.go @@ -48,7 +48,7 @@ func (self *ipcClient) reconnect() error { return err } -func startIpc(cfg IpcConfig, codec codec.Codec, api shared.EthereumApi) error { +func startIpc(cfg IpcConfig, codec codec.Codec, initializer func(conn net.Conn) (shared.EthereumApi, error)) error { os.Remove(cfg.Endpoint) // in case it still exists from a previous run l, err := net.Listen("unix", cfg.Endpoint) @@ -69,6 +69,13 @@ func startIpc(cfg IpcConfig, codec codec.Codec, api shared.EthereumApi) error { id := newIpcConnId() glog.V(logger.Debug).Infof("New IPC connection with id %06d started\n", id) + api, err := initializer(conn) + if err != nil { + glog.V(logger.Error).Infof("Unable to initialize IPC connection - %v\n", err) + conn.Close() + continue + } + go handle(id, conn, api, codec) } diff --git a/rpc/comms/ipc_windows.go b/rpc/comms/ipc_windows.go index ee49f069b..b2fe2b29d 100644 --- a/rpc/comms/ipc_windows.go +++ b/rpc/comms/ipc_windows.go @@ -667,7 +667,7 @@ func (self *ipcClient) reconnect() error { return err } -func startIpc(cfg IpcConfig, codec codec.Codec, api shared.EthereumApi) error { +func startIpc(cfg IpcConfig, codec codec.Codec, initializer func(conn net.Conn) (shared.EthereumApi, error)) error { os.Remove(cfg.Endpoint) // in case it still exists from a previous run l, err := Listen(cfg.Endpoint) @@ -687,6 +687,13 @@ func startIpc(cfg IpcConfig, codec codec.Codec, api shared.EthereumApi) error { id := newIpcConnId() glog.V(logger.Debug).Infof("New IPC connection with id %06d started\n", id) + api, err := initializer(conn) + if err != nil { + glog.V(logger.Error).Infof("Unable to initialize IPC connection - %v\n", err) + conn.Close() + continue + } + go handle(id, conn, api, codec) } diff --git a/rpc/jeth.go b/rpc/jeth.go index 07add2bad..158bfb64c 100644 --- a/rpc/jeth.go +++ b/rpc/jeth.go @@ -18,12 +18,17 @@ package rpc import ( "encoding/json" - "fmt" + "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/jsre" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/rpc/comms" "github.com/ethereum/go-ethereum/rpc/shared" + "github.com/ethereum/go-ethereum/rpc/useragent" + "github.com/ethereum/go-ethereum/xeth" + "github.com/robertkrimen/otto" ) @@ -31,10 +36,21 @@ type Jeth struct { ethApi shared.EthereumApi re *jsre.JSRE client comms.EthereumClient + fe xeth.Frontend } -func NewJeth(ethApi shared.EthereumApi, re *jsre.JSRE, client comms.EthereumClient) *Jeth { - return &Jeth{ethApi, re, client} +func NewJeth(ethApi shared.EthereumApi, re *jsre.JSRE, client comms.EthereumClient, fe xeth.Frontend) *Jeth { + // enable the jeth as the user agent + req := shared.Request{ + Id: 0, + Method: useragent.EnableUserAgentMethod, + Jsonrpc: shared.JsonRpcVersion, + Params: []byte("[]"), + } + client.Send(&req) + client.Recv() + + return &Jeth{ethApi, re, client, fe} } func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) { @@ -72,16 +88,34 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { if err != nil { return self.err(call, -32603, err.Error(), req.Id) } - respif, err = self.client.Recv() + recv: + respif, err = self.client.Recv() if err != nil { return self.err(call, -32603, err.Error(), req.Id) } + agentreq, isRequest := respif.(*shared.Request) + if isRequest { + self.handleRequest(agentreq) + goto recv // receive response after agent interaction + } + + sucres, isSuccessResponse := respif.(*shared.SuccessResponse) + errres, isErrorResponse := respif.(*shared.ErrorResponse) + if !isSuccessResponse && !isErrorResponse { + return self.err(call, -32603, fmt.Sprintf("Invalid response type (%T)", respif), req.Id) + } + call.Otto.Set("ret_jsonrpc", shared.JsonRpcVersion) call.Otto.Set("ret_id", req.Id) - res, _ := json.Marshal(respif) + var res []byte + if isSuccessResponse { + res, err = json.Marshal(sucres.Result) + } else if isErrorResponse { + res, err = json.Marshal(errres.Error) + } call.Otto.Set("ret_result", string(res)) call.Otto.Set("response_idx", i) @@ -105,3 +139,48 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { return } + +// handleRequest will handle user agent requests by interacting with the user and sending +// the user response back to the geth service +func (self *Jeth) handleRequest(req *shared.Request) bool { + var err error + var args []interface{} + if err = json.Unmarshal(req.Params, &args); err != nil { + glog.V(logger.Info).Infof("Unable to parse agent request - %v\n", err) + return false + } + + switch req.Method { + case useragent.AskPasswordMethod: + return self.askPassword(req.Id, req.Jsonrpc, args) + case useragent.ConfirmTransactionMethod: + return self.confirmTransaction(req.Id, req.Jsonrpc, args) + } + + return false +} + +// askPassword will ask the user to supply the password for a given account +func (self *Jeth) askPassword(id interface{}, jsonrpc string, args []interface{}) bool { + var err error + var passwd string + if len(args) >= 1 { + if account, ok := args[0].(string); ok { + fmt.Printf("Unlock account %s\n", account) + passwd, err = utils.PromptPassword("Passphrase: ", true) + } else { + return false + } + } + + if err = self.client.Send(shared.NewRpcResponse(id, jsonrpc, passwd, err)); err != nil { + glog.V(logger.Info).Infof("Unable to send user agent ask password response - %v\n", err) + } + + return err == nil +} + +func (self *Jeth) confirmTransaction(id interface{}, jsonrpc string, args []interface{}) bool { + // Accept all tx which are send from this console + return self.client.Send(shared.NewRpcResponse(id, jsonrpc, true, nil)) == nil +} diff --git a/rpc/useragent/agent.go b/rpc/useragent/agent.go new file mode 100644 index 000000000..df0739e65 --- /dev/null +++ b/rpc/useragent/agent.go @@ -0,0 +1,24 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// package user agent provides frontends and agents which can interact with the user +package useragent + +var ( + AskPasswordMethod = "agent_askPassword" + ConfirmTransactionMethod = "agent_confirmTransaction" + EnableUserAgentMethod = "admin_enableUserAgent" +) diff --git a/rpc/useragent/remote_frontend.go b/rpc/useragent/remote_frontend.go new file mode 100644 index 000000000..0dd4a6049 --- /dev/null +++ b/rpc/useragent/remote_frontend.go @@ -0,0 +1,141 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package useragent + +import ( + "encoding/json" + "fmt" + "net" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rpc/shared" +) + +// remoteFrontend implements xeth.Frontend and will communicate with an external +// user agent over a connection +type RemoteFrontend struct { + enabled bool + mgr *accounts.Manager + d *json.Decoder + e *json.Encoder + n int +} + +// NewRemoteFrontend creates a new frontend which will interact with an user agent +// over the given connection +func NewRemoteFrontend(conn net.Conn, mgr *accounts.Manager) *RemoteFrontend { + return &RemoteFrontend{false, mgr, json.NewDecoder(conn), json.NewEncoder(conn), 0} +} + +// Enable will enable user interaction +func (fe *RemoteFrontend) Enable() { + fe.enabled = true +} + +// UnlockAccount asks the user agent for the user password and tries to unlock the account. +// It will try 3 attempts before giving up. +func (fe *RemoteFrontend) UnlockAccount(address []byte) bool { + if !fe.enabled { + return false + } + + err := fe.send(AskPasswordMethod, common.Bytes2Hex(address)) + if err != nil { + glog.V(logger.Error).Infof("Unable to send password request to agent - %v\n", err) + return false + } + + passwdRes, err := fe.recv() + if err != nil { + glog.V(logger.Error).Infof("Unable to recv password response from agent - %v\n", err) + return false + } + + if passwd, ok := passwdRes.Result.(string); ok { + err = fe.mgr.Unlock(common.BytesToAddress(address), passwd) + } + + if err == nil { + return true + } + + glog.V(logger.Debug).Infoln("3 invalid account unlock attempts") + return false +} + +// ConfirmTransaction asks the user for approval +func (fe *RemoteFrontend) ConfirmTransaction(tx string) bool { + if !fe.enabled { + return true // backwards compatibility + } + + err := fe.send(ConfirmTransactionMethod, tx) + if err != nil { + glog.V(logger.Error).Infof("Unable to send tx confirmation request to agent - %v\n", err) + return false + } + + confirmResponse, err := fe.recv() + if err != nil { + glog.V(logger.Error).Infof("Unable to recv tx confirmation response from agent - %v\n", err) + return false + } + + if confirmed, ok := confirmResponse.Result.(bool); ok { + return confirmed + } + + return false +} + +// send request to the agent +func (fe *RemoteFrontend) send(method string, params ...interface{}) error { + fe.n += 1 + + p, err := json.Marshal(params) + if err != nil { + glog.V(logger.Info).Infof("Unable to send agent request %v\n", err) + return err + } + + req := shared.Request{ + Method: method, + Jsonrpc: shared.JsonRpcVersion, + Id: fe.n, + Params: p, + } + + return fe.e.Encode(&req) +} + +// recv user response from agent +func (fe *RemoteFrontend) recv() (*shared.SuccessResponse, error) { + var res json.RawMessage + if err := fe.d.Decode(&res); err != nil { + return nil, err + } + + var response shared.SuccessResponse + if err := json.Unmarshal(res, &response); err == nil { + return &response, nil + } + + return nil, fmt.Errorf("Invalid user agent response") +} diff --git a/tests/state_test.go b/tests/state_test.go index eb1900e1b..7090b0541 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -27,14 +27,13 @@ import ( func init() { if os.Getenv("JITVM") == "true" { vm.ForceJit = true - } else { - vm.DisableJit = true + vm.EnableJit = true } } func BenchmarkStateCall1024(b *testing.B) { fn := filepath.Join(stateTestDir, "stCallCreateCallCodeTest.json") - if err := BenchVmTest(fn, bconf{"Call1024BalanceTooLow", true, false}, b); err != nil { + if err := BenchVmTest(fn, bconf{"Call1024BalanceTooLow", true, os.Getenv("JITVM") == "true"}, b); err != nil { b.Error(err) } } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 695e50852..def9b0c36 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -71,8 +71,8 @@ func BenchStateTest(p string, conf bconf, b *testing.B) error { return fmt.Errorf("test not found: %s", conf.name) } - pNoJit := vm.DisableJit - vm.DisableJit = conf.nojit + pJit := vm.EnableJit + vm.EnableJit = conf.jit pForceJit := vm.ForceJit vm.ForceJit = conf.precomp @@ -94,7 +94,7 @@ func BenchStateTest(p string, conf bconf, b *testing.B) error { benchStateTest(test, env, b) } - vm.DisableJit = pNoJit + vm.EnableJit = pJit vm.ForceJit = pForceJit return nil diff --git a/tests/vm_test.go b/tests/vm_test.go index afa1424d5..96718db3c 100644 --- a/tests/vm_test.go +++ b/tests/vm_test.go @@ -17,20 +17,21 @@ package tests import ( + "os" "path/filepath" "testing" ) func BenchmarkVmAckermann32Tests(b *testing.B) { fn := filepath.Join(vmTestDir, "vmPerformanceTest.json") - if err := BenchVmTest(fn, bconf{"ackermann32", true, false}, b); err != nil { + if err := BenchVmTest(fn, bconf{"ackermann32", true, os.Getenv("JITVM") == "true"}, b); err != nil { b.Error(err) } } func BenchmarkVmFibonacci16Tests(b *testing.B) { fn := filepath.Join(vmTestDir, "vmPerformanceTest.json") - if err := BenchVmTest(fn, bconf{"fibonacci16", true, false}, b); err != nil { + if err := BenchVmTest(fn, bconf{"fibonacci16", true, os.Getenv("JITVM") == "true"}, b); err != nil { b.Error(err) } } diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index b29dcd20f..71a4f5e33 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -52,7 +52,7 @@ func RunVmTestWithReader(r io.Reader, skipTests []string) error { type bconf struct { name string precomp bool - nojit bool + jit bool } func BenchVmTest(p string, conf bconf, b *testing.B) error { @@ -67,8 +67,8 @@ func BenchVmTest(p string, conf bconf, b *testing.B) error { return fmt.Errorf("test not found: %s", conf.name) } - pNoJit := vm.DisableJit - vm.DisableJit = conf.nojit + pJit := vm.EnableJit + vm.EnableJit = conf.jit pForceJit := vm.ForceJit vm.ForceJit = conf.precomp @@ -99,7 +99,7 @@ func BenchVmTest(p string, conf bconf, b *testing.B) error { benchVmTest(test, env, b) } - vm.DisableJit = pNoJit + vm.EnableJit = pJit vm.ForceJit = pForceJit return nil diff --git a/trie/cache.go b/trie/cache.go index 99d8033a6..e475fc861 100644 --- a/trie/cache.go +++ b/trie/cache.go @@ -38,8 +38,6 @@ func NewCache(backend Backend) *Cache { } func (self *Cache) Get(key []byte) []byte { - key = append(StatePre, key...) - data := self.store[string(key)] if data == nil { data, _ = self.backend.Get(key) @@ -49,8 +47,6 @@ func (self *Cache) Get(key []byte) []byte { } func (self *Cache) Put(key []byte, data []byte) { - key = append(StatePre, key...) - self.batch.Put(key, data) self.store[string(key)] = data } diff --git a/trie/trie.go b/trie/trie.go index 2970bc185..abf48a850 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -27,8 +27,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) -var StatePre = []byte("state-") - func ParanoiaCheck(t1 *Trie, backend Backend) (bool, *Trie) { t2 := New(nil, backend) diff --git a/xeth/xeth.go b/xeth/xeth.go index 5110aa62c..5a57608bc 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -885,6 +885,10 @@ func isAddress(addr string) bool { return addrReg.MatchString(addr) } +func (self *XEth) Frontend() Frontend { + return self.frontend +} + func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { // this minimalistic recoding is enough (works for natspec.js) |