From 009b2216921b15962f2612687c1460a8342d49d6 Mon Sep 17 00:00:00 2001 From: zelig Date: Wed, 22 Apr 2015 23:11:11 +0100 Subject: solidity compiler and contract metadocs integration * common/compiler: solidity compiler + tests * rpc: eth_compilers, eth_compileSolidity + tests * fix natspec test using keystore API, notice exp dynamically changes addr, cleanup * resolver implements registrars and needs to create reg contract (temp) * xeth: solidity compiler. expose getter Solc() and paths setter SetSolc(solcPath) * ethereumApi: implement compiler related RPC calls using XEth - json struct tests * admin: make use of XEth.SetSolc to allow runtime setting of compiler paths * cli: command line flags solc to set custom solc bin path * js admin api with new features debug and contractInfo modules * wiki is the doc https://github.com/ethereum/go-ethereum/wiki/Contracts-and-Transactions --- cmd/geth/admin.go | 346 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 285 insertions(+), 61 deletions(-) (limited to 'cmd/geth/admin.go') diff --git a/cmd/geth/admin.go b/cmd/geth/admin.go index f15ce89a0..49e2dc6f8 100644 --- a/cmd/geth/admin.go +++ b/cmd/geth/admin.go @@ -1,16 +1,22 @@ package main import ( + "encoding/json" "errors" "fmt" + "math/big" "strconv" "time" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/compiler" + "github.com/ethereum/go-ethereum/common/natspec" + "github.com/ethereum/go-ethereum/common/resolver" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" @@ -43,6 +49,19 @@ func (js *jsre) adminBindings() { admin.Set("export", js.exportChain) admin.Set("verbosity", js.verbosity) admin.Set("progress", js.downloadProgress) + admin.Set("setSolc", js.setSolc) + + admin.Set("contractInfo", struct{}{}) + t, _ = admin.Get("contractInfo") + cinfo := t.Object() + // newRegistry officially not documented temporary option + cinfo.Set("start", js.startNatSpec) + cinfo.Set("stop", js.stopNatSpec) + cinfo.Set("newRegistry", js.newRegistry) + cinfo.Set("get", js.getContractInfo) + cinfo.Set("register", js.register) + cinfo.Set("registerUrl", js.registerUrl) + // cinfo.Set("verify", js.verify) admin.Set("miner", struct{}{}) t, _ = admin.Get("miner") @@ -55,14 +74,21 @@ func (js *jsre) adminBindings() { admin.Set("debug", struct{}{}) t, _ = admin.Get("debug") debug := t.Object() + js.re.Set("sleep", js.sleep) debug.Set("backtrace", js.backtrace) debug.Set("printBlock", js.printBlock) debug.Set("dumpBlock", js.dumpBlock) debug.Set("getBlockRlp", js.getBlockRlp) debug.Set("setHead", js.setHead) debug.Set("processBlock", js.debugBlock) + // undocumented temporary + debug.Set("waitForBlocks", js.waitForBlocks) } +// generic helper to getBlock by Number/Height or Hex depending on autodetected input +// if argument is missing the current block is returned +// if block is not found or there is problem with decoding +// the appropriate value is returned and block is guaranteed to be nil func (js *jsre) getBlock(call otto.FunctionCall) (*types.Block, error) { var block *types.Block if len(call.ArgumentList) > 0 { @@ -75,10 +101,14 @@ func (js *jsre) getBlock(call otto.FunctionCall) (*types.Block, error) { } else { return nil, errors.New("invalid argument for dump. Either hex string or number") } - return block, nil + } else { + block = js.ethereum.ChainManager().CurrentBlock() } - return nil, errors.New("requires block number or block hash as argument") + if block == nil { + return nil, errors.New("block not found") + } + return block, nil } func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value { @@ -152,11 +182,6 @@ func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value { return otto.UndefinedValue() } - if block == nil { - fmt.Println("block not found") - return otto.UndefinedValue() - } - old := vm.Debug vm.Debug = true _, err = js.ethereum.BlockProcessor().RetryProcess(block) @@ -175,11 +200,6 @@ func (js *jsre) setHead(call otto.FunctionCall) otto.Value { return otto.UndefinedValue() } - if block == nil { - fmt.Println("block not found") - return otto.UndefinedValue() - } - js.ethereum.ChainManager().SetHead(block) return otto.UndefinedValue() } @@ -196,12 +216,6 @@ func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value { fmt.Println(err) return otto.UndefinedValue() } - - if block == nil { - fmt.Println("block not found") - return otto.UndefinedValue() - } - encoded, _ := rlp.EncodeToBytes(block) return js.re.ToVal(fmt.Sprintf("%x", encoded)) } @@ -255,11 +269,13 @@ func (js *jsre) startMining(call otto.FunctionCall) otto.Value { return otto.FalseValue() } // threads now ignored + err = js.ethereum.StartMining() if err != nil { fmt.Println(err) return otto.FalseValue() } + return otto.TrueValue() } @@ -298,9 +314,8 @@ func (js *jsre) startRPC(call otto.FunctionCall) otto.Value { xeth := xeth.New(js.ethereum, nil) err = rpc.Start(xeth, config) - if err != nil { - fmt.Printf(err.Error()) + fmt.Println(err) return otto.FalseValue() } @@ -345,7 +360,8 @@ func (js *jsre) unlock(call otto.FunctionCall) otto.Value { fmt.Println("Please enter a passphrase now.") passphrase, err = readPassword("Passphrase: ", true) if err != nil { - utils.Fatalf("%v", err) + fmt.Println(err) + return otto.FalseValue() } } else { passphrase, err = arg.ToString() @@ -371,14 +387,17 @@ func (js *jsre) newAccount(call otto.FunctionCall) otto.Value { fmt.Println("Please enter a passphrase now.") auth, err := readPassword("Passphrase: ", true) if err != nil { - utils.Fatalf("%v", err) + fmt.Println(err) + return otto.FalseValue() } confirm, err := readPassword("Repeat Passphrase: ", false) if err != nil { - utils.Fatalf("%v", err) + fmt.Println(err) + return otto.FalseValue() } if auth != confirm { - utils.Fatalf("Passphrases did not match.") + fmt.Println("Passphrases did not match.") + return otto.FalseValue() } passphrase = auth } else { @@ -394,7 +413,7 @@ func (js *jsre) newAccount(call otto.FunctionCall) otto.Value { fmt.Printf("Could not create the account: %v", err) return otto.UndefinedValue() } - return js.re.ToVal("0x" + common.Bytes2Hex(acct.Address)) + return js.re.ToVal(common.ToHex(acct.Address)) } func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value { @@ -407,7 +426,7 @@ func (js *jsre) peers(call otto.FunctionCall) otto.Value { func (js *jsre) importChain(call otto.FunctionCall) otto.Value { if len(call.ArgumentList) == 0 { - fmt.Println("err: require file name") + fmt.Println("require file name. admin.importChain(filename)") return otto.FalseValue() } fn, err := call.Argument(0).ToString() @@ -424,7 +443,7 @@ func (js *jsre) importChain(call otto.FunctionCall) otto.Value { func (js *jsre) exportChain(call otto.FunctionCall) otto.Value { if len(call.ArgumentList) == 0 { - fmt.Println("err: require file name") + fmt.Println("require file name: admin.exportChain(filename)") return otto.FalseValue() } @@ -441,23 +460,9 @@ func (js *jsre) exportChain(call otto.FunctionCall) otto.Value { } func (js *jsre) printBlock(call otto.FunctionCall) otto.Value { - var block *types.Block - if len(call.ArgumentList) > 0 { - if call.Argument(0).IsNumber() { - num, _ := call.Argument(0).ToInteger() - block = js.ethereum.ChainManager().GetBlockByNumber(uint64(num)) - } else if call.Argument(0).IsString() { - hash, _ := call.Argument(0).ToString() - block = js.ethereum.ChainManager().GetBlock(common.HexToHash(hash)) - } else { - fmt.Println("invalid argument for dump. Either hex string or number") - } - - } else { - block = js.ethereum.ChainManager().CurrentBlock() - } - if block == nil { - fmt.Println("block not found") + block, err := js.getBlock(call) + if err != nil { + fmt.Println(err) return otto.UndefinedValue() } @@ -467,30 +472,249 @@ func (js *jsre) printBlock(call otto.FunctionCall) otto.Value { } func (js *jsre) dumpBlock(call otto.FunctionCall) otto.Value { - var block *types.Block - if len(call.ArgumentList) > 0 { - if call.Argument(0).IsNumber() { - num, _ := call.Argument(0).ToInteger() - block = js.ethereum.ChainManager().GetBlockByNumber(uint64(num)) - } else if call.Argument(0).IsString() { - hash, _ := call.Argument(0).ToString() - block = js.ethereum.ChainManager().GetBlock(common.HexToHash(hash)) - } else { - fmt.Println("invalid argument for dump. Either hex string or number") - } - - } else { - block = js.ethereum.ChainManager().CurrentBlock() - } - if block == nil { - fmt.Println("block not found") + block, err := js.getBlock(call) + if err != nil { + fmt.Println(err) return otto.UndefinedValue() } statedb := state.New(block.Root(), js.ethereum.StateDb()) dump := statedb.RawDump() return js.re.ToVal(dump) +} + +func (js *jsre) waitForBlocks(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) > 2 { + fmt.Println("requires 0, 1 or 2 arguments: admin.debug.waitForBlock(minHeight, timeout)") + return otto.FalseValue() + } + var n, timeout int64 + var timer <-chan time.Time + var height *big.Int + var err error + args := len(call.ArgumentList) + if args == 2 { + timeout, err = call.Argument(1).ToInteger() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + timer = time.NewTimer(time.Duration(timeout) * time.Second).C + } + if args >= 1 { + n, err = call.Argument(0).ToInteger() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + height = big.NewInt(n) + } + + if args == 0 { + height = js.xeth.CurrentBlock().Number() + height.Add(height, common.Big1) + } + + wait := js.wait + js.wait <- height + select { + case <-timer: + // if times out make sure the xeth loop does not block + go func() { + select { + case wait <- nil: + case <-wait: + } + }() + return otto.UndefinedValue() + case height = <-wait: + } + return js.re.ToVal(height.Uint64()) +} + +func (js *jsre) sleep(call otto.FunctionCall) otto.Value { + sec, err := call.Argument(0).ToInteger() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + time.Sleep(time.Duration(sec) * time.Second) + return otto.UndefinedValue() +} + +func (js *jsre) setSolc(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) != 1 { + fmt.Println("needs 1 argument: admin.contractInfo.setSolc(solcPath)") + return otto.FalseValue() + } + solcPath, err := call.Argument(0).ToString() + if err != nil { + return otto.FalseValue() + } + solc, err := js.xeth.SetSolc(solcPath) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + fmt.Println(solc.Info()) + return otto.TrueValue() +} + +func (js *jsre) register(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) != 4 { + fmt.Println("requires 4 arguments: admin.contractInfo.register(fromaddress, contractaddress, contract, filename)") + return otto.UndefinedValue() + } + sender, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + address, err := call.Argument(1).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + raw, err := call.Argument(2).Export() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + jsonraw, err := json.Marshal(raw) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + var contract compiler.Contract + err = json.Unmarshal(jsonraw, &contract) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + filename, err := call.Argument(3).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + contenthash, err := compiler.ExtractInfo(&contract, filename) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + // sender and contract address are passed as hex strings + codeb := js.xeth.CodeAtBytes(address) + codehash := common.BytesToHash(crypto.Sha3(codeb)) + + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + registry := resolver.New(js.xeth) + + _, err = registry.RegisterContentHash(common.HexToAddress(sender), codehash, contenthash) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + return js.re.ToVal(contenthash.Hex()) + +} + +func (js *jsre) registerUrl(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) != 3 { + fmt.Println("requires 3 arguments: admin.contractInfo.register(fromaddress, contenthash, filename)") + return otto.FalseValue() + } + sender, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + contenthash, err := call.Argument(1).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + url, err := call.Argument(2).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + registry := resolver.New(js.xeth) + + _, err = registry.RegisterUrl(common.HexToAddress(sender), common.HexToHash(contenthash), url) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + return otto.TrueValue() +} + +func (js *jsre) getContractInfo(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) != 1 { + fmt.Println("requires 1 argument: admin.contractInfo.register(contractaddress)") + return otto.FalseValue() + } + addr, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + infoDoc, err := natspec.FetchDocsForContract(addr, js.xeth, ds) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + var info compiler.ContractInfo + err = json.Unmarshal(infoDoc, &info) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + return js.re.ToVal(info) +} + +func (js *jsre) startNatSpec(call otto.FunctionCall) otto.Value { + js.ethereum.NatSpec = true + return otto.TrueValue() +} +func (js *jsre) stopNatSpec(call otto.FunctionCall) otto.Value { + js.ethereum.NatSpec = false + return otto.TrueValue() +} + +func (js *jsre) newRegistry(call otto.FunctionCall) otto.Value { + + if len(call.ArgumentList) != 1 { + fmt.Println("requires 1 argument: admin.contractInfo.newRegistry(adminaddress)") + return otto.FalseValue() + } + addr, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + registry := resolver.New(js.xeth) + err = registry.CreateContracts(common.HexToAddress(addr)) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + return otto.TrueValue() } // internal transaction type which will allow us to resend transactions using `eth.resend` -- cgit v1.2.3