js_test.go 11.5 KB
Newer Older
1 2 3 4
package main

import (
	"fmt"
5
	"io/ioutil"
6
	"os"
7
	"path/filepath"
8 9 10
	"regexp"
	"runtime"
	"strconv"
11 12 13
	"testing"

	"github.com/ethereum/go-ethereum/accounts"
14 15 16 17 18 19 20
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/common/compiler"
	"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"
21 22 23 24
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/eth"
)

25 26
const (
	testSolcPath = ""
27
	solcVersion  = "0.9.17"
28 29 30 31

	testKey     = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674"
	testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
	testBalance = "10000000000000000000"
Daniel A. Nagy's avatar
Daniel A. Nagy committed
32 33
	// of empty string
	testHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
34 35 36 37 38 39 40 41 42 43 44 45
)

var (
	testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}`
)

type testjethre struct {
	*jsre
	stateDb     *state.StateDB
	lastConfirm string
	ds          *docserver.DocServer
}
46

47
func (self *testjethre) UnlockAccount(acc []byte) bool {
48
	err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "")
49 50 51 52 53 54 55 56 57 58 59 60 61 62
	if err != nil {
		panic("unable to unlock")
	}
	return true
}

func (self *testjethre) ConfirmTransaction(tx string) bool {
	if self.ethereum.NatSpec {
		self.lastConfirm = natspec.GetNotice(self.xeth, tx, self.ds)
	}
	return true
}

func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
63
	tmp, err := ioutil.TempDir("", "geth-test")
64
	if err != nil {
65
		t.Fatal(err)
66 67
	}

68 69 70
	// set up mock genesis with balance on the testAddress
	core.GenesisData = []byte(testGenesis)

71
	ks := crypto.NewKeyStorePassphrase(filepath.Join(tmp, "keystore"))
72
	am := accounts.NewManager(ks)
73 74
	ethereum, err := eth.New(&eth.Config{
		DataDir:        tmp,
75
		AccountManager: am,
76
		MaxPeers:       0,
77 78 79
		Name:           "test",
	})
	if err != nil {
80
		t.Fatal("%v", err)
81
	}
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

	keyb, err := crypto.HexToECDSA(testKey)
	if err != nil {
		t.Fatal(err)
	}
	key := crypto.NewKeyFromECDSA(keyb)
	err = ks.StoreKey(key, "")
	if err != nil {
		t.Fatal(err)
	}

	err = am.Unlock(key.Address, "")
	if err != nil {
		t.Fatal(err)
	}

98
	assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
99 100 101 102 103 104 105 106
	ds, err := docserver.New("/")
	if err != nil {
		t.Errorf("Error creating DocServer: %v", err)
	}
	tf := &testjethre{ds: ds, stateDb: ethereum.ChainManager().State().Copy()}
	repl := newJSRE(ethereum, assetPath, testSolcPath, "", false, tf)
	tf.jsre = repl
	return tmp, tf, ethereum
107 108
}

109 110 111 112 113 114 115
// this line below is needed for transaction to be applied to the state in testing
// the heavy lifing is done in XEth.ApplyTestTxs
// this is fragile, overwriting xeth will result in
// process leaking since xeth loops cannot quit safely
// should be replaced by proper mining with testDAG for easy full integration tests
// txc, self.xeth = self.xeth.ApplyTestTxs(self.xeth.repl.stateDb, coinbase, txc)

116
func TestNodeInfo(t *testing.T) {
117
	tmp, repl, ethereum := testJEthRE(t)
118 119
	if err := ethereum.Start(); err != nil {
		t.Fatalf("error starting ethereum: %v", err)
120 121
	}
	defer ethereum.Stop()
122
	defer os.RemoveAll(tmp)
123 124
	want := `{"DiscPort":0,"IP":"0.0.0.0","ListenAddr":"","Name":"test","NodeID":"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","NodeUrl":"enode://00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000@0.0.0.0:0","TCPPort":0,"Td":"0"}`
	checkEvalJSON(t, repl, `admin.nodeInfo()`, want)
125 126 127
}

func TestAccounts(t *testing.T) {
128
	tmp, repl, ethereum := testJEthRE(t)
129 130
	if err := ethereum.Start(); err != nil {
		t.Fatalf("error starting ethereum: %v", err)
131 132
	}
	defer ethereum.Stop()
133
	defer os.RemoveAll(tmp)
134

135 136
	checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`"]`)
	checkEvalJSON(t, repl, `eth.coinbase`, `"`+testAddress+`"`)
137

138
	val, err := repl.re.Run(`admin.newAccount("password")`)
139 140 141
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
142 143 144
	addr := val.String()
	if !regexp.MustCompile(`0x[0-9a-f]{40}`).MatchString(addr) {
		t.Errorf("address not hex: %q", addr)
145 146
	}

147 148 149
	// skip until order fixed #824
	// checkEvalJSON(t, repl, `eth.accounts`, `["`+testAddress+`", "`+addr+`"]`)
	// checkEvalJSON(t, repl, `eth.coinbase`, `"`+testAddress+`"`)
150 151 152
}

func TestBlockChain(t *testing.T) {
153
	tmp, repl, ethereum := testJEthRE(t)
154 155
	if err := ethereum.Start(); err != nil {
		t.Fatalf("error starting ethereum: %v", err)
156 157
	}
	defer ethereum.Stop()
158
	defer os.RemoveAll(tmp)
159 160
	// get current block dump before export/import.
	val, err := repl.re.Run("JSON.stringify(admin.debug.dumpBlock())")
161 162 163
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
164
	beforeExport := val.String()
165

166
	// do the export
167
	extmp, err := ioutil.TempDir("", "geth-test-export")
168
	if err != nil {
169
		t.Fatal(err)
170
	}
171 172
	defer os.RemoveAll(extmp)
	tmpfile := filepath.Join(extmp, "export.chain")
173
	tmpfileq := strconv.Quote(tmpfile)
174

175
	checkEvalJSON(t, repl, `admin.export(`+tmpfileq+`)`, `true`)
176 177
	if _, err := os.Stat(tmpfile); err != nil {
		t.Fatal(err)
178 179
	}

180
	// check import, verify that dumpBlock gives the same result.
181
	checkEvalJSON(t, repl, `admin.import(`+tmpfileq+`)`, `true`)
182
	checkEvalJSON(t, repl, `admin.debug.dumpBlock()`, beforeExport)
183 184 185
}

func TestMining(t *testing.T) {
186
	tmp, repl, ethereum := testJEthRE(t)
187 188
	if err := ethereum.Start(); err != nil {
		t.Fatalf("error starting ethereum: %v", err)
189 190
	}
	defer ethereum.Stop()
191
	defer os.RemoveAll(tmp)
192
	checkEvalJSON(t, repl, `eth.mining`, `false`)
193 194 195
}

func TestRPC(t *testing.T) {
196
	tmp, repl, ethereum := testJEthRE(t)
197
	if err := ethereum.Start(); err != nil {
198 199 200 201
		t.Errorf("error starting ethereum: %v", err)
		return
	}
	defer ethereum.Stop()
202
	defer os.RemoveAll(tmp)
203

204 205 206
	checkEvalJSON(t, repl, `admin.startRPC("127.0.0.1", 5004)`, `true`)
}

207 208 209 210 211 212 213 214 215 216 217 218 219
func TestCheckTestAccountBalance(t *testing.T) {
	tmp, repl, ethereum := testJEthRE(t)
	if err := ethereum.Start(); err != nil {
		t.Errorf("error starting ethereum: %v", err)
		return
	}
	defer ethereum.Stop()
	defer os.RemoveAll(tmp)

	repl.re.Run(`primary = "` + testAddress + `"`)
	checkEvalJSON(t, repl, `eth.getBalance(primary)`, `"`+testBalance+`"`)
}

Daniel A. Nagy's avatar
Daniel A. Nagy committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
func TestSignature(t *testing.T) {
	tmp, repl, ethereum := testJEthRE(t)
	if err := ethereum.Start(); err != nil {
		t.Errorf("error starting ethereum: %v", err)
		return
	}
	defer ethereum.Stop()
	defer os.RemoveAll(tmp)

	val, err := repl.re.Run(`eth.sign({from: "` + testAddress + `", data: "` + testHash + `"})`)

	// This is a very preliminary test, lacking actual signature verification
	if err != nil {
		t.Errorf("Error runnig js: %v", err)
		return
	}
	output := val.String()
	t.Logf("Output: %v", output)

	regex := regexp.MustCompile(`^0x[0-9a-f]{130}$`)
	if !regex.MatchString(output) {
		t.Errorf("Signature is not 65 bytes represented in hexadecimal.")
		return
	}
}

246
func TestContract(t *testing.T) {
247
	t.Skip()
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276

	tmp, repl, ethereum := testJEthRE(t)
	if err := ethereum.Start(); err != nil {
		t.Errorf("error starting ethereum: %v", err)
		return
	}
	defer ethereum.Stop()
	defer os.RemoveAll(tmp)

	var txc uint64
	coinbase := common.HexToAddress(testAddress)
	resolver.New(repl.xeth).CreateContracts(coinbase)

	source := `contract test {\n` +
		"   /// @notice Will multiply `a` by 7." + `\n` +
		`   function multiply(uint a) returns(uint d) {\n` +
		`       return a * 7;\n` +
		`   }\n` +
		`}\n`

	checkEvalJSON(t, repl, `admin.contractInfo.stop()`, `true`)

	contractInfo, err := ioutil.ReadFile("info_test.json")
	if err != nil {
		t.Fatalf("%v", err)
	}
	checkEvalJSON(t, repl, `primary = eth.accounts[0]`, `"`+testAddress+`"`)
	checkEvalJSON(t, repl, `source = "`+source+`"`, `"`+source+`"`)

277 278
	// if solc is found with right version, test it, otherwise read from file
	sol, err := compiler.New("")
279 280
	if err != nil {
		t.Logf("solc not found: skipping compiler test")
281 282 283 284 285 286
	} else if sol.Version() != solcVersion {
		err = fmt.Errorf("solc wrong version found (%v, expect %v): skipping compiler test", sol.Version(), solcVersion)
		t.Log(err)
	}

	if err != nil {
287 288 289 290 291 292 293 294 295 296 297
		info, err := ioutil.ReadFile("info_test.json")
		if err != nil {
			t.Fatalf("%v", err)
		}
		_, err = repl.re.Run(`contract = JSON.parse(` + strconv.Quote(string(info)) + `)`)
		if err != nil {
			t.Errorf("%v", err)
		}
	} else {
		checkEvalJSON(t, repl, `contract = eth.compile.solidity(source)`, string(contractInfo))
	}
298

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
	checkEvalJSON(t, repl, `contract.code`, `"605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056"`)

	checkEvalJSON(
		t, repl,
		`contractaddress = eth.sendTransaction({from: primary, data: contract.code })`,
		`"0x5dcaace5982778b409c524873b319667eba5d074"`,
	)

	callSetup := `abiDef = JSON.parse('[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]');
Multiply7 = eth.contract(abiDef);
multiply7 = new Multiply7(contractaddress);
`

	_, err = repl.re.Run(callSetup)
	if err != nil {
		t.Errorf("unexpected error registering, got %v", err)
	}

	// updatespec
	// why is this sometimes failing?
	// checkEvalJSON(t, repl, `multiply7.multiply.call(6)`, `42`)
	expNotice := ""
	if repl.lastConfirm != expNotice {
		t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm)
	}

	// why 0?
	checkEvalJSON(t, repl, `eth.getBlock("pending", true).transactions.length`, `0`)

	txc, repl.xeth = repl.xeth.ApplyTestTxs(repl.stateDb, coinbase, txc)

	checkEvalJSON(t, repl, `admin.contractInfo.start()`, `true`)
	checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary, gas: "1000000", gasPrice: "100000" })`, `undefined`)
	expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x4a6c99e127191d2ee302e42182c338344b39a37a47cdbb17ab0f26b6802eb4d1'): {"params":[{"to":"0x5dcaace5982778b409c524873b319667eba5d074","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}`
	if repl.lastConfirm != expNotice {
		t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm)
	}

	checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`)
338
	checkEvalJSON(t, repl, `contenthash = admin.contractInfo.register(primary, contractaddress, contract, filename)`, `"0x0d067e2dd99a4d8f0c0279738b17130dd415a89f24a23f0e7cf68c546ae3089d"`)
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
	checkEvalJSON(t, repl, `admin.contractInfo.registerUrl(primary, contenthash, "file://"+filename)`, `true`)
	if err != nil {
		t.Errorf("unexpected error registering, got %v", err)
	}

	checkEvalJSON(t, repl, `admin.contractInfo.start()`, `true`)

	// update state
	txc, repl.xeth = repl.xeth.ApplyTestTxs(repl.stateDb, coinbase, txc)

	checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary, gas: "1000000", gasPrice: "100000" })`, `undefined`)
	expNotice = "Will multiply 6 by 7."
	if repl.lastConfirm != expNotice {
		t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm)
	}

}

func checkEvalJSON(t *testing.T, re *testjethre, expr, want string) error {
358
	val, err := re.re.Run("JSON.stringify(" + expr + ")")
359 360
	if err == nil && val.String() != want {
		err = fmt.Errorf("Output mismatch for `%s`:\ngot:  %s\nwant: %s", expr, val.String(), want)
361
	}
362 363
	if err != nil {
		_, file, line, _ := runtime.Caller(1)
364
		file = filepath.Base(file)
365 366
		fmt.Printf("\t%s:%d: %v\n", file, line, err)
		t.Fail()
367
	}
368
	return err
369
}