diff --git a/cmd/geth/admin.go b/cmd/geth/admin.go index 53dd0e6ad..523b7c406 100644 --- a/cmd/geth/admin.go +++ b/cmd/geth/admin.go @@ -35,6 +35,7 @@ func (js *jsre) adminBindings() { eth := ethO.Object() eth.Set("pendingTransactions", js.pendingTransactions) eth.Set("resend", js.resend) + eth.Set("sign", js.sign) js.re.Set("admin", struct{}{}) t, _ := js.re.Get("admin") @@ -177,6 +178,30 @@ func (js *jsre) resend(call otto.FunctionCall) otto.Value { return otto.FalseValue() } +func (js *jsre) sign(call otto.FunctionCall) otto.Value { + if len(call.ArgumentList) != 2 { + fmt.Println("requires 2 arguments: eth.sign(signer, data)") + return otto.UndefinedValue() + } + signer, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + data, err := call.Argument(1).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + v, err := js.xeth.Sign(signer, data, false) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + return js.re.ToVal(v) +} + func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value { block, err := js.getBlock(call) if err != nil { diff --git a/cmd/geth/info_test.json b/cmd/geth/info_test.json index 1e0c271ac..63f2163a9 100644 --- a/cmd/geth/info_test.json +++ b/cmd/geth/info_test.json @@ -1 +1 @@ -{"code":"605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056","info":{"abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"compilerVersion":"0.9.17","developerDoc":{"methods":{}},"language":"Solidity","languageVersion":"0","source":"contract test {\n /// @notice Will multiply `a` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply `a` by 7."}}}}} \ No newline at end of file +{"code":"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056","info":{"abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"compilerVersion":"0.9.23","developerDoc":{"methods":{}},"language":"Solidity","languageVersion":"0","source":"contract test {\n /// @notice Will multiply `a` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply `a` by 7."}}}}} \ No newline at end of file diff --git a/cmd/geth/js.go b/cmd/geth/js.go index 4ddb3bd9c..f99051a1e 100644 --- a/cmd/geth/js.go +++ b/cmd/geth/js.go @@ -71,7 +71,7 @@ type jsre struct { prompter } -func newJSRE(ethereum *eth.Ethereum, libPath, solcPath, corsDomain string, interactive bool, f xeth.Frontend) *jsre { +func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, interactive bool, f xeth.Frontend) *jsre { js := &jsre{ethereum: ethereum, ps1: "> "} // set default cors domain used by startRpc from CLI flag js.corsDomain = corsDomain @@ -81,7 +81,6 @@ func newJSRE(ethereum *eth.Ethereum, libPath, solcPath, corsDomain string, inter js.xeth = xeth.New(ethereum, f) js.wait = js.xeth.UpdateState() // update state in separare forever blocks - js.xeth.SetSolc(solcPath) js.re = re.New(libPath) js.apiBindings(f) js.adminBindings() diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go index 6368efbfc..41e1034e9 100644 --- a/cmd/geth/js_test.go +++ b/cmd/geth/js_test.go @@ -24,7 +24,7 @@ import ( const ( testSolcPath = "" - solcVersion = "0.9.17" + solcVersion = "0.9.23" testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674" testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" @@ -34,6 +34,7 @@ const ( ) var ( + versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`)) testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}` ) @@ -75,6 +76,7 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) { AccountManager: am, MaxPeers: 0, Name: "test", + SolcPath: testSolcPath, }) if err != nil { t.Fatal("%v", err) @@ -101,7 +103,7 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) { t.Errorf("Error creating DocServer: %v", err) } tf := &testjethre{ds: ds, stateDb: ethereum.ChainManager().State().Copy()} - repl := newJSRE(ethereum, assetPath, testSolcPath, "", false, tf) + repl := newJSRE(ethereum, assetPath, "", false, tf) tf.jsre = repl return tmp, tf, ethereum } @@ -228,11 +230,11 @@ func TestSignature(t *testing.T) { defer ethereum.Stop() defer os.RemoveAll(tmp) - val, err := repl.re.Run(`eth.sign({from: "` + testAddress + `", data: "` + testHash + `"})`) + val, err := repl.re.Run(`eth.sign("` + testAddress + `", "` + testHash + `")`) // This is a very preliminary test, lacking actual signature verification if err != nil { - t.Errorf("Error runnig js: %v", err) + t.Errorf("Error running js: %v", err) return } output := val.String() @@ -246,7 +248,6 @@ func TestSignature(t *testing.T) { } func TestContract(t *testing.T) { - t.Skip() tmp, repl, ethereum := testJEthRE(t) if err := ethereum.Start(); err != nil { @@ -259,7 +260,9 @@ func TestContract(t *testing.T) { var txc uint64 coinbase := common.HexToAddress(testAddress) resolver.New(repl.xeth).CreateContracts(coinbase) + // time.Sleep(1000 * time.Millisecond) + // checkEvalJSON(t, repl, `eth.getBlock("pending", true).transactions.length`, `2`) source := `contract test {\n` + " /// @notice Will multiply `a` by 7." + `\n` + ` function multiply(uint a) returns(uint d) {\n` + @@ -279,10 +282,9 @@ func TestContract(t *testing.T) { // if solc is found with right version, test it, otherwise read from file sol, err := compiler.New("") if err != nil { - t.Logf("solc not found: skipping compiler test") + t.Logf("solc not found: mocking contract compilation step") } else if sol.Version() != solcVersion { - err = fmt.Errorf("solc wrong version found (%v, expect %v): skipping compiler test", sol.Version(), solcVersion) - t.Log(err) + t.Logf("WARNING: solc different version found (%v, test written for %v, may need to update)", sol.Version(), solcVersion) } if err != nil { @@ -295,10 +297,10 @@ func TestContract(t *testing.T) { t.Errorf("%v", err) } } else { - checkEvalJSON(t, repl, `contract = eth.compile.solidity(source)`, string(contractInfo)) + checkEvalJSON(t, repl, `contract = eth.compile.solidity(source).test`, string(contractInfo)) } - checkEvalJSON(t, repl, `contract.code`, `"605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056"`) + checkEvalJSON(t, repl, `contract.code`, `"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"`) checkEvalJSON( t, repl, @@ -308,15 +310,16 @@ func TestContract(t *testing.T) { callSetup := `abiDef = JSON.parse('[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]'); Multiply7 = eth.contract(abiDef); -multiply7 = new Multiply7(contractaddress); +multiply7 = Multiply7.at(contractaddress); ` - + // time.Sleep(1500 * time.Millisecond) _, err = repl.re.Run(callSetup) if err != nil { - t.Errorf("unexpected error registering, got %v", err) + t.Errorf("unexpected error setting up contract, got %v", err) } - // updatespec + // checkEvalJSON(t, repl, `eth.getBlock("pending", true).transactions.length`, `3`) + // why is this sometimes failing? // checkEvalJSON(t, repl, `multiply7.multiply.call(6)`, `42`) expNotice := "" @@ -324,20 +327,23 @@ multiply7 = new Multiply7(contractaddress); t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm) } - // why 0? - checkEvalJSON(t, repl, `eth.getBlock("pending", true).transactions.length`, `0`) - txc, repl.xeth = repl.xeth.ApplyTestTxs(repl.stateDb, coinbase, txc) checkEvalJSON(t, repl, `admin.contractInfo.start()`, `true`) checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary, gas: "1000000", gasPrice: "100000" })`, `undefined`) - expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x4a6c99e127191d2ee302e42182c338344b39a37a47cdbb17ab0f26b6802eb4d1'): {"params":[{"to":"0x5dcaace5982778b409c524873b319667eba5d074","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}` + expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x87e2802265838c7f14bb69eecd2112911af6767907a702eeaa445239fb20711b'): {"params":[{"to":"0x5dcaace5982778b409c524873b319667eba5d074","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}` if repl.lastConfirm != expNotice { t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm) } + var contenthash = `"0x86d2b7cf1e72e9a7a3f8d96601f0151742a2f780f1526414304fbe413dc7f9bd"` + if sol != nil { + modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"`+sol.Version()+`"`)) + _ = modContractInfo + // contenthash = crypto.Sha3(modContractInfo) + } checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`) - checkEvalJSON(t, repl, `contenthash = admin.contractInfo.register(primary, contractaddress, contract, filename)`, `"0x0d067e2dd99a4d8f0c0279738b17130dd415a89f24a23f0e7cf68c546ae3089d"`) + checkEvalJSON(t, repl, `contenthash = admin.contractInfo.register(primary, contractaddress, contract, filename)`, contenthash) checkEvalJSON(t, repl, `admin.contractInfo.registerUrl(primary, contenthash, "file://"+filename)`, `true`) if err != nil { t.Errorf("unexpected error registering, got %v", err) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 2afc92f10..ba253bbeb 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -99,7 +99,15 @@ The output of this command is supposed to be machine-readable. Usage: "import ethereum presale wallet", }, }, - }, + Description: ` + + get wallet import /path/to/my/presale.wallet + +will prompt for your password and imports your ether presale account. +It can be used non-interactively with the --password option taking a +passwordfile as argument containing the wallet password in plaintext. + +`}, { Action: accountList, Name: "account", @@ -326,7 +334,6 @@ func console(ctx *cli.Context) { repl := newJSRE( ethereum, ctx.String(utils.JSpathFlag.Name), - ctx.String(utils.SolcPathFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name), true, nil, @@ -348,7 +355,6 @@ func execJSFiles(ctx *cli.Context) { repl := newJSRE( ethereum, ctx.String(utils.JSpathFlag.Name), - ctx.String(utils.SolcPathFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name), false, nil, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index f646e4fcc..2766e7517 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -313,6 +313,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config { Dial: true, BootNodes: ctx.GlobalString(BootnodesFlag.Name), GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)), + SolcPath: ctx.GlobalString(SolcPathFlag.Name), } } diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index 6790f9a1d..0cfd41c95 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -18,7 +18,8 @@ import ( ) const ( - flair = "Christian and Lefteris (c) 2014-2015" + // flair = "Christian and Lefteris (c) 2014-2015" + flair = "" languageVersion = "0" ) @@ -91,7 +92,7 @@ func (sol *Solidity) Version() string { return sol.version } -func (sol *Solidity) Compile(source string) (contract *Contract, err error) { +func (sol *Solidity) Compile(source string) (contracts map[string]*Contract, err error) { if len(source) == 0 { err = fmt.Errorf("empty source") @@ -122,58 +123,61 @@ func (sol *Solidity) Compile(source string) (contract *Contract, err error) { err = fmt.Errorf("solc error: missing code output") return } - if len(matches) > 1 { - err = fmt.Errorf("multi-contract sources are not supported") - return - } - _, file := filepath.Split(matches[0]) - base := strings.Split(file, ".")[0] - codeFile := filepath.Join(wd, base+".binary") - abiDefinitionFile := filepath.Join(wd, base+".abi") - userDocFile := filepath.Join(wd, base+".docuser") - developerDocFile := filepath.Join(wd, base+".docdev") + contracts = make(map[string]*Contract) + for _, path := range matches { + _, file := filepath.Split(path) + base := strings.Split(file, ".")[0] - code, err := ioutil.ReadFile(codeFile) - if err != nil { - err = fmt.Errorf("error reading compiler output for code: %v", err) - return - } - abiDefinitionJson, err := ioutil.ReadFile(abiDefinitionFile) - if err != nil { - err = fmt.Errorf("error reading compiler output for abiDefinition: %v", err) - return - } - var abiDefinition interface{} - err = json.Unmarshal(abiDefinitionJson, &abiDefinition) + codeFile := filepath.Join(wd, base+".binary") + abiDefinitionFile := filepath.Join(wd, base+".abi") + userDocFile := filepath.Join(wd, base+".docuser") + developerDocFile := filepath.Join(wd, base+".docdev") - userDocJson, err := ioutil.ReadFile(userDocFile) - if err != nil { - err = fmt.Errorf("error reading compiler output for userDoc: %v", err) - return - } - var userDoc interface{} - err = json.Unmarshal(userDocJson, &userDoc) + var code, abiDefinitionJson, userDocJson, developerDocJson []byte + code, err = ioutil.ReadFile(codeFile) + if err != nil { + err = fmt.Errorf("error reading compiler output for code: %v", err) + return + } + abiDefinitionJson, err = ioutil.ReadFile(abiDefinitionFile) + if err != nil { + err = fmt.Errorf("error reading compiler output for abiDefinition: %v", err) + return + } + var abiDefinition interface{} + err = json.Unmarshal(abiDefinitionJson, &abiDefinition) - developerDocJson, err := ioutil.ReadFile(developerDocFile) - if err != nil { - err = fmt.Errorf("error reading compiler output for developerDoc: %v", err) - return - } - var developerDoc interface{} - err = json.Unmarshal(developerDocJson, &developerDoc) + userDocJson, err = ioutil.ReadFile(userDocFile) + if err != nil { + err = fmt.Errorf("error reading compiler output for userDoc: %v", err) + return + } + var userDoc interface{} + err = json.Unmarshal(userDocJson, &userDoc) - contract = &Contract{ - Code: string(code), - Info: ContractInfo{ - Source: source, - Language: "Solidity", - LanguageVersion: languageVersion, - CompilerVersion: sol.version, - AbiDefinition: abiDefinition, - UserDoc: userDoc, - DeveloperDoc: developerDoc, - }, + developerDocJson, err = ioutil.ReadFile(developerDocFile) + if err != nil { + err = fmt.Errorf("error reading compiler output for developerDoc: %v", err) + return + } + var developerDoc interface{} + err = json.Unmarshal(developerDocJson, &developerDoc) + + contract := &Contract{ + Code: "0x" + string(code), + Info: ContractInfo{ + Source: source, + Language: "Solidity", + LanguageVersion: languageVersion, + CompilerVersion: sol.version, + AbiDefinition: abiDefinition, + UserDoc: userDoc, + DeveloperDoc: developerDoc, + }, + } + + contracts[base] = contract } return diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go index 68e54a7ec..46f733e59 100644 --- a/common/compiler/solidity_test.go +++ b/common/compiler/solidity_test.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" ) -const solcVersion = "0.9.17" +const solcVersion = "0.9.23" var ( source = ` @@ -20,37 +20,45 @@ contract test { } } ` - code = "605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056" - info = `{"source":"\ncontract test {\n /// @notice Will multiply ` + "`a`" + ` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","language":"Solidity","languageVersion":"0","compilerVersion":"0.9.17","abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}},"developerDoc":{"methods":{}}}` + code = "0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056" + info = `{"source":"\ncontract test {\n /// @notice Will multiply ` + "`a`" + ` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","language":"Solidity","languageVersion":"0","compilerVersion":"0.9.23","abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}},"developerDoc":{"methods":{}}}` - infohash = common.HexToHash("0x834075768a68e500e459b9c3213750c84de3df47156500cb01bb664d3f88c60a") + infohash = common.HexToHash("0xea782f674eb898e477c20e8a7cf11c2c28b09fa68b5278732104f7a101aed255") ) func TestCompiler(t *testing.T) { sol, err := New("") if err != nil { - t.Skip("no solc installed") + t.Skip("solc not found: skip") + } else if sol.Version() != solcVersion { + t.Logf("WARNING: a newer version of solc found (%v, expect %v)", sol.Version(), solcVersion) } - contract, err := sol.Compile(source) + contracts, err := sol.Compile(source) if err != nil { - t.Errorf("error compiling source. result %v: %v", contract, err) + t.Errorf("error compiling source. result %v: %v", contracts, err) return } - /* - if contract.Code != code { - t.Errorf("wrong code, expected\n%s, got\n%s", code, contract.Code) - } - */ + + if len(contracts) != 1 { + t.Errorf("one contract expected, got\n%s", len(contracts)) + } + + if contracts["test"].Code != code { + t.Errorf("wrong code, expected\n%s, got\n%s", code, contracts["test"].Code) + } + } func TestCompileError(t *testing.T) { sol, err := New("") if err != nil || sol.version != solcVersion { - t.Skip("no solc installed") + t.Skip("solc not found: skip") + } else if sol.Version() != solcVersion { + t.Logf("WARNING: a newer version of solc found (%v, expect %v)", sol.Version(), solcVersion) } - contract, err := sol.Compile(source[2:]) + contracts, err := sol.Compile(source[2:]) if err == nil { - t.Errorf("error expected compiling source. got none. result %v", contract) + t.Errorf("error expected compiling source. got none. result %v", contracts) return } } @@ -78,11 +86,11 @@ func TestExtractInfo(t *testing.T) { os.Remove(filename) cinfohash, err := ExtractInfo(contract, filename) if err != nil { - t.Errorf("%v", err) + t.Errorf("error extracting info: %v", err) } got, err := ioutil.ReadFile(filename) if err != nil { - t.Errorf("%v", err) + t.Errorf("error reading '%v': %v", filename, err) } if string(got) != info { t.Errorf("incorrect info.json extracted, expected:\n%s\ngot\n%s", info, string(got)) diff --git a/eth/backend.go b/eth/backend.go index 519a4c410..44ceb89e8 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -79,6 +80,7 @@ type Config struct { GasPrice *big.Int MinerThreads int AccountManager *accounts.Manager + SolcPath string // NewDB is used to create databases. // If nil, the default is to create leveldb databases on disk. @@ -181,6 +183,8 @@ type Ethereum struct { pow *ethash.Ethash protocolManager *ProtocolManager downloader *downloader.Downloader + SolcPath string + solc *compiler.Solidity net *p2p.Server eventMux *event.TypeMux @@ -264,6 +268,7 @@ func New(config *Config) (*Ethereum, error) { netVersionId: config.NetworkId, NatSpec: config.NatSpec, MinerThreads: config.MinerThreads, + SolcPath: config.SolcPath, } eth.pow = ethash.New() @@ -571,3 +576,18 @@ func saveBlockchainVersion(db common.Database, bcVersion int) { db.Put([]byte("BlockchainVersion"), common.NewValue(bcVersion).Bytes()) } } + +func (self *Ethereum) Solc() (*compiler.Solidity, error) { + var err error + if self.solc == nil { + self.solc, err = compiler.New(self.SolcPath) + } + return self.solc, err +} + +// set in js console via admin interface or wrapper from cli flags +func (self *Ethereum) SetSolc(solcPath string) (*compiler.Solidity, error) { + self.SolcPath = solcPath + self.solc = nil + return self.Solc() +} diff --git a/jsre/ethereum_js.go b/jsre/ethereum_js.go index a61ffcbbf..6c0ae33d7 100644 --- a/jsre/ethereum_js.go +++ b/jsre/ethereum_js.go @@ -1,7 +1,6 @@ package jsre -const Ethereum_JS = ` -require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o. */ -/** - * @file abi.js - * @author Marek Kotewicz - * @author Gav Wood - * @date 2014 - */ - -var utils = require('../utils/utils'); -var coder = require('./coder'); -var solUtils = require('./utils'); - /** - * Formats input params to bytes - * - * @method formatInput - * @param {Array} abi inputs of method - * @param {Array} params that will be formatted to bytes - * @returns bytes representation of input params - */ -var formatInput = function (inputs, params) { - var i = inputs.map(function (input) { - return input.type; - }); - return coder.encodeParams(i, params); -}; - -/** - * Formats output bytes back to param list - * - * @method formatOutput - * @param {Array} abi outputs of method - * @param {String} bytes represention of output - * @returns {Array} output params - */ -var formatOutput = function (outs, bytes) { - var o = outs.map(function (out) { - return out.type; - }); - - return coder.decodeParams(o, bytes); -}; - -/** - * Should be called to create input parser for contract with given abi - * - * @method inputParser - * @param {Array} contract abi - * @returns {Object} input parser object for given json abi - * TODO: refactor creating the parser, do not double logic from contract - */ -var inputParser = function (json) { - var parser = {}; - json.forEach(function (method) { - var displayName = utils.extractDisplayName(method.name); - var typeName = utils.extractTypeName(method.name); - - var impl = function () { - var params = Array.prototype.slice.call(arguments); - return formatInput(method.inputs, params); - }; - - if (parser[displayName] === undefined) { - parser[displayName] = impl; - } - - parser[displayName][typeName] = impl; - }); - - return parser; -}; - -/** - * Should be called to create output parser for contract with given abi - * - * @method outputParser - * @param {Array} contract abi - * @returns {Object} output parser for given json abi - */ -var outputParser = function (json) { - var parser = {}; - json.forEach(function (method) { - - var displayName = utils.extractDisplayName(method.name); - var typeName = utils.extractTypeName(method.name); - - var impl = function (output) { - return formatOutput(method.outputs, output); - }; - - if (parser[displayName] === undefined) { - parser[displayName] = impl; - } - - parser[displayName][typeName] = impl; - }); - - return parser; -}; - -var formatConstructorParams = function (abi, params) { - var constructor = solUtils.getConstructor(abi, params.length); - if (!constructor) { - if (params.length > 0) { - console.warn("didn't found matching constructor, using default one"); - } - return ''; - } - return formatInput(constructor.inputs, params); -}; - -module.exports = { - inputParser: inputParser, - outputParser: outputParser, - formatInput: formatInput, - formatOutput: formatOutput, - formatConstructorParams: formatConstructorParams -}; - -},{"../utils/utils":8,"./coder":2,"./utils":5}],2:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** * @file coder.js * @author Marek Kotewicz * @date 2015 @@ -208,7 +72,7 @@ SolidityType.prototype.isType = function (name) { * @method formatInput * @param {Object} param - plain object, or an array of objects * @param {Bool} arrayType - true if a param should be encoded as an array - * @return {SolidityParam} encoded param wrapped in SolidityParam object + * @return {SolidityParam} encoded param wrapped in SolidityParam object */ SolidityType.prototype.formatInput = function (param, arrayType) { if (utils.isArray(param) && arrayType) { // TODO: should fail if this two are not the same @@ -216,10 +80,9 @@ SolidityType.prototype.formatInput = function (param, arrayType) { return param.map(function (p) { return self._inputFormatter(p); }).reduce(function (acc, current) { - acc.appendArrayElement(current); - return acc; - }, new SolidityParam(f.formatInputInt(param.length).value)); - } + return acc.combine(current); + }, f.formatInputInt(param.length)).withOffset(32); + } return this._inputFormatter(param); }; @@ -233,11 +96,11 @@ SolidityType.prototype.formatInput = function (param, arrayType) { */ SolidityType.prototype.formatOutput = function (param, arrayType) { if (arrayType) { - // let's assume, that we solidity will never return long arrays :P + // let's assume, that we solidity will never return long arrays :P var result = []; - var length = new BigNumber(param.value, 16); + var length = new BigNumber(param.dynamicPart().slice(0, 64), 16); for (var i = 0; i < length * 64; i += 64) { - result.push(this._outputFormatter(new SolidityParam(param.suffix.slice(i, i + 64)))); + result.push(this._outputFormatter(new SolidityParam(param.dynamicPart().substr(i + 64, 64)))); } return result; } @@ -245,31 +108,21 @@ SolidityType.prototype.formatOutput = function (param, arrayType) { }; /** - * Should be used to check if a type is variadic + * Should be used to slice single param from bytes * - * @method isVariadicType + * @method sliceParam + * @param {String} bytes + * @param {Number} index of param to slice * @param {String} type - * @returns {Bool} true if the type is variadic + * @returns {SolidityParam} param */ -SolidityType.prototype.isVariadicType = function (type) { - return isArrayType(type) || this._mode === 'bytes'; -}; - -/** - * Should be used to shift param from params group - * - * @method shiftParam - * @param {String} type - * @returns {SolidityParam} shifted param - */ -SolidityType.prototype.shiftParam = function (type, param) { +SolidityType.prototype.sliceParam = function (bytes, index, type) { if (this._mode === 'bytes') { - return param.shiftBytes(); + return SolidityParam.decodeBytes(bytes, index); } else if (isArrayType(type)) { - var length = new BigNumber(param.value.slice(0, 64), 16); - return param.shiftArray(length); + return SolidityParam.decodeArray(bytes, index); } - return param.shiftValue(); + return SolidityParam.decodeParam(bytes, index); }; /** @@ -284,7 +137,7 @@ var SolidityCoder = function (types) { * * @method _requireType * @param {String} type - * @returns {SolidityType} + * @returns {SolidityType} * @throws {Error} throws if no matching type is found */ SolidityCoder.prototype._requireType = function (type) { @@ -299,20 +152,6 @@ SolidityCoder.prototype._requireType = function (type) { return solidityType; }; -/** - * Should be used to transform plain bytes to SolidityParam object - * - * @method _bytesToParam - * @param {Array} types of params - * @param {String} bytes to be transformed to SolidityParam - * @return {SolidityParam} SolidityParam for this group of params - */ -SolidityCoder.prototype._bytesToParam = function (types, bytes) { - var value = bytes.slice(0, types.length * 64); - var suffix = bytes.slice(types.length * 64); - return new SolidityParam(value, suffix); -}; - /** * Should be used to transform plain param of given type to SolidityParam * @@ -347,24 +186,11 @@ SolidityCoder.prototype.encodeParam = function (type, param) { */ SolidityCoder.prototype.encodeParams = function (types, params) { var self = this; - return types.map(function (type, index) { + var solidityParams = types.map(function (type, index) { return self._formatInput(type, params[index]); - }).reduce(function (acc, solidityParam) { - acc.append(solidityParam); - return acc; - }, new SolidityParam()).encode(); -}; + }); -/** - * Should be used to transform SolidityParam to plain param - * - * @method _formatOutput - * @param {String} type - * @param {SolidityParam} param - * @return {Object} plain param - */ -SolidityCoder.prototype._formatOutput = function (type, param) { - return this._requireType(type).formatOutput(param, isArrayType(type)); + return SolidityParam.encodeList(solidityParams); }; /** @@ -376,7 +202,7 @@ SolidityCoder.prototype._formatOutput = function (type, param) { * @return {Object} plain param */ SolidityCoder.prototype.decodeParam = function (type, bytes) { - return this._formatOutput(type, this._bytesToParam([type], bytes)); + return this.decodeParams([type], bytes)[0]; }; /** @@ -389,10 +215,9 @@ SolidityCoder.prototype.decodeParam = function (type, bytes) { */ SolidityCoder.prototype.decodeParams = function (types, bytes) { var self = this; - var param = this._bytesToParam(types, bytes); - return types.map(function (type) { + return types.map(function (type, index) { var solidityType = self._requireType(type); - var p = solidityType.shiftParam(type, param); + var p = solidityType.sliceParam(bytes, index, type); return solidityType.formatOutput(p, isArrayType(type)); }); }; @@ -459,7 +284,7 @@ var coder = new SolidityCoder([ module.exports = coder; -},{"../utils/utils":8,"./formatters":3,"./param":4,"bignumber.js":"bignumber.js"}],3:[function(require,module,exports){ +},{"../utils/utils":7,"./formatters":2,"./param":3,"bignumber.js":"bignumber.js"}],2:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -476,7 +301,7 @@ module.exports = coder; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** +/** * @file formatters.js * @author Marek Kotewicz * @date 2015 @@ -525,7 +350,7 @@ var formatInputBytes = function (value) { */ var formatInputDynamicBytes = function (value) { var result = utils.fromAscii(value, c.ETH_PADDING).substr(2); - return new SolidityParam(formatInputInt(value.length).value, result); + return new SolidityParam(formatInputInt(value.length).value + result, 32); }; /** @@ -571,7 +396,7 @@ var signedIsNegative = function (value) { * @returns {BigNumber} right-aligned output bytes formatted to big number */ var formatOutputInt = function (param) { - var value = param.value || "0"; + var value = param.staticPart() || "0"; // check if it's negative number // it it is, return two's complement @@ -589,7 +414,7 @@ var formatOutputInt = function (param) { * @returns {BigNumeber} right-aligned output bytes formatted to uint */ var formatOutputUInt = function (param) { - var value = param.value || "0"; + var value = param.staticPart() || "0"; return new BigNumber(value, 16); }; @@ -601,7 +426,7 @@ var formatOutputUInt = function (param) { * @returns {BigNumber} input bytes formatted to real */ var formatOutputReal = function (param) { - return formatOutputInt(param).dividedBy(new BigNumber(2).pow(128)); + return formatOutputInt(param).dividedBy(new BigNumber(2).pow(128)); }; /** @@ -612,7 +437,7 @@ var formatOutputReal = function (param) { * @returns {BigNumber} input bytes formatted to ureal */ var formatOutputUReal = function (param) { - return formatOutputUInt(param).dividedBy(new BigNumber(2).pow(128)); + return formatOutputUInt(param).dividedBy(new BigNumber(2).pow(128)); }; /** @@ -623,7 +448,7 @@ var formatOutputUReal = function (param) { * @returns {Boolean} right-aligned input bytes formatted to bool */ var formatOutputBool = function (param) { - return param.value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; + return param.staticPart() === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; }; /** @@ -635,7 +460,7 @@ var formatOutputBool = function (param) { */ var formatOutputBytes = function (param) { // length might also be important! - return utils.toAscii(param.value); + return utils.toAscii(param.staticPart()); }; /** @@ -647,7 +472,7 @@ var formatOutputBytes = function (param) { */ var formatOutputDynamicBytes = function (param) { // length might also be important! - return utils.toAscii(param.suffix); + return utils.toAscii(param.dynamicPart().slice(64)); }; /** @@ -658,7 +483,7 @@ var formatOutputDynamicBytes = function (param) { * @returns {String} address */ var formatOutputAddress = function (param) { - var value = param.value; + var value = param.staticPart(); return "0x" + value.slice(value.length - 40, value.length); }; @@ -679,7 +504,7 @@ module.exports = { }; -},{"../utils/config":7,"../utils/utils":8,"./param":4,"bignumber.js":"bignumber.js"}],4:[function(require,module,exports){ +},{"../utils/config":5,"../utils/utils":7,"./param":3,"bignumber.js":"bignumber.js"}],3:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -696,136 +521,202 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** +/** * @file param.js * @author Marek Kotewicz * @date 2015 */ +var utils = require('../utils/utils'); + /** * SolidityParam object prototype. * Should be used when encoding, decoding solidity bytes */ -var SolidityParam = function (value, suffix) { +var SolidityParam = function (value, offset) { this.value = value || ''; - this.suffix = suffix || ''; + this.offset = offset; // offset in bytes }; /** - * This method should be used to encode two params one after another + * This method should be used to get length of params's dynamic part * - * @method append - * @param {SolidityParam} param that it appended after this + * @method dynamicPartLength + * @returns {Number} length of dynamic part (in bytes) */ -SolidityParam.prototype.append = function (param) { - this.value += param.value; - this.suffix += param.suffix; +SolidityParam.prototype.dynamicPartLength = function () { + return this.dynamicPart().length / 2; }; /** - * This method should be used to encode next param in an array + * This method should be used to create copy of solidity param with different offset * - * @method appendArrayElement - * @param {SolidityParam} param that is appended to an array + * @method withOffset + * @param {Number} offset length in bytes + * @returns {SolidityParam} new solidity param with applied offset */ -SolidityParam.prototype.appendArrayElement = function (param) { - this.suffix += param.value; - //this.suffix += param.suffix; // we do not support nested dynamic types +SolidityParam.prototype.withOffset = function (offset) { + return new SolidityParam(this.value, offset); }; /** - * This method should be used to create bytearrays from param + * This method should be used to combine solidity params together + * eg. when appending an array + * + * @method combine + * @param {SolidityParam} param with which we should combine + * @param {SolidityParam} result of combination + */ +SolidityParam.prototype.combine = function (param) { + return new SolidityParam(this.value + param.value); +}; + +/** + * This method should be called to check if param has dynamic size. + * If it has, it returns true, otherwise false + * + * @method isDynamic + * @returns {Boolean} + */ +SolidityParam.prototype.isDynamic = function () { + return this.value.length > 64; +}; + +/** + * This method should be called to transform offset to bytes + * + * @method offsetAsBytes + * @returns {String} bytes representation of offset + */ +SolidityParam.prototype.offsetAsBytes = function () { + return !this.isDynamic() ? '' : utils.padLeft(utils.toTwosComplement(this.offset).toString(16), 64); +}; + +/** + * This method should be called to get static part of param + * + * @method staticPart + * @returns {String} offset if it is a dynamic param, otherwise value + */ +SolidityParam.prototype.staticPart = function () { + if (!this.isDynamic()) { + return this.value; + } + return this.offsetAsBytes(); +}; + +/** + * This method should be called to get dynamic part of param + * + * @method dynamicPart + * @returns {String} returns a value if it is a dynamic param, otherwise empty string + */ +SolidityParam.prototype.dynamicPart = function () { + return this.isDynamic() ? this.value : ''; +}; + +/** + * This method should be called to encode param * * @method encode - * @return {String} encoded param(s) + * @returns {String} */ SolidityParam.prototype.encode = function () { - return this.value + this.suffix; + return this.staticPart() + this.dynamicPart(); }; /** - * This method should be used to shift first param from group of params + * This method should be called to encode array of params * - * @method shiftValue - * @return {SolidityParam} first value param + * @method encodeList + * @param {Array[SolidityParam]} params + * @returns {String} */ -SolidityParam.prototype.shiftValue = function () { - var value = this.value.slice(0, 64); - this.value = this.value.slice(64); - return new SolidityParam(value); +SolidityParam.encodeList = function (params) { + + // updating offsets + var totalOffset = params.length * 32; + var offsetParams = params.map(function (param) { + if (!param.isDynamic()) { + return param; + } + var offset = totalOffset; + totalOffset += param.dynamicPartLength(); + return param.withOffset(offset); + }); + + // encode everything! + return offsetParams.reduce(function (result, param) { + return result + param.dynamicPart(); + }, offsetParams.reduce(function (result, param) { + return result + param.staticPart(); + }, '')); }; /** - * This method should be used to first bytes param from group of params + * This method should be used to decode plain (static) solidity param at given index * - * @method shiftBytes - * @return {SolidityParam} first bytes param + * @method decodeParam + * @param {String} bytes + * @param {Number} index + * @returns {SolidityParam} */ -SolidityParam.prototype.shiftBytes = function () { - return this.shiftArray(1); +SolidityParam.decodeParam = function (bytes, index) { + index = index || 0; + return new SolidityParam(bytes.substr(index * 64, 64)); }; /** - * This method should be used to shift an array from group of params - * - * @method shiftArray - * @param {Number} size of an array to shift - * @return {SolidityParam} first array param + * This method should be called to get offset value from bytes at given index + * + * @method getOffset + * @param {String} bytes + * @param {Number} index + * @returns {Number} offset as number */ -SolidityParam.prototype.shiftArray = function (length) { - var value = this.value.slice(0, 64); - this.value = this.value.slice(64); - var suffix = this.suffix.slice(0, 64 * length); - this.suffix = this.suffix.slice(64 * length); - return new SolidityParam(value, suffix); +var getOffset = function (bytes, index) { + // we can do this cause offset is rather small + return parseInt('0x' + bytes.substr(index * 64, 64)); +}; + +/** + * This method should be called to decode solidity bytes param at given index + * + * @method decodeBytes + * @param {String} bytes + * @param {Number} index + * @returns {SolidityParam} + */ +SolidityParam.decodeBytes = function (bytes, index) { + index = index || 0; + //TODO add support for strings longer than 32 bytes + //var length = parseInt('0x' + bytes.substr(offset * 64, 64)); + + var offset = getOffset(bytes, index); + + // 2 * , cause we also parse length + return new SolidityParam(bytes.substr(offset * 2, 2 * 64)); +}; + +/** + * This method should be used to decode solidity array at given index + * + * @method decodeArray + * @param {String} bytes + * @param {Number} index + * @returns {SolidityParam} + */ +SolidityParam.decodeArray = function (bytes, index) { + index = index || 0; + var offset = getOffset(bytes, index); + var length = parseInt('0x' + bytes.substr(offset * 2, 64)); + return new SolidityParam(bytes.substr(offset * 2, (length + 1) * 64)); }; module.exports = SolidityParam; -},{}],5:[function(require,module,exports){ -/* - This file is part of ethereum.js. - - ethereum.js is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ethereum.js is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ethereum.js. If not, see . -*/ -/** - * @file utils.js - * @author Marek Kotewicz - * @date 2015 - */ - -/** - * Returns the contstructor with matching number of arguments - * - * @method getConstructor - * @param {Array} abi - * @param {Number} numberOfArgs - * @returns {Object} constructor function abi - */ -var getConstructor = function (abi, numberOfArgs) { - return abi.filter(function (f) { - return f.type === 'constructor' && f.inputs.length === numberOfArgs; - })[0]; -}; - -module.exports = { - getConstructor: getConstructor -}; - - -},{}],6:[function(require,module,exports){ +},{"../utils/utils":7}],4:[function(require,module,exports){ 'use strict'; // go env doesn't have and need XMLHttpRequest @@ -836,7 +727,7 @@ if (typeof XMLHttpRequest === 'undefined') { } -},{}],7:[function(require,module,exports){ +},{}],5:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -861,13 +752,13 @@ if (typeof XMLHttpRequest === 'undefined') { /** * Utils - * + * * @module utils */ /** * Utility functions - * + * * @class [utils] config * @constructor */ @@ -875,26 +766,26 @@ if (typeof XMLHttpRequest === 'undefined') { /// required to define ETH_BIGNUMBER_ROUNDING_MODE var BigNumber = require('bignumber.js'); -var ETH_UNITS = [ - 'wei', - 'Kwei', - 'Mwei', - 'Gwei', - 'szabo', - 'finney', - 'ether', - 'grand', - 'Mether', - 'Gether', - 'Tether', - 'Pether', - 'Eether', - 'Zether', - 'Yether', - 'Nether', - 'Dether', - 'Vether', - 'Uether' +var ETH_UNITS = [ + 'wei', + 'Kwei', + 'Mwei', + 'Gwei', + 'szabo', + 'finney', + 'ether', + 'grand', + 'Mether', + 'Gether', + 'Tether', + 'Pether', + 'Eether', + 'Zether', + 'Yether', + 'Nether', + 'Dether', + 'Vether', + 'Uether' ]; module.exports = { @@ -908,7 +799,7 @@ module.exports = { }; -},{"bignumber.js":"bignumber.js"}],8:[function(require,module,exports){ +},{"bignumber.js":"bignumber.js"}],6:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -925,7 +816,39 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** +/** + * @file sha3.js + * @author Marek Kotewicz + * @date 2015 + */ + +var sha3 = require('crypto-js/sha3'); + +module.exports = function (str) { + return sha3(str, { + outputLength: 256 + }).toString(); +}; + + +},{"crypto-js/sha3":30}],7:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** * @file utils.js * @author Marek Kotewicz * @date 2015 @@ -933,13 +856,13 @@ module.exports = { /** * Utils - * + * * @module utils */ /** * Utility functions - * + * * @class [utils] utils * @constructor */ @@ -978,7 +901,7 @@ var padLeft = function (string, chars, sign) { return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; }; -/** +/** * Should be called to get sting from it's hex representation * * @method toAscii @@ -1003,9 +926,9 @@ var toAscii = function(hex) { return str; }; - + /** - * Shold be called to get hex representation (prefixed by 0x) of ascii string + * Shold be called to get hex representation (prefixed by 0x) of ascii string * * @method toHexNative * @param {String} string @@ -1022,7 +945,7 @@ var toHexNative = function(str) { }; /** - * Shold be called to get hex representation (prefixed by 0x) of ascii string + * Shold be called to get hex representation (prefixed by 0x) of ascii string * * @method fromAscii * @param {String} string @@ -1055,13 +978,13 @@ var transformToFullName = function (json) { /** * Should be called to get display name of contract function - * + * * @method extractDisplayName * @param {String} name of function/event * @returns {String} display name for function/event eg. multiply(uint256) -> multiply */ var extractDisplayName = function (name) { - var length = name.indexOf('('); + var length = name.indexOf('('); return length !== -1 ? name.substr(0, length) : name; }; @@ -1169,7 +1092,7 @@ var getValueOfUnit = function (unit) { var fromWei = function(number, unit) { var returnValue = toBigNumber(number).dividedBy(getValueOfUnit(unit)); - return isBigNumber(number) ? returnValue : returnValue.toString(10); + return isBigNumber(number) ? returnValue : returnValue.toString(10); }; /** @@ -1195,7 +1118,7 @@ var fromWei = function(number, unit) { var toWei = function(number, unit) { var returnValue = toBigNumber(number).times(getValueOfUnit(unit)); - return isBigNumber(number) ? returnValue : returnValue.toString(10); + return isBigNumber(number) ? returnValue : returnValue.toString(10); }; /** @@ -1214,7 +1137,7 @@ var toBigNumber = function(number) { if (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) { return new BigNumber(number.replace('0x',''), 16); } - + return new BigNumber(number.toString(10), 10); }; @@ -1266,7 +1189,7 @@ var toAddress = function (address) { if (isStrictAddress(address)) { return address; } - + if (/^[0-9a-f]{40}$/.test(address)) { return '0x' + address; } @@ -1274,12 +1197,13 @@ var toAddress = function (address) { return '0x' + padLeft(toHex(address).substr(2), 40); }; + /** * Returns true if object is BigNumber, otherwise false * * @method isBigNumber * @param {Object} - * @return {Boolean} + * @return {Boolean} */ var isBigNumber = function (object) { return object instanceof BigNumber || @@ -1288,7 +1212,7 @@ var isBigNumber = function (object) { /** * Returns true if object is string, otherwise false - * + * * @method isString * @param {Object} * @return {Boolean} @@ -1339,12 +1263,12 @@ var isBoolean = function (object) { * @return {Boolean} */ var isArray = function (object) { - return object instanceof Array; + return object instanceof Array; }; /** * Returns true if given string is valid json object - * + * * @method isJson * @param {String} * @return {Boolean} @@ -1384,12 +1308,12 @@ module.exports = { }; -},{"bignumber.js":"bignumber.js"}],9:[function(require,module,exports){ +},{"bignumber.js":"bignumber.js"}],8:[function(require,module,exports){ module.exports={ - "version": "0.3.5" + "version": "0.4.2" } -},{}],10:[function(require,module,exports){ +},{}],9:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1427,16 +1351,9 @@ var utils = require('./utils/utils'); var formatters = require('./web3/formatters'); var RequestManager = require('./web3/requestmanager'); var c = require('./utils/config'); -var Method = require('./web3/method'); var Property = require('./web3/property'); - -var web3Methods = [ - new Method({ - name: 'sha3', - call: 'web3_sha3', - params: 1 - }) -]; +var Batch = require('./web3/batch'); +var sha3 = require('./utils/sha3'); var web3Properties = [ new Property({ @@ -1521,6 +1438,10 @@ web3.toBigNumber = utils.toBigNumber; web3.toWei = utils.toWei; web3.fromWei = utils.fromWei; web3.isAddress = utils.isAddress; +web3.sha3 = sha3; +web3.createBatch = function () { + return new Batch(); +}; // ADD defaultblock Object.defineProperty(web3.eth, 'defaultBlock', { @@ -1544,7 +1465,6 @@ Object.defineProperty(web3.eth, 'defaultAccount', { }); /// setups all api methods -setupMethods(web3, web3Methods); setupProperties(web3, web3Properties); setupMethods(web3.net, net.methods); setupProperties(web3.net, net.properties); @@ -1556,7 +1476,7 @@ setupMethods(web3.shh, shh.methods); module.exports = web3; -},{"./utils/config":7,"./utils/utils":8,"./version.json":9,"./web3/db":12,"./web3/eth":14,"./web3/filter":16,"./web3/formatters":17,"./web3/method":21,"./web3/net":22,"./web3/property":23,"./web3/requestmanager":25,"./web3/shh":26,"./web3/watches":27}],11:[function(require,module,exports){ +},{"./utils/config":5,"./utils/sha3":6,"./utils/utils":7,"./version.json":8,"./web3/batch":10,"./web3/db":12,"./web3/eth":14,"./web3/filter":16,"./web3/formatters":17,"./web3/net":22,"./web3/property":23,"./web3/requestmanager":25,"./web3/shh":26,"./web3/watches":27}],10:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1573,20 +1493,109 @@ module.exports = web3; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** +/** + * @file batch.js + * @author Marek Kotewicz + * @date 2015 + */ + +var RequestManager = require('./requestmanager'); + +var Batch = function () { + this.requests = []; +}; + +/** + * Should be called to add create new request to batch request + * + * @method add + * @param {Object} jsonrpc requet object + */ +Batch.prototype.add = function (request) { + this.requests.push(request); +}; + +/** + * Should be called to execute batch request + * + * @method execute + */ +Batch.prototype.execute = function () { + var requests = this.requests; + RequestManager.getInstance().sendBatch(requests, function (err, results) { + results = results || []; + requests.map(function (request, index) { + return results[index] || {}; + }).map(function (result, index) { + return requests[index].format ? requests[index].format(result.result) : result.result; + }).forEach(function (result, index) { + if (requests[index].callback) { + requests[index].callback(err, result); + } + }); + }); +}; + +module.exports = Batch; + + +},{"./requestmanager":25}],11:[function(require,module,exports){ +/* + This file is part of ethereum.js. + + ethereum.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethereum.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with ethereum.js. If not, see . +*/ +/** * @file contract.js * @author Marek Kotewicz * @date 2014 */ -var web3 = require('../web3'); -var solAbi = require('../solidity/abi'); +var web3 = require('../web3'); var utils = require('../utils/utils'); +var coder = require('../solidity/coder'); var SolidityEvent = require('./event'); var SolidityFunction = require('./function'); -var addFunctionsToContract = function (contract, desc) { - desc.filter(function (json) { +/** + * Should be called to encode constructor params + * + * @method encodeConstructorParams + * @param {Array} abi + * @param {Array} constructor params + */ +var encodeConstructorParams = function (abi, params) { + return abi.filter(function (json) { + return json.type === 'constructor' && json.inputs.length === params.length; + }).map(function (json) { + return json.inputs.map(function (input) { + return input.type; + }); + }).map(function (types) { + return coder.encodeParams(types, params); + })[0] || ''; +}; + +/** + * Should be called to add functions to contract object + * + * @method addFunctionsToContract + * @param {Contract} contract + * @param {Array} abi + */ +var addFunctionsToContract = function (contract, abi) { + abi.filter(function (json) { return json.type === 'function'; }).map(function (json) { return new SolidityFunction(json, contract.address); @@ -1595,8 +1604,15 @@ var addFunctionsToContract = function (contract, desc) { }); }; -var addEventsToContract = function (contract, desc) { - desc.filter(function (json) { +/** + * Should be called to add events to contract object + * + * @method addEventsToContract + * @param {Contract} contract + * @param {Array} abi + */ +var addEventsToContract = function (contract, abi) { + abi.filter(function (json) { return json.type === 'event'; }).map(function (json) { return new SolidityEvent(json, contract.address); @@ -1606,65 +1622,106 @@ var addEventsToContract = function (contract, desc) { }; /** - * This method should be called when we want to call / transact some solidity method from javascript - * it returns an object which has same methods available as solidity contract description - * usage example: + * Should be called to create new ContractFactory * - * var abi = [{ - * name: 'myMethod', - * inputs: [{ name: 'a', type: 'string' }], - * outputs: [{name: 'd', type: 'string' }] - * }]; // contract abi - * - * var MyContract = web3.eth.contract(abi); // creation of contract prototype - * - * var contractInstance = new MyContract('0x0123123121'); - * - * contractInstance.myMethod('this is test string param for call'); // myMethod call (implicit, default) - * contractInstance.call().myMethod('this is test string param for call'); // myMethod call (explicit) - * contractInstance.sendTransaction().myMethod('this is test string param for transact'); // myMethod sendTransaction - * - * @param abi - abi json description of the contract, which is being created - * @returns contract object + * @method contract + * @param {Array} abi + * @returns {ContractFactory} new contract factory */ var contract = function (abi) { - - // return prototype - return Contract.bind(null, abi); + return new ContractFactory(abi); }; -var Contract = function (abi, options) { +/** + * Should be called to create new ContractFactory instance + * + * @method ContractFactory + * @param {Array} abi + */ +var ContractFactory = function (abi) { + this.abi = abi; +}; - this.address = ''; - if (utils.isAddress(options)) { - this.address = options; - } else { // is an object! - // TODO, parse the rest of the args - options = options || {}; - var args = Array.prototype.slice.call(arguments, 2); - var bytes = solAbi.formatConstructorParams(abi, args); - options.data += bytes; - this.address = web3.eth.sendTransaction(options); +/** + * Should be called to create new contract on a blockchain + * + * @method new + * @param {Any} contract constructor param1 (optional) + * @param {Any} contract constructor param2 (optional) + * @param {Object} contract transaction object (required) + * @param {Function} callback + * @returns {Contract} returns contract if no callback was passed, + * otherwise calls callback function (err, contract) + */ +ContractFactory.prototype.new = function () { + // parse arguments + var options = {}; // required! + var callback; + + var args = Array.prototype.slice.call(arguments); + if (utils.isFunction(args[args.length - 1])) { + callback = args.pop(); } + var last = args[args.length - 1]; + if (utils.isObject(last) && !utils.isArray(last)) { + options = args.pop(); + } + + // throw an error if there are no options + + var bytes = encodeConstructorParams(this.abi, args); + options.data += bytes; + + if (!callback) { + var address = web3.eth.sendTransaction(options); + return this.at(address); + } + + var self = this; + web3.eth.sendTransaction(options, function (err, address) { + if (err) { + callback(err); + } + self.at(address, callback); + }); +}; + +/** + * Should be called to get access to existing contract on a blockchain + * + * @method at + * @param {Address} contract address (required) + * @param {Function} callback {optional) + * @returns {Contract} returns contract if no callback was passed, + * otherwise calls callback function (err, contract) + */ +ContractFactory.prototype.at = function (address, callback) { + // TODO: address is required + + if (callback) { + callback(null, new Contract(this.abi, address)); + } + return new Contract(this.abi, address); +}; + +/** + * Should be called to create new contract instance + * + * @method Contract + * @param {Array} abi + * @param {Address} contract address + */ +var Contract = function (abi, address) { + this.address = address; addFunctionsToContract(this, abi); addEventsToContract(this, abi); }; -Contract.prototype.call = function () { - console.error('contract.call is deprecated'); - return this; -}; - -Contract.prototype.sendTransaction = function () { - console.error('contract.sendTransact is deprecated'); - return this; -}; - module.exports = contract; -},{"../solidity/abi":1,"../utils/utils":8,"../web3":10,"./event":15,"./function":18}],12:[function(require,module,exports){ +},{"../solidity/coder":1,"../utils/utils":7,"../web3":9,"./event":15,"./function":18}],12:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -1739,7 +1796,7 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** +/** * @file errors.js * @author Marek Kotewicz * @date 2015 @@ -1925,12 +1982,6 @@ var getTransactionCount = new Method({ outputFormatter: utils.toDecimal }); -var sign = new Method({ - name: 'sign', - call: 'eth_sign', - params: 1 -}); - var sendTransaction = new Method({ name: 'sendTransaction', call: 'eth_sendTransaction', @@ -1945,6 +1996,14 @@ var call = new Method({ inputFormatter: [formatters.inputTransactionFormatter, formatters.inputDefaultBlockNumberFormatter] }); +var estimateGas = new Method({ + name: 'estimateGas', + call: 'eth_estimateGas', + params: 1, + inputFormatter: [formatters.inputTransactionFormatter], + outputFormatter: utils.toDecimal +}); + var compileSolidity = new Method({ name: 'compile.solidity', call: 'eth_compileSolidity', @@ -1963,6 +2022,18 @@ var compileSerpent = new Method({ params: 1 }); +var submitWork = new Method({ + name: 'submitWork', + call: 'eth_submitWork', + params: 3 +}); + +var getWork = new Method({ + name: 'getWork', + call: 'eth_getWork', + params: 0 +}); + var methods = [ getBalance, getStorageAt, @@ -1976,11 +2047,13 @@ var methods = [ getTransactionFromBlock, getTransactionCount, call, - sign, + estimateGas, sendTransaction, compileSolidity, compileLLL, compileSerpent, + submitWork, + getWork ]; /// @returns an array of objects describing web3.eth api properties @@ -2023,7 +2096,7 @@ module.exports = { }; -},{"../utils/utils":8,"./formatters":17,"./method":21,"./property":23}],15:[function(require,module,exports){ +},{"../utils/utils":7,"./formatters":17,"./method":21,"./property":23}],15:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2040,7 +2113,7 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** +/** * @file event.js * @author Marek Kotewicz * @date 2014 @@ -2050,6 +2123,7 @@ var utils = require('../utils/utils'); var coder = require('../solidity/coder'); var web3 = require('../web3'); var formatters = require('./formatters'); +var sha3 = require('../utils/sha3'); /** * This prototype should be used to create event filters @@ -2103,12 +2177,12 @@ SolidityEvent.prototype.typeName = function () { * @return {String} event signature */ SolidityEvent.prototype.signature = function () { - return web3.sha3(web3.fromAscii(this._name)).slice(2); + return sha3(this._name); }; /** * Should be used to encode indexed params and options to one final object - * + * * @method encode * @param {Object} indexed * @param {Object} options @@ -2139,7 +2213,7 @@ SolidityEvent.prototype.encode = function (indexed, options) { if (value === undefined || value === null) { return null; } - + if (utils.isArray(value)) { return value.map(function (v) { return '0x' + coder.encodeParam(i.type, v); @@ -2161,17 +2235,17 @@ SolidityEvent.prototype.encode = function (indexed, options) { * @return {Object} result object with decoded indexed && not indexed params */ SolidityEvent.prototype.decode = function (data) { - + data.data = data.data || ''; data.topics = data.topics || []; var argTopics = this._anonymous ? data.topics : data.topics.slice(1); var indexedData = argTopics.map(function (topics) { return topics.slice(2); }).join(""); - var indexedParams = coder.decodeParams(this.types(true), indexedData); + var indexedParams = coder.decodeParams(this.types(true), indexedData); var notIndexedData = data.data.slice(2); var notIndexedParams = coder.decodeParams(this.types(false), notIndexedData); - + var result = formatters.outputLogFormatter(data); result.event = this.displayName(); result.address = data.address; @@ -2219,7 +2293,7 @@ SolidityEvent.prototype.attachToContract = function (contract) { module.exports = SolidityEvent; -},{"../solidity/coder":2,"../utils/utils":8,"../web3":10,"./formatters":17}],16:[function(require,module,exports){ +},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9,"./formatters":17}],16:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2276,7 +2350,7 @@ var getOptions = function (options) { if (utils.isString(options)) { return options; - } + } options = options || {}; @@ -2292,8 +2366,8 @@ var getOptions = function (options) { to: options.to, address: options.address, fromBlock: formatters.inputBlockNumberFormatter(options.fromBlock), - toBlock: formatters.inputBlockNumberFormatter(options.toBlock) - }; + toBlock: formatters.inputBlockNumberFormatter(options.toBlock) + }; }; var Filter = function (options, methods, formatter) { @@ -2376,7 +2450,7 @@ Filter.prototype.get = function (callback) { module.exports = Filter; -},{"../utils/utils":8,"./formatters":17,"./requestmanager":25}],17:[function(require,module,exports){ +},{"../utils/utils":7,"./formatters":17,"./requestmanager":25}],17:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2393,7 +2467,7 @@ module.exports = Filter; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** +/** * @file formatters.js * @author Marek Kotewicz * @author Fabian Vogelsteller @@ -2451,18 +2525,18 @@ var inputTransactionFormatter = function (options){ delete options.code; } - ['gasPrice', 'gas', 'value'].filter(function (key) { + ['gasPrice', 'gas', 'value', 'nonce'].filter(function (key) { return options[key] !== undefined; }).forEach(function(key){ options[key] = utils.fromDecimal(options[key]); }); - return options; + return options; }; /** * Formats the output of a transaction to its proper values - * + * * @method outputTransactionFormatter * @param {Object} transaction * @returns {Object} transaction @@ -2481,7 +2555,7 @@ var outputTransactionFormatter = function (tx){ * Formats the output of a block to its proper values * * @method outputBlockFormatter - * @param {Object} block object + * @param {Object} block object * @returns {Object} block object */ var outputBlockFormatter = function(block) { @@ -2508,7 +2582,7 @@ var outputBlockFormatter = function(block) { /** * Formats the output of a log - * + * * @method outputLogFormatter * @param {Object} log object * @returns {Object} log @@ -2549,7 +2623,7 @@ var inputPostFormatter = function(post) { return utils.fromAscii(topic); }); - return post; + return post; }; /** @@ -2596,7 +2670,7 @@ module.exports = { }; -},{"../utils/config":7,"../utils/utils":8}],18:[function(require,module,exports){ +},{"../utils/config":5,"../utils/utils":7}],18:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2613,7 +2687,7 @@ module.exports = { You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** +/** * @file function.js * @author Marek Kotewicz * @date 2015 @@ -2622,6 +2696,7 @@ module.exports = { var web3 = require('../web3'); var coder = require('../solidity/coder'); var utils = require('../utils/utils'); +var sha3 = require('../utils/sha3'); /** * This prototype should be used to call/sendTransaction to solidity functions @@ -2638,18 +2713,23 @@ var SolidityFunction = function (json, address) { this._address = address; }; +SolidityFunction.prototype.extractCallback = function (args) { + if (utils.isFunction(args[args.length - 1])) { + return args.pop(); // modify the args array! + } +}; + /** * Should be used to create payload from arguments * * @method toPayload - * @param {...} solidity function params + * @param {Array} solidity function params * @param {Object} optional payload options */ -SolidityFunction.prototype.toPayload = function () { - var args = Array.prototype.slice.call(arguments); +SolidityFunction.prototype.toPayload = function (args) { var options = {}; if (args.length > this._inputTypes.length && utils.isObject(args[args.length -1])) { - options = args.pop(); + options = args[args.length - 1]; } options.to = this._address; options.data = '0x' + this.signature() + coder.encodeParams(this._inputTypes, args); @@ -2663,24 +2743,46 @@ SolidityFunction.prototype.toPayload = function () { * @return {String} function signature */ SolidityFunction.prototype.signature = function () { - return web3.sha3(web3.fromAscii(this._name)).slice(2, 10); + return sha3(this._name).slice(0, 8); }; -/** - * Should be used to call function - * - * @method call - * @param {Object} options - * @return {String} output bytes - */ -SolidityFunction.prototype.call = function () { - var payload = this.toPayload.apply(this, Array.prototype.slice.call(arguments)); - var output = web3.eth.call(payload); + +SolidityFunction.prototype.unpackOutput = function (output) { + if (output === null) { + return; + } + output = output.length >= 2 ? output.slice(2) : output; var result = coder.decodeParams(this._outputTypes, output); return result.length === 1 ? result[0] : result; }; +/** + * Calls a contract function. + * + * @method call + * @param {...Object} Contract function arguments + * @param {function} If the last argument is a function, the contract function + * call will be asynchronous, and the callback will be passed the + * error and result. + * @return {String} output bytes + */ +SolidityFunction.prototype.call = function () { + var args = Array.prototype.slice.call(arguments); + var callback = this.extractCallback(args); + var payload = this.toPayload(args); + + if (!callback) { + var output = web3.eth.call(payload); + return this.unpackOutput(output); + } + + var self = this; + web3.eth.call(payload, function (error, output) { + callback(error, self.unpackOutput(output)); + }); +}; + /** * Should be used to sendTransaction to solidity function * @@ -2688,8 +2790,16 @@ SolidityFunction.prototype.call = function () { * @param {Object} options */ SolidityFunction.prototype.sendTransaction = function () { - var payload = this.toPayload.apply(this, Array.prototype.slice.call(arguments)); - web3.eth.sendTransaction(payload); + var args = Array.prototype.slice.call(arguments); + var callback = this.extractCallback(args); + var payload = this.toPayload(args); + + if (!callback) { + web3.eth.sendTransaction(payload); + return; + } + + web3.eth.sendTransaction(payload, callback); }; /** @@ -2704,7 +2814,7 @@ SolidityFunction.prototype.displayName = function () { /** * Should be used to get function type name - * + * * @method typeName * @return {String} type name of the function */ @@ -2712,6 +2822,25 @@ SolidityFunction.prototype.typeName = function () { return utils.extractTypeName(this._name); }; +/** + * Should be called to get rpc requests from solidity function + * + * @method request + * @returns {Object} + */ +SolidityFunction.prototype.request = function () { + var args = Array.prototype.slice.call(arguments); + var callback = this.extractCallback(args); + var payload = this.toPayload(args); + var format = this.unpackOutput.bind(this); + + return { + callback: callback, + payload: payload, + format: format + }; +}; + /** * Should be called to execute function * @@ -2719,7 +2848,7 @@ SolidityFunction.prototype.typeName = function () { */ SolidityFunction.prototype.execute = function () { var transaction = !this._constant; - + // send transaction if (transaction) { return this.sendTransaction.apply(this, Array.prototype.slice.call(arguments)); @@ -2737,6 +2866,7 @@ SolidityFunction.prototype.execute = function () { */ SolidityFunction.prototype.attachToContract = function (contract) { var execute = this.execute.bind(this); + execute.request = this.request.bind(this); execute.call = this.call.bind(this); execute.sendTransaction = this.sendTransaction.bind(this); var displayName = this.displayName(); @@ -2749,7 +2879,7 @@ SolidityFunction.prototype.attachToContract = function (contract) { module.exports = SolidityFunction; -},{"../solidity/coder":2,"../utils/utils":8,"../web3":10}],19:[function(require,module,exports){ +},{"../solidity/coder":1,"../utils/sha3":6,"../utils/utils":7,"../web3":9}],19:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2787,7 +2917,7 @@ HttpProvider.prototype.send = function (payload) { var request = new XMLHttpRequest(); request.open('POST', this.host, false); - + try { request.send(JSON.stringify(payload)); } catch(error) { @@ -2800,15 +2930,32 @@ HttpProvider.prototype.send = function (payload) { //if (request.status !== 200) { //return; //} - return JSON.parse(request.responseText); + + var result = request.responseText; + + try { + result = JSON.parse(result); + } catch(e) { + throw errors.InvalidResponse(result); + } + + return result; }; HttpProvider.prototype.sendAsync = function (payload, callback) { var request = new XMLHttpRequest(); request.onreadystatechange = function() { if (request.readyState === 4) { - // TODO: handle the error properly here!!! - callback(null, JSON.parse(request.responseText)); + var result = request.responseText; + var error = null; + + try { + result = JSON.parse(result); + } catch(e) { + error = errors.InvalidResponse(result); + } + + callback(error, result); } }; @@ -2824,7 +2971,7 @@ HttpProvider.prototype.sendAsync = function (payload, callback) { module.exports = HttpProvider; -},{"./errors":13,"xmlhttprequest":6}],20:[function(require,module,exports){ +},{"./errors":13,"xmlhttprequest":4}],20:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -2974,12 +3121,11 @@ Method.prototype.extractCallback = function (args) { if (utils.isFunction(args[args.length - 1])) { return args.pop(); // modify the args array! } - return null; }; /** * Should be called to check if the number of arguments is correct - * + * * @method validateArgs * @param {Array} arguments * @throws {Error} if it is not @@ -2992,7 +3138,7 @@ Method.prototype.validateArgs = function (args) { /** * Should be called to format input args of method - * + * * @method formatInput * @param {Array} * @return {Array} @@ -3020,20 +3166,21 @@ Method.prototype.formatOutput = function (result) { /** * Should attach function to method - * + * * @method attachToObject * @param {Object} * @param {Function} */ Method.prototype.attachToObject = function (obj) { var func = this.send.bind(this); + func.request = this.request.bind(this); func.call = this.call; // that's ugly. filter.js uses it var name = this.name.split('.'); if (name.length > 1) { obj[name[0]] = obj[name[0]] || {}; obj[name[0]][name[1]] = func; } else { - obj[name[0]] = func; + obj[name[0]] = func; } }; @@ -3057,6 +3204,19 @@ Method.prototype.toPayload = function (args) { }; }; +/** + * Should be called to create pure JSONRPC request which can be used in batch request + * + * @method request + * @param {...} params + * @return {Object} jsonrpc request + */ +Method.prototype.request = function () { + var payload = this.toPayload(Array.prototype.slice.call(arguments)); + payload.format = this.formatOutput.bind(this); + return payload; +}; + /** * Should send request to the API * @@ -3069,7 +3229,7 @@ Method.prototype.send = function () { if (payload.callback) { var self = this; return RequestManager.getInstance().sendAsync(payload, function (err, result) { - payload.callback(null, self.formatOutput(result)); + payload.callback(err, self.formatOutput(result)); }); } return this.formatOutput(RequestManager.getInstance().send(payload)); @@ -3078,7 +3238,7 @@ Method.prototype.send = function () { module.exports = Method; -},{"../utils/utils":8,"./errors":13,"./requestmanager":25}],22:[function(require,module,exports){ +},{"../utils/utils":7,"./errors":13,"./requestmanager":25}],22:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -3128,7 +3288,7 @@ module.exports = { }; -},{"../utils/utils":8,"./property":23}],23:[function(require,module,exports){ +},{"../utils/utils":7,"./property":23}],23:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -3164,7 +3324,7 @@ var Property = function (options) { /** * Should be called to format input args of method - * + * * @method formatInput * @param {Array} * @return {Array} @@ -3186,7 +3346,7 @@ Property.prototype.formatOutput = function (result) { /** * Should attach function to method - * + * * @method attachToObject * @param {Object} * @param {Function} @@ -3194,16 +3354,23 @@ Property.prototype.formatOutput = function (result) { Property.prototype.attachToObject = function (obj) { var proto = { get: this.get.bind(this), - set: this.set.bind(this) }; - var name = this.name.split('.'); - if (name.length > 1) { - obj[name[0]] = obj[name[0]] || {}; - Object.defineProperty(obj[name[0]], name[1], proto); - } else { - Object.defineProperty(obj, name[0], proto); + var names = this.name.split('.'); + var name = names[0]; + if (names.length > 1) { + obj[names[0]] = obj[names[0]] || {}; + obj = obj[names[0]]; + name = names[1]; } + + Object.defineProperty(obj, name, proto); + + var toAsyncName = function (prefix, name) { + return prefix + name.charAt(0).toUpperCase() + name.slice(1); + }; + + obj[toAsyncName('get', name)] = this.getAsync.bind(this); }; /** @@ -3219,15 +3386,20 @@ Property.prototype.get = function () { }; /** - * Should be used to set value of the property + * Should be used to asynchrounously get value of property * - * @method set - * @param {Object} new value of the property + * @method getAsync + * @param {Function} */ -Property.prototype.set = function (value) { - return RequestManager.getInstance().send({ - method: this.setter, - params: [this.formatInput(value)] +Property.prototype.getAsync = function (callback) { + var self = this; + RequestManager.getInstance().sendAsync({ + method: this.getter + }, function (err, result) { + if (err) { + return callback(err); + } + callback(err, self.formatOutput(result)); }); }; @@ -3286,7 +3458,7 @@ module.exports = QtSyncProvider; You should have received a copy of the GNU Lesser General Public License along with ethereum.js. If not, see . */ -/** +/** * @file requestmanager.js * @author Jeffrey Wilcke * @author Marek Kotewicz @@ -3368,7 +3540,7 @@ RequestManager.prototype.sendAsync = function (data, callback) { if (err) { return callback(err); } - + if (!Jsonrpc.getInstance().isValidResponse(result)) { return callback(errors.InvalidResponse(result)); } @@ -3377,6 +3549,33 @@ RequestManager.prototype.sendAsync = function (data, callback) { }); }; +/** + * Should be called to asynchronously send batch request + * + * @method sendBatch + * @param {Array} batch data + * @param {Function} callback + */ +RequestManager.prototype.sendBatch = function (data, callback) { + if (!this.provider) { + return callback(errors.InvalidProvider()); + } + + var payload = Jsonrpc.getInstance().toBatchPayload(data); + + this.provider.sendAsync(payload, function (err, results) { + if (err) { + return callback(err); + } + + if (!utils.isArray(results)) { + return callback(errors.InvalidResponse(results)); + } + + callback(err, results); + }); +}; + /** * Should be used to set provider of request manager * @@ -3427,7 +3626,7 @@ RequestManager.prototype.stopPolling = function (pollId) { */ RequestManager.prototype.reset = function () { this.polls.forEach(function (poll) { - poll.uninstall(poll.id); + poll.uninstall(poll.id); }); this.polls = []; @@ -3465,7 +3664,7 @@ RequestManager.prototype.poll = function () { if (error) { return; } - + if (!utils.isArray(results)) { throw errors.InvalidResponse(results); } @@ -3490,7 +3689,7 @@ RequestManager.prototype.poll = function () { module.exports = RequestManager; -},{"../utils/config":7,"../utils/utils":8,"./errors":13,"./jsonrpc":20}],26:[function(require,module,exports){ +},{"../utils/config":5,"../utils/utils":7,"./errors":13,"./jsonrpc":20}],26:[function(require,module,exports){ /* This file is part of ethereum.js. @@ -3517,8 +3716,8 @@ var Method = require('./method'); var formatters = require('./formatters'); var post = new Method({ - name: 'post', - call: 'shh_post', + name: 'post', + call: 'shh_post', params: 1, inputFormatter: [formatters.inputPostFormatter] }); @@ -3588,7 +3787,20 @@ var Method = require('./method'); /// @returns an array of objects describing web3.eth.filter api methods var eth = function () { var newFilterCall = function (args) { - return typeof args[0] === 'string' ? 'eth_newBlockFilter' : 'eth_newFilter'; + var type = args[0]; + + switch(type) { + case 'latest': + args.pop(); + this.params = 0; + return 'eth_newBlockFilter'; + case 'pending': + args.pop(); + this.params = 0; + return 'eth_newPendingTransactionFilter'; + default: + return 'eth_newFilter'; + } }; var newFilter = new Method({ @@ -3665,7 +3877,1382 @@ module.exports = { },{"./method":21}],28:[function(require,module,exports){ -},{}],"bignumber.js":[function(require,module,exports){ +},{}],29:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(); + } + else if (typeof define === "function" && define.amd) { + // AMD + define([], factory); + } + else { + // Global (browser) + root.CryptoJS = factory(); + } +}(this, function () { + + /** + * CryptoJS core components. + */ + var CryptoJS = CryptoJS || (function (Math, undefined) { + /** + * CryptoJS namespace. + */ + var C = {}; + + /** + * Library namespace. + */ + var C_lib = C.lib = {}; + + /** + * Base object for prototypal inheritance. + */ + var Base = C_lib.Base = (function () { + function F() {} + + return { + /** + * Creates a new object that inherits from this object. + * + * @param {Object} overrides Properties to copy into the new object. + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * field: 'value', + * + * method: function () { + * } + * }); + */ + extend: function (overrides) { + // Spawn + F.prototype = this; + var subtype = new F(); + + // Augment + if (overrides) { + subtype.mixIn(overrides); + } + + // Create default initializer + if (!subtype.hasOwnProperty('init')) { + subtype.init = function () { + subtype.$super.init.apply(this, arguments); + }; + } + + // Initializer's prototype is the subtype object + subtype.init.prototype = subtype; + + // Reference supertype + subtype.$super = this; + + return subtype; + }, + + /** + * Extends this object and runs the init method. + * Arguments to create() will be passed to init(). + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var instance = MyType.create(); + */ + create: function () { + var instance = this.extend(); + instance.init.apply(instance, arguments); + + return instance; + }, + + /** + * Initializes a newly created object. + * Override this method to add some logic when your objects are created. + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * init: function () { + * // ... + * } + * }); + */ + init: function () { + }, + + /** + * Copies properties into this object. + * + * @param {Object} properties The properties to mix in. + * + * @example + * + * MyType.mixIn({ + * field: 'value' + * }); + */ + mixIn: function (properties) { + for (var propertyName in properties) { + if (properties.hasOwnProperty(propertyName)) { + this[propertyName] = properties[propertyName]; + } + } + + // IE won't copy toString using the loop above + if (properties.hasOwnProperty('toString')) { + this.toString = properties.toString; + } + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = instance.clone(); + */ + clone: function () { + return this.init.prototype.extend(this); + } + }; + }()); + + /** + * An array of 32-bit words. + * + * @property {Array} words The array of 32-bit words. + * @property {number} sigBytes The number of significant bytes in this word array. + */ + var WordArray = C_lib.WordArray = Base.extend({ + /** + * Initializes a newly created word array. + * + * @param {Array} words (Optional) An array of 32-bit words. + * @param {number} sigBytes (Optional) The number of significant bytes in the words. + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.create(); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6); + */ + init: function (words, sigBytes) { + words = this.words = words || []; + + if (sigBytes != undefined) { + this.sigBytes = sigBytes; + } else { + this.sigBytes = words.length * 4; + } + }, + + /** + * Converts this word array to a string. + * + * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex + * + * @return {string} The stringified word array. + * + * @example + * + * var string = wordArray + ''; + * var string = wordArray.toString(); + * var string = wordArray.toString(CryptoJS.enc.Utf8); + */ + toString: function (encoder) { + return (encoder || Hex).stringify(this); + }, + + /** + * Concatenates a word array to this word array. + * + * @param {WordArray} wordArray The word array to append. + * + * @return {WordArray} This word array. + * + * @example + * + * wordArray1.concat(wordArray2); + */ + concat: function (wordArray) { + // Shortcuts + var thisWords = this.words; + var thatWords = wordArray.words; + var thisSigBytes = this.sigBytes; + var thatSigBytes = wordArray.sigBytes; + + // Clamp excess bits + this.clamp(); + + // Concat + if (thisSigBytes % 4) { + // Copy one byte at a time + for (var i = 0; i < thatSigBytes; i++) { + var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8); + } + } else if (thatWords.length > 0xffff) { + // Copy one word at a time + for (var i = 0; i < thatSigBytes; i += 4) { + thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2]; + } + } else { + // Copy all words at once + thisWords.push.apply(thisWords, thatWords); + } + this.sigBytes += thatSigBytes; + + // Chainable + return this; + }, + + /** + * Removes insignificant bits. + * + * @example + * + * wordArray.clamp(); + */ + clamp: function () { + // Shortcuts + var words = this.words; + var sigBytes = this.sigBytes; + + // Clamp + words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8); + words.length = Math.ceil(sigBytes / 4); + }, + + /** + * Creates a copy of this word array. + * + * @return {WordArray} The clone. + * + * @example + * + * var clone = wordArray.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone.words = this.words.slice(0); + + return clone; + }, + + /** + * Creates a word array filled with random bytes. + * + * @param {number} nBytes The number of random bytes to generate. + * + * @return {WordArray} The random word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.random(16); + */ + random: function (nBytes) { + var words = []; + + var r = (function (m_w) { + var m_w = m_w; + var m_z = 0x3ade68b1; + var mask = 0xffffffff; + + return function () { + m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask; + m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask; + var result = ((m_z << 0x10) + m_w) & mask; + result /= 0x100000000; + result += 0.5; + return result * (Math.random() > .5 ? 1 : -1); + } + }); + + for (var i = 0, rcache; i < nBytes; i += 4) { + var _r = r((rcache || Math.random()) * 0x100000000); + + rcache = _r() * 0x3ade67b7; + words.push((_r() * 0x100000000) | 0); + } + + return new WordArray.init(words, nBytes); + } + }); + + /** + * Encoder namespace. + */ + var C_enc = C.enc = {}; + + /** + * Hex encoding strategy. + */ + var Hex = C_enc.Hex = { + /** + * Converts a word array to a hex string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The hex string. + * + * @static + * + * @example + * + * var hexString = CryptoJS.enc.Hex.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var hexChars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + hexChars.push((bite >>> 4).toString(16)); + hexChars.push((bite & 0x0f).toString(16)); + } + + return hexChars.join(''); + }, + + /** + * Converts a hex string to a word array. + * + * @param {string} hexStr The hex string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Hex.parse(hexString); + */ + parse: function (hexStr) { + // Shortcut + var hexStrLength = hexStr.length; + + // Convert + var words = []; + for (var i = 0; i < hexStrLength; i += 2) { + words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4); + } + + return new WordArray.init(words, hexStrLength / 2); + } + }; + + /** + * Latin1 encoding strategy. + */ + var Latin1 = C_enc.Latin1 = { + /** + * Converts a word array to a Latin1 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The Latin1 string. + * + * @static + * + * @example + * + * var latin1String = CryptoJS.enc.Latin1.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var latin1Chars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + latin1Chars.push(String.fromCharCode(bite)); + } + + return latin1Chars.join(''); + }, + + /** + * Converts a Latin1 string to a word array. + * + * @param {string} latin1Str The Latin1 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Latin1.parse(latin1String); + */ + parse: function (latin1Str) { + // Shortcut + var latin1StrLength = latin1Str.length; + + // Convert + var words = []; + for (var i = 0; i < latin1StrLength; i++) { + words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8); + } + + return new WordArray.init(words, latin1StrLength); + } + }; + + /** + * UTF-8 encoding strategy. + */ + var Utf8 = C_enc.Utf8 = { + /** + * Converts a word array to a UTF-8 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The UTF-8 string. + * + * @static + * + * @example + * + * var utf8String = CryptoJS.enc.Utf8.stringify(wordArray); + */ + stringify: function (wordArray) { + try { + return decodeURIComponent(escape(Latin1.stringify(wordArray))); + } catch (e) { + throw new Error('Malformed UTF-8 data'); + } + }, + + /** + * Converts a UTF-8 string to a word array. + * + * @param {string} utf8Str The UTF-8 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Utf8.parse(utf8String); + */ + parse: function (utf8Str) { + return Latin1.parse(unescape(encodeURIComponent(utf8Str))); + } + }; + + /** + * Abstract buffered block algorithm template. + * + * The property blockSize must be implemented in a concrete subtype. + * + * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0 + */ + var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({ + /** + * Resets this block algorithm's data buffer to its initial state. + * + * @example + * + * bufferedBlockAlgorithm.reset(); + */ + reset: function () { + // Initial values + this._data = new WordArray.init(); + this._nDataBytes = 0; + }, + + /** + * Adds new data to this block algorithm's buffer. + * + * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8. + * + * @example + * + * bufferedBlockAlgorithm._append('data'); + * bufferedBlockAlgorithm._append(wordArray); + */ + _append: function (data) { + // Convert string to WordArray, else assume WordArray already + if (typeof data == 'string') { + data = Utf8.parse(data); + } + + // Append + this._data.concat(data); + this._nDataBytes += data.sigBytes; + }, + + /** + * Processes available data blocks. + * + * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype. + * + * @param {boolean} doFlush Whether all blocks and partial blocks should be processed. + * + * @return {WordArray} The processed data. + * + * @example + * + * var processedData = bufferedBlockAlgorithm._process(); + * var processedData = bufferedBlockAlgorithm._process(!!'flush'); + */ + _process: function (doFlush) { + // Shortcuts + var data = this._data; + var dataWords = data.words; + var dataSigBytes = data.sigBytes; + var blockSize = this.blockSize; + var blockSizeBytes = blockSize * 4; + + // Count blocks ready + var nBlocksReady = dataSigBytes / blockSizeBytes; + if (doFlush) { + // Round up to include partial blocks + nBlocksReady = Math.ceil(nBlocksReady); + } else { + // Round down to include only full blocks, + // less the number of blocks that must remain in the buffer + nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0); + } + + // Count words ready + var nWordsReady = nBlocksReady * blockSize; + + // Count bytes ready + var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes); + + // Process blocks + if (nWordsReady) { + for (var offset = 0; offset < nWordsReady; offset += blockSize) { + // Perform concrete-algorithm logic + this._doProcessBlock(dataWords, offset); + } + + // Remove processed words + var processedWords = dataWords.splice(0, nWordsReady); + data.sigBytes -= nBytesReady; + } + + // Return processed words + return new WordArray.init(processedWords, nBytesReady); + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = bufferedBlockAlgorithm.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone._data = this._data.clone(); + + return clone; + }, + + _minBufferSize: 0 + }); + + /** + * Abstract hasher template. + * + * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits) + */ + var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({ + /** + * Configuration options. + */ + cfg: Base.extend(), + + /** + * Initializes a newly created hasher. + * + * @param {Object} cfg (Optional) The configuration options to use for this hash computation. + * + * @example + * + * var hasher = CryptoJS.algo.SHA256.create(); + */ + init: function (cfg) { + // Apply config defaults + this.cfg = this.cfg.extend(cfg); + + // Set initial values + this.reset(); + }, + + /** + * Resets this hasher to its initial state. + * + * @example + * + * hasher.reset(); + */ + reset: function () { + // Reset data buffer + BufferedBlockAlgorithm.reset.call(this); + + // Perform concrete-hasher logic + this._doReset(); + }, + + /** + * Updates this hasher with a message. + * + * @param {WordArray|string} messageUpdate The message to append. + * + * @return {Hasher} This hasher. + * + * @example + * + * hasher.update('message'); + * hasher.update(wordArray); + */ + update: function (messageUpdate) { + // Append + this._append(messageUpdate); + + // Update the hash + this._process(); + + // Chainable + return this; + }, + + /** + * Finalizes the hash computation. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} messageUpdate (Optional) A final message update. + * + * @return {WordArray} The hash. + * + * @example + * + * var hash = hasher.finalize(); + * var hash = hasher.finalize('message'); + * var hash = hasher.finalize(wordArray); + */ + finalize: function (messageUpdate) { + // Final message update + if (messageUpdate) { + this._append(messageUpdate); + } + + // Perform concrete-hasher logic + var hash = this._doFinalize(); + + return hash; + }, + + blockSize: 512/32, + + /** + * Creates a shortcut function to a hasher's object interface. + * + * @param {Hasher} hasher The hasher to create a helper for. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256); + */ + _createHelper: function (hasher) { + return function (message, cfg) { + return new hasher.init(cfg).finalize(message); + }; + }, + + /** + * Creates a shortcut function to the HMAC's object interface. + * + * @param {Hasher} hasher The hasher to use in this HMAC helper. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256); + */ + _createHmacHelper: function (hasher) { + return function (message, key) { + return new C_algo.HMAC.init(hasher, key).finalize(message); + }; + } + }); + + /** + * Algorithm namespace. + */ + var C_algo = C.algo = {}; + + return C; + }(Math)); + + + return CryptoJS; + +})); +},{}],30:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./x64-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./x64-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_x64 = C.x64; + var X64Word = C_x64.Word; + var C_algo = C.algo; + + // Constants tables + var RHO_OFFSETS = []; + var PI_INDEXES = []; + var ROUND_CONSTANTS = []; + + // Compute Constants + (function () { + // Compute rho offset constants + var x = 1, y = 0; + for (var t = 0; t < 24; t++) { + RHO_OFFSETS[x + 5 * y] = ((t + 1) * (t + 2) / 2) % 64; + + var newX = y % 5; + var newY = (2 * x + 3 * y) % 5; + x = newX; + y = newY; + } + + // Compute pi index constants + for (var x = 0; x < 5; x++) { + for (var y = 0; y < 5; y++) { + PI_INDEXES[x + 5 * y] = y + ((2 * x + 3 * y) % 5) * 5; + } + } + + // Compute round constants + var LFSR = 0x01; + for (var i = 0; i < 24; i++) { + var roundConstantMsw = 0; + var roundConstantLsw = 0; + + for (var j = 0; j < 7; j++) { + if (LFSR & 0x01) { + var bitPosition = (1 << j) - 1; + if (bitPosition < 32) { + roundConstantLsw ^= 1 << bitPosition; + } else /* if (bitPosition >= 32) */ { + roundConstantMsw ^= 1 << (bitPosition - 32); + } + } + + // Compute next LFSR + if (LFSR & 0x80) { + // Primitive polynomial over GF(2): x^8 + x^6 + x^5 + x^4 + 1 + LFSR = (LFSR << 1) ^ 0x71; + } else { + LFSR <<= 1; + } + } + + ROUND_CONSTANTS[i] = X64Word.create(roundConstantMsw, roundConstantLsw); + } + }()); + + // Reusable objects for temporary values + var T = []; + (function () { + for (var i = 0; i < 25; i++) { + T[i] = X64Word.create(); + } + }()); + + /** + * SHA-3 hash algorithm. + */ + var SHA3 = C_algo.SHA3 = Hasher.extend({ + /** + * Configuration options. + * + * @property {number} outputLength + * The desired number of bits in the output hash. + * Only values permitted are: 224, 256, 384, 512. + * Default: 512 + */ + cfg: Hasher.cfg.extend({ + outputLength: 512 + }), + + _doReset: function () { + var state = this._state = [] + for (var i = 0; i < 25; i++) { + state[i] = new X64Word.init(); + } + + this.blockSize = (1600 - 2 * this.cfg.outputLength) / 32; + }, + + _doProcessBlock: function (M, offset) { + // Shortcuts + var state = this._state; + var nBlockSizeLanes = this.blockSize / 2; + + // Absorb + for (var i = 0; i < nBlockSizeLanes; i++) { + // Shortcuts + var M2i = M[offset + 2 * i]; + var M2i1 = M[offset + 2 * i + 1]; + + // Swap endian + M2i = ( + (((M2i << 8) | (M2i >>> 24)) & 0x00ff00ff) | + (((M2i << 24) | (M2i >>> 8)) & 0xff00ff00) + ); + M2i1 = ( + (((M2i1 << 8) | (M2i1 >>> 24)) & 0x00ff00ff) | + (((M2i1 << 24) | (M2i1 >>> 8)) & 0xff00ff00) + ); + + // Absorb message into state + var lane = state[i]; + lane.high ^= M2i1; + lane.low ^= M2i; + } + + // Rounds + for (var round = 0; round < 24; round++) { + // Theta + for (var x = 0; x < 5; x++) { + // Mix column lanes + var tMsw = 0, tLsw = 0; + for (var y = 0; y < 5; y++) { + var lane = state[x + 5 * y]; + tMsw ^= lane.high; + tLsw ^= lane.low; + } + + // Temporary values + var Tx = T[x]; + Tx.high = tMsw; + Tx.low = tLsw; + } + for (var x = 0; x < 5; x++) { + // Shortcuts + var Tx4 = T[(x + 4) % 5]; + var Tx1 = T[(x + 1) % 5]; + var Tx1Msw = Tx1.high; + var Tx1Lsw = Tx1.low; + + // Mix surrounding columns + var tMsw = Tx4.high ^ ((Tx1Msw << 1) | (Tx1Lsw >>> 31)); + var tLsw = Tx4.low ^ ((Tx1Lsw << 1) | (Tx1Msw >>> 31)); + for (var y = 0; y < 5; y++) { + var lane = state[x + 5 * y]; + lane.high ^= tMsw; + lane.low ^= tLsw; + } + } + + // Rho Pi + for (var laneIndex = 1; laneIndex < 25; laneIndex++) { + // Shortcuts + var lane = state[laneIndex]; + var laneMsw = lane.high; + var laneLsw = lane.low; + var rhoOffset = RHO_OFFSETS[laneIndex]; + + // Rotate lanes + if (rhoOffset < 32) { + var tMsw = (laneMsw << rhoOffset) | (laneLsw >>> (32 - rhoOffset)); + var tLsw = (laneLsw << rhoOffset) | (laneMsw >>> (32 - rhoOffset)); + } else /* if (rhoOffset >= 32) */ { + var tMsw = (laneLsw << (rhoOffset - 32)) | (laneMsw >>> (64 - rhoOffset)); + var tLsw = (laneMsw << (rhoOffset - 32)) | (laneLsw >>> (64 - rhoOffset)); + } + + // Transpose lanes + var TPiLane = T[PI_INDEXES[laneIndex]]; + TPiLane.high = tMsw; + TPiLane.low = tLsw; + } + + // Rho pi at x = y = 0 + var T0 = T[0]; + var state0 = state[0]; + T0.high = state0.high; + T0.low = state0.low; + + // Chi + for (var x = 0; x < 5; x++) { + for (var y = 0; y < 5; y++) { + // Shortcuts + var laneIndex = x + 5 * y; + var lane = state[laneIndex]; + var TLane = T[laneIndex]; + var Tx1Lane = T[((x + 1) % 5) + 5 * y]; + var Tx2Lane = T[((x + 2) % 5) + 5 * y]; + + // Mix rows + lane.high = TLane.high ^ (~Tx1Lane.high & Tx2Lane.high); + lane.low = TLane.low ^ (~Tx1Lane.low & Tx2Lane.low); + } + } + + // Iota + var lane = state[0]; + var roundConstant = ROUND_CONSTANTS[round]; + lane.high ^= roundConstant.high; + lane.low ^= roundConstant.low;; + } + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + var blockSizeBits = this.blockSize * 32; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x1 << (24 - nBitsLeft % 32); + dataWords[((Math.ceil((nBitsLeft + 1) / blockSizeBits) * blockSizeBits) >>> 5) - 1] |= 0x80; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Shortcuts + var state = this._state; + var outputLengthBytes = this.cfg.outputLength / 8; + var outputLengthLanes = outputLengthBytes / 8; + + // Squeeze + var hashWords = []; + for (var i = 0; i < outputLengthLanes; i++) { + // Shortcuts + var lane = state[i]; + var laneMsw = lane.high; + var laneLsw = lane.low; + + // Swap endian + laneMsw = ( + (((laneMsw << 8) | (laneMsw >>> 24)) & 0x00ff00ff) | + (((laneMsw << 24) | (laneMsw >>> 8)) & 0xff00ff00) + ); + laneLsw = ( + (((laneLsw << 8) | (laneLsw >>> 24)) & 0x00ff00ff) | + (((laneLsw << 24) | (laneLsw >>> 8)) & 0xff00ff00) + ); + + // Squeeze state to retrieve hash + hashWords.push(laneLsw); + hashWords.push(laneMsw); + } + + // Return final computed hash + return new WordArray.init(hashWords, outputLengthBytes); + }, + + clone: function () { + var clone = Hasher.clone.call(this); + + var state = clone._state = this._state.slice(0); + for (var i = 0; i < 25; i++) { + state[i] = state[i].clone(); + } + + return clone; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA3('message'); + * var hash = CryptoJS.SHA3(wordArray); + */ + C.SHA3 = Hasher._createHelper(SHA3); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA3(message, key); + */ + C.HmacSHA3 = Hasher._createHmacHelper(SHA3); + }(Math)); + + + return CryptoJS.SHA3; + +})); +},{"./core":29,"./x64-core":31}],31:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function (undefined) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var X32WordArray = C_lib.WordArray; + + /** + * x64 namespace. + */ + var C_x64 = C.x64 = {}; + + /** + * A 64-bit word. + */ + var X64Word = C_x64.Word = Base.extend({ + /** + * Initializes a newly created 64-bit word. + * + * @param {number} high The high 32 bits. + * @param {number} low The low 32 bits. + * + * @example + * + * var x64Word = CryptoJS.x64.Word.create(0x00010203, 0x04050607); + */ + init: function (high, low) { + this.high = high; + this.low = low; + } + + /** + * Bitwise NOTs this word. + * + * @return {X64Word} A new x64-Word object after negating. + * + * @example + * + * var negated = x64Word.not(); + */ + // not: function () { + // var high = ~this.high; + // var low = ~this.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise ANDs this word with the passed word. + * + * @param {X64Word} word The x64-Word to AND with this word. + * + * @return {X64Word} A new x64-Word object after ANDing. + * + * @example + * + * var anded = x64Word.and(anotherX64Word); + */ + // and: function (word) { + // var high = this.high & word.high; + // var low = this.low & word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise ORs this word with the passed word. + * + * @param {X64Word} word The x64-Word to OR with this word. + * + * @return {X64Word} A new x64-Word object after ORing. + * + * @example + * + * var ored = x64Word.or(anotherX64Word); + */ + // or: function (word) { + // var high = this.high | word.high; + // var low = this.low | word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise XORs this word with the passed word. + * + * @param {X64Word} word The x64-Word to XOR with this word. + * + * @return {X64Word} A new x64-Word object after XORing. + * + * @example + * + * var xored = x64Word.xor(anotherX64Word); + */ + // xor: function (word) { + // var high = this.high ^ word.high; + // var low = this.low ^ word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Shifts this word n bits to the left. + * + * @param {number} n The number of bits to shift. + * + * @return {X64Word} A new x64-Word object after shifting. + * + * @example + * + * var shifted = x64Word.shiftL(25); + */ + // shiftL: function (n) { + // if (n < 32) { + // var high = (this.high << n) | (this.low >>> (32 - n)); + // var low = this.low << n; + // } else { + // var high = this.low << (n - 32); + // var low = 0; + // } + + // return X64Word.create(high, low); + // }, + + /** + * Shifts this word n bits to the right. + * + * @param {number} n The number of bits to shift. + * + * @return {X64Word} A new x64-Word object after shifting. + * + * @example + * + * var shifted = x64Word.shiftR(7); + */ + // shiftR: function (n) { + // if (n < 32) { + // var low = (this.low >>> n) | (this.high << (32 - n)); + // var high = this.high >>> n; + // } else { + // var low = this.high >>> (n - 32); + // var high = 0; + // } + + // return X64Word.create(high, low); + // }, + + /** + * Rotates this word n bits to the left. + * + * @param {number} n The number of bits to rotate. + * + * @return {X64Word} A new x64-Word object after rotating. + * + * @example + * + * var rotated = x64Word.rotL(25); + */ + // rotL: function (n) { + // return this.shiftL(n).or(this.shiftR(64 - n)); + // }, + + /** + * Rotates this word n bits to the right. + * + * @param {number} n The number of bits to rotate. + * + * @return {X64Word} A new x64-Word object after rotating. + * + * @example + * + * var rotated = x64Word.rotR(7); + */ + // rotR: function (n) { + // return this.shiftR(n).or(this.shiftL(64 - n)); + // }, + + /** + * Adds this word with the passed word. + * + * @param {X64Word} word The x64-Word to add with this word. + * + * @return {X64Word} A new x64-Word object after adding. + * + * @example + * + * var added = x64Word.add(anotherX64Word); + */ + // add: function (word) { + // var low = (this.low + word.low) | 0; + // var carry = (low >>> 0) < (this.low >>> 0) ? 1 : 0; + // var high = (this.high + word.high + carry) | 0; + + // return X64Word.create(high, low); + // } + }); + + /** + * An array of 64-bit words. + * + * @property {Array} words The array of CryptoJS.x64.Word objects. + * @property {number} sigBytes The number of significant bytes in this word array. + */ + var X64WordArray = C_x64.WordArray = Base.extend({ + /** + * Initializes a newly created word array. + * + * @param {Array} words (Optional) An array of CryptoJS.x64.Word objects. + * @param {number} sigBytes (Optional) The number of significant bytes in the words. + * + * @example + * + * var wordArray = CryptoJS.x64.WordArray.create(); + * + * var wordArray = CryptoJS.x64.WordArray.create([ + * CryptoJS.x64.Word.create(0x00010203, 0x04050607), + * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) + * ]); + * + * var wordArray = CryptoJS.x64.WordArray.create([ + * CryptoJS.x64.Word.create(0x00010203, 0x04050607), + * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) + * ], 10); + */ + init: function (words, sigBytes) { + words = this.words = words || []; + + if (sigBytes != undefined) { + this.sigBytes = sigBytes; + } else { + this.sigBytes = words.length * 8; + } + }, + + /** + * Converts this 64-bit word array to a 32-bit word array. + * + * @return {CryptoJS.lib.WordArray} This word array's data as a 32-bit word array. + * + * @example + * + * var x32WordArray = x64WordArray.toX32(); + */ + toX32: function () { + // Shortcuts + var x64Words = this.words; + var x64WordsLength = x64Words.length; + + // Convert + var x32Words = []; + for (var i = 0; i < x64WordsLength; i++) { + var x64Word = x64Words[i]; + x32Words.push(x64Word.high); + x32Words.push(x64Word.low); + } + + return X32WordArray.create(x32Words, this.sigBytes); + }, + + /** + * Creates a copy of this word array. + * + * @return {X64WordArray} The clone. + * + * @example + * + * var clone = x64WordArray.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + + // Clone "words" array + var words = clone.words = this.words.slice(0); + + // Clone each X64Word object + var wordsLength = words.length; + for (var i = 0; i < wordsLength; i++) { + words[i] = words[i].clone(); + } + + return clone; + } + }); + }()); + + + return CryptoJS; + +})); +},{"./core":29}],"bignumber.js":[function(require,module,exports){ 'use strict'; module.exports = BigNumber; // jshint ignore:line @@ -3676,7 +5263,6 @@ var web3 = require('./lib/web3'); web3.providers.HttpProvider = require('./lib/web3/httpprovider'); web3.providers.QtSyncProvider = require('./lib/web3/qtsync'); web3.eth.contract = require('./lib/web3/contract'); -web3.abi = require('./lib/solidity/abi'); // dont override global variable if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') { @@ -3686,7 +5272,7 @@ if (typeof window !== 'undefined' && typeof window.web3 === 'undefined') { module.exports = web3; -},{"./lib/solidity/abi":1,"./lib/web3":10,"./lib/web3/contract":11,"./lib/web3/httpprovider":19,"./lib/web3/qtsync":24}]},{},["web3"]) +},{"./lib/web3":9,"./lib/web3/contract":11,"./lib/web3/httpprovider":19,"./lib/web3/qtsync":24}]},{},["web3"]) //# sourceMappingURL=web3-light.js.map diff --git a/miner/worker.go b/miner/worker.go index 5fa5d3777..5e4ff7510 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -270,6 +270,7 @@ func (self *worker) makeCurrent() { } block.Header().Extra = self.extra + // when 08 is processed ancestors contain 07 (quick block) current := env(block, self.eth) for _, ancestor := range self.chain.GetAncestors(block, 7) { for _, uncle := range ancestor.Uncles() { diff --git a/rpc/api.go b/rpc/api.go index 0c1409d71..b6f6ac3f9 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -158,16 +158,16 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err v := api.xethAtStateNum(args.BlockNumber).CodeAtBytes(args.Address) *reply = newHexData(v) - case "eth_sign": - args := new(NewSigArgs) - if err := json.Unmarshal(req.Params, &args); err != nil { - return err - } - v, err := api.xeth().Sign(args.From, args.Data, false) - if err != nil { - return err - } - *reply = v + // case "eth_sign": + // args := new(NewSigArgs) + // if err := json.Unmarshal(req.Params, &args); err != nil { + // return err + // } + // v, err := api.xeth().Sign(args.From, args.Data, false) + // if err != nil { + // return err + // } + // *reply = v case "eth_sendTransaction", "eth_transact": args := new(NewTxArgs) @@ -347,7 +347,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err solc, _ := api.xeth().Solc() if solc == nil { - return NewNotImplementedError(req.Method) + return NewNotAvailableError(req.Method, "solc (solidity compiler) not found") } args := new(SourceArgs) @@ -355,12 +355,11 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err return err } - contract, err := solc.Compile(args.Source) + contracts, err := solc.Compile(args.Source) if err != nil { return err } - contract.Code = newHexData(contract.Code).String() - *reply = contract + *reply = contracts case "eth_newFilter": args := new(BlockFilterArgs) diff --git a/rpc/api_test.go b/rpc/api_test.go index b49e27bd1..d1dcad266 100644 --- a/rpc/api_test.go +++ b/rpc/api_test.go @@ -2,14 +2,11 @@ package rpc import ( "encoding/json" - // "sync" - "testing" - // "time" - // "fmt" - "io/ioutil" "strconv" + "testing" "github.com/ethereum/go-ethereum/common/compiler" + "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/xeth" ) @@ -30,12 +27,15 @@ func TestWeb3Sha3(t *testing.T) { } } +const solcVersion = "0.9.23" + func TestCompileSolidity(t *testing.T) { - t.Skip() solc, err := compiler.New("") if solc == nil { - t.Skip("no solidity compiler") + t.Skip("no solc found: skip") + } else if solc.Version() != solcVersion { + t.Logf("WARNING: solc different version found (%v, test written for %v, may need to update)", solc.Version(), solcVersion) } source := `contract test {\n` + " /// @notice Will multiply `a` by 7." + `\n` + @@ -46,16 +46,16 @@ func TestCompileSolidity(t *testing.T) { jsonstr := `{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["` + source + `"],"id":64}` - //expCode := "605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056" + expCode := "0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056" expAbiDefinition := `[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]` expUserDoc := `{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}}` expDeveloperDoc := `{"methods":{}}` - expCompilerVersion := `0.9.13` + expCompilerVersion := solc.Version() expLanguage := "Solidity" expLanguageVersion := "0" expSource := source - api := NewEthereumApi(&xeth.XEth{}) + api := NewEthereumApi(xeth.NewTest(ð.Ethereum{}, nil)) var req RpcRequest json.Unmarshal([]byte(jsonstr), &req) @@ -70,26 +70,34 @@ func TestCompileSolidity(t *testing.T) { t.Errorf("expected no error, got %v", err) } - var contract = compiler.Contract{} - err = json.Unmarshal(respjson, &contract) + var contracts = make(map[string]*compiler.Contract) + err = json.Unmarshal(respjson, &contracts) if err != nil { t.Errorf("expected no error, got %v", err) } - /* - if contract.Code != expCode { - t.Errorf("Expected %s got %s", expCode, contract.Code) - } - */ + if len(contracts) != 1 { + t.Errorf("expected one contract, got %v", len(contracts)) + } + + contract := contracts["test"] + + if contract.Code != expCode { + t.Errorf("Expected \n%s got \n%s", expCode, contract.Code) + } + if strconv.Quote(contract.Info.Source) != `"`+expSource+`"` { t.Errorf("Expected \n'%s' got \n'%s'", expSource, strconv.Quote(contract.Info.Source)) } + if contract.Info.Language != expLanguage { t.Errorf("Expected %s got %s", expLanguage, contract.Info.Language) } + if contract.Info.LanguageVersion != expLanguageVersion { t.Errorf("Expected %s got %s", expLanguageVersion, contract.Info.LanguageVersion) } + if contract.Info.CompilerVersion != expCompilerVersion { t.Errorf("Expected %s got %s", expCompilerVersion, contract.Info.CompilerVersion) } @@ -112,8 +120,6 @@ func TestCompileSolidity(t *testing.T) { if string(abidef) != expAbiDefinition { t.Errorf("Expected \n'%s' got \n'%s'", expAbiDefinition, string(abidef)) } - ioutil.WriteFile("/tmp/abidef", []byte(string(abidef)), 0700) - ioutil.WriteFile("/tmp/expabidef", []byte(expAbiDefinition), 0700) if string(userdoc) != expUserDoc { t.Errorf("Expected \n'%s' got \n'%s'", expUserDoc, string(userdoc)) diff --git a/rpc/args.go b/rpc/args.go index 686872a59..27824f12c 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -166,45 +166,45 @@ type NewTxArgs struct { BlockNumber int64 } -type NewSigArgs struct { - From string - Data string -} +// type NewSigArgs struct { +// From string +// Data string +// } -func (args *NewSigArgs) UnmarshalJSON(b []byte) (err error) { - var obj []json.RawMessage - var ext struct { - From string - Data string - } +// func (args *NewSigArgs) UnmarshalJSON(b []byte) (err error) { +// var obj []json.RawMessage +// var ext struct { +// From string +// Data string +// } - // Decode byte slice to array of RawMessages - if err := json.Unmarshal(b, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } +// // Decode byte slice to array of RawMessages +// if err := json.Unmarshal(b, &obj); err != nil { +// return NewDecodeParamError(err.Error()) +// } - // Check for sufficient params - if len(obj) < 1 { - return NewInsufficientParamsError(len(obj), 1) - } +// // Check for sufficient params +// if len(obj) < 1 { +// return NewInsufficientParamsError(len(obj), 1) +// } - // Decode 0th RawMessage to temporary struct - if err := json.Unmarshal(obj[0], &ext); err != nil { - return NewDecodeParamError(err.Error()) - } +// // Decode 0th RawMessage to temporary struct +// if err := json.Unmarshal(obj[0], &ext); err != nil { +// return NewDecodeParamError(err.Error()) +// } - if len(ext.From) == 0 { - return NewValidationError("from", "is required") - } +// if len(ext.From) == 0 { +// return NewValidationError("from", "is required") +// } - if len(ext.Data) == 0 { - return NewValidationError("data", "is required") - } +// if len(ext.Data) == 0 { +// return NewValidationError("data", "is required") +// } - args.From = ext.From - args.Data = ext.Data - return nil -} +// args.From = ext.From +// args.Data = ext.Data +// return nil +// } func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { var obj []json.RawMessage diff --git a/xeth/xeth.go b/xeth/xeth.go index 7de3e31be..4925fe635 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -66,12 +66,16 @@ type XEth struct { // regmut sync.Mutex // register map[string][]*interface{} // TODO improve return type - solcPath string - solc *compiler.Solidity - agent *miner.RemoteAgent } +func NewTest(eth *eth.Ethereum, frontend Frontend) *XEth { + return &XEth{ + backend: eth, + frontend: frontend, + } +} + // New creates an XEth that uses the given frontend. // If a nil Frontend is provided, a default frontend which // confirms all transactions will be used. @@ -379,17 +383,12 @@ func (self *XEth) Accounts() []string { // accessor for solidity compiler. // memoized if available, retried on-demand if not func (self *XEth) Solc() (*compiler.Solidity, error) { - var err error - if self.solc == nil { - self.solc, err = compiler.New(self.solcPath) - } - return self.solc, err + return self.backend.Solc() } // set in js console via admin interface or wrapper from cli flags func (self *XEth) SetSolc(solcPath string) (*compiler.Solidity, error) { - self.solcPath = solcPath - self.solc = nil + self.backend.SetSolc(solcPath) return self.Solc() }