From 009b2216921b15962f2612687c1460a8342d49d6 Mon Sep 17 00:00:00 2001 From: zelig Date: Wed, 22 Apr 2015 23:11:11 +0100 Subject: solidity compiler and contract metadocs integration * common/compiler: solidity compiler + tests * rpc: eth_compilers, eth_compileSolidity + tests * fix natspec test using keystore API, notice exp dynamically changes addr, cleanup * resolver implements registrars and needs to create reg contract (temp) * xeth: solidity compiler. expose getter Solc() and paths setter SetSolc(solcPath) * ethereumApi: implement compiler related RPC calls using XEth - json struct tests * admin: make use of XEth.SetSolc to allow runtime setting of compiler paths * cli: command line flags solc to set custom solc bin path * js admin api with new features debug and contractInfo modules * wiki is the doc https://github.com/ethereum/go-ethereum/wiki/Contracts-and-Transactions --- common/natspec/natspec.go | 134 ++++++++------- common/natspec/natspec_e2e_test.go | 328 +++++++++++++------------------------ common/natspec/natspec_test.go | 89 +++++----- 3 files changed, 219 insertions(+), 332 deletions(-) (limited to 'common/natspec') diff --git a/common/natspec/natspec.go b/common/natspec/natspec.go index 38e7c1a9d..7e5f053c7 100644 --- a/common/natspec/natspec.go +++ b/common/natspec/natspec.go @@ -17,118 +17,119 @@ import ( type abi2method map[[8]byte]*method type NatSpec struct { - jsvm *otto.Otto - userDocJson, abiDocJson []byte - userDoc userDoc - tx, data string - // abiDoc abiDoc -} - -func getFallbackNotice(comment, tx string) string { - - return "About to submit transaction (" + comment + "): " + tx - + jsvm *otto.Otto + abiDocJson []byte + userDoc userDoc + tx, data string } +// main entry point for to get natspec notice for a transaction +// the implementation is frontend friendly in that it always gives back +// a notice that is safe to display +// :FIXME: the second return value is an error, which can be used to fine-tune bahaviour func GetNotice(xeth *xeth.XEth, tx string, http *docserver.DocServer) (notice string) { - ns, err := New(xeth, tx, http) if err != nil { if ns == nil { - return getFallbackNotice("no NatSpec info found for contract", tx) + return getFallbackNotice(fmt.Sprintf("no NatSpec info found for contract: %v", err), tx) } else { - return getFallbackNotice("invalid NatSpec info", tx) + return getFallbackNotice(fmt.Sprintf("invalid NatSpec info: %v", err), tx) } } - notice, err2 := ns.Notice() - - if err2 != nil { - return getFallbackNotice("NatSpec notice error \""+err2.Error()+"\"", tx) + notice, err = ns.Notice() + if err != nil { + return getFallbackNotice(fmt.Sprintf("NatSpec notice error: %v", err), tx) } return +} +func getFallbackNotice(comment, tx string) string { + return fmt.Sprintf("About to submit transaction (%s): %s", comment, tx) } -func New(xeth *xeth.XEth, tx string, http *docserver.DocServer) (self *NatSpec, err error) { +type transaction struct { + To string `json:"to"` + Data string `json:"data"` +} - // extract contract address from tx +type jsonTx struct { + Params []transaction `json:"params"` +} + +type contractInfo struct { + Source string `json:"source"` + Language string `json:"language"` + Version string `json:"compilerVersion"` + AbiDefinition json.RawMessage `json:"abiDefinition"` + UserDoc userDoc `json:"userDoc"` + DeveloperDoc json.RawMessage `json:"developerDoc"` +} - var obj map[string]json.RawMessage - err = json.Unmarshal([]byte(tx), &obj) +func New(xeth *xeth.XEth, jsontx string, http *docserver.DocServer) (self *NatSpec, err error) { + + // extract contract address from tx + var tx jsonTx + err = json.Unmarshal([]byte(jsontx), &tx) if err != nil { return } - var tmp []map[string]string - err = json.Unmarshal(obj["params"], &tmp) + t := tx.Params[0] + contractAddress := t.To + + content, err := FetchDocsForContract(contractAddress, xeth, http) if err != nil { return } - contractAddress := tmp[0]["to"] + self, err = NewWithDocs(content, jsontx, t.Data) + return +} + +// also called by admin.contractInfo.get +func FetchDocsForContract(contractAddress string, xeth *xeth.XEth, http *docserver.DocServer) (content []byte, err error) { // retrieve contract hash from state - if !xeth.IsContract(contractAddress) { - err = fmt.Errorf("NatSpec error: contract not found") - return - } codehex := xeth.CodeAt(contractAddress) - codeHash := common.BytesToHash(crypto.Sha3(common.Hex2Bytes(codehex[2:]))) - // parse out host/domain + codeb := xeth.CodeAtBytes(contractAddress) + if codehex == "0x" { + err = fmt.Errorf("contract (%v) not found", contractAddress) + return + } + codehash := common.BytesToHash(crypto.Sha3(codeb)) // set up nameresolver with natspecreg + urlhint contract addresses - res := resolver.New( - xeth, - resolver.URLHintContractAddress, - resolver.HashRegContractAddress, - ) + res := resolver.New(xeth) // resolve host via HashReg/UrlHint Resolver - uri, hash, err := res.KeyToUrl(codeHash) + uri, hash, err := res.KeyToUrl(codehash) if err != nil { return } // get content via http client and authenticate content using hash - content, err := http.GetAuthContent(uri, hash) - if err != nil { - return - } - - // get abi, userdoc - var obj2 map[string]json.RawMessage - err = json.Unmarshal(content, &obj2) + content, err = http.GetAuthContent(uri, hash) if err != nil { return } - abi := []byte(obj2["abi"]) - userdoc := []byte(obj2["userdoc"]) - - self, err = NewWithDocs(abi, userdoc, tx) return } -func NewWithDocs(abiDocJson, userDocJson []byte, tx string) (self *NatSpec, err error) { +func NewWithDocs(infoDoc []byte, tx string, data string) (self *NatSpec, err error) { - var obj map[string]json.RawMessage - err = json.Unmarshal([]byte(tx), &obj) + var contract contractInfo + err = json.Unmarshal(infoDoc, &contract) if err != nil { return } - var tmp []map[string]string - err = json.Unmarshal(obj["params"], &tmp) - if err != nil { - return - } - data := tmp[0]["data"] self = &NatSpec{ - jsvm: otto.New(), - abiDocJson: abiDocJson, - userDocJson: userDocJson, - tx: tx, - data: data, + jsvm: otto.New(), + abiDocJson: []byte(contract.AbiDefinition), + userDoc: contract.UserDoc, + tx: tx, + data: data, } // load and require natspec js (but it is meant to be protected environment) @@ -137,13 +138,6 @@ func NewWithDocs(abiDocJson, userDocJson []byte, tx string) (self *NatSpec, err return } _, err = self.jsvm.Run("var natspec = require('natspec');") - if err != nil { - return - } - - err = json.Unmarshal(userDocJson, &self.userDoc) - // err = parseAbiJson(abiDocJson, &self.abiDoc) - return } diff --git a/common/natspec/natspec_e2e_test.go b/common/natspec/natspec_e2e_test.go index e54b9ee96..f9b0c1dcc 100644 --- a/common/natspec/natspec_e2e_test.go +++ b/common/natspec/natspec_e2e_test.go @@ -1,8 +1,8 @@ package natspec import ( + "fmt" "io/ioutil" - "math/big" "os" "testing" @@ -14,39 +14,26 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/rpc" xe "github.com/ethereum/go-ethereum/xeth" ) -type testFrontend struct { - t *testing.T - ethereum *eth.Ethereum - xeth *xe.XEth - api *rpc.EthereumApi - coinbase string - stateDb *state.StateDB - txc uint64 - lastConfirm string - makeNatSpec bool -} - const ( - testAccount = "e273f01c99144c438695e10f24926dc1f9fbf62d" - testBalance = "1000000000000" -) + testBalance = "10000000000000000000" -const testFileName = "long_file_name_for_testing_registration_of_URLs_longer_than_32_bytes.content" + testFileName = "long_file_name_for_testing_registration_of_URLs_longer_than_32_bytes.content" -const testNotice = "Register key `utils.toHex(_key)` <- content `utils.toHex(_content)`" -const testExpNotice = "Register key 0xadd1a7d961cff0242089674ec2ef6fca671ab15e1fe80e38859fc815b98d88ab <- content 0xc00d5bcc872e17813df6ec5c646bb281a6e2d3b454c2c400c78192adf3344af9" -const testExpNotice2 = `About to submit transaction (NatSpec notice error "abi key does not match any method"): {"id":6,"jsonrpc":"2.0","method":"eth_transact","params":[{"from":"0xe273f01c99144c438695e10f24926dc1f9fbf62d","to":"0xb737b91f8e95cf756766fc7c62c9a8ff58470381","value":"100000000000","gas":"100000","gasPrice":"100000","data":"0x31e12c20"}]}` -const testExpNotice3 = `About to submit transaction (no NatSpec info found for contract): {"id":6,"jsonrpc":"2.0","method":"eth_transact","params":[{"from":"0xe273f01c99144c438695e10f24926dc1f9fbf62d","to":"0x8b839ad85686967a4f418eccc81962eaee314ac3","value":"100000000000","gas":"100000","gasPrice":"100000","data":"0x300a3bbfc00d5bcc872e17813df6ec5c646bb281a6e2d3b454c2c400c78192adf3344af900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000"}]}` + testNotice = "Register key `utils.toHex(_key)` <- content `utils.toHex(_content)`" -const testUserDoc = ` + testExpNotice = "Register key 0xadd1a7d961cff0242089674ec2ef6fca671ab15e1fe80e38859fc815b98d88ab <- content 0xb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7" + + testExpNotice2 = `About to submit transaction (NatSpec notice error: abi key does not match any method): {"params":[{"to":"%s","data": "0x31e12c20"}]}` + + testExpNotice3 = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x1392c62d05b2d149e22a339c531157ae06b44d39a674cce500064b12b9aeb019'): {"params":[{"to":"%s","data": "0x300a3bbfb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066696c653a2f2f2f746573742e636f6e74656e74"}]}` +) + +const ( + testUserDoc = ` { - "source": "...", - "language": "Solidity", - "languageVersion": 1, "methods": { "register(uint256,uint256)": { "notice": "` + testNotice + `" @@ -60,8 +47,7 @@ const testUserDoc = ` ] } ` - -const testABI = ` + testAbiDefinition = ` [{ "name": "register", "constant": false, @@ -77,70 +63,81 @@ const testABI = ` }] ` -const testDocs = ` + testContractInfo = ` { - "userdoc": ` + testUserDoc + `, - "abi": ` + testABI + ` + "userDoc": ` + testUserDoc + `, + "abiDefinition": ` + testAbiDefinition + ` } ` +) + +type testFrontend struct { + t *testing.T + // resolver *resolver.Resolver + ethereum *eth.Ethereum + xeth *xe.XEth + coinbase common.Address + stateDb *state.StateDB + txc uint64 + lastConfirm string + wantNatSpec bool +} -func (f *testFrontend) UnlockAccount(acc []byte) bool { - f.t.Logf("Unlocking account %v\n", common.Bytes2Hex(acc)) - f.ethereum.AccountManager().Unlock(acc, "password") +func (self *testFrontend) UnlockAccount(acc []byte) bool { + self.ethereum.AccountManager().Unlock(acc, "password") return true } -func (f *testFrontend) ConfirmTransaction(tx string) bool { - //f.t.Logf("ConfirmTransaction called tx = %v", tx) - if f.makeNatSpec { +func (self *testFrontend) ConfirmTransaction(tx string) bool { + if self.wantNatSpec { ds, err := docserver.New("/tmp/") if err != nil { - f.t.Errorf("Error creating DocServer: %v", err) + self.t.Errorf("Error creating DocServer: %v", err) } - f.lastConfirm = GetNotice(f.xeth, tx, ds) + self.lastConfirm = GetNotice(self.xeth, tx, ds) } return true } -var port = 30300 - func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) { + os.RemoveAll("/tmp/eth-natspec/") - err = os.MkdirAll("/tmp/eth-natspec/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/", os.ModePerm) + + err = os.MkdirAll("/tmp/eth-natspec/keys", os.ModePerm) if err != nil { - t.Errorf("%v", err) - return + panic(err) } - err = os.MkdirAll("/tmp/eth-natspec/data", os.ModePerm) + + // create a testAddress + ks := crypto.NewKeyStorePassphrase("/tmp/eth-natspec/keys") + am := accounts.NewManager(ks) + testAccount, err := am.NewAccount("password") if err != nil { - t.Errorf("%v", err) - return + panic(err) } - ks := crypto.NewKeyStorePlain("/tmp/eth-natspec/keys") - ioutil.WriteFile("/tmp/eth-natspec/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/e273f01c99144c438695e10f24926dc1f9fbf62d", - []byte(`{"Id":"RhRXD+fNRKS4jx+7ZfEsNA==","Address":"4nPwHJkUTEOGleEPJJJtwfn79i0=","PrivateKey":"h4ACVpe74uIvi5Cg/2tX/Yrm2xdr3J7QoMbMtNX2CNc="}`), os.ModePerm) + testAddress := common.Bytes2Hex(testAccount.Address) + + // set up mock genesis with balance on the testAddress + core.GenesisData = []byte(`{ + "` + testAddress + `": {"balance": "` + testBalance + `"} + }`) - port++ + // only use minimalistic stack with no networking ethereum, err = eth.New(ð.Config{ DataDir: "/tmp/eth-natspec", - AccountManager: accounts.NewManager(ks), - Name: "test", + AccountManager: am, + MaxPeers: 0, }) if err != nil { - t.Errorf("%v", err) - return + panic(err) } return } func testInit(t *testing.T) (self *testFrontend) { - - core.GenesisData = []byte(`{ - "` + testAccount + `": {"balance": "` + testBalance + `"} - }`) - + // initialise and start minimal ethereum stack ethereum, err := testEth(t) if err != nil { t.Errorf("error creating ethereum: %v", err) @@ -152,190 +149,95 @@ func testInit(t *testing.T) (self *testFrontend) { return } + // mock frontend self = &testFrontend{t: t, ethereum: ethereum} self.xeth = xe.New(ethereum, self) - self.api = rpc.NewEthereumApi(self.xeth) - addr := self.xeth.Coinbase() + addr, _ := ethereum.Etherbase() self.coinbase = addr - if addr != "0x"+testAccount { - t.Errorf("CoinBase %v does not match TestAccount 0x%v", addr, testAccount) - } - t.Logf("CoinBase is %v", addr) - - balance := self.xeth.BalanceAt(testAccount) - /*if balance != core.TestBalance { - t.Errorf("Balance %v does not match TestBalance %v", balance, core.TestBalance) - }*/ - t.Logf("Balance is %v", balance) - self.stateDb = self.ethereum.ChainManager().State().Copy() - return - -} - -func (self *testFrontend) insertTx(addr, contract, fnsig string, args []string) { - - //cb := common.HexToAddress(self.coinbase) - //coinbase := self.ethereum.ChainManager().State().GetStateObject(cb) + // initialise the registry contracts + // self.resolver.CreateContracts(addr) + resolver.New(self.xeth).CreateContracts(addr) + self.applyTxs() + // t.Logf("HashReg contract registered at %v", resolver.HashRegContractAddress) + // t.Logf("URLHint contract registered at %v", resolver.UrlHintContractAddress) - hash := common.Bytes2Hex(crypto.Sha3([]byte(fnsig))) - data := "0x" + hash[0:8] - for _, arg := range args { - data = data + common.Bytes2Hex(common.Hex2BytesFixed(arg, 32)) - } - self.t.Logf("Tx data: %v", data) - - jsontx := ` -[{ - "from": "` + addr + `", - "to": "` + contract + `", - "value": "100000000000", - "gas": "100000", - "gasPrice": "100000", - "data": "` + data + `" -}] -` - req := &rpc.RpcRequest{ - Jsonrpc: "2.0", - Method: "eth_transact", - Params: []byte(jsontx), - Id: 6, - } - - var reply interface{} - err0 := self.api.GetRequestReply(req, &reply) - if err0 != nil { - self.t.Errorf("GetRequestReply error: %v", err0) - } - - //self.xeth.Transact(addr, contract, "100000000000", "100000", "100000", data) + return } +// this is needed for transaction to be applied to the state in testing +// the heavy lifing is done in XEth.ApplyTestTxs +// this is fragile, +// and does process leaking since xeth loops cannot quit safely +// should be replaced by proper mining with testDAG for easy full integration tests func (self *testFrontend) applyTxs() { - - cb := common.HexToAddress(self.coinbase) - block := self.ethereum.ChainManager().NewBlock(cb) - coinbase := self.stateDb.GetStateObject(cb) - coinbase.SetGasPool(big.NewInt(10000000)) - txs := self.ethereum.TxPool().GetQueuedTransactions() - - for i := 0; i < len(txs); i++ { - for _, tx := range txs { - //self.t.Logf("%v %v %v", i, tx.Nonce(), self.txc) - if tx.Nonce() == self.txc { - _, gas, err := core.ApplyMessage(core.NewEnv(self.stateDb, self.ethereum.ChainManager(), tx, block), tx, coinbase) - //self.ethereum.TxPool().RemoveSet([]*types.Transaction{tx}) - self.t.Logf("ApplyMessage: gas %v err %v", gas, err) - self.txc++ - } - } - } - - //self.ethereum.TxPool().RemoveSet(txs) - self.xeth = self.xeth.WithState(self.stateDb) - -} - -func (self *testFrontend) registerURL(hash common.Hash, url string) { - hashHex := common.Bytes2Hex(hash[:]) - urlBytes := []byte(url) - var bb bool = true - var cnt byte - for bb { - bb = len(urlBytes) > 0 - urlb := urlBytes - if len(urlb) > 32 { - urlb = urlb[:32] - } - urlHex := common.Bytes2Hex(urlb) - self.insertTx(self.coinbase, resolver.URLHintContractAddress, "register(uint256,uint8,uint256)", []string{hashHex, common.Bytes2Hex([]byte{cnt}), urlHex}) - if len(urlBytes) > 32 { - urlBytes = urlBytes[32:] - } else { - urlBytes = nil - } - cnt++ - } -} - -func (self *testFrontend) setOwner() { - - self.insertTx(self.coinbase, resolver.HashRegContractAddress, "setowner()", []string{}) - - /*owner := self.xeth.StorageAt("0x"+resolver.HashRegContractAddress, "0x0000000000000000000000000000000000000000000000000000000000000000") - self.t.Logf("owner = %v", owner) - if owner != self.coinbase { - self.t.Errorf("setowner() unsuccessful, owner != coinbase") - }*/ -} - -func (self *testFrontend) registerNatSpec(codehash, dochash common.Hash) { - - codeHex := common.Bytes2Hex(codehash[:]) - docHex := common.Bytes2Hex(dochash[:]) - self.insertTx(self.coinbase, resolver.HashRegContractAddress, "register(uint256,uint256)", []string{codeHex, docHex}) -} - -func (self *testFrontend) testResolver() *resolver.Resolver { - return resolver.New(self.xeth, resolver.URLHintContractAddress, resolver.HashRegContractAddress) + self.txc, self.xeth = self.xeth.ApplyTestTxs(self.stateDb, self.coinbase, self.txc) + return } +// end to end test func TestNatspecE2E(t *testing.T) { - t.Skip() + // t.Skip() tf := testInit(t) defer tf.ethereum.Stop() - resolver.CreateContracts(tf.xeth, testAccount) - t.Logf("URLHint contract registered at %v", resolver.URLHintContractAddress) - t.Logf("HashReg contract registered at %v", resolver.HashRegContractAddress) - tf.applyTxs() - - ioutil.WriteFile("/tmp/"+testFileName, []byte(testDocs), os.ModePerm) - dochash := common.BytesToHash(crypto.Sha3([]byte(testDocs))) + // create a contractInfo file (mock cloud-deployed contract metadocs) + // incidentally this is the info for the registry contract itself + ioutil.WriteFile("/tmp/"+testFileName, []byte(testContractInfo), os.ModePerm) + dochash := common.BytesToHash(crypto.Sha3([]byte(testContractInfo))) - codehex := tf.xeth.CodeAt(resolver.HashRegContractAddress) - codehash := common.BytesToHash(crypto.Sha3(common.Hex2Bytes(codehex[2:]))) + // take the codehash for the contract we wanna test + // codehex := tf.xeth.CodeAt(resolver.HashRegContractAddress) + codeb := tf.xeth.CodeAtBytes(resolver.HashRegContractAddress) + codehash := common.BytesToHash(crypto.Sha3(codeb)) - tf.setOwner() - tf.registerNatSpec(codehash, dochash) - tf.registerURL(dochash, "file:///"+testFileName) - tf.applyTxs() - - chash, err := tf.testResolver().KeyToContentHash(codehash) + // use resolver to register codehash->dochash->url + registry := resolver.New(tf.xeth) + _, err := registry.Register(tf.coinbase, codehash, dochash, "file:///"+testFileName) if err != nil { - t.Errorf("Can't find content hash") - } - t.Logf("chash = %x err = %v", chash, err) - url, err2 := tf.testResolver().ContentHashToUrl(dochash) - if err2 != nil { - t.Errorf("Can't find URL hint") + t.Errorf("error registering: %v", err) } - t.Logf("url = %v err = %v", url, err2) + // apply txs to the state + tf.applyTxs() // NatSpec info for register method of HashReg contract installed // now using the same transactions to check confirm messages - tf.makeNatSpec = true - tf.registerNatSpec(codehash, dochash) - t.Logf("Confirm message: %v\n", tf.lastConfirm) + tf.wantNatSpec = true // this is set so now the backend uses natspec confirmation + _, err = registry.RegisterContentHash(tf.coinbase, codehash, dochash) + if err != nil { + t.Errorf("error calling contract registry: %v", err) + } + if tf.lastConfirm != testExpNotice { - t.Errorf("Wrong confirm message, expected '%v', got '%v'", testExpNotice, tf.lastConfirm) + t.Errorf("Wrong confirm message. expected '%v', got '%v'", testExpNotice, tf.lastConfirm) } - tf.setOwner() - t.Logf("Confirm message for unknown method: %v\n", tf.lastConfirm) - if tf.lastConfirm != testExpNotice2 { - t.Errorf("Wrong confirm message, expected '%v', got '%v'", testExpNotice2, tf.lastConfirm) + // test unknown method + exp := fmt.Sprintf(testExpNotice2, resolver.HashRegContractAddress) + _, err = registry.SetOwner(tf.coinbase) + if err != nil { + t.Errorf("error setting owner: %v", err) + } + + if tf.lastConfirm != exp { + t.Errorf("Wrong confirm message, expected '%v', got '%v'", exp, tf.lastConfirm) + } + + // test unknown contract + exp = fmt.Sprintf(testExpNotice3, resolver.UrlHintContractAddress) + + _, err = registry.RegisterUrl(tf.coinbase, dochash, "file:///test.content") + if err != nil { + t.Errorf("error registering: %v", err) } - tf.registerURL(dochash, "file:///test.content") - t.Logf("Confirm message for unknown contract: %v\n", tf.lastConfirm) - if tf.lastConfirm != testExpNotice3 { - t.Errorf("Wrong confirm message, expected '%v', got '%v'", testExpNotice3, tf.lastConfirm) + if tf.lastConfirm != exp { + t.Errorf("Wrong confirm message, expected '%v', got '%v'", exp, tf.lastConfirm) } } diff --git a/common/natspec/natspec_test.go b/common/natspec/natspec_test.go index 35a59469a..05df9e750 100644 --- a/common/natspec/natspec_test.go +++ b/common/natspec/natspec_test.go @@ -4,70 +4,65 @@ import ( "testing" ) -func makeUserdoc(desc string) []byte { +func makeInfoDoc(desc string) []byte { return []byte(` { - "source": "...", + "source": "contract test { }", "language": "Solidity", - "languageVersion": 1, - "methods": { - "multiply(uint256)": { - "notice": "` + desc + `" + "compilerVersion": "1", + "userDoc": { + "methods": { + "multiply(uint256)": { + "notice": "` + desc + `" + }, + "balance(address)": { + "notice": "` + "`(balanceInmGAV / 1000).fixed(0,3)`" + ` GAV is the total funds available to ` + "`who.address()`." + `" + } }, - "balance(address)": { - "notice": "` + "`(balanceInmGAV / 1000).fixed(0,3)`" + ` GAV is the total funds available to ` + "`who.address()`." + `" - } + "invariants": [ + { "notice": "The sum total amount of GAV in the system is 1 million." } + ], + "construction": [ + { "notice": "Endows ` + "`message.caller.address()`" + ` with 1m GAV." } + ] }, - "invariants": [ - { "notice": "The sum total amount of GAV in the system is 1 million." } - ], - "construction": [ - { "notice": "Endows ` + "`message.caller.address()`" + ` with 1m GAV." } - ] -} -`) + "abiDefinition": [{ + "name": "multiply", + "constant": false, + "type": "function", + "inputs": [{ + "name": "a", + "type": "uint256" + }], + "outputs": [{ + "name": "d", + "type": "uint256" + }] + }] +}`) } var data = "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" var tx = ` { - "jsonrpc": "2.0", - "method": "eth_call", "params": [{ "to": "0x8521742d3f456bd237e312d6e30724960f72517a", "data": "0xc6888fa1000000000000000000000000000000000000000000000000000000000000007a" }], - "id": 6 } ` -var abi = []byte(` -[{ - "name": "multiply", - "constant": false, - "type": "function", - "inputs": [{ - "name": "a", - "type": "uint256" - }], - "outputs": [{ - "name": "d", - "type": "uint256" - }] -}] -`) - func TestNotice(t *testing.T) { desc := "Will multiply `a` by 7 and return `a * 7`." expected := "Will multiply 122 by 7 and return 854." - userdoc := makeUserdoc(desc) - - ns, err := NewWithDocs(abi, userdoc, tx) + infodoc := makeInfoDoc(desc) + ns, err := NewWithDocs(infodoc, tx, data) if err != nil { t.Errorf("New: error: %v", err) + return } notice, err := ns.Notice() @@ -78,8 +73,6 @@ func TestNotice(t *testing.T) { if notice != expected { t.Errorf("incorrect notice. expected %v, got %v", expected, notice) - } else { - t.Logf("returned notice \"%v\"", notice) } } @@ -87,10 +80,10 @@ func TestNotice(t *testing.T) { func TestMissingMethod(t *testing.T) { desc := "Will multiply `a` by 7 and return `a * 7`." - userdoc := makeUserdoc(desc) expected := "natspec.js error evaluating expression: Natspec evaluation failed, method does not exist" - ns, err := NewWithDocs(abi, userdoc, tx) + infodoc := makeInfoDoc(desc) + ns, err := NewWithDocs(infodoc, tx, data) if err != nil { t.Errorf("New: error: %v", err) } @@ -113,9 +106,8 @@ func TestInvalidDesc(t *testing.T) { desc := "Will multiply 122 by \"7\" and return 854." expected := "invalid character '7' after object key:value pair" - userdoc := makeUserdoc(desc) - - _, err := NewWithDocs(abi, userdoc, tx) + infodoc := makeInfoDoc(desc) + _, err := NewWithDocs(infodoc, tx, data) if err == nil { t.Errorf("expected error, got nothing", err) } else { @@ -131,9 +123,8 @@ func TestWrongInputParams(t *testing.T) { desc := "Will multiply `e` by 7 and return `a * 7`." expected := "natspec.js error evaluating expression: Natspec evaluation failed, wrong input params" - userdoc := makeUserdoc(desc) - - ns, err := NewWithDocs(abi, userdoc, tx) + infodoc := makeInfoDoc(desc) + ns, err := NewWithDocs(infodoc, tx, data) if err != nil { t.Errorf("New: error: %v", err) } -- cgit