From eb433731aa535a47c4a828ea15eafabd37a8278b Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 12:06:14 +0100 Subject: Fixed filter and refactored code --- core/filter.go | 2 +- rpc/args.go | 51 ++++++++++++++++++++++++++++----------------------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/core/filter.go b/core/filter.go index 901931d99..ba5d5e14e 100644 --- a/core/filter.go +++ b/core/filter.go @@ -4,8 +4,8 @@ import ( "math" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" ) type AccountChange struct { diff --git a/rpc/args.go b/rpc/args.go index 5b655024c..892e3f3e1 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -3,6 +3,8 @@ package rpc import ( "bytes" "encoding/json" + "errors" + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -442,6 +444,26 @@ type BlockFilterArgs struct { Max int } +func toNumber(v interface{}) (int64, error) { + var str string + if v != nil { + var ok bool + str, ok = v.(string) + if !ok { + return 0, errors.New("is not a string or undefined") + } + } else { + str = "latest" + } + + switch str { + case "latest": + return -1, nil + default: + return int64(common.Big(v.(string)).Int64()), nil + } +} + func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) { var obj []struct { FromBlock interface{} `json:"fromBlock"` @@ -460,30 +482,13 @@ func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) { return NewInsufficientParamsError(len(obj), 1) } - fromstr, ok := obj[0].FromBlock.(string) - if !ok { - return NewDecodeParamError("FromBlock is not a string") + args.Earliest, err = toNumber(obj[0].FromBlock) + if err != nil { + return NewDecodeParamError(fmt.Sprintf("FromBlock %v", err)) } - - switch fromstr { - case "latest": - args.Earliest = -1 - default: - args.Earliest = int64(common.Big(obj[0].FromBlock.(string)).Int64()) - } - - tostr, ok := obj[0].ToBlock.(string) - if !ok { - return NewDecodeParamError("ToBlock is not a string") - } - - switch tostr { - case "latest": - args.Latest = -1 - case "pending": - args.Latest = -2 - default: - args.Latest = int64(common.Big(obj[0].ToBlock.(string)).Int64()) + args.Latest, err = toNumber(obj[0].FromBlock) + if err != nil { + return NewDecodeParamError(fmt.Sprintf("ToBlock %v", err)) } args.Max = int(common.Big(obj[0].Limit).Int64()) -- cgit From ace5b5a1bf881ab1219f8252144faf1836e0a994 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 12:06:45 +0100 Subject: updated web3.js --- cmd/mist/assets/ext/ethereum.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/mist/assets/ext/ethereum.js b/cmd/mist/assets/ext/ethereum.js index 9f073d909..17164bea8 160000 --- a/cmd/mist/assets/ext/ethereum.js +++ b/cmd/mist/assets/ext/ethereum.js @@ -1 +1 @@ -Subproject commit 9f073d9091cd2d2b46dd46afea9dcf5d825ecd7c +Subproject commit 17164bea8b330d6a118b40d6fbe222ffb12a0e9f -- cgit From 4ba850639ede569bda4413b8e986b26726950d08 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 12:15:12 +0100 Subject: updated web3.js light for console --- jsre/ethereum_js.go | 2 +- rpc/api.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jsre/ethereum_js.go b/jsre/ethereum_js.go index fb0170288..403c438bc 100644 --- a/jsre/ethereum_js.go +++ b/jsre/ethereum_js.go @@ -1,3 +1,3 @@ package jsre -const Ethereum_JS = `require=function t(e,n,r){function o(a,s){if(!n[a]){if(!e[a]){var u="function"==typeof require&&require;if(!s&&u)return u(a,!0);if(i)return i(a,!0);var c=new Error("Cannot find module '"+a+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[a]={exports:{}};e[a][0].call(l.exports,function(t){var n=e[a][1][t];return o(n?n:t)},l,l.exports,t,e,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;ay;y++)g.push(d(e.slice(0,u))),e=e.slice(u);n.push(g)}else o.prefixedType("bytes")(t[c].type)?(l=l.slice(u),n.push(d(e.slice(0,u))),e=e.slice(u)):(n.push(d(e.slice(0,u))),e=e.slice(u))}),n},d=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),o=n.extractTypeName(t.name),i=function(){var e=Array.prototype.slice.call(arguments);return l(t.inputs,e)};void 0===e[r]&&(e[r]=i),e[r][o]=i}),e},h=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),o=n.extractTypeName(t.name),i=function(e){return m(t.outputs,e)};void 0===e[r]&&(e[r]=i),e[r][o]=i}),e};e.exports={inputParser:d,outputParser:h,formatInput:l,formatOutput:m}},{"../utils/config":5,"../utils/utils":6,"./formatters":2,"./types":3}],2:[function(t,e){var n=t("bignumber.js"),r=t("../utils/utils"),o=t("../utils/config"),i=function(t,e,n){return new Array(e-t.length+1).join(n?n:"0")+t},a=function(t){var e=2*o.ETH_PADDING;return n.config(o.ETH_BIGNUMBER_ROUNDING_MODE),i(r.toTwosComplement(t).round().toString(16),e)},s=function(t){return r.fromAscii(t,o.ETH_PADDING).substr(2)},u=function(t){return"000000000000000000000000000000000000000000000000000000000000000"+(t?"1":"0")},c=function(t){return a(new n(t).times(new n(2).pow(128)))},l=function(t){return"1"===new n(t.substr(0,1),16).toString(2).substr(0,1)},f=function(t){return t=t||"0",l(t)?new n(t,16).minus(new n("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16)).minus(1):new n(t,16)},p=function(t){return t=t||"0",new n(t,16)},m=function(t){return f(t).dividedBy(new n(2).pow(128))},d=function(t){return p(t).dividedBy(new n(2).pow(128))},h=function(t){return"0x"+t},g=function(t){return"0000000000000000000000000000000000000000000000000000000000000001"===t?!0:!1},y=function(t){return r.toAscii(t)},v=function(t){return"0x"+t.slice(t.length-40,t.length)};e.exports={formatInputInt:a,formatInputString:s,formatInputBool:u,formatInputReal:c,formatOutputInt:f,formatOutputUInt:p,formatOutputReal:m,formatOutputUReal:d,formatOutputHash:h,formatOutputBool:g,formatOutputString:y,formatOutputAddress:v}},{"../utils/config":5,"../utils/utils":6,"bignumber.js":"bignumber.js"}],3:[function(t,e){var n=t("./formatters"),r=function(t){return function(e){return 0===e.indexOf(t)}},o=function(t){return function(e){return t===e}},i=function(){return[{type:r("uint"),format:n.formatInputInt},{type:r("int"),format:n.formatInputInt},{type:r("bytes"),format:n.formatInputString},{type:r("real"),format:n.formatInputReal},{type:r("ureal"),format:n.formatInputReal},{type:o("address"),format:n.formatInputInt},{type:o("bool"),format:n.formatInputBool}]},a=function(){return[{type:r("uint"),format:n.formatOutputUInt},{type:r("int"),format:n.formatOutputInt},{type:r("bytes"),format:n.formatOutputString},{type:r("real"),format:n.formatOutputReal},{type:r("ureal"),format:n.formatOutputUReal},{type:o("address"),format:n.formatOutputAddress},{type:o("bool"),format:n.formatOutputBool}]};e.exports={prefixedType:r,namedType:o,inputTypes:i,outputTypes:a}},{"./formatters":2}],4:[function(t,e,n){"use strict";n.XMLHttpRequest="undefined"==typeof XMLHttpRequest?{}:XMLHttpRequest},{}],5:[function(t,e){var n=t("bignumber.js"),r=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:r,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:n.ROUND_DOWN},ETH_POLLING_TIMEOUT:1e3,ETH_DEFAULTBLOCK:"latest"}},{"bignumber.js":"bignumber.js"}],6:[function(t,e){var n=t("bignumber.js"),r={wei:"1",kwei:"1000",ada:"1000",mwei:"1000000",babbage:"1000000",gwei:"1000000000",shannon:"1000000000",szabo:"1000000000000",finney:"1000000000000000",ether:"1000000000000000000",kether:"1000000000000000000000",grand:"1000000000000000000000",einstein:"1000000000000000000000",mether:"1000000000000000000000000",gether:"1000000000000000000000000000",tether:"1000000000000000000000000000000"},o=function(t,e){for(var n=!1,r=0;rn;n+=2){var o=parseInt(t.substr(n,2),16);if(0===o)break;e+=String.fromCharCode(o)}return e},a=function(t){for(var e="",n=0;n1?(t[n[0]]=t[n[0]]||{},t[n[0]][n[1]]=e):t[n[0]]=e},e.exports=o},{"../utils/utils":6,"./errors":11}],19:[function(t,e){var n=t("../utils/utils"),r=t("./property"),o=[],i=[new r({name:"listening",getter:"net_listening"}),new r({name:"peerCount",getter:"net_peerCount",outputFormatter:n.toDecimal})];e.exports={methods:o,properties:i}},{"../utils/utils":6,"./property":20}],20:[function(t,e){var n=(t("../utils/utils"),t("./errors"),function(t){this.name=t.name,this.getter=t.getter,this.setter=t.setter,this.outputFormatter=t.outputFormatter,this.inputFormatter=t.inputFormatter});n.prototype.formatInput=function(t){return this.inputFormatter?this.inputFormatter(t):t},n.prototype.formatOutput=function(t){return this.outputFormatter&&null!==t?this.outputFormatter(t):t},n.prototype.attachToObject=function(t,e){var n=this.name.split(".");n.length>1?(t[n[0]]=t[n[0]]||{},Object.defineProperty(t[n[0]],n[1],e)):Object.defineProperty(t,n[0],e)},e.exports=n},{"../utils/utils":6,"./errors":11}],21:[function(t,e){var n=function(){};n.prototype.send=function(t){var e=navigator.qt.callMethod(JSON.stringify(t));return JSON.parse(e)},e.exports=n},{}],22:[function(t,e){var n=t("./jsonrpc"),r=t("../utils/utils"),o=t("../utils/config"),i=t("./errors"),a=function(t){this.jsonrpc=new n,this.provider=t,this.polls=[],this.timeout=null,this.poll()};a.prototype.send=function(t){if(!this.provider)return console.error(i.InvalidProvider),null;var e=this.jsonrpc.toPayload(t.method,t.params),n=this.provider.send(e);if(!this.jsonrpc.isValidResponse(n))throw i.InvalidResponse(n);return n.result},a.prototype.sendAsync=function(t,e){if(!this.provider)return e(i.InvalidProvider);var n=this.jsonrpc.toPayload(t.method,t.params),r=this;this.provider.sendAsync(n,function(t,n){return t?e(t):r.jsonrpc.isValidResponse(n)?void e(null,n.result):e(i.InvalidResponse(n))})},a.prototype.setProvider=function(t){this.provider=t},a.prototype.startPolling=function(t,e,n,r){this.polls.push({data:t,id:e,callback:n,uninstall:r})},a.prototype.stopPolling=function(t){for(var e=this.polls.length;e--;){var n=this.polls[e];n.id===t&&this.polls.splice(e,1)}},a.prototype.reset=function(){this.polls.forEach(function(t){t.uninstall(t.id)}),this.polls=[],this.timeout&&(clearTimeout(this.timeout),this.timeout=null),this.poll()},a.prototype.poll=function(){if(this.timeout=setTimeout(this.poll.bind(this),o.ETH_POLLING_TIMEOUT),this.polls.length){if(!this.provider)return void console.error(i.InvalidProvider);var t=this.jsonrpc.toBatchPayload(this.polls.map(function(t){return t.data})),e=this;this.provider.sendAsync(t,function(t,n){if(!t){if(!r.isArray(n))throw i.InvalidResponse(n);n.map(function(t,n){return t.callback=e.polls[n].callback,t}).filter(function(t){var n=e.jsonrpc.isValidResponse(t);return n||t.callback(i.InvalidResponse(t)),n}).filter(function(t){return r.isArray(t.result)&&t.result.length>0}).forEach(function(t){t.callback(null,t)})}})}},e.exports=a},{"../utils/config":5,"../utils/utils":6,"./errors":11,"./jsonrpc":17}],23:[function(t,e){var n=t("./method"),r=t("./formatters"),o=new n({name:"post",call:"shh_post",params:1,inputFormatter:r.inputPostFormatter}),i=new n({name:"newIdentity",call:"shh_newIdentity",params:0}),a=new n({name:"hasIdentity",call:"shh_hasIdentity",params:1}),s=new n({name:"newGroup",call:"shh_newGroup",params:0}),u=new n({name:"addToGroup",call:"shh_addToGroup",params:0}),c=[o,i,a,s,u];e.exports={methods:c}},{"./formatters":15,"./method":18}],24:[function(t,e){var n=t("../web3"),r=t("../utils/config"),o=function(t){return n.sha3(n.fromAscii(t)).slice(0,2+2*r.ETH_SIGNATURE_LENGTH)},i=function(t){return n.sha3(n.fromAscii(t))};e.exports={functionSignatureFromAscii:o,eventSignatureFromAscii:i}},{"../utils/config":5,"../web3":8}],25:[function(t,e){var n=t("./method"),r=function(){var t=function(t){return"string"==typeof t[0]?"eth_newBlockFilter":"eth_newFilter"},e=new n({name:"newFilter",call:t,params:1}),r=new n({name:"uninstallFilter",call:"eth_uninstallFilter",params:1}),o=new n({name:"getLogs",call:"eth_getFilterLogs",params:1});return[e,r,o]},o=function(){var t=new n({name:"newFilter",call:"shh_newFilter",params:1}),e=new n({name:"uninstallFilter",call:"shh_uninstallFilter",params:1}),r=new n({name:"getLogs",call:"shh_getMessages",params:1});return[t,e,r]};e.exports={eth:r,shh:o}},{"./method":18}],26:[function(){},{}],"bignumber.js":[function(t,e){"use strict";e.exports=BigNumber},{}],"ethereum.js":[function(t,e){var n=t("./lib/web3");n.providers.HttpProvider=t("./lib/web3/httpprovider"),n.providers.QtSyncProvider=t("./lib/web3/qtsync"),n.eth.contract=t("./lib/web3/contract"),n.abi=t("./lib/solidity/abi"),e.exports=n},{"./lib/solidity/abi":1,"./lib/web3":8,"./lib/web3/contract":9,"./lib/web3/httpprovider":16,"./lib/web3/qtsync":21}]},{},["ethereum.js"]);` +const Ethereum_JS = `require=function t(e,n,r){function o(a,u){if(!n[a]){if(!e[a]){var s="function"==typeof require&&require;if(!u&&s)return s(a,!0);if(i)return i(a,!0);var c=new Error("Cannot find module '"+a+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[a]={exports:{}};e[a][0].call(l.exports,function(t){var n=e[a][1][t];return o(n?n:t)},l,l.exports,t,e,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;ay;y++)g.push(d(e.slice(0,s))),e=e.slice(s);n.push(g)}else o.prefixedType("bytes")(t[c].type)?(l=l.slice(s),n.push(d(e.slice(0,s))),e=e.slice(s)):(n.push(d(e.slice(0,s))),e=e.slice(s))}),n},d=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),o=n.extractTypeName(t.name),i=function(){var e=Array.prototype.slice.call(arguments);return l(t.inputs,e)};void 0===e[r]&&(e[r]=i),e[r][o]=i}),e},h=function(t){var e={};return t.forEach(function(t){var r=n.extractDisplayName(t.name),o=n.extractTypeName(t.name),i=function(e){return m(t.outputs,e)};void 0===e[r]&&(e[r]=i),e[r][o]=i}),e};e.exports={inputParser:d,outputParser:h,formatInput:l,formatOutput:m}},{"../utils/config":5,"../utils/utils":6,"./formatters":2,"./types":3}],2:[function(t,e){var n=t("bignumber.js"),r=t("../utils/utils"),o=t("../utils/config"),i=function(t,e,n){return new Array(e-t.length+1).join(n?n:"0")+t},a=function(t){var e=2*o.ETH_PADDING;return n.config(o.ETH_BIGNUMBER_ROUNDING_MODE),i(r.toTwosComplement(t).round().toString(16),e)},u=function(t){return r.fromAscii(t,o.ETH_PADDING).substr(2)},s=function(t){return"000000000000000000000000000000000000000000000000000000000000000"+(t?"1":"0")},c=function(t){return a(new n(t).times(new n(2).pow(128)))},l=function(t){return"1"===new n(t.substr(0,1),16).toString(2).substr(0,1)},f=function(t){return t=t||"0",l(t)?new n(t,16).minus(new n("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",16)).minus(1):new n(t,16)},p=function(t){return t=t||"0",new n(t,16)},m=function(t){return f(t).dividedBy(new n(2).pow(128))},d=function(t){return p(t).dividedBy(new n(2).pow(128))},h=function(t){return"0x"+t},g=function(t){return"0000000000000000000000000000000000000000000000000000000000000001"===t?!0:!1},y=function(t){return r.toAscii(t)},b=function(t){return"0x"+t.slice(t.length-40,t.length)};e.exports={formatInputInt:a,formatInputString:u,formatInputBool:s,formatInputReal:c,formatOutputInt:f,formatOutputUInt:p,formatOutputReal:m,formatOutputUReal:d,formatOutputHash:h,formatOutputBool:g,formatOutputString:y,formatOutputAddress:b}},{"../utils/config":5,"../utils/utils":6,"bignumber.js":"bignumber.js"}],3:[function(t,e){var n=t("./formatters"),r=function(t){return function(e){return 0===e.indexOf(t)}},o=function(t){return function(e){return t===e}},i=function(){return[{type:r("uint"),format:n.formatInputInt},{type:r("int"),format:n.formatInputInt},{type:r("bytes"),format:n.formatInputString},{type:r("real"),format:n.formatInputReal},{type:r("ureal"),format:n.formatInputReal},{type:o("address"),format:n.formatInputInt},{type:o("bool"),format:n.formatInputBool}]},a=function(){return[{type:r("uint"),format:n.formatOutputUInt},{type:r("int"),format:n.formatOutputInt},{type:r("bytes"),format:n.formatOutputString},{type:r("real"),format:n.formatOutputReal},{type:r("ureal"),format:n.formatOutputUReal},{type:o("address"),format:n.formatOutputAddress},{type:o("bool"),format:n.formatOutputBool}]};e.exports={prefixedType:r,namedType:o,inputTypes:i,outputTypes:a}},{"./formatters":2}],4:[function(t,e,n){"use strict";n.XMLHttpRequest="undefined"==typeof XMLHttpRequest?{}:XMLHttpRequest},{}],5:[function(t,e){var n=t("bignumber.js"),r=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:r,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:n.ROUND_DOWN},ETH_POLLING_TIMEOUT:1e3,ETH_DEFAULTBLOCK:"latest"}},{"bignumber.js":"bignumber.js"}],6:[function(t,e){var n=t("bignumber.js"),r={wei:"1",kwei:"1000",ada:"1000",mwei:"1000000",babbage:"1000000",gwei:"1000000000",shannon:"1000000000",szabo:"1000000000000",finney:"1000000000000000",ether:"1000000000000000000",kether:"1000000000000000000000",grand:"1000000000000000000000",einstein:"1000000000000000000000",mether:"1000000000000000000000000",gether:"1000000000000000000000000000",tether:"1000000000000000000000000000000"},o=function(t,e){for(var n=!1,r=0;rn;n+=2){var o=parseInt(t.substr(n,2),16);if(0===o)break;e+=String.fromCharCode(o)}return e},a=function(t){for(var e="",n=0;n1?(t[n[0]]||(t[n[0]]={}),t[n[0]][n[1]]=r):t[n[0]]=r})},g=function(t,e){e.forEach(function(e){var n=e.name.split("."),r={};r.get=function(){return e.newProperty&&console.warn("This property is deprecated please use web3."+e.newProperty+" instead."),x.manager.send({method:e.getter,outputFormatter:e.outputFormatter})},e.setter&&(r.set=function(t){return e.newProperty&&console.warn("This property is deprecated please use web3."+e.newProperty+" instead."),x.manager.send({method:e.setter,params:[t],inputFormatter:e.inputFormatter})}),r.enumerable=!e.newProperty,n.length>1?(t[n[0]]||(t[n[0]]={}),Object.defineProperty(t[n[0]],n[1],r)):Object.defineProperty(t,e.name,r)})},y=function(t,e,n,r){x.manager.startPolling({method:t,params:[e]},e,n,r)},b=function(t){x.manager.stopPolling(t)},v={startPolling:y.bind(null,"eth_getFilterChanges"),stopPolling:b},w={startPolling:y.bind(null,"shh_getFilterChanges"),stopPolling:b},x={version:{api:n.version},manager:f(),providers:{},setProvider:function(t){x.manager.setProvider(t)},reset:function(){x.manager.reset()},toHex:c.toHex,toAscii:c.toAscii,fromAscii:c.fromAscii,toDecimal:c.toDecimal,fromDecimal:c.fromDecimal,toBigNumber:c.toBigNumber,toWei:c.toWei,fromWei:c.fromWei,isAddress:c.isAddress,net:{},eth:{contractFromAbi:function(t){return console.warn("Initiating a contract like this is deprecated please use var MyContract = eth.contract(abi); new MyContract(address); instead."),function(e){e=e||"0xc6d9d2cd449a754c494264e1809c50e34d64562b";var n=x.eth.contract(e,t);return n.address=e,n}},filter:function(t,e,n){return t._isEvent?t(e,n):s(t,v,l.outputLogFormatter)},watch:function(t,e,n){return console.warn("eth.watch() is deprecated please use eth.filter() instead."),this.filter(t,e,n)}},db:{},shh:{filter:function(t){return s(t,w,l.outputPostFormatter)},watch:function(t){return console.warn("shh.watch() is deprecated please use shh.filter() instead."),this.filter(t)}}};Object.defineProperty(x.eth,"defaultBlock",{get:function(){return p.ETH_DEFAULTBLOCK},set:function(t){return p.ETH_DEFAULTBLOCK=t,p.ETH_DEFAULTBLOCK}}),h(x,m),g(x,d),h(x.net,r.methods),g(x.net,r.properties),h(x.eth,o.methods),g(x.eth,o.properties),h(x.db,i.methods()),h(x.shh,a.methods()),h(v,u.eth()),h(w,u.shh()),e.exports=x},{"./utils/config":5,"./utils/utils":6,"./version.json":7,"./web3/db":10,"./web3/eth":11,"./web3/filter":13,"./web3/formatters":14,"./web3/net":17,"./web3/requestmanager":19,"./web3/shh":20,"./web3/watches":22}],9:[function(t,e){function n(t,e){t.forEach(function(t){if(-1===t.name.indexOf("(")){var e=t.name,n=t.inputs.map(function(t){return t.type}).join();t.name=e+"("+n+")"}});var n={};return c(n),l(n,t,e),f(n,t,e),p(n,t,e),n}var r=t("../web3"),o=t("../solidity/abi"),i=t("../utils/utils"),a=t("./event"),u=t("./signature"),s=function(t){r._currentContractAbi=t.abi,r._currentContractAddress=t.address,r._currentContractMethodName=t.method,r._currentContractMethodParams=t.params},c=function(t){t.call=function(e){return t._isTransaction=!1,t._options=e,t},t.sendTransaction=function(e){return t._isTransaction=!0,t._options=e,t},t.transact=function(e){return console.warn("myContract.transact() is deprecated please use myContract.sendTransaction() instead."),t.sendTransaction(e)},t._options={},["gas","gasPrice","value","from"].forEach(function(e){t[e]=function(n){return t._options[e]=n,t}})},l=function(t,e,n){var a=o.inputParser(e),c=o.outputParser(e);i.filterFunctions(e).forEach(function(o){var l=i.extractDisplayName(o.name),f=i.extractTypeName(o.name),p=function(){var i=Array.prototype.slice.call(arguments),p=u.functionSignatureFromAscii(o.name),m=a[l][f].apply(null,i),d=t._options||{};d.to=n,d.data=p+m;var h=t._isTransaction===!0||t._isTransaction!==!1&&!o.constant,g=d.collapse!==!1;if(t._options={},t._isTransaction=null,h)return s({abi:e,address:n,method:o.name,params:i}),void r.eth.sendTransaction(d);var y=r.eth.call(d),b=c[l][f](y);return g&&(1===b.length?b=b[0]:0===b.length&&(b=null)),b};void 0===t[l]&&(t[l]=p),t[l][f]=p})},f=function(t,e,n){t.address=n,t._onWatchEventResult=function(t){var n=event.getMatchingEvent(i.filterEvents(e)),r=a.outputParser(n);return r(t)},Object.defineProperty(t,"topics",{get:function(){return i.filterEvents(e).map(function(t){return u.eventSignatureFromAscii(t.name)})}})},p=function(t,e,n){i.filterEvents(e).forEach(function(e){var o=function(){var t=Array.prototype.slice.call(arguments),o=u.eventSignatureFromAscii(e.name),i=a.inputParser(n,o,e),s=i.apply(null,t),c=function(t){var n=a.outputParser(e);return n(t)};return r.eth.filter(s,void 0,void 0,c)};o._isEvent=!0;var s=i.extractDisplayName(e.name),c=i.extractTypeName(e.name);void 0===t[s]&&(t[s]=o),t[s][c]=o})},m=function(t){return t instanceof Array&&1===arguments.length?n.bind(null,t):(console.warn("Initiating a contract like this is deprecated please use var MyContract = eth.contract(abi); new MyContract(address); instead."),new n(arguments[1],arguments[0]))};e.exports=m},{"../solidity/abi":1,"../utils/utils":6,"../web3":8,"./event":12,"./signature":21}],10:[function(t,e){var n=function(){return[{name:"putString",call:"db_putString"},{name:"getString",call:"db_getString"},{name:"putHex",call:"db_putHex"},{name:"getHex",call:"db_getHex"}]};e.exports={methods:n}},{}],11:[function(t,e){var n=t("./formatters"),r=t("../utils/utils"),o=function(t){return r.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getBlockByHash":"eth_getBlockByNumber"},i=function(t){return r.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getTransactionByBlockHashAndIndex":"eth_getTransactionByBlockNumberAndIndex"},a=function(t){return r.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getUncleByBlockHashAndIndex":"eth_getUncleByBlockNumberAndIndex"},u=function(t){return r.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getBlockTransactionCountByHash":"eth_getBlockTransactionCountByNumber"},s=function(t){return r.isString(t[0])&&0===t[0].indexOf("0x")?"eth_getUncleCountByBlockHash":"eth_getUncleCountByBlockNumber"},c=[{name:"getBalance",call:"eth_getBalance",addDefaultblock:2,outputFormatter:n.convertToBigNumber},{name:"getStorage",call:"eth_getStorage",addDefaultblock:2},{name:"getStorageAt",call:"eth_getStorageAt",addDefaultblock:3,inputFormatter:r.toHex},{name:"getCode",call:"eth_getCode",addDefaultblock:2},{name:"getBlock",call:o,outputFormatter:n.outputBlockFormatter,inputFormatter:[r.toHex,function(t){return t?!0:!1}]},{name:"getUncle",call:a,outputFormatter:n.outputBlockFormatter,inputFormatter:[r.toHex,r.toHex,function(t){return t?!0:!1}]},{name:"getCompilers",call:"eth_getCompilers"},{name:"getBlockTransactionCount",call:u,outputFormatter:r.toDecimal,inputFormatter:r.toHex},{name:"getBlockUncleCount",call:s,outputFormatter:r.toDecimal,inputFormatter:r.toHex},{name:"getTransaction",call:"eth_getTransactionByHash",outputFormatter:n.outputTransactionFormatter},{name:"getTransactionFromBlock",call:i,outputFormatter:n.outputTransactionFormatter,inputFormatter:r.toHex},{name:"getTransactionCount",call:"eth_getTransactionCount",addDefaultblock:2,outputFormatter:r.toDecimal},{name:"sendTransaction",call:"eth_sendTransaction",inputFormatter:n.inputTransactionFormatter},{name:"call",call:"eth_call",addDefaultblock:2,inputFormatter:n.inputCallFormatter},{name:"compile.solidity",call:"eth_compileSolidity"},{name:"compile.lll",call:"eth_compileLLL",inputFormatter:r.toHex},{name:"compile.serpent",call:"eth_compileSerpent",inputFormatter:r.toHex},{name:"flush",call:"eth_flush"},{name:"balanceAt",call:"eth_balanceAt",newMethod:"eth.getBalance"},{name:"stateAt",call:"eth_stateAt",newMethod:"eth.getStorageAt"},{name:"storageAt",call:"eth_storageAt",newMethod:"eth.getStorage"},{name:"countAt",call:"eth_countAt",newMethod:"eth.getTransactionCount"},{name:"codeAt",call:"eth_codeAt",newMethod:"eth.getCode"},{name:"transact",call:"eth_transact",newMethod:"eth.sendTransaction"},{name:"block",call:o,newMethod:"eth.getBlock"},{name:"transaction",call:i,newMethod:"eth.getTransaction"},{name:"uncle",call:a,newMethod:"eth.getUncle"},{name:"compilers",call:"eth_compilers",newMethod:"eth.getCompilers"},{name:"solidity",call:"eth_solidity",newMethod:"eth.compile.solidity"},{name:"lll",call:"eth_lll",newMethod:"eth.compile.lll"},{name:"serpent",call:"eth_serpent",newMethod:"eth.compile.serpent"},{name:"transactionCount",call:u,newMethod:"eth.getBlockTransactionCount"},{name:"uncleCount",call:s,newMethod:"eth.getBlockUncleCount"},{name:"logs",call:"eth_logs"}],l=[{name:"coinbase",getter:"eth_coinbase"},{name:"mining",getter:"eth_mining"},{name:"gasPrice",getter:"eth_gasPrice",outputFormatter:n.convertToBigNumber},{name:"accounts",getter:"eth_accounts"},{name:"blockNumber",getter:"eth_blockNumber",outputFormatter:r.toDecimal},{name:"listening",getter:"net_listening",setter:"eth_setListening",newProperty:"net.listening"},{name:"peerCount",getter:"net_peerCount",newProperty:"net.peerCount"},{name:"number",getter:"eth_number",newProperty:"eth.blockNumber"}];e.exports={methods:c,properties:l}},{"../utils/utils":6,"./formatters":14}],12:[function(t,e){var n=t("../solidity/abi"),r=t("../utils/utils"),o=t("./signature"),i=function(t,e){return t.filter(function(t){return t.indexed===e})},a=function(t,e){var n=r.findIndex(t,function(t){return t.name===e});return-1===n?void console.error("indexed param with name "+e+" not found"):t[n]},u=function(t,e){return Object.keys(e).map(function(r){var o=[a(i(t.inputs,!0),r)],u=e[r];return u instanceof Array?u.map(function(t){return n.formatInput(o,[t])}):"0x"+n.formatInput(o,[u])})},s=function(t,e,n){return function(r,o){var i=o||{};return i.address=t,i.topics=[],i.topics.push(e),r&&(i.topics=i.topics.concat(u(n,r))),i}},c=function(t,e,n){var r=e.slice(),o=n.slice();return t.reduce(function(t,e){var n;return n=e.indexed?r.splice(0,1)[0]:o.splice(0,1)[0],t[e.name]=n,t},{})},l=function(t){return function(e){var o={event:r.extractDisplayName(t.name),number:e.number,hash:e.hash,args:{}};if(e.topics=e.topic,!e.topics)return o;var a=i(t.inputs,!0),u="0x"+e.topics.slice(1,e.topics.length).map(function(t){return t.slice(2)}).join(""),s=n.formatOutput(a,u),l=i(t.inputs,!1),f=n.formatOutput(l,e.data);return o.args=c(t.inputs,s,f),o}},f=function(t,e){for(var n=0;n Date: Thu, 26 Mar 2015 12:15:25 +0100 Subject: debug log --- rpc/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/api.go b/rpc/api.go index 230d9ff9c..aa5b54199 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -50,7 +50,7 @@ func (api *EthereumApi) Close() { func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error { // Spec at https://github.com/ethereum/wiki/wiki/JSON-RPC - rpclogger.Infof("%s %s", req.Method, req.Params) + rpclogger.Debugf("%s %s", req.Method, req.Params) switch req.Method { case "web3_sha3": -- cgit From 83b0cad76667c570a1be81653b531e6a9d513928 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 12:34:59 +0100 Subject: fixed block filter args --- rpc/args.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/args.go b/rpc/args.go index 892e3f3e1..61331d993 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -482,7 +482,7 @@ func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) { return NewInsufficientParamsError(len(obj), 1) } - args.Earliest, err = toNumber(obj[0].FromBlock) + args.Earliest, err = toNumber(obj[0].ToBlock) if err != nil { return NewDecodeParamError(fmt.Sprintf("FromBlock %v", err)) } -- cgit From c33dc3e328c791be8c82b514ba07523f065402e1 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 12:40:09 +0100 Subject: moved helper --- rpc/args.go | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/rpc/args.go b/rpc/args.go index 61331d993..1928ec218 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -36,6 +36,26 @@ func blockHeight(raw interface{}, number *int64) (err error) { return nil } +func toNumber(v interface{}) (int64, error) { + var str string + if v != nil { + var ok bool + str, ok = v.(string) + if !ok { + return 0, errors.New("is not a string or undefined") + } + } else { + str = "latest" + } + + switch str { + case "latest": + return -1, nil + default: + return int64(common.Big(v.(string)).Int64()), nil + } +} + type GetBlockByHashArgs struct { BlockHash string IncludeTxs bool @@ -444,26 +464,6 @@ type BlockFilterArgs struct { Max int } -func toNumber(v interface{}) (int64, error) { - var str string - if v != nil { - var ok bool - str, ok = v.(string) - if !ok { - return 0, errors.New("is not a string or undefined") - } - } else { - str = "latest" - } - - switch str { - case "latest": - return -1, nil - default: - return int64(common.Big(v.(string)).Int64()), nil - } -} - func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) { var obj []struct { FromBlock interface{} `json:"fromBlock"` -- cgit From a718515b3d43f00497231f981b5ea757b71d55ff Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 13:14:24 +0100 Subject: Squashed 'tests/files/' changes from a7081bc..c6d9629 c6d9629 added another test git-subtree-dir: tests/files git-subtree-split: c6d96293710a37489fa3b074a9fc228e0393f152 --- PoWTests/ethash_tests.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/PoWTests/ethash_tests.json b/PoWTests/ethash_tests.json index 4ce41ac6d..ceacd78a7 100644 --- a/PoWTests/ethash_tests.json +++ b/PoWTests/ethash_tests.json @@ -9,5 +9,16 @@ "full_size": 1073739904, "header_hash": "2a8de2adf89af77358250bf908bf04ba94a6e8c3ba87775564a41d269a05e4ce", "cache_hash": "35ded12eecf2ce2e8da2e15c06d463aae9b84cb2530a00b932e4bbc484cde353" + }, + "second": { + "nonce": "307692cf71b12f6d", + "mixhash": "e55d02c555a7969361cf74a9ec6211d8c14e4517930a00442f171bdb1698d175", + "header": "f901f7a01bef91439a3e070a6586851c11e6fd79bbbea074b2b836727b8e75c7d4a6b698a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ea3cb5f94fa2ddd52ec6dd6eb75cf824f4058ca1a00c6e51346be0670ce63ac5f05324e27d20b180146269c5aab844d09a2b108c64a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004002832fefd880845511ed2a80a0e55d02c555a7969361cf74a9ec6211d8c14e4517930a00442f171bdb1698d17588307692cf71b12f6d", + "seed": "0000000000000000000000000000000000000000000000000000000000000000", + "result": "ab9b13423cface72cbec8424221651bc2e384ef0f7a560e038fc68c8d8684829", + "cache_size": 16776896, + "full_size": 1073739904, + "header_hash": "100cbec5e5ef82991290d0d93d758f19082e71f234cf479192a8b94df6da6bfe", + "cache_hash": "35ded12eecf2ce2e8da2e15c06d463aae9b84cb2530a00b932e4bbc484cde353" } } -- cgit From d36501a6e54e1c794af0c7109e937f7f7c74de79 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 17:45:03 +0100 Subject: Fixed miner * Miners could stall because the worker wasn't aware the miner was done --- miner/agent.go | 23 ++++++++++++++++------- miner/remote_agent.go | 1 + miner/worker.go | 26 ++++++++++++++++++-------- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/miner/agent.go b/miner/agent.go index 5661d2982..c650fa2f3 100644 --- a/miner/agent.go +++ b/miner/agent.go @@ -1,12 +1,15 @@ package miner import ( + "sync" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/pow" ) type CpuMiner struct { + chMu sync.Mutex c chan *types.Block quit chan struct{} quitCurrentOp chan struct{} @@ -43,16 +46,13 @@ func (self *CpuMiner) Start() { } func (self *CpuMiner) update() { - justStarted := true out: for { select { case block := <-self.c: - if justStarted { - justStarted = true - } else { - self.quitCurrentOp <- struct{}{} - } + self.chMu.Lock() + self.quitCurrentOp <- struct{}{} + self.chMu.Unlock() go self.mine(block) case <-self.quit: @@ -60,6 +60,7 @@ out: } } + close(self.quitCurrentOp) done: // Empty channel for { @@ -75,12 +76,20 @@ done: func (self *CpuMiner) mine(block *types.Block) { minerlogger.Debugf("(re)started agent[%d]. mining...\n", self.index) + + // Reset the channel + self.chMu.Lock() + self.quitCurrentOp = make(chan struct{}, 1) + self.chMu.Unlock() + + // Mine nonce, mixDigest, _ := self.pow.Search(block, self.quitCurrentOp) if nonce != 0 { block.SetNonce(nonce) block.Header().MixDigest = common.BytesToHash(mixDigest) self.returnCh <- block - //self.returnCh <- Work{block.Number().Uint64(), nonce, mixDigest, seedHash} + } else { + self.returnCh <- nil } } diff --git a/miner/remote_agent.go b/miner/remote_agent.go index e92dd5963..aa04a58aa 100644 --- a/miner/remote_agent.go +++ b/miner/remote_agent.go @@ -50,6 +50,7 @@ out: break out case work := <-a.workCh: a.work = work + a.returnCh <- nil } } } diff --git a/miner/worker.go b/miner/worker.go index e0287ea8d..e3680dea3 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -5,6 +5,7 @@ import ( "math/big" "sort" "sync" + "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" @@ -58,13 +59,14 @@ type Agent interface { } type worker struct { - mu sync.Mutex + mu sync.Mutex + agents []Agent recv chan *types.Block mux *event.TypeMux quit chan struct{} pow pow.PoW - atWork int + atWork int64 eth core.Backend chain *core.ChainManager @@ -107,7 +109,7 @@ func (self *worker) start() { func (self *worker) stop() { self.mining = false - self.atWork = 0 + atomic.StoreInt64(&self.atWork, 0) close(self.quit) } @@ -135,9 +137,6 @@ out: self.uncleMu.Unlock() } - if self.atWork == 0 { - self.commitNewWork() - } case <-self.quit: // stop all agents for _, agent := range self.agents { @@ -146,6 +145,11 @@ out: break out case <-timer.C: minerlogger.Infoln("Hash rate:", self.HashRate(), "Khash") + + // XXX In case all mined a possible uncle + if atomic.LoadInt64(&self.atWork) == 0 { + self.commitNewWork() + } } } @@ -155,6 +159,12 @@ out: func (self *worker) wait() { for { for block := range self.recv { + atomic.AddInt64(&self.atWork, -1) + + if block == nil { + continue + } + if err := self.chain.InsertChain(types.Blocks{block}); err == nil { for _, uncle := range block.Uncles() { delete(self.possibleUncles, uncle.Hash()) @@ -170,7 +180,6 @@ func (self *worker) wait() { } else { self.commitNewWork() } - self.atWork-- } } } @@ -182,8 +191,9 @@ func (self *worker) push() { // push new work to agents for _, agent := range self.agents { + atomic.AddInt64(&self.atWork, 1) + agent.Work() <- self.current.block.Copy() - self.atWork++ } } } -- cgit From c32bca45ad753da69a5530a1ee96ff069e937fc2 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 17:45:09 +0100 Subject: Stack limit --- core/vm/stack.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/vm/stack.go b/core/vm/stack.go index c5c2774db..1e093476b 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -5,6 +5,8 @@ import ( "math/big" ) +const maxStack = 1024 + func newStack() *stack { return &stack{} } @@ -15,6 +17,10 @@ type stack struct { } func (st *stack) push(d *big.Int) { + if len(st.data) == maxStack { + panic(fmt.Sprintf("stack limit reached (%d)", maxStack)) + } + stackItem := new(big.Int).Set(d) if len(st.data) > st.ptr { st.data[st.ptr] = stackItem -- cgit From 658204bafcba6332e979aee690dc5cff6e46fb42 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 17:55:30 +0100 Subject: bump --- cmd/ethereum/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 2cf81b9e7..2f417aacb 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -42,7 +42,7 @@ import ( const ( ClientIdentifier = "Ethereum(G)" - Version = "0.9.3" + Version = "0.9.4" ) var ( -- cgit From c4ea921876b0535022882c568b5cc6b0269db7d4 Mon Sep 17 00:00:00 2001 From: zelig Date: Mon, 23 Mar 2015 13:00:06 +0000 Subject: import/export accounts - cli: add passwordfile flag - cli: change unlock flag only takes account - cli: with unlock you are prompted for password or use passfile with password flag - cli: unlockAccount used in normal client start (run) and accountExport - cli: getPassword used in accountCreate and accountImport - accounts: Manager.Import, Manager.Export - crypto: SaveECDSA (to complement LoadECDSA) to save to file - crypto: NewKeyFromECDSA added (used in accountImport and New = generated constructor) --- accounts/account_manager.go | 20 +++++ cmd/ethereum/main.go | 174 ++++++++++++++++++++++++++++++++++++-------- cmd/utils/flags.go | 8 +- crypto/crypto.go | 5 ++ crypto/key.go | 18 +++-- 5 files changed, 188 insertions(+), 37 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 646dc8376..670d4337f 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -208,3 +208,23 @@ func zeroKey(k *ecdsa.PrivateKey) { b[i] = 0 } } + +func (am *Manager) Export(path string, addr []byte, keyAuth string) error { + key, err := am.keyStore.GetKey(addr, keyAuth) + if err != nil { + return err + } + return crypto.SaveECDSA(path, key.PrivateKey) +} + +func (am *Manager) Import(path string, keyAuth string) (Account, error) { + privateKeyECDSA, err := crypto.LoadECDSA(path) + if err != nil { + return Account{}, err + } + key := crypto.NewKeyFromECDSA(privateKeyECDSA) + if err = am.keyStore.StoreKey(key, keyAuth); err != nil { + return Account{}, err + } + return Account{Address: key.Address}, nil +} diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 2f417aacb..276480195 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -26,11 +26,11 @@ import ( "os" "runtime" "strconv" - "strings" "time" "github.com/codegangsta/cli" "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" @@ -83,11 +83,62 @@ The output of this command is supposed to be machine-readable. Action: accountList, Name: "list", Usage: "print account addresses", + Description: ` + +`, }, { Action: accountCreate, Name: "new", Usage: "create a new account", + Description: ` + + ethereum account new + +Creates a new accountThe account is saved in encrypted format, you are prompted for a passphrase. +You must remember this passphrase to unlock your account in future. +For non-interactive use the passphrase can be specified with the --password flag: + + ethereum --password account new + + `, + }, + { + Action: accountImport, + Name: "import", + Usage: "import a private key into a new account", + Description: ` + + ethereum account import + +Imports a private key from and creates a new account with the address derived from the key. +The keyfile is assumed to contain an unencrypted private key in canonical EC format. + +The account is saved in encrypted format, you are prompted for a passphrase. +You must remember this passphrase to unlock your account in future. +For non-interactive use the passphrase can be specified with the --password flag: + + ethereum --password account import + + `, + }, + { + Action: accountExport, + Name: "export", + Usage: "export an account into key file", + Description: ` + + ethereum account export
+ +Exports the given account's private key into keyfile using the canonical EC format. +The account needs to be unlocked, if it is not the user is prompted for a passphrase to unlock it. +For non-interactive use, the password can be specified with the --unlock flag: + + ethereum --unlock account export
+ +Note: +Since you can directly copy your encrypted accounts to another ethereum instance, this import/export mechanism is not needed when you transfer an account between nodes. + `, }, }, }, @@ -130,6 +181,7 @@ The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP Ja } app.Flags = []cli.Flag{ utils.UnlockedAccountFlag, + utils.PasswordFileFlag, utils.BootnodesFlag, utils.DataDirFlag, utils.JSpathFlag, @@ -218,23 +270,43 @@ func execJSFiles(ctx *cli.Context) { ethereum.WaitForShutdown() } -func startEth(ctx *cli.Context, eth *eth.Ethereum) { - utils.StartEthereum(eth) +func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (passphrase string) { + if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) { + var err error + // Load startup keys. XXX we are going to need a different format + // Attempt to unlock the account + passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) + if len(passfile) == 0 { + fmt.Println("Please enter a passphrase now.") + auth, err := readPassword("Passphrase: ", true) + if err != nil { + utils.Fatalf("%v", err) + } - // Load startup keys. XXX we are going to need a different format - account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) - if len(account) > 0 { - split := strings.Split(account, ":") - if len(split) != 2 { - utils.Fatalf("Illegal 'unlock' format (address:password)") + passphrase = auth + + } else { + if passphrase, err = common.ReadAllFile(passfile); err != nil { + utils.Fatalf("Unable to read password file '%s': %v", passfile, err) + } } - am := eth.AccountManager() - // Attempt to unlock the account - err := am.Unlock(common.FromHex(split[0]), split[1]) + + err = am.Unlock(common.FromHex(account), passphrase) if err != nil { utils.Fatalf("Unlock account failed '%v'", err) } } + return +} + +func startEth(ctx *cli.Context, eth *eth.Ethereum) { + utils.StartEthereum(eth) + am := eth.AccountManager() + + account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) + if len(account) > 0 { + unlockAccount(ctx, am, account) + } // Start auxiliary services if enabled. if ctx.GlobalBool(utils.RPCEnabledFlag.Name) { utils.StartRPC(eth, ctx) @@ -255,30 +327,74 @@ func accountList(ctx *cli.Context) { } } -func accountCreate(ctx *cli.Context) { - am := utils.GetAccountManager(ctx) - passphrase := "" +func getPassPhrase(ctx *cli.Context) (passphrase string) { if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) { - fmt.Println("The new account will be encrypted with a passphrase.") - fmt.Println("Please enter a passphrase now.") - auth, err := readPassword("Passphrase: ", true) - if err != nil { - utils.Fatalf("%v", err) - } - confirm, err := readPassword("Repeat Passphrase: ", false) - if err != nil { - utils.Fatalf("%v", err) - } - if auth != confirm { - utils.Fatalf("Passphrases did not match.") + passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) + if len(passfile) == 0 { + fmt.Println("The new account will be encrypted with a passphrase.") + fmt.Println("Please enter a passphrase now.") + auth, err := readPassword("Passphrase: ", true) + if err != nil { + utils.Fatalf("%v", err) + } + confirm, err := readPassword("Repeat Passphrase: ", false) + if err != nil { + utils.Fatalf("%v", err) + } + if auth != confirm { + utils.Fatalf("Passphrases did not match.") + } + passphrase = auth + + } else { + var err error + if passphrase, err = common.ReadAllFile(passfile); err != nil { + utils.Fatalf("Unable to read password file '%s': %v", passfile, err) + } } - passphrase = auth } + return +} + +func accountCreate(ctx *cli.Context) { + am := utils.GetAccountManager(ctx) + passphrase := getPassPhrase(ctx) acct, err := am.NewAccount(passphrase) if err != nil { utils.Fatalf("Could not create the account: %v", err) } - fmt.Printf("Address: %x\n", acct.Address) + fmt.Printf("Address: %x\n", acct) +} + +func accountImport(ctx *cli.Context) { + keyfile := ctx.Args().First() + if len(keyfile) == 0 { + utils.Fatalf("keyfile must be given as argument") + } + am := utils.GetAccountManager(ctx) + passphrase := getPassPhrase(ctx) + acct, err := am.Import(keyfile, passphrase) + if err != nil { + utils.Fatalf("Could not create the account: %v", err) + } + fmt.Printf("Address: %x\n", acct) +} + +func accountExport(ctx *cli.Context) { + account := ctx.Args().First() + if len(account) == 0 { + utils.Fatalf("account address must be given as first argument") + } + keyfile := ctx.Args().Get(1) + if len(keyfile) == 0 { + utils.Fatalf("keyfile must be given as second argument") + } + am := utils.GetAccountManager(ctx) + auth := unlockAccount(ctx, am, account) + err := am.Export(keyfile, common.FromHex(account), auth) + if err != nil { + utils.Fatalf("Account export failed: %v", err) + } } func importchain(ctx *cli.Context) { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 94b043d73..f94ec3a69 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -104,7 +104,13 @@ var ( } UnlockedAccountFlag = cli.StringFlag{ Name: "unlock", - Usage: "Unlock a given account untill this programs exits (address:password)", + Usage: "unlock the account given until this program exits (prompts for password).", + Value: "", + } + PasswordFileFlag = cli.StringFlag{ + Name: "password", + Usage: "Password used when saving a new account and unlocking an existing account. If you create a new account make sure you remember this password.", + Value: "", } // logging and debug settings diff --git a/crypto/crypto.go b/crypto/crypto.go index c3d47b629..2d26dd25e 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -139,6 +139,11 @@ func LoadECDSA(file string) (*ecdsa.PrivateKey, error) { return ToECDSA(buf), nil } +// SaveECDSA saves a secp256k1 private key from the given file. +func SaveECDSA(file string, key *ecdsa.PrivateKey) error { + return common.WriteFile(file, FromECDSA(key)) +} + func GenerateKey() (*ecdsa.PrivateKey, error) { return ecdsa.GenerateKey(S256(), rand.Reader) } diff --git a/crypto/key.go b/crypto/key.go index 9dbf37467..0b84bfec1 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -85,6 +85,16 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) { return err } +func NewKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key { + id := uuid.NewRandom() + key := &Key{ + Id: id, + Address: PubkeyToAddress(privateKeyECDSA.PublicKey), + PrivateKey: privateKeyECDSA, + } + return key +} + func NewKey(rand io.Reader) *Key { randBytes := make([]byte, 64) _, err := rand.Read(randBytes) @@ -97,11 +107,5 @@ func NewKey(rand io.Reader) *Key { panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) } - id := uuid.NewRandom() - key := &Key{ - Id: id, - Address: PubkeyToAddress(privateKeyECDSA.PublicKey), - PrivateKey: privateKeyECDSA, - } - return key + return NewKeyFromECDSA(privateKeyECDSA) } -- cgit From 859f1f08ca48de99408c825eba8d6ed4bfea3235 Mon Sep 17 00:00:00 2001 From: zelig Date: Mon, 23 Mar 2015 21:34:05 +0000 Subject: blockpool: wrap intermittent status test in a loop --- blockpool/status_test.go | 83 +++++++++++++++++++++++++++--------------------- blockpool/test/util.go | 12 ++++--- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/blockpool/status_test.go b/blockpool/status_test.go index cbaa8bb55..a87b99d7c 100644 --- a/blockpool/status_test.go +++ b/blockpool/status_test.go @@ -1,7 +1,7 @@ package blockpool import ( - // "fmt" + "fmt" "testing" "time" @@ -45,17 +45,15 @@ func getStatusValues(s *Status) []int { func checkStatus(t *testing.T, bp *BlockPool, syncing bool, expected []int) (err error) { s := bp.Status() if s.Syncing != syncing { - t.Errorf("status for Syncing incorrect. expected %v, got %v", syncing, s.Syncing) + err = fmt.Errorf("status for Syncing incorrect. expected %v, got %v", syncing, s.Syncing) + return } got := getStatusValues(s) for i, v := range expected { - if i == 0 || i == 7 { - continue //hack - } err = test.CheckInt(statusFields[i], got[i], v, t) // fmt.Printf("%v: %v (%v)\n", statusFields[i], got[i], v) if err != nil { - return err + return } } return @@ -63,6 +61,25 @@ func checkStatus(t *testing.T, bp *BlockPool, syncing bool, expected []int) (err func TestBlockPoolStatus(t *testing.T) { test.LogInit() + var err error + n := 3 + for n > 0 { + n-- + err = testBlockPoolStatus(t) + if err != nil { + t.Log(err) + continue + } else { + return + } + } + if err != nil { + t.Errorf("no pass out of 3: %v", err) + } +} + +func testBlockPoolStatus(t *testing.T) (err error) { + _, blockPool, blockPoolTester := newTestBlockPool(t) blockPoolTester.blockChain[0] = nil blockPoolTester.initRefBlockChain(12) @@ -70,6 +87,7 @@ func TestBlockPoolStatus(t *testing.T) { delete(blockPoolTester.refBlockChain, 6) blockPool.Start() + defer blockPool.Stop() blockPoolTester.tds = make(map[int]int) blockPoolTester.tds[9] = 1 blockPoolTester.tds[11] = 3 @@ -79,73 +97,67 @@ func TestBlockPoolStatus(t *testing.T) { peer2 := blockPoolTester.newPeer("peer2", 2, 6) peer3 := blockPoolTester.newPeer("peer3", 3, 11) peer4 := blockPoolTester.newPeer("peer4", 1, 9) - // peer1 := blockPoolTester.newPeer("peer1", 1, 9) - // peer2 := blockPoolTester.newPeer("peer2", 2, 6) - // peer3 := blockPoolTester.newPeer("peer3", 3, 11) - // peer4 := blockPoolTester.newPeer("peer4", 1, 9) peer2.blocksRequestsMap = peer1.blocksRequestsMap var expected []int - var err error expected = []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - err = checkStatus(t, blockPool, false, expected) + err = checkStatus(nil, blockPool, false, expected) if err != nil { return } peer1.AddPeer() expected = []int{0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlocks(8, 9) - expected = []int{0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0} - // err = checkStatus(t, blockPool, true, expected) + expected = []int{1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0} + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlockHashes(9, 8, 7, 3, 2) expected = []int{6, 5, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0} - // expected = []int{5, 5, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlocks(3, 7, 8) expected = []int{6, 5, 3, 3, 0, 1, 0, 0, 1, 1, 1, 1, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlocks(2, 3) expected = []int{6, 5, 4, 4, 0, 1, 0, 0, 1, 1, 1, 1, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer4.AddPeer() expected = []int{6, 5, 4, 4, 0, 2, 0, 0, 2, 2, 1, 1, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer4.sendBlockHashes(12, 11) expected = []int{6, 5, 4, 4, 0, 2, 0, 0, 2, 2, 1, 1, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer2.AddPeer() expected = []int{6, 5, 4, 4, 0, 3, 0, 0, 3, 3, 1, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } @@ -153,76 +165,76 @@ func TestBlockPoolStatus(t *testing.T) { peer2.serveBlocks(5, 6) peer2.serveBlockHashes(6, 5, 4, 3, 2) expected = []int{10, 8, 5, 5, 0, 3, 1, 0, 3, 3, 2, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer2.serveBlocks(2, 3, 4) expected = []int{10, 8, 6, 6, 0, 3, 1, 0, 3, 3, 2, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } blockPool.RemovePeer("peer2") expected = []int{10, 8, 6, 6, 0, 3, 1, 0, 3, 2, 2, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlockHashes(2, 1, 0) expected = []int{11, 9, 6, 6, 0, 3, 1, 0, 3, 2, 2, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlocks(1, 2) expected = []int{11, 9, 7, 7, 0, 3, 1, 0, 3, 2, 2, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer1.serveBlocks(4, 5) expected = []int{11, 9, 8, 8, 0, 3, 1, 0, 3, 2, 2, 2, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer3.AddPeer() expected = []int{11, 9, 8, 8, 0, 4, 1, 0, 4, 3, 2, 3, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer3.serveBlocks(10, 11) expected = []int{12, 9, 9, 9, 0, 4, 1, 0, 4, 3, 3, 3, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer3.serveBlockHashes(11, 10, 9) expected = []int{14, 11, 9, 9, 0, 4, 1, 0, 4, 3, 3, 3, 0} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer4.sendBlocks(11, 12) expected = []int{14, 11, 9, 9, 0, 4, 1, 0, 4, 3, 4, 3, 1} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } peer3.serveBlocks(9, 10) expected = []int{14, 11, 10, 10, 0, 4, 1, 0, 4, 3, 4, 3, 1} - err = checkStatus(t, blockPool, true, expected) + err = checkStatus(nil, blockPool, true, expected) if err != nil { return } @@ -231,10 +243,9 @@ func TestBlockPoolStatus(t *testing.T) { blockPool.Wait(waitTimeout) time.Sleep(200 * time.Millisecond) expected = []int{14, 3, 11, 3, 8, 4, 1, 8, 4, 3, 4, 3, 1} - err = checkStatus(t, blockPool, false, expected) + err = checkStatus(nil, blockPool, false, expected) if err != nil { return } - - blockPool.Stop() + return nil } diff --git a/blockpool/test/util.go b/blockpool/test/util.go index 0349493c3..930601278 100644 --- a/blockpool/test/util.go +++ b/blockpool/test/util.go @@ -10,16 +10,20 @@ import ( func CheckInt(name string, got int, expected int, t *testing.T) (err error) { if got != expected { - t.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got) - err = fmt.Errorf("") + err = fmt.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got) + if t != nil { + t.Error(err) + } } return } func CheckDuration(name string, got time.Duration, expected time.Duration, t *testing.T) (err error) { if got != expected { - t.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got) - err = fmt.Errorf("") + err = fmt.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got) + if t != nil { + t.Error(err) + } } return } -- cgit From fd8d18ec280c3fe2c3d2651870c31c65b02039ba Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 24 Mar 2015 12:37:00 +0000 Subject: unlocking coinbase - extract accounts.getKey method - if given empty address it retrieves coinbase (first account) - cli -unlock coinbase will unlock coinbase --- accounts/account_manager.go | 15 +++++++++++++-- cmd/ethereum/main.go | 5 ++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 670d4337f..21ef46991 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -101,6 +101,17 @@ func (am *Manager) firstAddr() ([]byte, error) { return addrs[0], nil } +func (am *Manager) getKey(addr []byte, keyAuth string) (*crypto.Key, error) { + if len(addr) == 0 { + var err error + addr, err = am.firstAddr() + if err != nil { + return nil, err + } + } + return am.keyStore.GetKey(addr, keyAuth) +} + func (am *Manager) DeleteAccount(address []byte, auth string) error { return am.keyStore.DeleteKey(address, auth) } @@ -119,7 +130,7 @@ func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) // TimedUnlock unlocks the account with the given address. // When timeout has passed, the account will be locked again. func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duration) error { - key, err := am.keyStore.GetKey(addr, keyAuth) + key, err := am.getKey(addr, keyAuth) if err != nil { return err } @@ -132,7 +143,7 @@ func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duratio // stays unlocked until the program exits or until a TimedUnlock // timeout (started after the call to Unlock) expires. func (am *Manager) Unlock(addr []byte, keyAuth string) error { - key, err := am.keyStore.GetKey(addr, keyAuth) + key, err := am.getKey(addr, keyAuth) if err != nil { return err } diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 276480195..fea3fbf61 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -134,7 +134,7 @@ Exports the given account's private key into keyfile using the canonical EC form The account needs to be unlocked, if it is not the user is prompted for a passphrase to unlock it. For non-interactive use, the password can be specified with the --unlock flag: - ethereum --unlock account export
+ ethereum --password account export
Note: Since you can directly copy your encrypted accounts to another ethereum instance, this import/export mechanism is not needed when you transfer an account between nodes. @@ -305,6 +305,9 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) { account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) if len(account) > 0 { + if account == "coinbase" { + account = "" + } unlockAccount(ctx, am, account) } // Start auxiliary services if enabled. -- cgit From 1c4c71dcff442e3ae30e510fef312d3c05341f30 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 24 Mar 2015 14:09:06 +0000 Subject: cli: fix liner not closing (spuriously opened) in noninteractive jsre --- cmd/ethereum/js.go | 4 ++-- cmd/ethereum/main.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index 1f0033daa..599af0a16 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -67,14 +67,14 @@ type jsre struct { prompter } -func newJSRE(ethereum *eth.Ethereum, libPath string) *jsre { +func newJSRE(ethereum *eth.Ethereum, libPath string, interactive bool) *jsre { js := &jsre{ethereum: ethereum, ps1: "> "} js.xeth = xeth.New(ethereum, js) js.re = re.New(libPath) js.apiBindings() js.adminBindings() - if !liner.TerminalSupported() { + if !liner.TerminalSupported() || !interactive { js.prompter = dumbterm{bufio.NewReader(os.Stdin)} } else { lr := liner.NewLiner() diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index fea3fbf61..59c6ef485 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -246,7 +246,7 @@ func console(ctx *cli.Context) { } startEth(ctx, ethereum) - repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name)) + repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), true) repl.interactive() ethereum.Stop() @@ -261,7 +261,7 @@ func execJSFiles(ctx *cli.Context) { } startEth(ctx, ethereum) - repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name)) + repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), false) for _, file := range ctx.Args() { repl.exec(file) } -- cgit From 34d5a6c156a014ce000b4f850f2b0f11533387f0 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 24 Mar 2015 16:05:27 +0000 Subject: cli: help formatting --- cmd/ethereum/main.go | 31 ++++++++++++++++++------------- cmd/utils/flags.go | 2 +- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 59c6ef485..39a0a9d7f 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -83,9 +83,6 @@ The output of this command is supposed to be machine-readable. Action: accountList, Name: "list", Usage: "print account addresses", - Description: ` - -`, }, { Action: accountCreate, @@ -111,12 +108,14 @@ For non-interactive use the passphrase can be specified with the --password flag ethereum account import -Imports a private key from and creates a new account with the address derived from the key. -The keyfile is assumed to contain an unencrypted private key in canonical EC format. +Imports a private key from and creates a new account with the address +derived from the key. +The keyfile is assumed to contain an unencrypted private key in canonical EC +format. The account is saved in encrypted format, you are prompted for a passphrase. You must remember this passphrase to unlock your account in future. -For non-interactive use the passphrase can be specified with the --password flag: +For non-interactive use the passphrase can be specified with the -password flag: ethereum --password account import @@ -130,14 +129,18 @@ For non-interactive use the passphrase can be specified with the --password flag ethereum account export
-Exports the given account's private key into keyfile using the canonical EC format. -The account needs to be unlocked, if it is not the user is prompted for a passphrase to unlock it. -For non-interactive use, the password can be specified with the --unlock flag: +Exports the given account's private key into keyfile using the canonical EC +format. +The account needs to be unlocked, if it is not the user is prompted for a +passphrase to unlock it. +For non-interactive use, the passphrase can be specified with the --unlock flag: ethereum --password account export
Note: -Since you can directly copy your encrypted accounts to another ethereum instance, this import/export mechanism is not needed when you transfer an account between nodes. +As you can directly copy your encrypted accounts to another ethereum instance, +this import/export mechanism is not needed when you transfer an account between +nodes. `, }, }, @@ -156,16 +159,18 @@ Use "ethereum dump 0" to dump the genesis block. Name: "console", Usage: `Ethereum Console: interactive JavaScript environment`, Description: ` -Console is an interactive shell for the Ethereum JavaScript runtime environment which exposes a node admin interface as well as the DAPP JavaScript API. +Console is an interactive shell for the Ethereum JavaScript runtime environment +which exposes a node admin interface as well as the DAPP JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console `, }, { Action: execJSFiles, Name: "js", - Usage: `executes the given JavaScript files in the Ethereum Frontier JavaScript VM`, + Usage: `executes the given JavaScript files in the Ethereum JavaScript VM`, Description: ` -The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console +The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP +JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console `, }, { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index f94ec3a69..dda409502 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -109,7 +109,7 @@ var ( } PasswordFileFlag = cli.StringFlag{ Name: "password", - Usage: "Password used when saving a new account and unlocking an existing account. If you create a new account make sure you remember this password.", + Usage: "Path to password file for (un)locking an existing account.", Value: "", } -- cgit From d1b52efdb581ca90613d2047b974d3a128f9bc58 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 24 Mar 2015 16:19:11 +0000 Subject: cli: implement ethereum presale wallet import via cli --- accounts/account_manager.go | 12 ++++++++ cmd/ethereum/main.go | 74 ++++++++++++++++++++++++++++----------------- 2 files changed, 59 insertions(+), 27 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 21ef46991..f063f8ca5 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -239,3 +239,15 @@ func (am *Manager) Import(path string, keyAuth string) (Account, error) { } return Account{Address: key.Address}, nil } + +func (am *Manager) ImportPreSaleKey(keyJSON []byte, password string) (acc Account, err error) { + var key *crypto.Key + key, err = crypto.ImportPreSaleKey(am.keyStore, keyJSON, password) + if err != nil { + return + } + if err = am.keyStore.StoreKey(key, password); err != nil { + return + } + return Account{Address: key.Address}, nil +} diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 39a0a9d7f..57729b206 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -23,6 +23,7 @@ package main import ( "bufio" "fmt" + "io/ioutil" "os" "runtime" "strconv" @@ -74,6 +75,19 @@ Regular users do not need to execute it. The output of this command is supposed to be machine-readable. `, }, + + { + Action: accountList, + Name: "wallet", + Usage: "ethereum presale wallet", + Subcommands: []cli.Command{ + { + Action: importWallet, + Name: "import", + Usage: "import ethereum presale wallet", + }, + }, + }, { Action: accountList, Name: "account", @@ -280,22 +294,7 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (pass var err error // Load startup keys. XXX we are going to need a different format // Attempt to unlock the account - passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) - if len(passfile) == 0 { - fmt.Println("Please enter a passphrase now.") - auth, err := readPassword("Passphrase: ", true) - if err != nil { - utils.Fatalf("%v", err) - } - - passphrase = auth - - } else { - if passphrase, err = common.ReadAllFile(passfile); err != nil { - utils.Fatalf("Unable to read password file '%s': %v", passfile, err) - } - } - + passphrase := getPassPhrase(ctx, "", false) err = am.Unlock(common.FromHex(account), passphrase) if err != nil { utils.Fatalf("Unlock account failed '%v'", err) @@ -335,22 +334,23 @@ func accountList(ctx *cli.Context) { } } -func getPassPhrase(ctx *cli.Context) (passphrase string) { +func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase string) { if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) { passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) if len(passfile) == 0 { - fmt.Println("The new account will be encrypted with a passphrase.") - fmt.Println("Please enter a passphrase now.") + fmt.Println(desc) auth, err := readPassword("Passphrase: ", true) if err != nil { utils.Fatalf("%v", err) } - confirm, err := readPassword("Repeat Passphrase: ", false) - if err != nil { - utils.Fatalf("%v", err) - } - if auth != confirm { - utils.Fatalf("Passphrases did not match.") + if confirmation { + confirm, err := readPassword("Repeat Passphrase: ", false) + if err != nil { + utils.Fatalf("%v", err) + } + if auth != confirm { + utils.Fatalf("Passphrases did not match.") + } } passphrase = auth @@ -366,7 +366,7 @@ func getPassPhrase(ctx *cli.Context) (passphrase string) { func accountCreate(ctx *cli.Context) { am := utils.GetAccountManager(ctx) - passphrase := getPassPhrase(ctx) + passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true) acct, err := am.NewAccount(passphrase) if err != nil { utils.Fatalf("Could not create the account: %v", err) @@ -374,13 +374,33 @@ func accountCreate(ctx *cli.Context) { fmt.Printf("Address: %x\n", acct) } +func importWallet(ctx *cli.Context) { + keyfile := ctx.Args().First() + if len(keyfile) == 0 { + utils.Fatalf("keyfile must be given as argument") + } + keyJson, err := ioutil.ReadFile(keyfile) + if err != nil { + utils.Fatalf("Could not read wallet file: %v", err) + } + + am := utils.GetAccountManager(ctx) + passphrase := getPassPhrase(ctx, "", false) + + acct, err := am.ImportPreSaleKey(keyJson, passphrase) + if err != nil { + utils.Fatalf("Could not create the account: %v", err) + } + fmt.Printf("Address: %x\n", acct) +} + func accountImport(ctx *cli.Context) { keyfile := ctx.Args().First() if len(keyfile) == 0 { utils.Fatalf("keyfile must be given as argument") } am := utils.GetAccountManager(ctx) - passphrase := getPassPhrase(ctx) + passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true) acct, err := am.Import(keyfile, passphrase) if err != nil { utils.Fatalf("Could not create the account: %v", err) -- cgit From fee224f07582a3f4c74f214347a89061ce75d2a1 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 24 Mar 2015 21:53:46 +0000 Subject: cli test: fix test newJSRE interactive argument --- cmd/ethereum/js_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ethereum/js_test.go b/cmd/ethereum/js_test.go index a6058b318..580bc7a2b 100644 --- a/cmd/ethereum/js_test.go +++ b/cmd/ethereum/js_test.go @@ -47,7 +47,7 @@ func testJEthRE(t *testing.T) (repl *jsre, ethereum *eth.Ethereum, err error) { return } assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext") - repl = newJSRE(ethereum, assetPath) + repl = newJSRE(ethereum, assetPath, false) return } -- cgit From 23e41a57ad7e7cb4bc5a1cbad28bbf8d65907fdd Mon Sep 17 00:00:00 2001 From: zelig Date: Wed, 25 Mar 2015 10:41:36 +0000 Subject: Applying: fix adming js test regression (maybe otto update?) --- cmd/ethereum/js.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go index 599af0a16..8e88a1c54 100644 --- a/cmd/ethereum/js.go +++ b/cmd/ethereum/js.go @@ -102,7 +102,7 @@ func (js *jsre) apiBindings() { jethObj := t.Object() jethObj.Set("send", jeth.Send) - err := js.re.Compile("bignum.js", re.BigNumber_JS) + err := js.re.Compile("bignumber.js", re.BigNumber_JS) if err != nil { utils.Fatalf("Error loading bignumber.js: %v", err) } -- cgit From 4ec38e39320ee9abccd96da765a9c65fccd04151 Mon Sep 17 00:00:00 2001 From: zelig Date: Wed, 25 Mar 2015 14:58:52 +0000 Subject: common: remove WriteFile and ReadAllFile (use ioutil instead) --- cmd/ethereum/js_test.go | 6 +++--- cmd/ethereum/main.go | 5 +++-- cmd/mist/bindings.go | 7 ++++--- cmd/mist/gui.go | 5 +++-- common/path.go | 30 ------------------------------ common/path_test.go | 47 +---------------------------------------------- crypto/crypto.go | 6 ++++-- jsre/jsre_test.go | 8 ++++---- 8 files changed, 22 insertions(+), 92 deletions(-) diff --git a/cmd/ethereum/js_test.go b/cmd/ethereum/js_test.go index 580bc7a2b..5b962f621 100644 --- a/cmd/ethereum/js_test.go +++ b/cmd/ethereum/js_test.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "io/ioutil" "os" "path" "testing" @@ -9,7 +10,6 @@ import ( "github.com/robertkrimen/otto" "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" ) @@ -30,8 +30,8 @@ func testJEthRE(t *testing.T) (repl *jsre, ethereum *eth.Ethereum, err error) { } // FIXME: this does not work ATM ks := crypto.NewKeyStorePlain("/tmp/eth/keys") - common.WriteFile("/tmp/eth/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/e273f01c99144c438695e10f24926dc1f9fbf62d", - []byte(`{"Id":"RhRXD+fNRKS4jx+7ZfEsNA==","Address":"4nPwHJkUTEOGleEPJJJtwfn79i0=","PrivateKey":"h4ACVpe74uIvi5Cg/2tX/Yrm2xdr3J7QoMbMtNX2CNc="}`)) + ioutil.WriteFile("/tmp/eth/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/e273f01c99144c438695e10f24926dc1f9fbf62d", + []byte(`{"Id":"RhRXD+fNRKS4jx+7ZfEsNA==","Address":"4nPwHJkUTEOGleEPJJJtwfn79i0=","PrivateKey":"h4ACVpe74uIvi5Cg/2tX/Yrm2xdr3J7QoMbMtNX2CNc="}`), os.ModePerm) port++ ethereum, err = eth.New(ð.Config{ diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 57729b206..6bbe1044f 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -355,10 +355,11 @@ func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase passphrase = auth } else { - var err error - if passphrase, err = common.ReadAllFile(passfile); err != nil { + passbytes, err := ioutil.ReadFile(passfile) + if err != nil { utils.Fatalf("Unable to read password file '%s': %v", passfile, err) } + passphrase = string(passbytes) } } return diff --git a/cmd/mist/bindings.go b/cmd/mist/bindings.go index 8a9ec7cb1..e7ce50c35 100644 --- a/cmd/mist/bindings.go +++ b/cmd/mist/bindings.go @@ -22,13 +22,14 @@ package main import ( "encoding/json" + "io/ioutil" "os" "strconv" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" ) type plugin struct { @@ -46,14 +47,14 @@ func (self *Gui) AddPlugin(pluginPath string) { self.plugins[pluginPath] = plugin{Name: pluginPath, Path: pluginPath} json, _ := json.MarshalIndent(self.plugins, "", " ") - common.WriteFile(self.eth.DataDir+"/plugins.json", json) + ioutil.WriteFile(self.eth.DataDir+"/plugins.json", json, os.ModePerm) } func (self *Gui) RemovePlugin(pluginPath string) { delete(self.plugins, pluginPath) json, _ := json.MarshalIndent(self.plugins, "", " ") - common.WriteFile(self.eth.DataDir+"/plugins.json", json) + ioutil.WriteFile(self.eth.DataDir+"/plugins.json", json, os.ModePerm) } func (self *Gui) DumpState(hash, path string) { diff --git a/cmd/mist/gui.go b/cmd/mist/gui.go index 08f02f833..d37d6f81b 100644 --- a/cmd/mist/gui.go +++ b/cmd/mist/gui.go @@ -25,6 +25,7 @@ import "C" import ( "encoding/json" "fmt" + "io/ioutil" "math/big" "path" "runtime" @@ -91,8 +92,8 @@ func NewWindow(ethereum *eth.Ethereum) *Gui { plugins: make(map[string]plugin), serviceEvents: make(chan ServEv, 1), } - data, _ := common.ReadAllFile(path.Join(ethereum.DataDir, "plugins.json")) - json.Unmarshal([]byte(data), &gui.plugins) + data, _ := ioutil.ReadFile(path.Join(ethereum.DataDir, "plugins.json")) + json.Unmarshal(data, &gui.plugins) return gui } diff --git a/common/path.go b/common/path.go index d38b1fd5b..a74a0d5bd 100644 --- a/common/path.go +++ b/common/path.go @@ -2,7 +2,6 @@ package common import ( "fmt" - "io/ioutil" "os" "os/user" "path" @@ -43,35 +42,6 @@ func FileExist(filePath string) bool { return true } -func ReadAllFile(filePath string) (string, error) { - file, err := os.Open(filePath) - if err != nil { - return "", err - } - - data, err := ioutil.ReadAll(file) - if err != nil { - return "", err - } - - return string(data), nil -} - -func WriteFile(filePath string, content []byte) error { - fh, err := os.OpenFile(filePath, os.O_TRUNC|os.O_RDWR|os.O_CREATE, os.ModePerm) - if err != nil { - return err - } - defer fh.Close() - - _, err = fh.Write(content) - if err != nil { - return err - } - - return nil -} - func AbsolutePath(Datadir string, filename string) string { if path.IsAbs(filename) { return filename diff --git a/common/path_test.go b/common/path_test.go index c831d1a57..4b90c543b 100644 --- a/common/path_test.go +++ b/common/path_test.go @@ -2,56 +2,11 @@ package common import ( "os" - "testing" + // "testing" checker "gopkg.in/check.v1" ) -func TestGoodFile(t *testing.T) { - goodpath := "~/goethereumtest.pass" - path := ExpandHomePath(goodpath) - contentstring := "3.14159265358979323846" - - err := WriteFile(path, []byte(contentstring)) - if err != nil { - t.Error("Could not write file") - } - - if !FileExist(path) { - t.Error("File not found at", path) - } - - v, err := ReadAllFile(path) - if err != nil { - t.Error("Could not read file", path) - } - if v != contentstring { - t.Error("Expected", contentstring, "Got", v) - } - -} - -func TestBadFile(t *testing.T) { - badpath := "/this/path/should/not/exist/goethereumtest.fail" - path := ExpandHomePath(badpath) - contentstring := "3.14159265358979323846" - - err := WriteFile(path, []byte(contentstring)) - if err == nil { - t.Error("Wrote file, but should not be able to", path) - } - - if FileExist(path) { - t.Error("Found file, but should not be able to", path) - } - - v, err := ReadAllFile(path) - if err == nil { - t.Error("Read file, but should not be able to", v) - } - -} - type CommonSuite struct{} var _ = checker.Suite(&CommonSuite{}) diff --git a/crypto/crypto.go b/crypto/crypto.go index 2d26dd25e..442942c6c 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -9,6 +9,7 @@ import ( "crypto/sha256" "fmt" "io" + "io/ioutil" "os" "encoding/hex" @@ -139,9 +140,10 @@ func LoadECDSA(file string) (*ecdsa.PrivateKey, error) { return ToECDSA(buf), nil } -// SaveECDSA saves a secp256k1 private key from the given file. +// SaveECDSA saves a secp256k1 private key to the given file with restrictive +// permissions func SaveECDSA(file string, key *ecdsa.PrivateKey) error { - return common.WriteFile(file, FromECDSA(key)) + return ioutil.WriteFile(file, FromECDSA(key), 0600) } func GenerateKey() (*ecdsa.PrivateKey, error) { diff --git a/jsre/jsre_test.go b/jsre/jsre_test.go index 8a771dae8..667ed4bdc 100644 --- a/jsre/jsre_test.go +++ b/jsre/jsre_test.go @@ -2,9 +2,9 @@ package jsre import ( "github.com/robertkrimen/otto" + "io/ioutil" + "os" "testing" - - "github.com/ethereum/go-ethereum/common" ) type testNativeObjectBinding struct { @@ -26,7 +26,7 @@ func (no *testNativeObjectBinding) TestMethod(call otto.FunctionCall) otto.Value func TestExec(t *testing.T) { jsre := New("/tmp") - common.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`)) + ioutil.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`), os.ModePerm) err := jsre.Exec("test.js") if err != nil { t.Errorf("expected no error, got %v", err) @@ -64,7 +64,7 @@ func TestBind(t *testing.T) { func TestLoadScript(t *testing.T) { jsre := New("/tmp") - common.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`)) + ioutil.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`), os.ModePerm) _, err := jsre.Run(`loadScript("test.js")`) if err != nil { t.Errorf("expected no error, got %v", err) -- cgit From 11d2ebc06ffffa8846d5d55cae5663fac6f685f1 Mon Sep 17 00:00:00 2001 From: zelig Date: Wed, 25 Mar 2015 15:45:56 +0000 Subject: unlocking coinbase without knowing address - accounts: remove Manager.getKey - cli: for -unlock coinbase, use account manager Coinbase() --- accounts/account_manager.go | 18 +++--------------- cmd/ethereum/main.go | 9 +++++++-- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index f063f8ca5..392518703 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -36,9 +36,8 @@ import ( "bytes" "crypto/ecdsa" crand "crypto/rand" - "os" - "errors" + "os" "sync" "time" @@ -101,17 +100,6 @@ func (am *Manager) firstAddr() ([]byte, error) { return addrs[0], nil } -func (am *Manager) getKey(addr []byte, keyAuth string) (*crypto.Key, error) { - if len(addr) == 0 { - var err error - addr, err = am.firstAddr() - if err != nil { - return nil, err - } - } - return am.keyStore.GetKey(addr, keyAuth) -} - func (am *Manager) DeleteAccount(address []byte, auth string) error { return am.keyStore.DeleteKey(address, auth) } @@ -130,7 +118,7 @@ func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) // TimedUnlock unlocks the account with the given address. // When timeout has passed, the account will be locked again. func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duration) error { - key, err := am.getKey(addr, keyAuth) + key, err := am.keyStore.GetKey(addr, keyAuth) if err != nil { return err } @@ -143,7 +131,7 @@ func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duratio // stays unlocked until the program exits or until a TimedUnlock // timeout (started after the call to Unlock) expires. func (am *Manager) Unlock(addr []byte, keyAuth string) error { - key, err := am.getKey(addr, keyAuth) + key, err := am.keyStore.GetKey(addr, keyAuth) if err != nil { return err } diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 6bbe1044f..8983b85a6 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -294,7 +294,7 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (pass var err error // Load startup keys. XXX we are going to need a different format // Attempt to unlock the account - passphrase := getPassPhrase(ctx, "", false) + passphrase = getPassPhrase(ctx, "", false) err = am.Unlock(common.FromHex(account), passphrase) if err != nil { utils.Fatalf("Unlock account failed '%v'", err) @@ -310,7 +310,11 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) { account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) if len(account) > 0 { if account == "coinbase" { - account = "" + accbytes, err := am.Coinbase() + if err != nil { + utils.Fatalf("no coinbase account: %v", err) + } + account = common.ToHex(accbytes) } unlockAccount(ctx, am, account) } @@ -420,6 +424,7 @@ func accountExport(ctx *cli.Context) { } am := utils.GetAccountManager(ctx) auth := unlockAccount(ctx, am, account) + err := am.Export(keyfile, common.FromHex(account), auth) if err != nil { utils.Fatalf("Account export failed: %v", err) -- cgit From abbdf4156057de8a4f866b0840defc00c2c500db Mon Sep 17 00:00:00 2001 From: zelig Date: Wed, 25 Mar 2015 16:10:44 +0000 Subject: output error message if unlock address is invalid (fixes the wierd "read /path: is a directory") msg --- cmd/ethereum/main.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 8983b85a6..2e721dc71 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -295,7 +295,11 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (pass // Load startup keys. XXX we are going to need a different format // Attempt to unlock the account passphrase = getPassPhrase(ctx, "", false) - err = am.Unlock(common.FromHex(account), passphrase) + accbytes := common.FromHex(account) + if len(accbytes) == 0 { + utils.Fatalf("Invalid account address '%s'", account) + } + err = am.Unlock(accbytes, passphrase) if err != nil { utils.Fatalf("Unlock account failed '%v'", err) } -- cgit From 7577d1261403dbabdb30e21415d34b4e5da466ec Mon Sep 17 00:00:00 2001 From: zelig Date: Thu, 26 Mar 2015 18:55:39 +0000 Subject: max paranoia mode to UNsupport unencrypted keys entirely - remove account export functionality from CLI - remove accountExport method, - remove unencrypted-keys flag from everywhere - improve documentation --- accounts/account_manager.go | 2 + cmd/ethereum/main.go | 149 ++++++++++++++++++++------------------------ cmd/utils/flags.go | 14 +---- 3 files changed, 73 insertions(+), 92 deletions(-) diff --git a/accounts/account_manager.go b/accounts/account_manager.go index 392518703..34a2c4891 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -208,6 +208,8 @@ func zeroKey(k *ecdsa.PrivateKey) { } } +// USE WITH CAUTION = this will save an unencrypted private key on disk +// no cli or js interface func (am *Manager) Export(path string, addr []byte, keyAuth string) error { key, err := am.keyStore.GetKey(addr, keyAuth) if err != nil { diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go index 2e721dc71..42321e8bc 100644 --- a/cmd/ethereum/main.go +++ b/cmd/ethereum/main.go @@ -77,9 +77,8 @@ The output of this command is supposed to be machine-readable. }, { - Action: accountList, - Name: "wallet", - Usage: "ethereum presale wallet", + Name: "wallet", + Usage: "ethereum presale wallet", Subcommands: []cli.Command{ { Action: importWallet, @@ -92,6 +91,28 @@ The output of this command is supposed to be machine-readable. Action: accountList, Name: "account", Usage: "manage accounts", + Description: ` + +Manage accounts lets you create new accounts, list all existing accounts, +import a private key into a new account. + +It supports interactive mode, when you are prompted for password as well as +non-interactive mode where passwords are supplied via a given password file. +Non-interactive mode is only meant for scripted use on test networks or known +safe environments. + +Make sure you remember the password you gave when creating a new account (with +either new or import). Without it you are not able to unlock your account. + +Note that exporting your key in unencrypted format is NOT supported. + +Keys are stored under /keys. +It is safe to transfer the entire directory or the individual keys therein +between ethereum nodes. +Make sure you backup your keys regularly. + +And finally. DO NOT FORGET YOUR PASSWORD. +`, Subcommands: []cli.Command{ { Action: accountList, @@ -106,12 +127,18 @@ The output of this command is supposed to be machine-readable. ethereum account new -Creates a new accountThe account is saved in encrypted format, you are prompted for a passphrase. -You must remember this passphrase to unlock your account in future. +Creates a new account. Prints the address. + +The account is saved in encrypted format, you are prompted for a passphrase. + +You must remember this passphrase to unlock your account in the future. + For non-interactive use the passphrase can be specified with the --password flag: ethereum --password account new +Note, this is meant to be used for testing only, it is a bad idea to save your +password to file or expose in any other way. `, }, { @@ -122,38 +149,23 @@ For non-interactive use the passphrase can be specified with the --password flag ethereum account import -Imports a private key from and creates a new account with the address -derived from the key. +Imports an unencrypted private key from and creates a new account. +Prints the address. + The keyfile is assumed to contain an unencrypted private key in canonical EC -format. +raw bytes format. The account is saved in encrypted format, you are prompted for a passphrase. -You must remember this passphrase to unlock your account in future. -For non-interactive use the passphrase can be specified with the -password flag: - ethereum --password account import +You must remember this passphrase to unlock your account in the future. - `, - }, - { - Action: accountExport, - Name: "export", - Usage: "export an account into key file", - Description: ` - - ethereum account export
- -Exports the given account's private key into keyfile using the canonical EC -format. -The account needs to be unlocked, if it is not the user is prompted for a -passphrase to unlock it. -For non-interactive use, the passphrase can be specified with the --unlock flag: +For non-interactive use the passphrase can be specified with the -password flag: - ethereum --password account export
+ ethereum --password account import Note: As you can directly copy your encrypted accounts to another ethereum instance, -this import/export mechanism is not needed when you transfer an account between +this import mechanism is not needed when you transfer an account between nodes. `, }, @@ -217,7 +229,6 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso utils.RPCEnabledFlag, utils.RPCListenAddrFlag, utils.RPCPortFlag, - utils.UnencryptedKeysFlag, utils.VMDebugFlag, utils.ProtocolVersionFlag, utils.NetworkIdFlag, @@ -290,19 +301,17 @@ func execJSFiles(ctx *cli.Context) { } func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (passphrase string) { - if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) { - var err error - // Load startup keys. XXX we are going to need a different format - // Attempt to unlock the account - passphrase = getPassPhrase(ctx, "", false) - accbytes := common.FromHex(account) - if len(accbytes) == 0 { - utils.Fatalf("Invalid account address '%s'", account) - } - err = am.Unlock(accbytes, passphrase) - if err != nil { - utils.Fatalf("Unlock account failed '%v'", err) - } + var err error + // Load startup keys. XXX we are going to need a different format + // Attempt to unlock the account + passphrase = getPassPhrase(ctx, "", false) + accbytes := common.FromHex(account) + if len(accbytes) == 0 { + utils.Fatalf("Invalid account address '%s'", account) + } + err = am.Unlock(accbytes, passphrase) + if err != nil { + utils.Fatalf("Unlock account failed '%v'", err) } return } @@ -343,32 +352,30 @@ func accountList(ctx *cli.Context) { } func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase string) { - if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) { - passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) - if len(passfile) == 0 { - fmt.Println(desc) - auth, err := readPassword("Passphrase: ", true) + passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) + if len(passfile) == 0 { + fmt.Println(desc) + auth, err := readPassword("Passphrase: ", true) + if err != nil { + utils.Fatalf("%v", err) + } + if confirmation { + confirm, err := readPassword("Repeat Passphrase: ", false) if err != nil { utils.Fatalf("%v", err) } - if confirmation { - confirm, err := readPassword("Repeat Passphrase: ", false) - if err != nil { - utils.Fatalf("%v", err) - } - if auth != confirm { - utils.Fatalf("Passphrases did not match.") - } + if auth != confirm { + utils.Fatalf("Passphrases did not match.") } - passphrase = auth + } + passphrase = auth - } else { - passbytes, err := ioutil.ReadFile(passfile) - if err != nil { - utils.Fatalf("Unable to read password file '%s': %v", passfile, err) - } - passphrase = string(passbytes) + } else { + passbytes, err := ioutil.ReadFile(passfile) + if err != nil { + utils.Fatalf("Unable to read password file '%s': %v", passfile, err) } + passphrase = string(passbytes) } return } @@ -417,24 +424,6 @@ func accountImport(ctx *cli.Context) { fmt.Printf("Address: %x\n", acct) } -func accountExport(ctx *cli.Context) { - account := ctx.Args().First() - if len(account) == 0 { - utils.Fatalf("account address must be given as first argument") - } - keyfile := ctx.Args().Get(1) - if len(keyfile) == 0 { - utils.Fatalf("keyfile must be given as second argument") - } - am := utils.GetAccountManager(ctx) - auth := unlockAccount(ctx, am, account) - - err := am.Export(keyfile, common.FromHex(account), auth) - if err != nil { - utils.Fatalf("Account export failed: %v", err) - } -} - func importchain(ctx *cli.Context) { if len(ctx.Args()) != 1 { utils.Fatalf("This command requires an argument.") diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index dda409502..f948cdb06 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -97,14 +97,9 @@ var ( Usage: "Enable mining", } - // key settings - UnencryptedKeysFlag = cli.BoolFlag{ - Name: "unencrypted-keys", - Usage: "disable private key disk encryption (for testing)", - } UnlockedAccountFlag = cli.StringFlag{ Name: "unlock", - Usage: "unlock the account given until this program exits (prompts for password).", + Usage: "unlock the account given until this program exits (prompts for password). '--unlock coinbase' unlocks the primary (coinbase) account", Value: "", } PasswordFileFlag = cli.StringFlag{ @@ -249,12 +244,7 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Dat func GetAccountManager(ctx *cli.Context) *accounts.Manager { dataDir := ctx.GlobalString(DataDirFlag.Name) - var ks crypto.KeyStore2 - if ctx.GlobalBool(UnencryptedKeysFlag.Name) { - ks = crypto.NewKeyStorePlain(path.Join(dataDir, "plainkeys")) - } else { - ks = crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys")) - } + ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys")) return accounts.NewManager(ks) } -- cgit From b0b0939879b9fb8453ec1c8fa2ceb522e56df3bc Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 26 Mar 2015 21:27:52 +0100 Subject: renamed ethereum => geth --- cmd/ethereum/admin.go | 256 ----------------------- cmd/ethereum/blocktest.go | 80 ------- cmd/ethereum/js.go | 246 ---------------------- cmd/ethereum/js_test.go | 262 ----------------------- cmd/ethereum/main.go | 516 ---------------------------------------------- cmd/geth/admin.go | 256 +++++++++++++++++++++++ cmd/geth/blocktest.go | 80 +++++++ cmd/geth/js.go | 246 ++++++++++++++++++++++ cmd/geth/js_test.go | 262 +++++++++++++++++++++++ cmd/geth/main.go | 516 ++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 1360 insertions(+), 1360 deletions(-) delete mode 100644 cmd/ethereum/admin.go delete mode 100644 cmd/ethereum/blocktest.go delete mode 100644 cmd/ethereum/js.go delete mode 100644 cmd/ethereum/js_test.go delete mode 100644 cmd/ethereum/main.go create mode 100644 cmd/geth/admin.go create mode 100644 cmd/geth/blocktest.go create mode 100644 cmd/geth/js.go create mode 100644 cmd/geth/js_test.go create mode 100644 cmd/geth/main.go diff --git a/cmd/ethereum/admin.go b/cmd/ethereum/admin.go deleted file mode 100644 index 139395dad..000000000 --- a/cmd/ethereum/admin.go +++ /dev/null @@ -1,256 +0,0 @@ -package main - -import ( - "fmt" - "net" - "net/http" - "os" - "time" - - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/xeth" - "github.com/robertkrimen/otto" -) - -/* -node admin bindings -*/ - -func (js *jsre) adminBindings() { - js.re.Set("admin", struct{}{}) - t, _ := js.re.Get("admin") - admin := t.Object() - admin.Set("suggestPeer", js.suggestPeer) - admin.Set("startRPC", js.startRPC) - admin.Set("startMining", js.startMining) - admin.Set("stopMining", js.stopMining) - admin.Set("nodeInfo", js.nodeInfo) - admin.Set("peers", js.peers) - admin.Set("newAccount", js.newAccount) - admin.Set("unlock", js.unlock) - admin.Set("import", js.importChain) - admin.Set("export", js.exportChain) - admin.Set("dumpBlock", js.dumpBlock) -} - -func (js *jsre) startMining(call otto.FunctionCall) otto.Value { - _, err := call.Argument(0).ToInteger() - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - // threads now ignored - err = js.ethereum.StartMining() - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - return otto.TrueValue() -} - -func (js *jsre) stopMining(call otto.FunctionCall) otto.Value { - js.ethereum.StopMining() - return otto.TrueValue() -} - -func (js *jsre) startRPC(call otto.FunctionCall) otto.Value { - addr, err := call.Argument(0).ToString() - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - port, err := call.Argument(1).ToInteger() - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - dataDir := js.ethereum.DataDir - - l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port)) - if err != nil { - fmt.Printf("Can't listen on %s:%d: %v", addr, port, err) - return otto.FalseValue() - } - go http.Serve(l, rpc.JSONRPC(xeth.New(js.ethereum, nil), dataDir)) - return otto.TrueValue() -} - -func (js *jsre) suggestPeer(call otto.FunctionCall) otto.Value { - nodeURL, err := call.Argument(0).ToString() - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - err = js.ethereum.SuggestPeer(nodeURL) - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - return otto.TrueValue() -} - -func (js *jsre) unlock(call otto.FunctionCall) otto.Value { - addr, err := call.Argument(0).ToString() - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - seconds, err := call.Argument(2).ToInteger() - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - arg := call.Argument(1) - var passphrase string - if arg.IsUndefined() { - fmt.Println("Please enter a passphrase now.") - passphrase, err = readPassword("Passphrase: ", true) - if err != nil { - utils.Fatalf("%v", err) - } - } else { - passphrase, err = arg.ToString() - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - } - am := js.ethereum.AccountManager() - // err := am.Unlock(common.FromHex(split[0]), split[1]) - // if err != nil { - // utils.Fatalf("Unlock account failed '%v'", err) - // } - err = am.TimedUnlock(common.FromHex(addr), passphrase, time.Duration(seconds)*time.Second) - if err != nil { - fmt.Printf("Unlock account failed '%v'\n", err) - return otto.FalseValue() - } - return otto.TrueValue() -} - -func (js *jsre) newAccount(call otto.FunctionCall) otto.Value { - arg := call.Argument(0) - var passphrase string - if arg.IsUndefined() { - fmt.Println("The new account will be encrypted with a passphrase.") - fmt.Println("Please enter a passphrase now.") - auth, err := readPassword("Passphrase: ", true) - if err != nil { - utils.Fatalf("%v", err) - } - confirm, err := readPassword("Repeat Passphrase: ", false) - if err != nil { - utils.Fatalf("%v", err) - } - if auth != confirm { - utils.Fatalf("Passphrases did not match.") - } - passphrase = auth - } else { - var err error - passphrase, err = arg.ToString() - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - } - acct, err := js.ethereum.AccountManager().NewAccount(passphrase) - if err != nil { - fmt.Printf("Could not create the account: %v", err) - return otto.UndefinedValue() - } - return js.re.ToVal(common.Bytes2Hex(acct.Address)) -} - -func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value { - return js.re.ToVal(js.ethereum.NodeInfo()) -} - -func (js *jsre) peers(call otto.FunctionCall) otto.Value { - return js.re.ToVal(js.ethereum.PeersInfo()) -} - -func (js *jsre) importChain(call otto.FunctionCall) otto.Value { - if len(call.ArgumentList) == 0 { - fmt.Println("err: require file name") - return otto.FalseValue() - } - - fn, err := call.Argument(0).ToString() - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - - var fh *os.File - fh, err = os.OpenFile(fn, os.O_RDONLY, os.ModePerm) - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - defer fh.Close() - - var blocks types.Blocks - if err = rlp.Decode(fh, &blocks); err != nil { - fmt.Println(err) - return otto.FalseValue() - } - - js.ethereum.ChainManager().Reset() - if err = js.ethereum.ChainManager().InsertChain(blocks); err != nil { - fmt.Println(err) - return otto.FalseValue() - } - - return otto.TrueValue() -} - -func (js *jsre) exportChain(call otto.FunctionCall) otto.Value { - if len(call.ArgumentList) == 0 { - fmt.Println("err: require file name") - return otto.FalseValue() - } - - fn, err := call.Argument(0).ToString() - if err != nil { - fmt.Println(err) - return otto.FalseValue() - } - if err := utils.ExportChain(js.ethereum.ChainManager(), fn); err != nil { - fmt.Println(err) - return otto.FalseValue() - } - return otto.TrueValue() -} - -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") - return otto.UndefinedValue() - } - - statedb := state.New(block.Root(), js.ethereum.StateDb()) - dump := statedb.RawDump() - return js.re.ToVal(dump) - -} diff --git a/cmd/ethereum/blocktest.go b/cmd/ethereum/blocktest.go deleted file mode 100644 index d9cdfa83f..000000000 --- a/cmd/ethereum/blocktest.go +++ /dev/null @@ -1,80 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/codegangsta/cli" - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/tests" -) - -var blocktestCmd = cli.Command{ - Action: runblocktest, - Name: "blocktest", - Usage: `loads a block test file`, - Description: ` -The first argument should be a block test file. -The second argument is the name of a block test from the file. - -The block test will be loaded into an in-memory database. -If loading succeeds, the RPC server is started. Clients will -be able to interact with the chain defined by the test. -`, -} - -func runblocktest(ctx *cli.Context) { - if len(ctx.Args()) != 3 { - utils.Fatalf("Usage: ethereum blocktest {rpc, norpc}") - } - file, testname, startrpc := ctx.Args()[0], ctx.Args()[1], ctx.Args()[2] - - bt, err := tests.LoadBlockTests(file) - if err != nil { - utils.Fatalf("%v", err) - } - test, ok := bt[testname] - if !ok { - utils.Fatalf("Test file does not contain test named %q", testname) - } - - cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx) - cfg.NewDB = func(path string) (common.Database, error) { return ethdb.NewMemDatabase() } - cfg.MaxPeers = 0 // disable network - ethereum, err := eth.New(cfg) - if err != nil { - utils.Fatalf("%v", err) - } - - // import the genesis block - ethereum.ResetWithGenesisBlock(test.Genesis) - - // import pre accounts - statedb, err := test.InsertPreState(ethereum.StateDb()) - if err != nil { - utils.Fatalf("could not insert genesis accounts: %v", err) - } - - // insert the test blocks, which will execute all transactions - chain := ethereum.ChainManager() - if err := chain.InsertChain(test.Blocks); err != nil { - utils.Fatalf("Block Test load error: %v", err) - } else { - fmt.Println("Block Test chain loaded") - } - - if err := test.ValidatePostState(statedb); err != nil { - utils.Fatalf("post state validation failed: %v", err) - } - fmt.Println("Block Test post state validated, starting ethereum.") - - if startrpc == "rpc" { - startEth(ctx, ethereum) - utils.StartRPC(ethereum, ctx) - ethereum.WaitForShutdown() - } else { - startEth(ctx, ethereum) - } -} diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go deleted file mode 100644 index 8e88a1c54..000000000 --- a/cmd/ethereum/js.go +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This 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 -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -// MA 02110-1301 USA - -package main - -import ( - "bufio" - "fmt" - "os" - "path" - "strings" - - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth" - re "github.com/ethereum/go-ethereum/jsre" - "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/xeth" - "github.com/peterh/liner" - "github.com/robertkrimen/otto" -) - -type prompter interface { - AppendHistory(string) - Prompt(p string) (string, error) - PasswordPrompt(p string) (string, error) -} - -type dumbterm struct{ r *bufio.Reader } - -func (r dumbterm) Prompt(p string) (string, error) { - fmt.Print(p) - return r.r.ReadString('\n') -} - -func (r dumbterm) PasswordPrompt(p string) (string, error) { - fmt.Println("!! Unsupported terminal, password will echo.") - fmt.Print(p) - input, err := bufio.NewReader(os.Stdin).ReadString('\n') - fmt.Println() - return input, err -} - -func (r dumbterm) AppendHistory(string) {} - -type jsre struct { - re *re.JSRE - ethereum *eth.Ethereum - xeth *xeth.XEth - ps1 string - atexit func() - - prompter -} - -func newJSRE(ethereum *eth.Ethereum, libPath string, interactive bool) *jsre { - js := &jsre{ethereum: ethereum, ps1: "> "} - js.xeth = xeth.New(ethereum, js) - js.re = re.New(libPath) - js.apiBindings() - js.adminBindings() - - if !liner.TerminalSupported() || !interactive { - js.prompter = dumbterm{bufio.NewReader(os.Stdin)} - } else { - lr := liner.NewLiner() - js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) - lr.SetCtrlCAborts(true) - js.prompter = lr - js.atexit = func() { - js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) - lr.Close() - } - } - return js -} - -func (js *jsre) apiBindings() { - - ethApi := rpc.NewEthereumApi(js.xeth, js.ethereum.DataDir) - ethApi.Close() - //js.re.Bind("jeth", rpc.NewJeth(ethApi, js.re.ToVal)) - - jeth := rpc.NewJeth(ethApi, js.re.ToVal, js.re) - //js.re.Bind("jeth", jeth) - js.re.Set("jeth", struct{}{}) - t, _ := js.re.Get("jeth") - jethObj := t.Object() - jethObj.Set("send", jeth.Send) - - err := js.re.Compile("bignumber.js", re.BigNumber_JS) - if err != nil { - utils.Fatalf("Error loading bignumber.js: %v", err) - } - - // we need to declare a dummy setTimeout. Otto does not support it - _, err = js.re.Eval("setTimeout = function(cb, delay) {};") - if err != nil { - utils.Fatalf("Error defining setTimeout: %v", err) - } - - err = js.re.Compile("ethereum.js", re.Ethereum_JS) - if err != nil { - utils.Fatalf("Error loading ethereum.js: %v", err) - } - - _, err = js.re.Eval("var web3 = require('ethereum.js');") - if err != nil { - utils.Fatalf("Error requiring web3: %v", err) - } - - _, err = js.re.Eval("web3.setProvider(jeth)") - if err != nil { - utils.Fatalf("Error setting web3 provider: %v", err) - } - _, err = js.re.Eval(` -var eth = web3.eth; -var shh = web3.shh; -var db = web3.db; -var net = web3.net; - `) - if err != nil { - utils.Fatalf("Error setting namespaces: %v", err) - } - -} - -func (self *jsre) ConfirmTransaction(tx *types.Transaction) bool { - p := fmt.Sprintf("Confirm Transaction %v\n[y/n] ", tx) - answer, _ := self.Prompt(p) - return strings.HasPrefix(strings.Trim(answer, " "), "y") -} - -func (self *jsre) UnlockAccount(addr []byte) bool { - fmt.Printf("Please unlock account %x.\n", addr) - pass, err := self.PasswordPrompt("Passphrase: ") - if err != nil { - return false - } - // TODO: allow retry - if err := self.ethereum.AccountManager().Unlock(addr, pass); err != nil { - return false - } else { - fmt.Println("Account is now unlocked for this session.") - return true - } -} - -func (self *jsre) exec(filename string) error { - if err := self.re.Exec(filename); err != nil { - return fmt.Errorf("Javascript Error: %v", err) - } - return nil -} - -func (self *jsre) interactive() { - for { - input, err := self.Prompt(self.ps1) - if err != nil { - break - } - if input == "" { - continue - } - str += input + "\n" - self.setIndent() - if indentCount <= 0 { - if input == "exit" { - break - } - hist := str[:len(str)-1] - self.AppendHistory(hist) - self.parseInput(str) - str = "" - } - } - if self.atexit != nil { - self.atexit() - } -} - -func (self *jsre) withHistory(op func(*os.File)) { - hist, err := os.OpenFile(path.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) - if err != nil { - fmt.Printf("unable to open history file: %v\n", err) - return - } - op(hist) - hist.Close() -} - -func (self *jsre) parseInput(code string) { - defer func() { - if r := recover(); r != nil { - fmt.Println("[native] error", r) - } - }() - value, err := self.re.Run(code) - if err != nil { - if ottoErr, ok := err.(*otto.Error); ok { - fmt.Println(ottoErr.String()) - } else { - fmt.Println(err) - } - return - } - self.printValue(value) -} - -var indentCount = 0 -var str = "" - -func (self *jsre) setIndent() { - open := strings.Count(str, "{") - open += strings.Count(str, "(") - closed := strings.Count(str, "}") - closed += strings.Count(str, ")") - indentCount = open - closed - if indentCount <= 0 { - self.ps1 = "> " - } else { - self.ps1 = strings.Join(make([]string, indentCount*2), "..") - self.ps1 += " " - } -} - -func (self *jsre) printValue(v interface{}) { - val, err := self.re.PrettyPrint(v) - if err == nil { - fmt.Printf("%v", val) - } -} diff --git a/cmd/ethereum/js_test.go b/cmd/ethereum/js_test.go deleted file mode 100644 index 5b962f621..000000000 --- a/cmd/ethereum/js_test.go +++ /dev/null @@ -1,262 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "os" - "path" - "testing" - - "github.com/robertkrimen/otto" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth" -) - -var port = 30300 - -func testJEthRE(t *testing.T) (repl *jsre, ethereum *eth.Ethereum, err error) { - os.RemoveAll("/tmp/eth/") - err = os.MkdirAll("/tmp/eth/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/", os.ModePerm) - if err != nil { - t.Errorf("%v", err) - return - } - err = os.MkdirAll("/tmp/eth/data", os.ModePerm) - if err != nil { - t.Errorf("%v", err) - return - } - // FIXME: this does not work ATM - ks := crypto.NewKeyStorePlain("/tmp/eth/keys") - ioutil.WriteFile("/tmp/eth/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/e273f01c99144c438695e10f24926dc1f9fbf62d", - []byte(`{"Id":"RhRXD+fNRKS4jx+7ZfEsNA==","Address":"4nPwHJkUTEOGleEPJJJtwfn79i0=","PrivateKey":"h4ACVpe74uIvi5Cg/2tX/Yrm2xdr3J7QoMbMtNX2CNc="}`), os.ModePerm) - - port++ - ethereum, err = eth.New(ð.Config{ - DataDir: "/tmp/eth", - AccountManager: accounts.NewManager(ks), - Port: fmt.Sprintf("%d", port), - MaxPeers: 10, - Name: "test", - }) - - if err != nil { - t.Errorf("%v", err) - return - } - assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext") - repl = newJSRE(ethereum, assetPath, false) - return -} - -func TestNodeInfo(t *testing.T) { - repl, ethereum, err := testJEthRE(t) - if err != nil { - t.Errorf("error creating jsre, got %v", err) - return - } - err = ethereum.Start() - if err != nil { - t.Errorf("error starting ethereum: %v", err) - return - } - defer ethereum.Stop() - - val, err := repl.re.Run("admin.nodeInfo()") - if err != nil { - t.Errorf("expected no error, got %v", err) - } - exp, err := val.Export() - if err != nil { - t.Errorf("expected no error, got %v", err) - } - nodeInfo, ok := exp.(*eth.NodeInfo) - if !ok { - t.Errorf("expected nodeInfo, got %v", err) - } - exp = "test" - got := nodeInfo.Name - if exp != got { - t.Errorf("expected %v, got %v", exp, got) - } - exp = 30301 - port := nodeInfo.DiscPort - if exp != port { - t.Errorf("expected %v, got %v", exp, port) - } - exp = 30301 - port = nodeInfo.TCPPort - if exp != port { - t.Errorf("expected %v, got %v", exp, port) - } -} - -func TestAccounts(t *testing.T) { - repl, ethereum, err := testJEthRE(t) - if err != nil { - t.Errorf("error creating jsre, got %v", err) - return - } - err = ethereum.Start() - if err != nil { - t.Errorf("error starting ethereum: %v", err) - return - } - defer ethereum.Stop() - - val, err := repl.re.Run("eth.coinbase") - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - pp, err := repl.re.PrettyPrint(val) - if err != nil { - t.Errorf("%v", err) - } - - if !val.IsString() { - t.Errorf("incorrect type, expected string, got %v: %v", val, pp) - } - strVal, _ := val.ToString() - expected := "0xe273f01c99144c438695e10f24926dc1f9fbf62d" - if strVal != expected { - t.Errorf("incorrect result, expected %s, got %v", expected, strVal) - } - - val, err = repl.re.Run(`admin.newAccount("password")`) - if err != nil { - t.Errorf("expected no error, got %v", err) - } - addr, err := val.ToString() - if err != nil { - t.Errorf("expected string, got %v", err) - } - - val, err = repl.re.Run("eth.accounts") - if err != nil { - t.Errorf("expected no error, got %v", err) - } - exp, err := val.Export() - if err != nil { - t.Errorf("expected no error, got %v", err) - } - interfaceAddr, ok := exp.([]interface{}) - if !ok { - t.Errorf("expected []string, got %T", exp) - } - - addrs := make([]string, len(interfaceAddr)) - for i, addr := range interfaceAddr { - var ok bool - if addrs[i], ok = addr.(string); !ok { - t.Errorf("expected addrs[%d] to be string. Got %T instead", i, addr) - } - } - - if len(addrs) != 2 || (addr != addrs[0][2:] && addr != addrs[1][2:]) { - t.Errorf("expected addrs == [, ], got %v (%v)", addrs, addr) - } - -} - -func TestBlockChain(t *testing.T) { - repl, ethereum, err := testJEthRE(t) - if err != nil { - t.Errorf("error creating jsre, got %v", err) - return - } - err = ethereum.Start() - if err != nil { - t.Errorf("error starting ethereum: %v", err) - return - } - defer ethereum.Stop() - - // should get current block - val0, err := repl.re.Run("admin.dumpBlock()") - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - fn := "/tmp/eth/data/blockchain.0" - _, err = repl.re.Run("admin.export(\"" + fn + "\")") - if err != nil { - t.Errorf("expected no error, got %v", err) - } - if _, err = os.Stat(fn); err != nil { - t.Errorf("expected no error on file, got %v", err) - } - - _, err = repl.re.Run("admin.import(\"" + fn + "\")") - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - var val1 otto.Value - - // should get current block - val1, err = repl.re.Run("admin.dumpBlock()") - if err != nil { - t.Errorf("expected no error, got %v", err) - } - - // FIXME: neither != , nor reflect.DeepEqual works, doing string comparison - v0 := fmt.Sprintf("%v", val0) - v1 := fmt.Sprintf("%v", val1) - if v0 != v1 { - t.Errorf("expected same head after export-import, got %v (!=%v)", v1, v0) - } -} - -func TestMining(t *testing.T) { - repl, ethereum, err := testJEthRE(t) - if err != nil { - t.Errorf("error creating jsre, got %v", err) - return - } - err = ethereum.Start() - if err != nil { - t.Errorf("error starting ethereum: %v", err) - return - } - defer ethereum.Stop() - - val, err := repl.re.Run("eth.mining") - if err != nil { - t.Errorf("expected no error, got %v", err) - } - var mining bool - mining, err = val.ToBoolean() - if err != nil { - t.Errorf("expected boolean, got %v", err) - } - if mining { - t.Errorf("expected false (not mining), got true") - } - -} - -func TestRPC(t *testing.T) { - repl, ethereum, err := testJEthRE(t) - if err != nil { - t.Errorf("error creating jsre, got %v", err) - return - } - err = ethereum.Start() - if err != nil { - t.Errorf("error starting ethereum: %v", err) - return - } - defer ethereum.Stop() - - val, err := repl.re.Run(`admin.startRPC("127.0.0.1", 5004)`) - if err != nil { - t.Errorf("expected no error, got %v", err) - } - success, _ := val.ToBoolean() - if !success { - t.Errorf("expected true (started), got false") - } -} diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go deleted file mode 100644 index 42321e8bc..000000000 --- a/cmd/ethereum/main.go +++ /dev/null @@ -1,516 +0,0 @@ -/* - This file is part of go-ethereum - - go-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - go-ethereum 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with go-ethereum. If not, see . -*/ -/** - * @authors - * Jeffrey Wilcke - */ -package main - -import ( - "bufio" - "fmt" - "io/ioutil" - "os" - "runtime" - "strconv" - "time" - - "github.com/codegangsta/cli" - "github.com/ethereum/ethash" - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/logger" - "github.com/peterh/liner" -) - -const ( - ClientIdentifier = "Ethereum(G)" - Version = "0.9.4" -) - -var ( - clilogger = logger.NewLogger("CLI") - app = utils.NewApp(Version, "the go-ethereum command line interface") -) - -func init() { - app.Action = run - app.HideVersion = true // we have a command to print the version - app.Commands = []cli.Command{ - blocktestCmd, - { - Action: makedag, - Name: "makedag", - Usage: "generate ethash dag (for testing)", - Description: ` -The makedag command generates an ethash DAG in /tmp/dag. - -This command exists to support the system testing project. -Regular users do not need to execute it. -`, - }, - { - Action: version, - Name: "version", - Usage: "print ethereum version numbers", - Description: ` -The output of this command is supposed to be machine-readable. -`, - }, - - { - Name: "wallet", - Usage: "ethereum presale wallet", - Subcommands: []cli.Command{ - { - Action: importWallet, - Name: "import", - Usage: "import ethereum presale wallet", - }, - }, - }, - { - Action: accountList, - Name: "account", - Usage: "manage accounts", - Description: ` - -Manage accounts lets you create new accounts, list all existing accounts, -import a private key into a new account. - -It supports interactive mode, when you are prompted for password as well as -non-interactive mode where passwords are supplied via a given password file. -Non-interactive mode is only meant for scripted use on test networks or known -safe environments. - -Make sure you remember the password you gave when creating a new account (with -either new or import). Without it you are not able to unlock your account. - -Note that exporting your key in unencrypted format is NOT supported. - -Keys are stored under /keys. -It is safe to transfer the entire directory or the individual keys therein -between ethereum nodes. -Make sure you backup your keys regularly. - -And finally. DO NOT FORGET YOUR PASSWORD. -`, - Subcommands: []cli.Command{ - { - Action: accountList, - Name: "list", - Usage: "print account addresses", - }, - { - Action: accountCreate, - Name: "new", - Usage: "create a new account", - Description: ` - - ethereum account new - -Creates a new account. Prints the address. - -The account is saved in encrypted format, you are prompted for a passphrase. - -You must remember this passphrase to unlock your account in the future. - -For non-interactive use the passphrase can be specified with the --password flag: - - ethereum --password account new - -Note, this is meant to be used for testing only, it is a bad idea to save your -password to file or expose in any other way. - `, - }, - { - Action: accountImport, - Name: "import", - Usage: "import a private key into a new account", - Description: ` - - ethereum account import - -Imports an unencrypted private key from and creates a new account. -Prints the address. - -The keyfile is assumed to contain an unencrypted private key in canonical EC -raw bytes format. - -The account is saved in encrypted format, you are prompted for a passphrase. - -You must remember this passphrase to unlock your account in the future. - -For non-interactive use the passphrase can be specified with the -password flag: - - ethereum --password account import - -Note: -As you can directly copy your encrypted accounts to another ethereum instance, -this import mechanism is not needed when you transfer an account between -nodes. - `, - }, - }, - }, - { - Action: dump, - Name: "dump", - Usage: `dump a specific block from storage`, - Description: ` -The arguments are interpreted as block numbers or hashes. -Use "ethereum dump 0" to dump the genesis block. -`, - }, - { - Action: console, - Name: "console", - Usage: `Ethereum Console: interactive JavaScript environment`, - Description: ` -Console is an interactive shell for the Ethereum JavaScript runtime environment -which exposes a node admin interface as well as the DAPP JavaScript API. -See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console -`, - }, - { - Action: execJSFiles, - Name: "js", - Usage: `executes the given JavaScript files in the Ethereum JavaScript VM`, - Description: ` -The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP -JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console -`, - }, - { - Action: importchain, - Name: "import", - Usage: `import a blockchain file`, - }, - { - Action: exportchain, - Name: "export", - Usage: `export blockchain into file`, - }, - } - app.Flags = []cli.Flag{ - utils.UnlockedAccountFlag, - utils.PasswordFileFlag, - utils.BootnodesFlag, - utils.DataDirFlag, - utils.JSpathFlag, - utils.ListenPortFlag, - utils.LogFileFlag, - utils.LogJSONFlag, - utils.LogLevelFlag, - utils.MaxPeersFlag, - utils.MinerThreadsFlag, - utils.MiningEnabledFlag, - utils.NATFlag, - utils.NodeKeyFileFlag, - utils.NodeKeyHexFlag, - utils.RPCEnabledFlag, - utils.RPCListenAddrFlag, - utils.RPCPortFlag, - utils.VMDebugFlag, - utils.ProtocolVersionFlag, - utils.NetworkIdFlag, - } - - // missing: - // flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file") - // flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0") - // flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false") - - // potential subcommands: - // flag.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)") - // flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given") - // flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key") -} - -func main() { - runtime.GOMAXPROCS(runtime.NumCPU()) - defer logger.Flush() - if err := app.Run(os.Args); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } -} - -func run(ctx *cli.Context) { - fmt.Printf("Welcome to the FRONTIER\n") - utils.HandleInterrupt() - cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx) - ethereum, err := eth.New(cfg) - if err != nil { - utils.Fatalf("%v", err) - } - - startEth(ctx, ethereum) - // this blocks the thread - ethereum.WaitForShutdown() -} - -func console(ctx *cli.Context) { - cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx) - ethereum, err := eth.New(cfg) - if err != nil { - utils.Fatalf("%v", err) - } - - startEth(ctx, ethereum) - repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), true) - repl.interactive() - - ethereum.Stop() - ethereum.WaitForShutdown() -} - -func execJSFiles(ctx *cli.Context) { - cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx) - ethereum, err := eth.New(cfg) - if err != nil { - utils.Fatalf("%v", err) - } - - startEth(ctx, ethereum) - repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), false) - for _, file := range ctx.Args() { - repl.exec(file) - } - - ethereum.Stop() - ethereum.WaitForShutdown() -} - -func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (passphrase string) { - var err error - // Load startup keys. XXX we are going to need a different format - // Attempt to unlock the account - passphrase = getPassPhrase(ctx, "", false) - accbytes := common.FromHex(account) - if len(accbytes) == 0 { - utils.Fatalf("Invalid account address '%s'", account) - } - err = am.Unlock(accbytes, passphrase) - if err != nil { - utils.Fatalf("Unlock account failed '%v'", err) - } - return -} - -func startEth(ctx *cli.Context, eth *eth.Ethereum) { - utils.StartEthereum(eth) - am := eth.AccountManager() - - account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) - if len(account) > 0 { - if account == "coinbase" { - accbytes, err := am.Coinbase() - if err != nil { - utils.Fatalf("no coinbase account: %v", err) - } - account = common.ToHex(accbytes) - } - unlockAccount(ctx, am, account) - } - // Start auxiliary services if enabled. - if ctx.GlobalBool(utils.RPCEnabledFlag.Name) { - utils.StartRPC(eth, ctx) - } - if ctx.GlobalBool(utils.MiningEnabledFlag.Name) { - eth.StartMining() - } -} - -func accountList(ctx *cli.Context) { - am := utils.GetAccountManager(ctx) - accts, err := am.Accounts() - if err != nil { - utils.Fatalf("Could not list accounts: %v", err) - } - for _, acct := range accts { - fmt.Printf("Address: %x\n", acct) - } -} - -func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase string) { - passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) - if len(passfile) == 0 { - fmt.Println(desc) - auth, err := readPassword("Passphrase: ", true) - if err != nil { - utils.Fatalf("%v", err) - } - if confirmation { - confirm, err := readPassword("Repeat Passphrase: ", false) - if err != nil { - utils.Fatalf("%v", err) - } - if auth != confirm { - utils.Fatalf("Passphrases did not match.") - } - } - passphrase = auth - - } else { - passbytes, err := ioutil.ReadFile(passfile) - if err != nil { - utils.Fatalf("Unable to read password file '%s': %v", passfile, err) - } - passphrase = string(passbytes) - } - return -} - -func accountCreate(ctx *cli.Context) { - am := utils.GetAccountManager(ctx) - passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true) - acct, err := am.NewAccount(passphrase) - if err != nil { - utils.Fatalf("Could not create the account: %v", err) - } - fmt.Printf("Address: %x\n", acct) -} - -func importWallet(ctx *cli.Context) { - keyfile := ctx.Args().First() - if len(keyfile) == 0 { - utils.Fatalf("keyfile must be given as argument") - } - keyJson, err := ioutil.ReadFile(keyfile) - if err != nil { - utils.Fatalf("Could not read wallet file: %v", err) - } - - am := utils.GetAccountManager(ctx) - passphrase := getPassPhrase(ctx, "", false) - - acct, err := am.ImportPreSaleKey(keyJson, passphrase) - if err != nil { - utils.Fatalf("Could not create the account: %v", err) - } - fmt.Printf("Address: %x\n", acct) -} - -func accountImport(ctx *cli.Context) { - keyfile := ctx.Args().First() - if len(keyfile) == 0 { - utils.Fatalf("keyfile must be given as argument") - } - am := utils.GetAccountManager(ctx) - passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true) - acct, err := am.Import(keyfile, passphrase) - if err != nil { - utils.Fatalf("Could not create the account: %v", err) - } - fmt.Printf("Address: %x\n", acct) -} - -func importchain(ctx *cli.Context) { - if len(ctx.Args()) != 1 { - utils.Fatalf("This command requires an argument.") - } - chainmgr, _, _ := utils.GetChain(ctx) - start := time.Now() - err := utils.ImportChain(chainmgr, ctx.Args().First()) - if err != nil { - utils.Fatalf("Import error: %v\n", err) - } - fmt.Printf("Import done in %v", time.Since(start)) - return -} - -func exportchain(ctx *cli.Context) { - if len(ctx.Args()) != 1 { - utils.Fatalf("This command requires an argument.") - } - chainmgr, _, _ := utils.GetChain(ctx) - start := time.Now() - err := utils.ExportChain(chainmgr, ctx.Args().First()) - if err != nil { - utils.Fatalf("Export error: %v\n", err) - } - fmt.Printf("Export done in %v", time.Since(start)) - return -} - -func dump(ctx *cli.Context) { - chainmgr, _, stateDb := utils.GetChain(ctx) - for _, arg := range ctx.Args() { - var block *types.Block - if hashish(arg) { - block = chainmgr.GetBlock(common.HexToHash(arg)) - } else { - num, _ := strconv.Atoi(arg) - block = chainmgr.GetBlockByNumber(uint64(num)) - } - if block == nil { - fmt.Println("{}") - utils.Fatalf("block not found") - } else { - statedb := state.New(block.Root(), stateDb) - fmt.Printf("%s\n", statedb.Dump()) - // fmt.Println(block) - } - } -} - -func makedag(ctx *cli.Context) { - chain, _, _ := utils.GetChain(ctx) - pow := ethash.New(chain) - fmt.Println("making cache") - pow.UpdateCache(true) - fmt.Println("making DAG") - pow.UpdateDAG() -} - -func version(c *cli.Context) { - fmt.Printf(`%v -Version: %v -Protocol Version: %d -Network Id: %d -GO: %s -OS: %s -GOPATH=%s -GOROOT=%s -`, ClientIdentifier, Version, c.GlobalInt(utils.ProtocolVersionFlag.Name), c.GlobalInt(utils.NetworkIdFlag.Name), runtime.Version(), runtime.GOOS, os.Getenv("GOPATH"), runtime.GOROOT()) -} - -// hashish returns true for strings that look like hashes. -func hashish(x string) bool { - _, err := strconv.Atoi(x) - return err != nil -} - -func readPassword(prompt string, warnTerm bool) (string, error) { - if liner.TerminalSupported() { - lr := liner.NewLiner() - defer lr.Close() - return lr.PasswordPrompt(prompt) - } - if warnTerm { - fmt.Println("!! Unsupported terminal, password will be echoed.") - } - fmt.Print(prompt) - input, err := bufio.NewReader(os.Stdin).ReadString('\n') - fmt.Println() - return input, err -} diff --git a/cmd/geth/admin.go b/cmd/geth/admin.go new file mode 100644 index 000000000..139395dad --- /dev/null +++ b/cmd/geth/admin.go @@ -0,0 +1,256 @@ +package main + +import ( + "fmt" + "net" + "net/http" + "os" + "time" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/xeth" + "github.com/robertkrimen/otto" +) + +/* +node admin bindings +*/ + +func (js *jsre) adminBindings() { + js.re.Set("admin", struct{}{}) + t, _ := js.re.Get("admin") + admin := t.Object() + admin.Set("suggestPeer", js.suggestPeer) + admin.Set("startRPC", js.startRPC) + admin.Set("startMining", js.startMining) + admin.Set("stopMining", js.stopMining) + admin.Set("nodeInfo", js.nodeInfo) + admin.Set("peers", js.peers) + admin.Set("newAccount", js.newAccount) + admin.Set("unlock", js.unlock) + admin.Set("import", js.importChain) + admin.Set("export", js.exportChain) + admin.Set("dumpBlock", js.dumpBlock) +} + +func (js *jsre) startMining(call otto.FunctionCall) otto.Value { + _, err := call.Argument(0).ToInteger() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + // threads now ignored + err = js.ethereum.StartMining() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + return otto.TrueValue() +} + +func (js *jsre) stopMining(call otto.FunctionCall) otto.Value { + js.ethereum.StopMining() + return otto.TrueValue() +} + +func (js *jsre) startRPC(call otto.FunctionCall) otto.Value { + addr, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + port, err := call.Argument(1).ToInteger() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + dataDir := js.ethereum.DataDir + + l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port)) + if err != nil { + fmt.Printf("Can't listen on %s:%d: %v", addr, port, err) + return otto.FalseValue() + } + go http.Serve(l, rpc.JSONRPC(xeth.New(js.ethereum, nil), dataDir)) + return otto.TrueValue() +} + +func (js *jsre) suggestPeer(call otto.FunctionCall) otto.Value { + nodeURL, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + err = js.ethereum.SuggestPeer(nodeURL) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + return otto.TrueValue() +} + +func (js *jsre) unlock(call otto.FunctionCall) otto.Value { + addr, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + seconds, err := call.Argument(2).ToInteger() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + arg := call.Argument(1) + var passphrase string + if arg.IsUndefined() { + fmt.Println("Please enter a passphrase now.") + passphrase, err = readPassword("Passphrase: ", true) + if err != nil { + utils.Fatalf("%v", err) + } + } else { + passphrase, err = arg.ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + } + am := js.ethereum.AccountManager() + // err := am.Unlock(common.FromHex(split[0]), split[1]) + // if err != nil { + // utils.Fatalf("Unlock account failed '%v'", err) + // } + err = am.TimedUnlock(common.FromHex(addr), passphrase, time.Duration(seconds)*time.Second) + if err != nil { + fmt.Printf("Unlock account failed '%v'\n", err) + return otto.FalseValue() + } + return otto.TrueValue() +} + +func (js *jsre) newAccount(call otto.FunctionCall) otto.Value { + arg := call.Argument(0) + var passphrase string + if arg.IsUndefined() { + fmt.Println("The new account will be encrypted with a passphrase.") + fmt.Println("Please enter a passphrase now.") + auth, err := readPassword("Passphrase: ", true) + if err != nil { + utils.Fatalf("%v", err) + } + confirm, err := readPassword("Repeat Passphrase: ", false) + if err != nil { + utils.Fatalf("%v", err) + } + if auth != confirm { + utils.Fatalf("Passphrases did not match.") + } + passphrase = auth + } else { + var err error + passphrase, err = arg.ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + } + acct, err := js.ethereum.AccountManager().NewAccount(passphrase) + if err != nil { + fmt.Printf("Could not create the account: %v", err) + return otto.UndefinedValue() + } + return js.re.ToVal(common.Bytes2Hex(acct.Address)) +} + +func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value { + return js.re.ToVal(js.ethereum.NodeInfo()) +} + +func (js *jsre) peers(call otto.FunctionCall) otto.Value { + return js.re.ToVal(js.ethereum.PeersInfo()) +} + +func (js *jsre) importChain(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) == 0 { + fmt.Println("err: require file name") + return otto.FalseValue() + } + + fn, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + var fh *os.File + fh, err = os.OpenFile(fn, os.O_RDONLY, os.ModePerm) + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + defer fh.Close() + + var blocks types.Blocks + if err = rlp.Decode(fh, &blocks); err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + js.ethereum.ChainManager().Reset() + if err = js.ethereum.ChainManager().InsertChain(blocks); err != nil { + fmt.Println(err) + return otto.FalseValue() + } + + return otto.TrueValue() +} + +func (js *jsre) exportChain(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) == 0 { + fmt.Println("err: require file name") + return otto.FalseValue() + } + + fn, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.FalseValue() + } + if err := utils.ExportChain(js.ethereum.ChainManager(), fn); err != nil { + fmt.Println(err) + return otto.FalseValue() + } + return otto.TrueValue() +} + +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") + return otto.UndefinedValue() + } + + statedb := state.New(block.Root(), js.ethereum.StateDb()) + dump := statedb.RawDump() + return js.re.ToVal(dump) + +} diff --git a/cmd/geth/blocktest.go b/cmd/geth/blocktest.go new file mode 100644 index 000000000..d9cdfa83f --- /dev/null +++ b/cmd/geth/blocktest.go @@ -0,0 +1,80 @@ +package main + +import ( + "fmt" + + "github.com/codegangsta/cli" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/tests" +) + +var blocktestCmd = cli.Command{ + Action: runblocktest, + Name: "blocktest", + Usage: `loads a block test file`, + Description: ` +The first argument should be a block test file. +The second argument is the name of a block test from the file. + +The block test will be loaded into an in-memory database. +If loading succeeds, the RPC server is started. Clients will +be able to interact with the chain defined by the test. +`, +} + +func runblocktest(ctx *cli.Context) { + if len(ctx.Args()) != 3 { + utils.Fatalf("Usage: ethereum blocktest {rpc, norpc}") + } + file, testname, startrpc := ctx.Args()[0], ctx.Args()[1], ctx.Args()[2] + + bt, err := tests.LoadBlockTests(file) + if err != nil { + utils.Fatalf("%v", err) + } + test, ok := bt[testname] + if !ok { + utils.Fatalf("Test file does not contain test named %q", testname) + } + + cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx) + cfg.NewDB = func(path string) (common.Database, error) { return ethdb.NewMemDatabase() } + cfg.MaxPeers = 0 // disable network + ethereum, err := eth.New(cfg) + if err != nil { + utils.Fatalf("%v", err) + } + + // import the genesis block + ethereum.ResetWithGenesisBlock(test.Genesis) + + // import pre accounts + statedb, err := test.InsertPreState(ethereum.StateDb()) + if err != nil { + utils.Fatalf("could not insert genesis accounts: %v", err) + } + + // insert the test blocks, which will execute all transactions + chain := ethereum.ChainManager() + if err := chain.InsertChain(test.Blocks); err != nil { + utils.Fatalf("Block Test load error: %v", err) + } else { + fmt.Println("Block Test chain loaded") + } + + if err := test.ValidatePostState(statedb); err != nil { + utils.Fatalf("post state validation failed: %v", err) + } + fmt.Println("Block Test post state validated, starting ethereum.") + + if startrpc == "rpc" { + startEth(ctx, ethereum) + utils.StartRPC(ethereum, ctx) + ethereum.WaitForShutdown() + } else { + startEth(ctx, ethereum) + } +} diff --git a/cmd/geth/js.go b/cmd/geth/js.go new file mode 100644 index 000000000..8e88a1c54 --- /dev/null +++ b/cmd/geth/js.go @@ -0,0 +1,246 @@ +// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301 USA + +package main + +import ( + "bufio" + "fmt" + "os" + "path" + "strings" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + re "github.com/ethereum/go-ethereum/jsre" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/xeth" + "github.com/peterh/liner" + "github.com/robertkrimen/otto" +) + +type prompter interface { + AppendHistory(string) + Prompt(p string) (string, error) + PasswordPrompt(p string) (string, error) +} + +type dumbterm struct{ r *bufio.Reader } + +func (r dumbterm) Prompt(p string) (string, error) { + fmt.Print(p) + return r.r.ReadString('\n') +} + +func (r dumbterm) PasswordPrompt(p string) (string, error) { + fmt.Println("!! Unsupported terminal, password will echo.") + fmt.Print(p) + input, err := bufio.NewReader(os.Stdin).ReadString('\n') + fmt.Println() + return input, err +} + +func (r dumbterm) AppendHistory(string) {} + +type jsre struct { + re *re.JSRE + ethereum *eth.Ethereum + xeth *xeth.XEth + ps1 string + atexit func() + + prompter +} + +func newJSRE(ethereum *eth.Ethereum, libPath string, interactive bool) *jsre { + js := &jsre{ethereum: ethereum, ps1: "> "} + js.xeth = xeth.New(ethereum, js) + js.re = re.New(libPath) + js.apiBindings() + js.adminBindings() + + if !liner.TerminalSupported() || !interactive { + js.prompter = dumbterm{bufio.NewReader(os.Stdin)} + } else { + lr := liner.NewLiner() + js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) + lr.SetCtrlCAborts(true) + js.prompter = lr + js.atexit = func() { + js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) + lr.Close() + } + } + return js +} + +func (js *jsre) apiBindings() { + + ethApi := rpc.NewEthereumApi(js.xeth, js.ethereum.DataDir) + ethApi.Close() + //js.re.Bind("jeth", rpc.NewJeth(ethApi, js.re.ToVal)) + + jeth := rpc.NewJeth(ethApi, js.re.ToVal, js.re) + //js.re.Bind("jeth", jeth) + js.re.Set("jeth", struct{}{}) + t, _ := js.re.Get("jeth") + jethObj := t.Object() + jethObj.Set("send", jeth.Send) + + err := js.re.Compile("bignumber.js", re.BigNumber_JS) + if err != nil { + utils.Fatalf("Error loading bignumber.js: %v", err) + } + + // we need to declare a dummy setTimeout. Otto does not support it + _, err = js.re.Eval("setTimeout = function(cb, delay) {};") + if err != nil { + utils.Fatalf("Error defining setTimeout: %v", err) + } + + err = js.re.Compile("ethereum.js", re.Ethereum_JS) + if err != nil { + utils.Fatalf("Error loading ethereum.js: %v", err) + } + + _, err = js.re.Eval("var web3 = require('ethereum.js');") + if err != nil { + utils.Fatalf("Error requiring web3: %v", err) + } + + _, err = js.re.Eval("web3.setProvider(jeth)") + if err != nil { + utils.Fatalf("Error setting web3 provider: %v", err) + } + _, err = js.re.Eval(` +var eth = web3.eth; +var shh = web3.shh; +var db = web3.db; +var net = web3.net; + `) + if err != nil { + utils.Fatalf("Error setting namespaces: %v", err) + } + +} + +func (self *jsre) ConfirmTransaction(tx *types.Transaction) bool { + p := fmt.Sprintf("Confirm Transaction %v\n[y/n] ", tx) + answer, _ := self.Prompt(p) + return strings.HasPrefix(strings.Trim(answer, " "), "y") +} + +func (self *jsre) UnlockAccount(addr []byte) bool { + fmt.Printf("Please unlock account %x.\n", addr) + pass, err := self.PasswordPrompt("Passphrase: ") + if err != nil { + return false + } + // TODO: allow retry + if err := self.ethereum.AccountManager().Unlock(addr, pass); err != nil { + return false + } else { + fmt.Println("Account is now unlocked for this session.") + return true + } +} + +func (self *jsre) exec(filename string) error { + if err := self.re.Exec(filename); err != nil { + return fmt.Errorf("Javascript Error: %v", err) + } + return nil +} + +func (self *jsre) interactive() { + for { + input, err := self.Prompt(self.ps1) + if err != nil { + break + } + if input == "" { + continue + } + str += input + "\n" + self.setIndent() + if indentCount <= 0 { + if input == "exit" { + break + } + hist := str[:len(str)-1] + self.AppendHistory(hist) + self.parseInput(str) + str = "" + } + } + if self.atexit != nil { + self.atexit() + } +} + +func (self *jsre) withHistory(op func(*os.File)) { + hist, err := os.OpenFile(path.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + fmt.Printf("unable to open history file: %v\n", err) + return + } + op(hist) + hist.Close() +} + +func (self *jsre) parseInput(code string) { + defer func() { + if r := recover(); r != nil { + fmt.Println("[native] error", r) + } + }() + value, err := self.re.Run(code) + if err != nil { + if ottoErr, ok := err.(*otto.Error); ok { + fmt.Println(ottoErr.String()) + } else { + fmt.Println(err) + } + return + } + self.printValue(value) +} + +var indentCount = 0 +var str = "" + +func (self *jsre) setIndent() { + open := strings.Count(str, "{") + open += strings.Count(str, "(") + closed := strings.Count(str, "}") + closed += strings.Count(str, ")") + indentCount = open - closed + if indentCount <= 0 { + self.ps1 = "> " + } else { + self.ps1 = strings.Join(make([]string, indentCount*2), "..") + self.ps1 += " " + } +} + +func (self *jsre) printValue(v interface{}) { + val, err := self.re.PrettyPrint(v) + if err == nil { + fmt.Printf("%v", val) + } +} diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go new file mode 100644 index 000000000..5b962f621 --- /dev/null +++ b/cmd/geth/js_test.go @@ -0,0 +1,262 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path" + "testing" + + "github.com/robertkrimen/otto" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" +) + +var port = 30300 + +func testJEthRE(t *testing.T) (repl *jsre, ethereum *eth.Ethereum, err error) { + os.RemoveAll("/tmp/eth/") + err = os.MkdirAll("/tmp/eth/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/", os.ModePerm) + if err != nil { + t.Errorf("%v", err) + return + } + err = os.MkdirAll("/tmp/eth/data", os.ModePerm) + if err != nil { + t.Errorf("%v", err) + return + } + // FIXME: this does not work ATM + ks := crypto.NewKeyStorePlain("/tmp/eth/keys") + ioutil.WriteFile("/tmp/eth/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/e273f01c99144c438695e10f24926dc1f9fbf62d", + []byte(`{"Id":"RhRXD+fNRKS4jx+7ZfEsNA==","Address":"4nPwHJkUTEOGleEPJJJtwfn79i0=","PrivateKey":"h4ACVpe74uIvi5Cg/2tX/Yrm2xdr3J7QoMbMtNX2CNc="}`), os.ModePerm) + + port++ + ethereum, err = eth.New(ð.Config{ + DataDir: "/tmp/eth", + AccountManager: accounts.NewManager(ks), + Port: fmt.Sprintf("%d", port), + MaxPeers: 10, + Name: "test", + }) + + if err != nil { + t.Errorf("%v", err) + return + } + assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext") + repl = newJSRE(ethereum, assetPath, false) + return +} + +func TestNodeInfo(t *testing.T) { + repl, ethereum, err := testJEthRE(t) + if err != nil { + t.Errorf("error creating jsre, got %v", err) + return + } + err = ethereum.Start() + if err != nil { + t.Errorf("error starting ethereum: %v", err) + return + } + defer ethereum.Stop() + + val, err := repl.re.Run("admin.nodeInfo()") + if err != nil { + t.Errorf("expected no error, got %v", err) + } + exp, err := val.Export() + if err != nil { + t.Errorf("expected no error, got %v", err) + } + nodeInfo, ok := exp.(*eth.NodeInfo) + if !ok { + t.Errorf("expected nodeInfo, got %v", err) + } + exp = "test" + got := nodeInfo.Name + if exp != got { + t.Errorf("expected %v, got %v", exp, got) + } + exp = 30301 + port := nodeInfo.DiscPort + if exp != port { + t.Errorf("expected %v, got %v", exp, port) + } + exp = 30301 + port = nodeInfo.TCPPort + if exp != port { + t.Errorf("expected %v, got %v", exp, port) + } +} + +func TestAccounts(t *testing.T) { + repl, ethereum, err := testJEthRE(t) + if err != nil { + t.Errorf("error creating jsre, got %v", err) + return + } + err = ethereum.Start() + if err != nil { + t.Errorf("error starting ethereum: %v", err) + return + } + defer ethereum.Stop() + + val, err := repl.re.Run("eth.coinbase") + if err != nil { + t.Errorf("expected no error, got %v", err) + } + + pp, err := repl.re.PrettyPrint(val) + if err != nil { + t.Errorf("%v", err) + } + + if !val.IsString() { + t.Errorf("incorrect type, expected string, got %v: %v", val, pp) + } + strVal, _ := val.ToString() + expected := "0xe273f01c99144c438695e10f24926dc1f9fbf62d" + if strVal != expected { + t.Errorf("incorrect result, expected %s, got %v", expected, strVal) + } + + val, err = repl.re.Run(`admin.newAccount("password")`) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + addr, err := val.ToString() + if err != nil { + t.Errorf("expected string, got %v", err) + } + + val, err = repl.re.Run("eth.accounts") + if err != nil { + t.Errorf("expected no error, got %v", err) + } + exp, err := val.Export() + if err != nil { + t.Errorf("expected no error, got %v", err) + } + interfaceAddr, ok := exp.([]interface{}) + if !ok { + t.Errorf("expected []string, got %T", exp) + } + + addrs := make([]string, len(interfaceAddr)) + for i, addr := range interfaceAddr { + var ok bool + if addrs[i], ok = addr.(string); !ok { + t.Errorf("expected addrs[%d] to be string. Got %T instead", i, addr) + } + } + + if len(addrs) != 2 || (addr != addrs[0][2:] && addr != addrs[1][2:]) { + t.Errorf("expected addrs == [, ], got %v (%v)", addrs, addr) + } + +} + +func TestBlockChain(t *testing.T) { + repl, ethereum, err := testJEthRE(t) + if err != nil { + t.Errorf("error creating jsre, got %v", err) + return + } + err = ethereum.Start() + if err != nil { + t.Errorf("error starting ethereum: %v", err) + return + } + defer ethereum.Stop() + + // should get current block + val0, err := repl.re.Run("admin.dumpBlock()") + if err != nil { + t.Errorf("expected no error, got %v", err) + } + + fn := "/tmp/eth/data/blockchain.0" + _, err = repl.re.Run("admin.export(\"" + fn + "\")") + if err != nil { + t.Errorf("expected no error, got %v", err) + } + if _, err = os.Stat(fn); err != nil { + t.Errorf("expected no error on file, got %v", err) + } + + _, err = repl.re.Run("admin.import(\"" + fn + "\")") + if err != nil { + t.Errorf("expected no error, got %v", err) + } + + var val1 otto.Value + + // should get current block + val1, err = repl.re.Run("admin.dumpBlock()") + if err != nil { + t.Errorf("expected no error, got %v", err) + } + + // FIXME: neither != , nor reflect.DeepEqual works, doing string comparison + v0 := fmt.Sprintf("%v", val0) + v1 := fmt.Sprintf("%v", val1) + if v0 != v1 { + t.Errorf("expected same head after export-import, got %v (!=%v)", v1, v0) + } +} + +func TestMining(t *testing.T) { + repl, ethereum, err := testJEthRE(t) + if err != nil { + t.Errorf("error creating jsre, got %v", err) + return + } + err = ethereum.Start() + if err != nil { + t.Errorf("error starting ethereum: %v", err) + return + } + defer ethereum.Stop() + + val, err := repl.re.Run("eth.mining") + if err != nil { + t.Errorf("expected no error, got %v", err) + } + var mining bool + mining, err = val.ToBoolean() + if err != nil { + t.Errorf("expected boolean, got %v", err) + } + if mining { + t.Errorf("expected false (not mining), got true") + } + +} + +func TestRPC(t *testing.T) { + repl, ethereum, err := testJEthRE(t) + if err != nil { + t.Errorf("error creating jsre, got %v", err) + return + } + err = ethereum.Start() + if err != nil { + t.Errorf("error starting ethereum: %v", err) + return + } + defer ethereum.Stop() + + val, err := repl.re.Run(`admin.startRPC("127.0.0.1", 5004)`) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + success, _ := val.ToBoolean() + if !success { + t.Errorf("expected true (started), got false") + } +} diff --git a/cmd/geth/main.go b/cmd/geth/main.go new file mode 100644 index 000000000..da505218b --- /dev/null +++ b/cmd/geth/main.go @@ -0,0 +1,516 @@ +/* + This file is part of go-ethereum + + go-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + go-ethereum 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with go-ethereum. If not, see . +*/ +/** + * @authors + * Jeffrey Wilcke + */ +package main + +import ( + "bufio" + "fmt" + "io/ioutil" + "os" + "runtime" + "strconv" + "time" + + "github.com/codegangsta/cli" + "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/logger" + "github.com/peterh/liner" +) + +const ( + ClientIdentifier = "Geth" + Version = "0.9.4" +) + +var ( + clilogger = logger.NewLogger("CLI") + app = utils.NewApp(Version, "the go-ethereum command line interface") +) + +func init() { + app.Action = run + app.HideVersion = true // we have a command to print the version + app.Commands = []cli.Command{ + blocktestCmd, + { + Action: makedag, + Name: "makedag", + Usage: "generate ethash dag (for testing)", + Description: ` +The makedag command generates an ethash DAG in /tmp/dag. + +This command exists to support the system testing project. +Regular users do not need to execute it. +`, + }, + { + Action: version, + Name: "version", + Usage: "print ethereum version numbers", + Description: ` +The output of this command is supposed to be machine-readable. +`, + }, + + { + Name: "wallet", + Usage: "ethereum presale wallet", + Subcommands: []cli.Command{ + { + Action: importWallet, + Name: "import", + Usage: "import ethereum presale wallet", + }, + }, + }, + { + Action: accountList, + Name: "account", + Usage: "manage accounts", + Description: ` + +Manage accounts lets you create new accounts, list all existing accounts, +import a private key into a new account. + +It supports interactive mode, when you are prompted for password as well as +non-interactive mode where passwords are supplied via a given password file. +Non-interactive mode is only meant for scripted use on test networks or known +safe environments. + +Make sure you remember the password you gave when creating a new account (with +either new or import). Without it you are not able to unlock your account. + +Note that exporting your key in unencrypted format is NOT supported. + +Keys are stored under /keys. +It is safe to transfer the entire directory or the individual keys therein +between ethereum nodes. +Make sure you backup your keys regularly. + +And finally. DO NOT FORGET YOUR PASSWORD. +`, + Subcommands: []cli.Command{ + { + Action: accountList, + Name: "list", + Usage: "print account addresses", + }, + { + Action: accountCreate, + Name: "new", + Usage: "create a new account", + Description: ` + + ethereum account new + +Creates a new account. Prints the address. + +The account is saved in encrypted format, you are prompted for a passphrase. + +You must remember this passphrase to unlock your account in the future. + +For non-interactive use the passphrase can be specified with the --password flag: + + ethereum --password account new + +Note, this is meant to be used for testing only, it is a bad idea to save your +password to file or expose in any other way. + `, + }, + { + Action: accountImport, + Name: "import", + Usage: "import a private key into a new account", + Description: ` + + ethereum account import + +Imports an unencrypted private key from and creates a new account. +Prints the address. + +The keyfile is assumed to contain an unencrypted private key in canonical EC +raw bytes format. + +The account is saved in encrypted format, you are prompted for a passphrase. + +You must remember this passphrase to unlock your account in the future. + +For non-interactive use the passphrase can be specified with the -password flag: + + ethereum --password account import + +Note: +As you can directly copy your encrypted accounts to another ethereum instance, +this import mechanism is not needed when you transfer an account between +nodes. + `, + }, + }, + }, + { + Action: dump, + Name: "dump", + Usage: `dump a specific block from storage`, + Description: ` +The arguments are interpreted as block numbers or hashes. +Use "ethereum dump 0" to dump the genesis block. +`, + }, + { + Action: console, + Name: "console", + Usage: `Geth Console: interactive JavaScript environment`, + Description: ` +The Geth console is an interactive shell for the JavaScript runtime environment +which exposes a node admin interface as well as the DAPP JavaScript API. +See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console +`, + }, + { + Action: execJSFiles, + Name: "js", + Usage: `executes the given JavaScript files in the Geth JavaScript VM`, + Description: ` +The JavaScript VM exposes a node admin interface as well as the DAPP +JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console +`, + }, + { + Action: importchain, + Name: "import", + Usage: `import a blockchain file`, + }, + { + Action: exportchain, + Name: "export", + Usage: `export blockchain into file`, + }, + } + app.Flags = []cli.Flag{ + utils.UnlockedAccountFlag, + utils.PasswordFileFlag, + utils.BootnodesFlag, + utils.DataDirFlag, + utils.JSpathFlag, + utils.ListenPortFlag, + utils.LogFileFlag, + utils.LogJSONFlag, + utils.LogLevelFlag, + utils.MaxPeersFlag, + utils.MinerThreadsFlag, + utils.MiningEnabledFlag, + utils.NATFlag, + utils.NodeKeyFileFlag, + utils.NodeKeyHexFlag, + utils.RPCEnabledFlag, + utils.RPCListenAddrFlag, + utils.RPCPortFlag, + utils.VMDebugFlag, + utils.ProtocolVersionFlag, + utils.NetworkIdFlag, + } + + // missing: + // flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file") + // flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0") + // flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false") + + // potential subcommands: + // flag.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)") + // flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given") + // flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key") +} + +func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) + defer logger.Flush() + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run(ctx *cli.Context) { + fmt.Printf("Welcome to the FRONTIER\n") + utils.HandleInterrupt() + cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx) + ethereum, err := eth.New(cfg) + if err != nil { + utils.Fatalf("%v", err) + } + + startEth(ctx, ethereum) + // this blocks the thread + ethereum.WaitForShutdown() +} + +func console(ctx *cli.Context) { + cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx) + ethereum, err := eth.New(cfg) + if err != nil { + utils.Fatalf("%v", err) + } + + startEth(ctx, ethereum) + repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), true) + repl.interactive() + + ethereum.Stop() + ethereum.WaitForShutdown() +} + +func execJSFiles(ctx *cli.Context) { + cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx) + ethereum, err := eth.New(cfg) + if err != nil { + utils.Fatalf("%v", err) + } + + startEth(ctx, ethereum) + repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), false) + for _, file := range ctx.Args() { + repl.exec(file) + } + + ethereum.Stop() + ethereum.WaitForShutdown() +} + +func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (passphrase string) { + var err error + // Load startup keys. XXX we are going to need a different format + // Attempt to unlock the account + passphrase = getPassPhrase(ctx, "", false) + accbytes := common.FromHex(account) + if len(accbytes) == 0 { + utils.Fatalf("Invalid account address '%s'", account) + } + err = am.Unlock(accbytes, passphrase) + if err != nil { + utils.Fatalf("Unlock account failed '%v'", err) + } + return +} + +func startEth(ctx *cli.Context, eth *eth.Ethereum) { + utils.StartEthereum(eth) + am := eth.AccountManager() + + account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) + if len(account) > 0 { + if account == "coinbase" { + accbytes, err := am.Coinbase() + if err != nil { + utils.Fatalf("no coinbase account: %v", err) + } + account = common.ToHex(accbytes) + } + unlockAccount(ctx, am, account) + } + // Start auxiliary services if enabled. + if ctx.GlobalBool(utils.RPCEnabledFlag.Name) { + utils.StartRPC(eth, ctx) + } + if ctx.GlobalBool(utils.MiningEnabledFlag.Name) { + eth.StartMining() + } +} + +func accountList(ctx *cli.Context) { + am := utils.GetAccountManager(ctx) + accts, err := am.Accounts() + if err != nil { + utils.Fatalf("Could not list accounts: %v", err) + } + for _, acct := range accts { + fmt.Printf("Address: %x\n", acct) + } +} + +func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase string) { + passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) + if len(passfile) == 0 { + fmt.Println(desc) + auth, err := readPassword("Passphrase: ", true) + if err != nil { + utils.Fatalf("%v", err) + } + if confirmation { + confirm, err := readPassword("Repeat Passphrase: ", false) + if err != nil { + utils.Fatalf("%v", err) + } + if auth != confirm { + utils.Fatalf("Passphrases did not match.") + } + } + passphrase = auth + + } else { + passbytes, err := ioutil.ReadFile(passfile) + if err != nil { + utils.Fatalf("Unable to read password file '%s': %v", passfile, err) + } + passphrase = string(passbytes) + } + return +} + +func accountCreate(ctx *cli.Context) { + am := utils.GetAccountManager(ctx) + passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true) + acct, err := am.NewAccount(passphrase) + if err != nil { + utils.Fatalf("Could not create the account: %v", err) + } + fmt.Printf("Address: %x\n", acct) +} + +func importWallet(ctx *cli.Context) { + keyfile := ctx.Args().First() + if len(keyfile) == 0 { + utils.Fatalf("keyfile must be given as argument") + } + keyJson, err := ioutil.ReadFile(keyfile) + if err != nil { + utils.Fatalf("Could not read wallet file: %v", err) + } + + am := utils.GetAccountManager(ctx) + passphrase := getPassPhrase(ctx, "", false) + + acct, err := am.ImportPreSaleKey(keyJson, passphrase) + if err != nil { + utils.Fatalf("Could not create the account: %v", err) + } + fmt.Printf("Address: %x\n", acct) +} + +func accountImport(ctx *cli.Context) { + keyfile := ctx.Args().First() + if len(keyfile) == 0 { + utils.Fatalf("keyfile must be given as argument") + } + am := utils.GetAccountManager(ctx) + passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true) + acct, err := am.Import(keyfile, passphrase) + if err != nil { + utils.Fatalf("Could not create the account: %v", err) + } + fmt.Printf("Address: %x\n", acct) +} + +func importchain(ctx *cli.Context) { + if len(ctx.Args()) != 1 { + utils.Fatalf("This command requires an argument.") + } + chainmgr, _, _ := utils.GetChain(ctx) + start := time.Now() + err := utils.ImportChain(chainmgr, ctx.Args().First()) + if err != nil { + utils.Fatalf("Import error: %v\n", err) + } + fmt.Printf("Import done in %v", time.Since(start)) + return +} + +func exportchain(ctx *cli.Context) { + if len(ctx.Args()) != 1 { + utils.Fatalf("This command requires an argument.") + } + chainmgr, _, _ := utils.GetChain(ctx) + start := time.Now() + err := utils.ExportChain(chainmgr, ctx.Args().First()) + if err != nil { + utils.Fatalf("Export error: %v\n", err) + } + fmt.Printf("Export done in %v", time.Since(start)) + return +} + +func dump(ctx *cli.Context) { + chainmgr, _, stateDb := utils.GetChain(ctx) + for _, arg := range ctx.Args() { + var block *types.Block + if hashish(arg) { + block = chainmgr.GetBlock(common.HexToHash(arg)) + } else { + num, _ := strconv.Atoi(arg) + block = chainmgr.GetBlockByNumber(uint64(num)) + } + if block == nil { + fmt.Println("{}") + utils.Fatalf("block not found") + } else { + statedb := state.New(block.Root(), stateDb) + fmt.Printf("%s\n", statedb.Dump()) + // fmt.Println(block) + } + } +} + +func makedag(ctx *cli.Context) { + chain, _, _ := utils.GetChain(ctx) + pow := ethash.New(chain) + fmt.Println("making cache") + pow.UpdateCache(true) + fmt.Println("making DAG") + pow.UpdateDAG() +} + +func version(c *cli.Context) { + fmt.Printf(`%v +Version: %v +Protocol Version: %d +Network Id: %d +GO: %s +OS: %s +GOPATH=%s +GOROOT=%s +`, ClientIdentifier, Version, c.GlobalInt(utils.ProtocolVersionFlag.Name), c.GlobalInt(utils.NetworkIdFlag.Name), runtime.Version(), runtime.GOOS, os.Getenv("GOPATH"), runtime.GOROOT()) +} + +// hashish returns true for strings that look like hashes. +func hashish(x string) bool { + _, err := strconv.Atoi(x) + return err != nil +} + +func readPassword(prompt string, warnTerm bool) (string, error) { + if liner.TerminalSupported() { + lr := liner.NewLiner() + defer lr.Close() + return lr.PasswordPrompt(prompt) + } + if warnTerm { + fmt.Println("!! Unsupported terminal, password will be echoed.") + } + fmt.Print(prompt) + input, err := bufio.NewReader(os.Stdin).ReadString('\n') + fmt.Println() + return input, err +} -- cgit