From b635cad9fe127e6b0ca6d993ce9a3b6c61ce79c6 Mon Sep 17 00:00:00 2001 From: zsfelfoldi Date: Tue, 7 Apr 2015 11:50:17 +0200 Subject: [PATCH] NatSpec passing end to end test --- common/natspec/natspec.go | 11 +- common/natspec/natspec_e2e_test.go | 184 +++++++++++++++++++++++++---- common/resolver/resolver.go | 30 +++-- common/resolver/resolver_test.go | 20 ++-- core/contracts.go | 42 ++++--- core/genesis.go | 5 +- rpc/api.go | 7 +- 7 files changed, 226 insertions(+), 73 deletions(-) diff --git a/common/natspec/natspec.go b/common/natspec/natspec.go index b047f19d6..0253ebd81 100644 --- a/common/natspec/natspec.go +++ b/common/natspec/natspec.go @@ -45,18 +45,19 @@ func New(xeth *xeth.XEth, tx string, http *docserver.DocServer) (self *NatSpec, err = fmt.Errorf("NatSpec error: contract not found") return } - codeHash := xeth.CodeAt(contractAddress) + codehex := xeth.CodeAt(contractAddress) + codeHash := common.BytesToHash(crypto.Sha3(common.Hex2Bytes(codehex))) // parse out host/domain // set up nameresolver with natspecreg + urlhint contract addresses res := resolver.New( xeth, - resolver.NameRegContractAddress, resolver.URLHintContractAddress, + resolver.HashRegContractAddress, ) - // resolve host via nameReg/UrlHint Resolver - uri, hash, err := res.NameToUrl(codeHash) + // resolve host via HashReg/UrlHint Resolver + uri, hash, err := res.KeyToUrl(codeHash) if err != nil { return } @@ -165,6 +166,7 @@ func (self *NatSpec) Notice() (notice string, err error) { } copy(abiKey[:], self.data[2:10]) meth := self.makeAbi2method(abiKey) + if meth == nil { err = fmt.Errorf("abi key %x does not match any method %v") return @@ -174,6 +176,7 @@ func (self *NatSpec) Notice() (notice string, err error) { } func (self *NatSpec) noticeForMethod(tx string, name, expression string) (notice string, err error) { + if _, err = self.jsvm.Run("var transaction = " + tx + ";"); err != nil { return "", fmt.Errorf("natspec.js error setting transaction: %v", err) } diff --git a/common/natspec/natspec_e2e_test.go b/common/natspec/natspec_e2e_test.go index 230fd5e99..43a1fdf1c 100644 --- a/common/natspec/natspec_e2e_test.go +++ b/common/natspec/natspec_e2e_test.go @@ -6,16 +6,19 @@ import ( "math/big" "os" "testing" + "time" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/docserver" + "github.com/ethereum/go-ethereum/common/natspec" "github.com/ethereum/go-ethereum/common/resolver" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" + //"github.com/ethereum/go-ethereum/core/state" //"github.com/ethereum/go-ethereum/core/types" "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" ) @@ -23,19 +26,81 @@ type testFrontend struct { t *testing.T ethereum *eth.Ethereum xeth *xe.XEth - stateDb *state.StateDB + api *rpc.EthereumApi coinbase string + txc int lastConfirm string + makeNatSpec bool } +const testNotice = "Register key `_key` <- content `_content`" +const testExpNotice = "Register key 8.9477152217924674838424037953991966239322087453347756267410168184682657981552e+76 <- content 2.9345842665291639932787537650068479186757226656217642728276414736233000517704e+76" + +const testUserDoc = ` +{ + "source": "...", + "language": "Solidity", + "languageVersion": 1, + "methods": { + "register(uint256,uint256)": { + "notice": "` + testNotice + `" + } + }, + "invariants": [ + { "notice": "" } + ], + "construction": [ + { "notice": "" } + ] +} +` + +const testABI = ` +[{ + "name": "register", + "constant": false, + "type": "function", + "inputs": [{ + "name": "_key", + "type": "uint256" + }, { + "name": "_content", + "type": "uint256" + }], + "outputs": [] +}] +` + +const testDocs = ` +{ + "userdoc": ` + testUserDoc + `, + "abi": ` + testABI + ` +} +` + func (f *testFrontend) UnlockAccount(acc []byte) bool { f.t.Logf("Unlocking account %v\n", common.Bytes2Hex(acc)) f.ethereum.AccountManager().Unlock(acc, "password") return true } -func (f *testFrontend) ConfirmTransaction(message string) bool { - f.lastConfirm = message +func (f *testFrontend) ConfirmTransaction(tx string) bool { + //f.t.Logf("ConfirmTransaction called tx = %v", tx) + if f.makeNatSpec { + ds, err := docserver.New("/tmp/") + if err != nil { + f.t.Errorf("Error creating DocServer: %v", err) + } + nat, err2 := natspec.New(f.xeth, tx, ds) + if err2 == nil { + f.lastConfirm, err = nat.Notice() + if err != nil { + f.t.Errorf("Notice error: %v", err) + } + } else { + f.t.Errorf("Error creating NatSpec: %v", err2) + } + } return true } @@ -89,6 +154,7 @@ func testInit(t *testing.T) (self *testFrontend) { self = &testFrontend{t: t, ethereum: ethereum} self.xeth = xe.New(ethereum, self) + self.api = rpc.NewEthereumApi(self.xeth) addr := self.xeth.Coinbase() self.coinbase = addr @@ -103,8 +169,6 @@ func testInit(t *testing.T) (self *testFrontend) { } t.Logf("Balance is %v", balance) - self.stateDb = self.ethereum.ChainManager().State().Copy() - return } @@ -117,33 +181,87 @@ func (self *testFrontend) insertTx(addr, contract, fnsig string, args []string) data = data + common.Bytes2Hex(common.Hex2BytesFixed(arg, 32)) } self.t.Logf("Tx data: %v", data) - self.xeth.Transact(addr, contract, "100000000000", "100000", "100000", data) - cb := common.HexToAddress(addr) + jsontx := ` +[{ + "from": "` + addr + `", + "to": "0x` + contract + `", + "value": "100000000000", + "gas": "100000", + "gasPrice": "100000", + "data": "` + data + `" +}] +` + self.txc++ + req := &rpc.RpcRequest{ + Jsonrpc: "2.0", + Method: "eth_transact", + Params: []byte(jsontx), + Id: self.txc, + } - coinbase := self.stateDb.GetStateObject(cb) - coinbase.SetGasPool(big.NewInt(100000)) + txcount := self.ethereum.TxPool().Size() + + var reply interface{} + err0 := self.api.GetRequestReply(req, &reply) + if err0 != nil { + self.t.Errorf("GetRequestReply error: %v", err0) + } + + for txcount == self.ethereum.TxPool().Size() { + time.Sleep(time.Millisecond) + } + + //self.xeth.Transact(addr, contract, "100000000000", "100000", "100000", data) +} + +func (self *testFrontend) applyTxs() { + + cb := common.HexToAddress(self.coinbase) + stateDb := self.ethereum.ChainManager().State().Copy() block := self.ethereum.ChainManager().NewBlock(cb) + coinbase := stateDb.GetStateObject(cb) + coinbase.SetGasPool(big.NewInt(1000000)) txs := self.ethereum.TxPool().GetTransactions() - tx := txs[0] - _, gas, err := core.ApplyMessage(core.NewEnv(self.stateDb, self.ethereum.ChainManager(), tx, block), tx, coinbase) - - self.t.Logf("ApplyMessage: gas %v err %v", gas, err) + for _, tx := range txs { + _, gas, err := core.ApplyMessage(core.NewEnv(stateDb, self.ethereum.ChainManager(), tx, block), tx, coinbase) + //self.ethereum.TxPool().RemoveSet([]*types.Transaction{tx}) + time.Sleep(time.Millisecond * 100) + self.t.Logf("ApplyMessage: gas %v err %v", gas, err) + } self.ethereum.TxPool().RemoveSet(txs) - self.xeth = self.xeth.WithState(self.stateDb) + self.xeth = self.xeth.WithState(stateDb) } func (self *testFrontend) registerURL(hash common.Hash, url string) { hashHex := common.Bytes2Hex(hash[:]) urlHex := common.Bytes2Hex([]byte(url)) - self.insertTx(self.coinbase, core.ContractAddrURLhint, "register(bytes32,bytes32)", []string{hashHex, urlHex}) + self.insertTx(self.coinbase, core.ContractAddrURLhint, "register(uint256,uint256)", []string{hashHex, urlHex}) +} + +func (self *testFrontend) setOwner() { + + self.insertTx(self.coinbase, core.ContractAddrHashReg, "setowner()", []string{}) + + /*owner := self.xeth.StorageAt("0x"+core.ContractAddrHashReg, "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, core.ContractAddrHashReg, "register(uint256,uint256)", []string{codeHex, docHex}) } func (self *testFrontend) testResolver() *resolver.Resolver { - return resolver.New(self.xeth, resolver.URLHintContractAddress, resolver.NameRegContractAddress) + return resolver.New(self.xeth, resolver.URLHintContractAddress, resolver.HashRegContractAddress) } func TestNatspecE2E(t *testing.T) { @@ -151,16 +269,34 @@ func TestNatspecE2E(t *testing.T) { tf := testInit(t) defer tf.ethereum.Stop() - ds, _ := docserver.New("/tmp/") + ioutil.WriteFile("/tmp/test.content", []byte(testDocs), os.ModePerm) + dochash := common.BytesToHash(crypto.Sha3([]byte(testDocs))) - chash := common.BytesToHash(crypto.Sha3([]byte("kutyafasza"))) - tf.registerURL(chash, "file:///test.content") - tf.registerURL(chash, "kf") + codehex := tf.xeth.CodeAt(core.ContractAddrHashReg) + codehash := common.BytesToHash(crypto.Sha3(common.Hex2Bytes(codehex))) - url, err2 := tf.testResolver().ContentHashToUrl(chash) + tf.setOwner() + tf.registerNatSpec(codehash, dochash) + tf.registerURL(dochash, "file:///test.content") + tf.applyTxs() - t.Logf("URL: %v err: %v", url, err2) + chash, err := tf.testResolver().KeyToContentHash(codehash) + 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.Logf("url = %v err = %v", url, err2) - ds.GetAuthContent(url, chash) + tf.makeNatSpec = true + tf.registerNatSpec(codehash, dochash) // call again just to get a confirm message + t.Logf("Confirm message: %v\n", tf.lastConfirm) + + if tf.lastConfirm != testExpNotice { + t.Errorf("Wrong confirm message, expected '%v', got '%v'", testExpNotice, tf.lastConfirm) + } } diff --git a/common/resolver/resolver.go b/common/resolver/resolver.go index 060e9dbb5..9c71ac85f 100644 --- a/common/resolver/resolver.go +++ b/common/resolver/resolver.go @@ -11,7 +11,7 @@ import ( /* Resolver implements the Ethereum DNS mapping -NameReg : Domain Name (or Code hash of Contract) -> Content Hash +HashReg : Key Hash (hash of domain name or contract code) -> Content Hash UrlHint : Content Hash -> Url Hint The resolver is meant to be called by the roundtripper transport implementation @@ -19,13 +19,13 @@ of a url scheme */ const ( URLHintContractAddress = core.ContractAddrURLhint - NameRegContractAddress = core.ContractAddrHashReg + HashRegContractAddress = core.ContractAddrHashReg ) type Resolver struct { backend Backend urlHintContractAddress string - nameRegContractAddress string + hashRegContractAddress string } type Backend interface { @@ -36,17 +36,23 @@ func New(eth Backend, uhca, nrca string) *Resolver { return &Resolver{eth, uhca, nrca} } -func (self *Resolver) NameToContentHash(name string) (chash common.Hash, err error) { - // look up in nameReg - key := storageAddress(1, common.Hex2BytesFixed(name, 32)) - hash := self.backend.StorageAt("0x"+self.nameRegContractAddress, key) - copy(chash[:], common.Hex2Bytes(hash)) +func (self *Resolver) KeyToContentHash(khash common.Hash) (chash common.Hash, err error) { + // look up in hashReg + key := storageAddress(1, khash[:]) + hash := self.backend.StorageAt("0x"+self.hashRegContractAddress, key) + + if hash == "0x0" || len(hash) < 3 { + err = fmt.Errorf("GetHashReg: content hash not found") + return + } + + copy(chash[:], common.Hex2BytesFixed(hash[2:], 32)) return } func (self *Resolver) ContentHashToUrl(chash common.Hash) (uri string, err error) { - // look up in nameReg - key := storageAddress(2, chash[:]) + // look up in URL reg + key := storageAddress(1, chash[:]) hex := self.backend.StorageAt("0x"+self.urlHintContractAddress, key) uri = string(common.Hex2Bytes(hex[2:])) l := len(uri) @@ -61,9 +67,9 @@ func (self *Resolver) ContentHashToUrl(chash common.Hash) (uri string, err error return } -func (self *Resolver) NameToUrl(name string) (uri string, hash common.Hash, err error) { +func (self *Resolver) KeyToUrl(key common.Hash) (uri string, hash common.Hash, err error) { // look up in urlHint - hash, err = self.NameToContentHash(name) + hash, err = self.KeyToContentHash(key) if err != nil { return } diff --git a/common/resolver/resolver_test.go b/common/resolver/resolver_test.go index 5652213f6..17a1c4f94 100644 --- a/common/resolver/resolver_test.go +++ b/common/resolver/resolver_test.go @@ -23,12 +23,12 @@ func NewTestBackend() *testBackend { self := &testBackend{} self.contracts = make(map[string](map[string]string)) - self.contracts[NameRegContractAddress] = make(map[string]string) - key := storageAddress(0, common.Hex2Bytes(codehash)) - self.contracts[NameRegContractAddress][key] = hash + self.contracts[HashRegContractAddress] = make(map[string]string) + key := storageAddress(1, common.Hex2Bytes(codehash)) + self.contracts[HashRegContractAddress][key] = hash self.contracts[URLHintContractAddress] = make(map[string]string) - key = storageAddress(0, common.Hex2Bytes(hash)) + key = storageAddress(1, common.Hex2Bytes(hash)) self.contracts[URLHintContractAddress][key] = url return self @@ -43,10 +43,12 @@ func (self *testBackend) StorageAt(ca, sa string) (res string) { return } -func TestNameToContentHash(t *testing.T) { +func TestKeyToContentHash(t *testing.T) { b := NewTestBackend() - res := New(b, URLHintContractAddress, NameRegContractAddress) - got, err := res.NameToContentHash(codehash) + res := New(b, URLHintContractAddress, HashRegContractAddress) + chash := common.Hash{} + copy(chash[:], common.Hex2BytesFixed(codehash, 32)) + got, err := res.KeyToContentHash(chash) if err != nil { t.Errorf("expected no error, got %v", err) } else { @@ -58,7 +60,7 @@ func TestNameToContentHash(t *testing.T) { func TestContentHashToUrl(t *testing.T) { b := NewTestBackend() - res := New(b, URLHintContractAddress, NameRegContractAddress) + res := New(b, URLHintContractAddress, HashRegContractAddress) chash := common.Hash{} copy(chash[:], common.Hex2Bytes(hash)) got, err := res.ContentHashToUrl(chash) @@ -71,5 +73,5 @@ func TestContentHashToUrl(t *testing.T) { } } -func TestNameToUrl(t *testing.T) { +func TestKeyToUrl(t *testing.T) { } diff --git a/core/contracts.go b/core/contracts.go index ffd98da98..9d63e4043 100644 --- a/core/contracts.go +++ b/core/contracts.go @@ -2,33 +2,43 @@ package core const ( // built-in contracts address and code ContractAddrURLhint = "0000000000000000000000000000000000000008" - //ContractCodeURLhint = "0x60bd80600c6000396000f30060003560e060020a900480632f92673214601557005b60216004356024356027565b60006000f35b6000805490816001019055506001600083815260200190815260200160002054600160a060020a0316600014806081575033600160a060020a03166001600084815260200190815260200160002054600160a060020a0316145b60885760b9565b3360016000848152602001908152602001600020819055508060026000848152602001908152602001600020819055505b505056" - ContractCodeURLhint = "0x60003560e060020a900480632f92673214601557005b60216004356024356027565b60006000f35b6000805490816001019055506001600083815260200190815260200160002054600160a060020a0316600014806081575033600160a060020a03166001600084815260200190815260200160002054600160a060020a0316145b60885760b9565b3360016000848152602001908152602001600020819055508060026000848152602001908152602001600020819055505b505056" - //"0x60b180600c6000396000f30060003560e060020a900480632f92673214601557005b60216004356024356027565b60006000f35b6000600083815260200190815260200160002054600160a060020a0316600014806075575033600160a060020a03166000600084815260200190815260200160002054600160a060020a0316145b607c5760ad565b3360006000848152602001908152602001600020819055508060016000848152602001908152602001600020819055505b505056" + //ContractCodeURLhint = "0x60b180600c6000396000f30060003560e060020a90048063d66d6c1014601557005b60216004356024356027565b60006000f35b6000600083815260200190815260200160002054600160a060020a0316600014806075575033600160a060020a03166000600084815260200190815260200160002054600160a060020a0316145b607c5760ad565b3360006000848152602001908152602001600020819055508060016000848152602001908152602001600020819055505b505056" + ContractCodeURLhint = "0x60003560e060020a90048063d66d6c1014601557005b60216004356024356027565b60006000f35b6000600083815260200190815260200160002054600160a060020a0316600014806075575033600160a060020a03166000600084815260200190815260200160002054600160a060020a0316145b607c5760ad565b3360006000848152602001908152602001600020819055508060016000848152602001908152602001600020819055505b505056" /* contract URLhint { - function register(bytes32 _hash, bytes32 _url) { - testcnt++; + function register(uint256 _hash, uint256 _url) { if (owner[_hash] == 0 || owner[_hash] == msg.sender) { owner[_hash] = msg.sender; url[_hash] = _url; } } - uint32 testcnt; - mapping (bytes32 => address) owner; - mapping (bytes32 => bytes32) url; + mapping (uint256 => address) owner; + mapping (uint256 => uint256) url; } */ ContractAddrHashReg = "0000000000000000000000000000000000000009" - ContractCodeHashReg = "0x3360008190555060628060136000396000f30060003560e060020a900480632f92673214601557005b60216004356024356027565b60006000f35b600054600160a060020a031633600160a060020a031614604557605e565b8060016000848152602001908152602001600020819055505b505056" + ContractCodeHashReg = "0x60003560e060020a9004806331e12c2014601f578063d66d6c1014602b57005b6025603d565b60006000f35b6037600435602435605d565b60006000f35b600054600160a060020a0316600014605357605b565b336000819055505b565b600054600160a060020a031633600160a060020a031614607b576094565b8060016000848152602001908152602001600020819055505b505056" + //ContractCodeHashReg = "0x609880600c6000396000f30060003560e060020a9004806331e12c2014601f578063d66d6c1014602b57005b6025603d565b60006000f35b6037600435602435605d565b60006000f35b600054600160a060020a0316600014605357605b565b336000819055505b565b600054600160a060020a031633600160a060020a031614607b576094565b8060016000848152602001908152602001600020819055505b505056" /* - import "owned"; - contract HashReg is owned { - function register(bytes32 _code, bytes32 _abi) onlyowner { - abis[_code] = _abi; - } - mapping (bytes32 => bytes32) abis; - } + contract HashReg { + function setowner() { + if (owner == 0) { + owner = msg.sender; + } + } + function register(uint256 _key, uint256 _content) { + if (msg.sender == owner) { + content[_key] = _content; + } + } + address owner; + mapping (uint256 => uint256) content; + } */ + + BuiltInContracts = ` + "` + ContractAddrURLhint + `": {"balance": "0", "code": "` + ContractCodeURLhint + `" }, + "` + ContractAddrHashReg + `": {"balance": "0", "code": "` + ContractCodeHashReg + `" }, + ` ) diff --git a/core/genesis.go b/core/genesis.go index 14ac2ec0f..c475ca637 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -58,13 +58,12 @@ func GenesisBlock(db common.Database) *types.Block { const ( TestAccount = "e273f01c99144c438695e10f24926dc1f9fbf62d" - TestBalance = "1000000000000000000" + TestBalance = "1000000000000" ) var genesisData = []byte(`{ "` + TestAccount + `": {"balance": "` + TestBalance + `"}, - "` + ContractAddrURLhint + `": {"balance": "` + TestBalance + `", "code": "` + ContractCodeURLhint + `" }, - "` + ContractAddrHashReg + `": {"balance": "` + TestBalance + `", "code": "` + ContractCodeHashReg + `" }, + ` + BuiltInContracts + ` "0000000000000000000000000000000000000001": {"balance": "1"}, "0000000000000000000000000000000000000002": {"balance": "1"}, "0000000000000000000000000000000000000003": {"balance": "1"}, diff --git a/rpc/api.go b/rpc/api.go index c02b03fb6..66283752b 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -168,11 +168,8 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err } // call ConfirmTransaction first - var obj []json.RawMessage - if err := json.Unmarshal(req.Params, &obj); err != nil { - return NewDecodeParamError(err.Error()) - } - if !api.xeth().ConfirmTransaction(string(obj[0])) { + tx, _ := json.Marshal(req) + if !api.xeth().ConfirmTransaction(string(tx)) { return fmt.Errorf("Transaction not confirmed") }