2015-07-06 19:54:22 -05:00
// Copyright 2015 The go-ethereum Authors
2015-07-22 11:48:40 -05:00
// This file is part of the go-ethereum library.
2015-07-06 19:54:22 -05:00
//
2015-07-23 11:35:11 -05:00
// The go-ethereum library is free software: you can redistribute it and/or modify
2015-07-06 19:54:22 -05:00
// 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.
//
2015-07-22 11:48:40 -05:00
// The go-ethereum library is distributed in the hope that it will be useful,
2015-07-06 19:54:22 -05:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
2015-07-22 11:48:40 -05:00
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2015-07-06 19:54:22 -05:00
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
2015-07-22 11:48:40 -05:00
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
2015-07-06 19:54:22 -05:00
2015-04-03 10:37:59 -05:00
package natspec
import (
2015-04-22 17:11:11 -05:00
"fmt"
2015-04-03 10:37:59 -05:00
"io/ioutil"
2015-07-05 13:19:42 -05:00
"math/big"
2015-04-03 10:37:59 -05:00
"os"
2015-09-22 03:34:58 -05:00
"path/filepath"
2015-07-05 13:19:42 -05:00
"runtime"
2015-04-03 10:37:59 -05:00
"testing"
2015-07-05 13:19:42 -05:00
"time"
2015-04-03 10:37:59 -05:00
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
2015-10-26 16:24:09 -05:00
"github.com/ethereum/go-ethereum/common/httpclient"
2015-06-23 09:48:33 -05:00
"github.com/ethereum/go-ethereum/common/registrar"
2015-04-03 10:37:59 -05:00
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
2015-07-10 07:29:40 -05:00
"github.com/ethereum/go-ethereum/ethdb"
2015-04-03 10:37:59 -05:00
xe "github.com/ethereum/go-ethereum/xeth"
)
2015-04-14 09:01:25 -05:00
const (
2015-09-22 03:34:58 -05:00
testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
2015-04-22 17:11:11 -05:00
testBalance = "10000000000000000000"
2015-09-22 03:34:58 -05:00
testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674"
2015-04-14 09:01:25 -05:00
2015-04-22 17:11:11 -05:00
testFileName = "long_file_name_for_testing_registration_of_URLs_longer_than_32_bytes.content"
2015-04-17 06:46:38 -05:00
2015-04-22 17:11:11 -05:00
testNotice = "Register key `utils.toHex(_key)` <- content `utils.toHex(_content)`"
2015-04-07 04:50:17 -05:00
2015-04-22 17:11:11 -05:00
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"}]} `
2015-09-22 03:34:58 -05:00
testExpNotice3 = ` About to submit transaction (no NatSpec info found for contract: HashToHash: content hash not found for '0x1392c62d05b2d149e22a339c531157ae06b44d39a674cce500064b12b9aeb019'): { "params":[ { "to":"%s","data": "0x300a3bbfb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066696c653a2f2f2f746573742e636f6e74656e74"}]} `
2015-04-22 17:11:11 -05:00
)
const (
testUserDoc = `
2015-04-07 04:50:17 -05:00
{
"methods" : {
"register(uint256,uint256)" : {
"notice" : "` + testNotice + `"
}
} ,
"invariants" : [
{ "notice" : "" }
] ,
"construction" : [
{ "notice" : "" }
]
}
`
2015-04-22 17:11:11 -05:00
testAbiDefinition = `
2015-04-07 04:50:17 -05:00
[ {
"name" : "register" ,
"constant" : false ,
"type" : "function" ,
"inputs" : [ {
"name" : "_key" ,
"type" : "uint256"
} , {
"name" : "_content" ,
"type" : "uint256"
} ] ,
"outputs" : [ ]
} ]
`
2015-04-22 17:11:11 -05:00
testContractInfo = `
2015-04-07 04:50:17 -05:00
{
2015-04-22 17:11:11 -05:00
"userDoc" : ` + testUserDoc + ` ,
"abiDefinition" : ` + testAbiDefinition + `
2015-04-07 04:50:17 -05:00
}
`
2015-04-22 17:11:11 -05:00
)
type testFrontend struct {
2015-06-23 09:48:33 -05:00
t * testing . T
2015-04-22 17:11:11 -05:00
ethereum * eth . Ethereum
xeth * xe . XEth
2015-07-05 13:19:42 -05:00
wait chan * big . Int
2015-04-22 17:11:11 -05:00
lastConfirm string
wantNatSpec bool
}
2015-04-07 04:50:17 -05:00
2015-09-22 03:34:58 -05:00
func ( self * testFrontend ) AskPassword ( ) ( string , bool ) {
return "" , true
}
2015-04-22 17:11:11 -05:00
func ( self * testFrontend ) UnlockAccount ( acc [ ] byte ) bool {
2015-04-26 10:18:06 -05:00
self . ethereum . AccountManager ( ) . Unlock ( common . BytesToAddress ( acc ) , "password" )
2015-04-03 10:37:59 -05:00
return true
}
2015-04-22 17:11:11 -05:00
func ( self * testFrontend ) ConfirmTransaction ( tx string ) bool {
if self . wantNatSpec {
2015-10-26 16:24:09 -05:00
client := httpclient . New ( "/tmp/" )
self . lastConfirm = GetNotice ( self . xeth , tx , client )
2015-04-07 04:50:17 -05:00
}
2015-04-06 01:01:36 -05:00
return true
}
2015-04-03 10:37:59 -05:00
2015-04-06 01:01:36 -05:00
func testEth ( t * testing . T ) ( ethereum * eth . Ethereum , err error ) {
2015-04-22 17:11:11 -05:00
2015-09-22 03:34:58 -05:00
tmp , err := ioutil . TempDir ( "" , "natspec-test" )
2015-04-03 10:37:59 -05:00
if err != nil {
2015-09-22 03:34:58 -05:00
t . Fatal ( err )
2015-04-03 10:37:59 -05:00
}
2015-09-22 03:34:58 -05:00
db , _ := ethdb . NewMemDatabase ( )
addr := common . HexToAddress ( testAddress )
core . WriteGenesisBlockForTesting ( db , core . GenesisAccount { addr , common . String2Big ( testBalance ) } )
ks := crypto . NewKeyStorePassphrase ( filepath . Join ( tmp , "keystore" ) )
2015-04-22 17:11:11 -05:00
am := accounts . NewManager ( ks )
2015-09-22 03:34:58 -05:00
keyb , err := crypto . HexToECDSA ( testKey )
2015-04-03 10:37:59 -05:00
if err != nil {
2015-09-22 03:34:58 -05:00
t . Fatal ( err )
}
key := crypto . NewKeyFromECDSA ( keyb )
err = ks . StoreKey ( key , "" )
if err != nil {
t . Fatal ( err )
2015-04-03 10:37:59 -05:00
}
2015-07-10 07:29:40 -05:00
2015-09-22 03:34:58 -05:00
err = am . Unlock ( key . Address , "" )
2015-04-03 10:37:59 -05:00
if err != nil {
2015-09-22 03:34:58 -05:00
t . Fatal ( err )
2015-04-03 10:37:59 -05:00
}
2015-09-22 03:34:58 -05:00
// only use minimalistic stack with no networking
return eth . New ( & eth . Config {
DataDir : tmp ,
AccountManager : am ,
Etherbase : common . HexToAddress ( testAddress ) ,
MaxPeers : 0 ,
PowTest : true ,
NewDB : func ( path string ) ( ethdb . Database , error ) { return db , nil } ,
GpoMinGasPrice : common . Big1 ,
GpobaseCorrectionFactor : 1 ,
GpoMaxGasPrice : common . Big1 ,
} )
2015-04-03 10:37:59 -05:00
}
2015-04-06 01:01:36 -05:00
func testInit ( t * testing . T ) ( self * testFrontend ) {
2015-04-22 17:11:11 -05:00
// initialise and start minimal ethereum stack
2015-04-06 01:01:36 -05:00
ethereum , err := testEth ( t )
if err != nil {
2015-04-19 13:24:46 -05:00
t . Errorf ( "error creating ethereum: %v" , err )
2015-04-06 01:01:36 -05:00
return
}
err = ethereum . Start ( )
if err != nil {
t . Errorf ( "error starting ethereum: %v" , err )
return
}
2015-04-22 17:11:11 -05:00
// mock frontend
2015-04-06 01:01:36 -05:00
self = & testFrontend { t : t , ethereum : ethereum }
self . xeth = xe . New ( ethereum , self )
2015-07-05 13:19:42 -05:00
self . wait = self . xeth . UpdateState ( )
addr , _ := self . ethereum . Etherbase ( )
2015-04-14 08:20:52 -05:00
2015-04-22 17:11:11 -05:00
// initialise the registry contracts
2015-06-23 09:48:33 -05:00
reg := registrar . New ( self . xeth )
2015-09-22 03:34:58 -05:00
registrar . GlobalRegistrarAddr = "0x0"
var txG , txH , txU string
txG , err = reg . SetGlobalRegistrar ( "" , addr )
2015-07-05 13:19:42 -05:00
if err != nil {
2015-09-22 03:34:58 -05:00
t . Fatalf ( "error creating GlobalRegistrar: %v" , err )
2015-07-05 13:19:42 -05:00
}
2015-09-22 03:34:58 -05:00
if ! processTxs ( self , t , 1 ) {
t . Fatalf ( "error mining txs" )
}
recG := self . xeth . GetTxReceipt ( common . HexToHash ( txG ) )
if recG == nil {
t . Fatalf ( "blockchain error creating GlobalRegistrar" )
}
registrar . GlobalRegistrarAddr = recG . ContractAddress . Hex ( )
2015-07-05 13:19:42 -05:00
2015-09-22 03:34:58 -05:00
txH , err = reg . SetHashReg ( "" , addr )
2015-06-23 09:48:33 -05:00
if err != nil {
t . Errorf ( "error creating HashReg: %v" , err )
}
2015-09-22 03:34:58 -05:00
if ! processTxs ( self , t , 1 ) {
t . Errorf ( "error mining txs" )
}
recH := self . xeth . GetTxReceipt ( common . HexToHash ( txH ) )
if recH == nil {
t . Fatalf ( "blockchain error creating HashReg" )
}
registrar . HashRegAddr = recH . ContractAddress . Hex ( )
txU , err = reg . SetUrlHint ( "" , addr )
2015-06-23 09:48:33 -05:00
if err != nil {
t . Errorf ( "error creating UrlHint: %v" , err )
}
2015-09-22 03:34:58 -05:00
if ! processTxs ( self , t , 1 ) {
2015-07-05 13:19:42 -05:00
t . Errorf ( "error mining txs" )
}
2015-09-22 03:34:58 -05:00
recU := self . xeth . GetTxReceipt ( common . HexToHash ( txU ) )
if recU == nil {
t . Fatalf ( "blockchain error creating UrlHint" )
}
registrar . UrlHintAddr = recU . ContractAddress . Hex ( )
2015-04-07 07:46:14 -05:00
2015-04-22 17:11:11 -05:00
return
2015-04-07 04:50:17 -05:00
}
2015-04-22 17:11:11 -05:00
// end to end test
2015-04-06 01:01:36 -05:00
func TestNatspecE2E ( t * testing . T ) {
2015-07-07 00:00:58 -05:00
t . Skip ( )
2015-04-06 01:01:36 -05:00
tf := testInit ( t )
defer tf . ethereum . Stop ( )
2015-07-05 13:19:42 -05:00
addr , _ := tf . ethereum . Etherbase ( )
2015-04-03 10:37:59 -05:00
2015-04-22 17:11:11 -05:00
// create a contractInfo file (mock cloud-deployed contract metadocs)
2015-09-22 03:34:58 -05:00
// incidentally this is the info for the HashReg contract itself
2015-04-22 17:11:11 -05:00
ioutil . WriteFile ( "/tmp/" + testFileName , [ ] byte ( testContractInfo ) , os . ModePerm )
2015-07-05 13:19:42 -05:00
dochash := crypto . Sha3Hash ( [ ] byte ( testContractInfo ) )
2015-04-03 10:37:59 -05:00
2015-04-22 17:11:11 -05:00
// take the codehash for the contract we wanna test
2015-06-23 09:48:33 -05:00
codeb := tf . xeth . CodeAtBytes ( registrar . HashRegAddr )
2015-07-05 13:19:42 -05:00
codehash := crypto . Sha3Hash ( codeb )
2015-04-03 10:37:59 -05:00
2015-06-23 09:48:33 -05:00
reg := registrar . New ( tf . xeth )
2015-07-05 13:19:42 -05:00
_ , err := reg . SetHashToHash ( addr , codehash , dochash )
2015-06-23 09:48:33 -05:00
if err != nil {
t . Errorf ( "error registering: %v" , err )
}
2015-07-05 13:19:42 -05:00
_ , err = reg . SetUrlToHash ( addr , dochash , "file:///" + testFileName )
2015-04-07 04:50:17 -05:00
if err != nil {
2015-04-22 17:11:11 -05:00
t . Errorf ( "error registering: %v" , err )
2015-04-07 04:50:17 -05:00
}
2015-07-05 13:19:42 -05:00
if ! processTxs ( tf , t , 5 ) {
return
}
2015-04-07 04:50:17 -05:00
2015-04-08 05:35:02 -05:00
// NatSpec info for register method of HashReg contract installed
// now using the same transactions to check confirm messages
2015-04-22 17:11:11 -05:00
tf . wantNatSpec = true // this is set so now the backend uses natspec confirmation
2015-07-05 13:19:42 -05:00
_ , err = reg . SetHashToHash ( addr , codehash , dochash )
2015-04-22 17:11:11 -05:00
if err != nil {
t . Errorf ( "error calling contract registry: %v" , err )
}
2015-06-23 09:48:33 -05:00
fmt . Printf ( "GlobalRegistrar: %v, HashReg: %v, UrlHint: %v\n" , registrar . GlobalRegistrarAddr , registrar . HashRegAddr , registrar . UrlHintAddr )
2015-04-07 04:50:17 -05:00
if tf . lastConfirm != testExpNotice {
2015-07-05 13:19:42 -05:00
t . Errorf ( "Wrong confirm message. expected\n'%v', got\n'%v'" , testExpNotice , tf . lastConfirm )
2015-04-07 04:50:17 -05:00
}
2015-04-03 10:37:59 -05:00
2015-04-22 17:11:11 -05:00
// test unknown method
2015-06-23 09:48:33 -05:00
exp := fmt . Sprintf ( testExpNotice2 , registrar . HashRegAddr )
2015-07-05 13:19:42 -05:00
_ , err = reg . SetOwner ( addr )
2015-04-22 17:11:11 -05:00
if err != nil {
t . Errorf ( "error setting owner: %v" , err )
}
if tf . lastConfirm != exp {
2015-07-05 13:19:42 -05:00
t . Errorf ( "Wrong confirm message, expected\n'%v', got\n'%v'" , exp , tf . lastConfirm )
2015-04-22 17:11:11 -05:00
}
// test unknown contract
2015-06-23 09:48:33 -05:00
exp = fmt . Sprintf ( testExpNotice3 , registrar . UrlHintAddr )
2015-04-22 17:11:11 -05:00
2015-07-05 13:19:42 -05:00
_ , err = reg . SetUrlToHash ( addr , dochash , "file:///test.content" )
2015-04-22 17:11:11 -05:00
if err != nil {
t . Errorf ( "error registering: %v" , err )
2015-04-08 05:35:02 -05:00
}
2015-04-22 17:11:11 -05:00
if tf . lastConfirm != exp {
t . Errorf ( "Wrong confirm message, expected '%v', got '%v'" , exp , tf . lastConfirm )
2015-04-08 05:35:02 -05:00
}
2015-04-03 10:37:59 -05:00
}
2015-07-05 13:19:42 -05:00
func pendingTransactions ( repl * testFrontend , t * testing . T ) ( txc int64 , err error ) {
txs := repl . ethereum . TxPool ( ) . GetTransactions ( )
return int64 ( len ( txs ) ) , nil
}
func processTxs ( repl * testFrontend , t * testing . T , expTxc int ) bool {
var txc int64
var err error
for i := 0 ; i < 50 ; i ++ {
txc , err = pendingTransactions ( repl , t )
if err != nil {
t . Errorf ( "unexpected error checking pending transactions: %v" , err )
return false
}
if expTxc < int ( txc ) {
t . Errorf ( "too many pending transactions: expected %v, got %v" , expTxc , txc )
return false
} else if expTxc == int ( txc ) {
break
}
time . Sleep ( 100 * time . Millisecond )
}
if int ( txc ) != expTxc {
t . Errorf ( "incorrect number of pending transactions, expected %v, got %v" , expTxc , txc )
return false
}
2015-06-12 00:45:23 -05:00
err = repl . ethereum . StartMining ( runtime . NumCPU ( ) , "" )
2015-07-05 13:19:42 -05:00
if err != nil {
t . Errorf ( "unexpected error mining: %v" , err )
return false
}
defer repl . ethereum . StopMining ( )
timer := time . NewTimer ( 100 * time . Second )
height := new ( big . Int ) . Add ( repl . xeth . CurrentBlock ( ) . Number ( ) , big . NewInt ( 1 ) )
repl . wait <- height
select {
case <- timer . C :
// if times out make sure the xeth loop does not block
go func ( ) {
select {
case repl . wait <- nil :
case <- repl . wait :
}
} ( )
case <- repl . wait :
}
txc , err = pendingTransactions ( repl , t )
if err != nil {
t . Errorf ( "unexpected error checking pending transactions: %v" , err )
return false
}
if txc != 0 {
t . Errorf ( "%d trasactions were not mined" , txc )
return false
}
return true
}