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
This commit is contained in:
zelig
2015-04-22 23:11:11 +01:00
parent 97c37356fd
commit 009b221692
21 changed files with 1463 additions and 517 deletions

View File

@ -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 New(xeth *xeth.XEth, tx string, http *docserver.DocServer) (self *NatSpec, err error) {
func getFallbackNotice(comment, tx string) string {
return fmt.Sprintf("About to submit transaction (%s): %s", comment, tx)
}
type transaction struct {
To string `json:"to"`
Data string `json:"data"`
}
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"`
}
func New(xeth *xeth.XEth, jsontx string, http *docserver.DocServer) (self *NatSpec, err error) {
// extract contract address from tx
var obj map[string]json.RawMessage
err = json.Unmarshal([]byte(tx), &obj)
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")
codehex := xeth.CodeAt(contractAddress)
codeb := xeth.CodeAtBytes(contractAddress)
if codehex == "0x" {
err = fmt.Errorf("contract (%v) not found", contractAddress)
return
}
codehex := xeth.CodeAt(contractAddress)
codeHash := common.BytesToHash(crypto.Sha3(common.Hex2Bytes(codehex[2:])))
// parse out host/domain
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)
content, err = http.GetAuthContent(uri, hash)
if err != nil {
return
}
// get abi, userdoc
var obj2 map[string]json.RawMessage
err = json.Unmarshal(content, &obj2)
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
}

View File

@ -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"
testFileName = "long_file_name_for_testing_registration_of_URLs_longer_than_32_bytes.content"
testNotice = "Register key `utils.toHex(_key)` <- content `utils.toHex(_content)`"
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 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"}]}`
const testUserDoc = `
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 + `
}
`
)
func (f *testFrontend) UnlockAccount(acc []byte) bool {
f.t.Logf("Unlocking account %v\n", common.Bytes2Hex(acc))
f.ethereum.AccountManager().Unlock(acc, "password")
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 (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)
if err != nil {
t.Errorf("%v", err)
return
}
err = os.MkdirAll("/tmp/eth-natspec/data", os.ModePerm)
if err != nil {
t.Errorf("%v", err)
return
}
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)
port++
os.RemoveAll("/tmp/eth-natspec/")
err = os.MkdirAll("/tmp/eth-natspec/keys", os.ModePerm)
if err != nil {
panic(err)
}
// create a testAddress
ks := crypto.NewKeyStorePassphrase("/tmp/eth-natspec/keys")
am := accounts.NewManager(ks)
testAccount, err := am.NewAccount("password")
if err != nil {
panic(err)
}
testAddress := common.Bytes2Hex(testAccount.Address)
// set up mock genesis with balance on the testAddress
core.GenesisData = []byte(`{
"` + testAddress + `": {"balance": "` + testBalance + `"}
}`)
// only use minimalistic stack with no networking
ethereum, err = eth.New(&eth.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()
// 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)
return
}
func (self *testFrontend) insertTx(addr, contract, fnsig string, args []string) {
//cb := common.HexToAddress(self.coinbase)
//coinbase := self.ethereum.ChainManager().State().GetStateObject(cb)
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)
}
// 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()
// 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)))
ioutil.WriteFile("/tmp/"+testFileName, []byte(testDocs), os.ModePerm)
dochash := common.BytesToHash(crypto.Sha3([]byte(testDocs)))
// 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))
codehex := tf.xeth.CodeAt(resolver.HashRegContractAddress)
codehash := common.BytesToHash(crypto.Sha3(common.Hex2Bytes(codehex[2:])))
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.Errorf("error registering: %v", err)
}
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.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)
}
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)
}
// 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)
}
if tf.lastConfirm != exp {
t.Errorf("Wrong confirm message, expected '%v', got '%v'", exp, tf.lastConfirm)
}
}

View File

@ -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)
}