2015-04-03 15:37:59 +00:00
package natspec
import (
2015-04-22 22:11:11 +00:00
"fmt"
2015-04-03 15:37:59 +00:00
"io/ioutil"
"os"
2015-05-12 15:04:35 +00:00
"strings"
2015-04-03 15:37:59 +00:00
"testing"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
2015-04-06 06:01:36 +00:00
"github.com/ethereum/go-ethereum/common/docserver"
2015-04-03 15:37:59 +00:00
"github.com/ethereum/go-ethereum/common/resolver"
"github.com/ethereum/go-ethereum/core"
2015-04-14 13:20:52 +00:00
"github.com/ethereum/go-ethereum/core/state"
2015-04-03 15:37:59 +00:00
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
xe "github.com/ethereum/go-ethereum/xeth"
)
2015-04-14 14:01:25 +00:00
const (
2015-04-22 22:11:11 +00:00
testBalance = "10000000000000000000"
2015-04-14 14:01:25 +00:00
2015-04-22 22:11:11 +00:00
testFileName = "long_file_name_for_testing_registration_of_URLs_longer_than_32_bytes.content"
2015-04-17 11:46:38 +00:00
2015-04-22 22:11:11 +00:00
testNotice = "Register key `utils.toHex(_key)` <- content `utils.toHex(_content)`"
2015-04-07 09:50:17 +00:00
2015-04-22 22:11:11 +00: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"}]} `
testExpNotice3 = ` About to submit transaction (no NatSpec info found for contract: content hash not found for '0x1392c62d05b2d149e22a339c531157ae06b44d39a674cce500064b12b9aeb019'): { "params":[ { "to":"%s","data": "0x300a3bbfb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066696c653a2f2f2f746573742e636f6e74656e74"}]} `
)
const (
testUserDoc = `
2015-04-07 09:50:17 +00:00
{
"methods" : {
"register(uint256,uint256)" : {
"notice" : "` + testNotice + `"
}
} ,
"invariants" : [
{ "notice" : "" }
] ,
"construction" : [
{ "notice" : "" }
]
}
`
2015-04-22 22:11:11 +00:00
testAbiDefinition = `
2015-04-07 09:50:17 +00:00
[ {
"name" : "register" ,
"constant" : false ,
"type" : "function" ,
"inputs" : [ {
"name" : "_key" ,
"type" : "uint256"
} , {
"name" : "_content" ,
"type" : "uint256"
} ] ,
"outputs" : [ ]
} ]
`
2015-04-22 22:11:11 +00:00
testContractInfo = `
2015-04-07 09:50:17 +00:00
{
2015-04-22 22:11:11 +00:00
"userDoc" : ` + testUserDoc + ` ,
"abiDefinition" : ` + testAbiDefinition + `
2015-04-07 09:50:17 +00:00
}
`
2015-04-22 22:11:11 +00:00
)
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
}
2015-04-07 09:50:17 +00:00
2015-04-22 22:11:11 +00:00
func ( self * testFrontend ) UnlockAccount ( acc [ ] byte ) bool {
2015-04-26 15:18:06 +00:00
self . ethereum . AccountManager ( ) . Unlock ( common . BytesToAddress ( acc ) , "password" )
2015-04-03 15:37:59 +00:00
return true
}
2015-04-22 22:11:11 +00:00
func ( self * testFrontend ) ConfirmTransaction ( tx string ) bool {
if self . wantNatSpec {
2015-04-07 09:50:17 +00:00
ds , err := docserver . New ( "/tmp/" )
if err != nil {
2015-04-22 22:11:11 +00:00
self . t . Errorf ( "Error creating DocServer: %v" , err )
2015-04-07 09:50:17 +00:00
}
2015-04-22 22:11:11 +00:00
self . lastConfirm = GetNotice ( self . xeth , tx , ds )
2015-04-07 09:50:17 +00:00
}
2015-04-06 06:01:36 +00:00
return true
}
2015-04-03 15:37:59 +00:00
2015-04-06 06:01:36 +00:00
func testEth ( t * testing . T ) ( ethereum * eth . Ethereum , err error ) {
2015-04-22 22:11:11 +00:00
2015-04-19 18:24:46 +00:00
os . RemoveAll ( "/tmp/eth-natspec/" )
2015-04-22 22:11:11 +00:00
2015-05-12 16:33:04 +00:00
err = os . MkdirAll ( "/tmp/eth-natspec/keystore" , os . ModePerm )
2015-04-03 15:37:59 +00:00
if err != nil {
2015-04-22 22:11:11 +00:00
panic ( err )
2015-04-03 15:37:59 +00:00
}
2015-04-22 22:11:11 +00:00
// create a testAddress
2015-05-12 16:33:04 +00:00
ks := crypto . NewKeyStorePassphrase ( "/tmp/eth-natspec/keystore" )
2015-04-22 22:11:11 +00:00
am := accounts . NewManager ( ks )
testAccount , err := am . NewAccount ( "password" )
2015-04-03 15:37:59 +00:00
if err != nil {
2015-04-22 22:11:11 +00:00
panic ( err )
2015-04-03 15:37:59 +00:00
}
2015-05-12 15:04:35 +00:00
testAddress := strings . TrimPrefix ( testAccount . Address . Hex ( ) , "0x" )
2015-04-22 22:11:11 +00:00
// set up mock genesis with balance on the testAddress
core . GenesisData = [ ] byte ( ` {
"` + testAddress + `" : { "balance" : "` + testBalance + `" }
} ` )
2015-04-03 15:37:59 +00:00
2015-04-22 22:11:11 +00:00
// only use minimalistic stack with no networking
2015-04-03 15:37:59 +00:00
ethereum , err = eth . New ( & eth . Config {
2015-04-19 18:24:46 +00:00
DataDir : "/tmp/eth-natspec" ,
2015-04-22 22:11:11 +00:00
AccountManager : am ,
MaxPeers : 0 ,
2015-04-03 15:37:59 +00:00
} )
if err != nil {
2015-04-22 22:11:11 +00:00
panic ( err )
2015-04-03 15:37:59 +00:00
}
return
}
2015-04-06 06:01:36 +00:00
func testInit ( t * testing . T ) ( self * testFrontend ) {
2015-04-22 22:11:11 +00:00
// initialise and start minimal ethereum stack
2015-04-06 06:01:36 +00:00
ethereum , err := testEth ( t )
if err != nil {
2015-04-19 18:24:46 +00:00
t . Errorf ( "error creating ethereum: %v" , err )
2015-04-06 06:01:36 +00:00
return
}
err = ethereum . Start ( )
if err != nil {
t . Errorf ( "error starting ethereum: %v" , err )
return
}
2015-04-22 22:11:11 +00:00
// mock frontend
2015-04-06 06:01:36 +00:00
self = & testFrontend { t : t , ethereum : ethereum }
self . xeth = xe . New ( ethereum , self )
2015-04-22 22:11:11 +00:00
addr , _ := ethereum . Etherbase ( )
2015-04-06 06:01:36 +00:00
self . coinbase = addr
2015-04-14 13:20:52 +00:00
self . stateDb = self . ethereum . ChainManager ( ) . State ( ) . Copy ( )
2015-04-22 22:11:11 +00:00
// 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)
2015-04-07 12:46:14 +00:00
2015-04-22 22:11:11 +00:00
return
2015-04-14 13:20:52 +00:00
2015-04-07 09:50:17 +00:00
}
2015-04-22 22:11:11 +00:00
// 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
2015-04-07 09:50:17 +00:00
func ( self * testFrontend ) applyTxs ( ) {
2015-04-22 22:11:11 +00:00
self . txc , self . xeth = self . xeth . ApplyTestTxs ( self . stateDb , self . coinbase , self . txc )
return
2015-04-06 06:01:36 +00:00
}
2015-04-03 15:37:59 +00:00
2015-04-22 22:11:11 +00:00
// end to end test
2015-04-06 06:01:36 +00:00
func TestNatspecE2E ( t * testing . T ) {
2015-04-22 22:11:11 +00:00
// t.Skip()
2015-04-03 15:37:59 +00:00
2015-04-06 06:01:36 +00:00
tf := testInit ( t )
defer tf . ethereum . Stop ( )
2015-04-03 15:37:59 +00:00
2015-04-22 22:11:11 +00:00
// 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 ) ) )
2015-04-03 15:37:59 +00:00
2015-04-22 22:11:11 +00:00
// 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 ) )
2015-04-03 15:37:59 +00:00
2015-04-22 22:11:11 +00:00
// use resolver to register codehash->dochash->url
registry := resolver . New ( tf . xeth )
_ , err := registry . Register ( tf . coinbase , codehash , dochash , "file:///" + testFileName )
2015-04-07 09:50:17 +00:00
if err != nil {
2015-04-22 22:11:11 +00:00
t . Errorf ( "error registering: %v" , err )
2015-04-07 09:50:17 +00:00
}
2015-04-22 22:11:11 +00:00
// apply txs to the state
tf . applyTxs ( )
2015-04-07 09:50:17 +00:00
2015-04-08 10:35:02 +00:00
// NatSpec info for register method of HashReg contract installed
// now using the same transactions to check confirm messages
2015-04-22 22:11:11 +00:00
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 )
}
2015-04-07 09:50:17 +00:00
if tf . lastConfirm != testExpNotice {
2015-04-22 22:11:11 +00:00
t . Errorf ( "Wrong confirm message. expected '%v', got '%v'" , testExpNotice , tf . lastConfirm )
2015-04-07 09:50:17 +00:00
}
2015-04-03 15:37:59 +00:00
2015-04-22 22:11:11 +00:00
// 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 )
2015-04-08 10:35:02 +00:00
}
2015-04-22 22:11:11 +00:00
if tf . lastConfirm != exp {
t . Errorf ( "Wrong confirm message, expected '%v', got '%v'" , exp , tf . lastConfirm )
2015-04-08 10:35:02 +00:00
}
2015-04-03 15:37:59 +00:00
}