teth 0.1.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +449 -33
- data/bin/teth +75 -26
- data/lib/teth/erbs/Gemfile +1 -1
- data/lib/teth/erbs/contract_test.rb +5 -2
- data/lib/teth/erbs/migration +3 -0
- data/lib/teth/minitest.rb +5 -12
- data/lib/teth/templates/bin/attach.sh +5 -0
- data/lib/teth/templates/bin/build.sh +13 -18
- data/lib/teth/templates/bin/import_keys.sh +2 -0
- data/lib/teth/templates/bin/init.sh +6 -1
- data/lib/teth/templates/bin/migrate.sh +4 -3
- data/lib/teth/templates/bin/private_blockchain.sh +4 -1
- data/lib/teth/templates/bin/solc_helper.rb +2 -2
- data/lib/teth/templates/bin/test.sh +18 -10
- data/lib/teth/templates/gitignore +3 -0
- data/lib/teth/templates/private_keys/import.sh +1 -1
- metadata +22 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 043f398e22fcc354d5eb00e278f4b68b652abc28
|
4
|
+
data.tar.gz: c29c699f115c04c957b430857568da8f61aaa911
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d87429ea3b59ddaea5b3d7e56b05ae6ebe232ea3ade8cc507b71b05d8c6ee6307ba2f64c08d3c6ad413a48ee760fb0be1f66314f78f28fc1ac20e69b6f9b547b
|
7
|
+
data.tar.gz: dc5fc847cc0d7a5dccf21086770f6ad90656aef27a47cc13f46c473ecec6dfbc042fba83ff3078e96e7e582931a612614608d686ac9b2580693c5be4e4eec9d2
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
Teth is a Ethereum smart contract test framework in ruby.It provides two testing environments: testing in ruby EVM and testing in geth.You don't need to understand ruby grammar, just enjoy syntactic sugar.
|
3
3
|
|
4
4
|
## Dependencies
|
5
|
-
- Solidity ~> 0.
|
5
|
+
- Solidity ~> 0.4.1
|
6
6
|
- Ruby ~> 2.2.2
|
7
7
|
- Go-ethereum ~> 1.4.11
|
8
8
|
|
@@ -13,53 +13,182 @@ bundle install teth
|
|
13
13
|
|
14
14
|
## How to use
|
15
15
|
|
16
|
-
Help command
|
16
|
+
#### Help command
|
17
17
|
```
|
18
18
|
$ teth
|
19
|
+
Usage: teth COMMAND [ARGS]
|
20
|
+
The most common teth commands are:
|
21
|
+
new Create a new Smart Contract application. "teth new my_app" creates a
|
22
|
+
new application called my_app in "./my_app" (short-cut alias: "n")
|
23
|
+
generate Generate new solidity smart contract and test files. "teth generate token"
|
24
|
+
creates Token contract and corresponding test files. (short-cut alias: "g")
|
25
|
+
test Run your ruby tests in ruby evm. (short-cut alias: "t")
|
26
|
+
init Bootstraps and initialises a new genesis block. "teth init" creates data directory
|
27
|
+
for private chain.(short-cut alias: "i")
|
28
|
+
import_keys Import keys to private chain (short-cut alias: "ik")
|
29
|
+
build Build contract (short-cut alias: "b")
|
30
|
+
migrate Deploy contract on private chain (short-cut alias: "m")
|
31
|
+
server Start geth server (short-cut alias: "s")
|
32
|
+
console Start geth attach (short-cut alias: "c")
|
33
|
+
gtest Run your javascript tests on geth (short-cut alias: "gt")
|
34
|
+
|
35
|
+
All commands can be run with -h (or --help) for more information.
|
36
|
+
|
19
37
|
```
|
38
|
+
#### Creeat project
|
20
39
|
Create a new Smart Contract application
|
40
|
+
```shell
|
41
|
+
$ teth n examples
|
42
|
+
Creating project examples...
|
43
|
+
Resolving dependencies...
|
44
|
+
Using ffi 1.9.14
|
45
|
+
Using little-plugger 1.1.4
|
46
|
+
Using multi_json 1.12.1
|
47
|
+
Using digest-sha3 1.1.0
|
48
|
+
Using ethash 0.2.0
|
49
|
+
Using fiddler-rb 0.1.2
|
50
|
+
Using lru_redux 1.1.0
|
51
|
+
Using minitest 5.9.0
|
52
|
+
Using rlp 0.7.3
|
53
|
+
Using bundler 1.12.5
|
54
|
+
Using bitcoin-secp256k1 0.4.0
|
55
|
+
Using logging 2.1.0
|
56
|
+
Using leveldb 0.1.9
|
57
|
+
Using block_logger 0.1.2
|
58
|
+
Using ruby-ethereum 0.9.5
|
59
|
+
Using teth 0.1.1
|
60
|
+
Bundle complete! 1 Gemfile dependency, 16 gems now installed.
|
61
|
+
Use `bundle show [gemname]` to see where a bundled gem is installed.
|
62
|
+
Done.
|
63
|
+
|
64
|
+
$ cd examples
|
21
65
|
```
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
66
|
+
#### Generate new Smart Contract and test files
|
67
|
+
```shell
|
68
|
+
$ teth g token
|
69
|
+
Creating Token contract file...
|
70
|
+
Creating token test files...
|
71
|
+
Done.
|
26
72
|
```
|
27
|
-
|
73
|
+
This generates contract file `contracts/Token.sol`, and test files `tests/token_test.rb`, `gtests/Token_test.js`.
|
74
|
+
|
75
|
+
Edit token contract and test file.
|
76
|
+
|
77
|
+
`contracts/Token.sol`
|
78
|
+
```javascript
|
79
|
+
pragma solidity ^0.4.0;
|
80
|
+
|
81
|
+
contract Token {
|
82
|
+
address issuer;
|
83
|
+
mapping (address => uint) balances;
|
84
|
+
|
85
|
+
event Issue(address account, uint amount);
|
86
|
+
event Transfer(address from, address to, uint amount);
|
87
|
+
|
88
|
+
function Token() {
|
89
|
+
issuer = msg.sender;
|
90
|
+
}
|
91
|
+
|
92
|
+
function issue(address account, uint amount) {
|
93
|
+
if (msg.sender != issuer) throw;
|
94
|
+
balances[account] += amount;
|
95
|
+
Issue(account, amount);
|
96
|
+
}
|
97
|
+
|
98
|
+
function transfer(address to, uint amount) {
|
99
|
+
if (balances[msg.sender] < amount) throw;
|
100
|
+
|
101
|
+
balances[msg.sender] -= amount;
|
102
|
+
balances[to] += amount;
|
103
|
+
|
104
|
+
Transfer(msg.sender, to, amount);
|
105
|
+
}
|
106
|
+
|
107
|
+
function getBalance(address account) constant returns (uint) {
|
108
|
+
return balances[account];
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
28
112
|
```
|
29
|
-
|
113
|
+
|
114
|
+
`tests/token_test.rb`
|
115
|
+
```ruby
|
116
|
+
require 'teth/minitest'
|
117
|
+
|
118
|
+
class TokenTest < Teth::Minitest
|
119
|
+
def test_contract_deployed
|
120
|
+
assert_equal false, contract.address.nil?
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_issue_balance
|
124
|
+
assert_equal 0, contract.getBalance(bob)
|
125
|
+
contract.issue bob, 100
|
126
|
+
assert_equal 100, contract.getBalance(bob)
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_issue_exception
|
130
|
+
assert_raises(TransactionFailed) do
|
131
|
+
contract.issue bob, 100, sender: eve_privkey
|
132
|
+
end
|
133
|
+
assert_equal 0, contract.getBalance(bob)
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_token_transfer
|
137
|
+
contract.issue bob, 100
|
138
|
+
contract.transfer carol, 90, sender: bob_privkey
|
139
|
+
assert_equal 90, contract.getBalance(carol)
|
140
|
+
|
141
|
+
assert_raises(TransactionFailed) { contract.transfer carol, 90, sender: bob_privkey }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
30
145
|
```
|
31
|
-
|
146
|
+
|
147
|
+
#### Run tests in ruby evm
|
148
|
+
```shell
|
149
|
+
$ teth t token
|
150
|
+
Test Token contract...
|
151
|
+
Run options: --seed 2192
|
152
|
+
|
153
|
+
# Running:
|
154
|
+
|
155
|
+
....
|
156
|
+
|
157
|
+
Finished in 1.935546s, 2.0666 runs/s, 3.6166 assertions/s.
|
158
|
+
|
159
|
+
4 runs, 7 assertions, 0 failures, 0 errors, 0 skips
|
160
|
+
Done.
|
32
161
|
```
|
33
162
|
|
34
|
-
|
163
|
+
#### Unit tests
|
35
164
|
You can wirte fast, simple tests.
|
36
165
|
```ruby
|
37
|
-
|
38
|
-
include Ethereum
|
166
|
+
require 'teth/minitest'
|
39
167
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
@c = @state.abi_contract @solidity_code, language: :solidity
|
168
|
+
class TokenTest < Teth::Minitest
|
169
|
+
def test_contract_deployed
|
170
|
+
assert_equal false, contract.address.nil?
|
44
171
|
end
|
45
172
|
|
46
173
|
def test_issue_balance
|
47
|
-
assert_equal 0,
|
48
|
-
|
49
|
-
assert_equal 100,
|
174
|
+
assert_equal 0, contract.getBalance(bob)
|
175
|
+
contract.issue bob, 100
|
176
|
+
assert_equal 100, contract.getBalance(bob)
|
50
177
|
end
|
51
178
|
|
52
179
|
def test_issue_exception
|
53
|
-
assert_raises(TransactionFailed)
|
54
|
-
|
180
|
+
assert_raises(TransactionFailed) do
|
181
|
+
contract.issue bob, 100, sender: eve_privkey
|
182
|
+
end
|
183
|
+
assert_equal 0, contract.getBalance(bob)
|
55
184
|
end
|
56
185
|
|
57
186
|
def test_token_transfer
|
58
|
-
|
59
|
-
|
60
|
-
assert_equal 90,
|
187
|
+
contract.issue bob, 100
|
188
|
+
contract.transfer carol, 90, sender: bob_privkey
|
189
|
+
assert_equal 90, contract.getBalance(carol)
|
61
190
|
|
62
|
-
assert_raises(TransactionFailed) {
|
191
|
+
assert_raises(TransactionFailed) { contract.transfer carol, 90, sender: bob_privkey }
|
63
192
|
end
|
64
193
|
end
|
65
194
|
|
@@ -67,11 +196,298 @@ end
|
|
67
196
|
More details:
|
68
197
|
https://github.com/seattlerb/minitest
|
69
198
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
199
|
+
#### Init geth block
|
200
|
+
```shell
|
201
|
+
$ teth init
|
202
|
+
Initialising a new genesis block...
|
203
|
+
***** Using geth at: geth
|
204
|
+
I0917 16:01:17.338908 ethdb/database.go:82] Alloted 16MB cache and 16 file handles to /Users/u2/cryptape/teth/examples/data/chaindata
|
205
|
+
I0917 16:01:17.347151 cmd/geth/main.go:299] successfully wrote genesis block and/or chain rule set: 611596e7979cd4e7ca1531260fa706093a5492ecbdf58f20a39545397e424d04
|
206
|
+
```
|
207
|
+
#### Import keys to geth
|
208
|
+
```shell
|
209
|
+
$ teth ik
|
210
|
+
Importing keys, this will take a while, please be patient......
|
211
|
+
***** Using geth at: geth
|
212
|
+
***** Import all pre-funded private keys
|
213
|
+
Notice: No need to input your password. The default password is 123456
|
214
|
+
spawn geth --datadir data account import ./private_keys/3ae88fe370c39384fc16da2c9e768cf5d2495b48.key
|
215
|
+
Your new account is locked with a password. Please give a password. Do not forget this password.
|
216
|
+
Passphrase:
|
217
|
+
Repeat passphrase:
|
218
|
+
Address: {3ae88fe370c39384fc16da2c9e768cf5d2495b48}
|
219
|
+
Notice: No need to input your password. The default password is 123456
|
220
|
+
spawn geth --datadir data account import ./private_keys/81063419f13cab5ac090cd8329d8fff9feead4a0.key
|
221
|
+
Your new account is locked with a password. Please give a password. Do not forget this password.
|
222
|
+
Passphrase:
|
223
|
+
Repeat passphrase:
|
224
|
+
Address: {81063419f13cab5ac090cd8329d8fff9feead4a0}
|
225
|
+
Notice: No need to input your password. The default password is 123456
|
226
|
+
spawn geth --datadir data account import ./private_keys/9da26fc2e1d6ad9fdd46138906b0104ae68a65d8.key
|
227
|
+
Your new account is locked with a password. Please give a password. Do not forget this password.
|
228
|
+
Passphrase:
|
229
|
+
Repeat passphrase:
|
230
|
+
Address: {9da26fc2e1d6ad9fdd46138906b0104ae68a65d8}
|
231
|
+
***** Done.
|
232
|
+
|
233
|
+
```
|
234
|
+
Notice: *This will take a while, please be patient. No need to input your password.*
|
235
|
+
|
236
|
+
#### Build contract
|
237
|
+
```shell
|
238
|
+
$ teth build token
|
239
|
+
Building contract Token
|
240
|
+
|
241
|
+
======= Token =======
|
242
|
+
Gas estimation:
|
243
|
+
construction:
|
244
|
+
20201 + 73400 = 93601
|
245
|
+
external:
|
246
|
+
|
247
|
+
|
248
|
+
-------------------------------------
|
249
|
+
Enter Gas:
|
250
|
+
400000
|
251
|
+
Enter Value To Be Transferred:
|
252
|
+
|
253
|
+
Enter Input:
|
254
|
+
|
255
|
+
Done.
|
256
|
+
```
|
257
|
+
Build all contracts if no contract name provided.
|
258
|
+
|
259
|
+
#### Start geth server
|
260
|
+
```shell
|
261
|
+
$ teth server
|
262
|
+
***** Using geth at: geth
|
263
|
+
Start geth server...
|
264
|
+
I0917 16:17:16.882572 ethdb/database.go:82] Alloted 128MB cache and 1024 file handles to data/chaindata
|
265
|
+
I0917 16:17:16.894415 ethdb/database.go:169] closed db:data/chaindata
|
266
|
+
I0917 16:17:16.895446 ethdb/database.go:82] Alloted 128MB cache and 1024 file handles to data/chaindata
|
267
|
+
I0917 16:17:16.946341 eth/backend.go:621] upgrading db log bloom bins
|
268
|
+
I0917 16:17:16.946478 eth/backend.go:629] upgrade completed in 142.276µs
|
269
|
+
I0917 16:17:16.946513 ethdb/database.go:82] Alloted 16MB cache and 16 file handles to data/dapp
|
270
|
+
I0917 16:17:16.951072 eth/backend.go:172] Protocol Versions: [63 62], Network Id: 31415926
|
271
|
+
I0917 16:17:16.951142 eth/backend.go:201] Blockchain DB Version: 3
|
272
|
+
I0917 16:17:16.953641 core/blockchain.go:206] Last header: #0 [611596e7…] TD=131072
|
273
|
+
I0917 16:17:16.953667 core/blockchain.go:207] Last block: #0 [611596e7…] TD=131072
|
274
|
+
I0917 16:17:16.953678 core/blockchain.go:208] Fast block: #0 [611596e7…] TD=131072
|
275
|
+
I0917 16:17:16.954420 p2p/server.go:313] Starting Server
|
276
|
+
I0917 16:17:16.955324 p2p/server.go:556] Listening on [::]:30303
|
277
|
+
I0917 16:17:16.957427 node/node.go:296] IPC endpoint opened: data/geth.ipc
|
278
|
+
I0917 16:17:16.959797 node/node.go:366] HTTP endpoint opened: http://localhost:8545
|
279
|
+
I0917 16:17:17.945231 cmd/geth/accountcmd.go:189] Unlocked account 3ae88fe370c39384fc16da2c9e768cf5d2495b48
|
280
|
+
I0917 16:17:19.158064 p2p/nat/nat.go:111] mapped network port tcp:30303 -> 30303 (ethereum p2p) using UPNP IGDv1-IP1
|
281
|
+
```
|
282
|
+
#### Migrate
|
283
|
+
Deploy your contract on geth, must keep teth sever started.
|
284
|
+
```shell
|
285
|
+
$ teth m
|
286
|
+
Migrating contract token
|
287
|
+
***** Using geth at: geth
|
288
|
+
null
|
289
|
+
Contract transaction send: TransactionHash: 0x3a9ca7a774a4bc5b3ba23b57f3c65a5debbfcbba422f902009909604ee668a63 waiting to be mined...
|
290
|
+
Compiled Object : TokenCompiled
|
291
|
+
Contract : TokenContract
|
292
|
+
Contract Instance : Token
|
293
|
+
true
|
294
|
+
Contract mined! Address: 0x3a020580345e79e223580d8d6a50e063667f19b5
|
295
|
+
Done.
|
296
|
+
|
297
|
+
```
|
298
|
+
This deploys contract on geth, and creates two files.One is `temp/db/Token.json` which keeps abi and address.
|
299
|
+
```json
|
300
|
+
{
|
301
|
+
"Token": {
|
302
|
+
"abi": [
|
303
|
+
{
|
304
|
+
"constant": false,
|
305
|
+
"inputs": [
|
306
|
+
{
|
307
|
+
"name": "account",
|
308
|
+
"type": "address"
|
309
|
+
},
|
310
|
+
{
|
311
|
+
"name": "amount",
|
312
|
+
"type": "uint256"
|
313
|
+
}
|
314
|
+
],
|
315
|
+
"name": "issue",
|
316
|
+
"outputs": [
|
317
|
+
|
318
|
+
],
|
319
|
+
"payable": false,
|
320
|
+
"type": "function"
|
321
|
+
},
|
322
|
+
{
|
323
|
+
"constant": false,
|
324
|
+
"inputs": [
|
325
|
+
{
|
326
|
+
"name": "to",
|
327
|
+
"type": "address"
|
328
|
+
},
|
329
|
+
{
|
330
|
+
"name": "amount",
|
331
|
+
"type": "uint256"
|
332
|
+
}
|
333
|
+
],
|
334
|
+
"name": "transfer",
|
335
|
+
"outputs": [
|
336
|
+
|
337
|
+
],
|
338
|
+
"payable": false,
|
339
|
+
"type": "function"
|
340
|
+
},
|
341
|
+
{
|
342
|
+
"constant": true,
|
343
|
+
"inputs": [
|
344
|
+
{
|
345
|
+
"name": "account",
|
346
|
+
"type": "address"
|
347
|
+
}
|
348
|
+
],
|
349
|
+
"name": "getBalance",
|
350
|
+
"outputs": [
|
351
|
+
{
|
352
|
+
"name": "",
|
353
|
+
"type": "uint256"
|
354
|
+
}
|
355
|
+
],
|
356
|
+
"payable": false,
|
357
|
+
"type": "function"
|
358
|
+
},
|
359
|
+
{
|
360
|
+
"inputs": [
|
361
|
+
|
362
|
+
],
|
363
|
+
"type": "constructor"
|
364
|
+
},
|
365
|
+
{
|
366
|
+
"anonymous": false,
|
367
|
+
"inputs": [
|
368
|
+
{
|
369
|
+
"indexed": false,
|
370
|
+
"name": "account",
|
371
|
+
"type": "address"
|
372
|
+
},
|
373
|
+
{
|
374
|
+
"indexed": false,
|
375
|
+
"name": "amount",
|
376
|
+
"type": "uint256"
|
377
|
+
}
|
378
|
+
],
|
379
|
+
"name": "Issue",
|
380
|
+
"type": "event"
|
381
|
+
},
|
382
|
+
{
|
383
|
+
"anonymous": false,
|
384
|
+
"inputs": [
|
385
|
+
{
|
386
|
+
"indexed": false,
|
387
|
+
"name": "from",
|
388
|
+
"type": "address"
|
389
|
+
},
|
390
|
+
{
|
391
|
+
"indexed": false,
|
392
|
+
"name": "to",
|
393
|
+
"type": "address"
|
394
|
+
},
|
395
|
+
{
|
396
|
+
"indexed": false,
|
397
|
+
"name": "amount",
|
398
|
+
"type": "uint256"
|
399
|
+
}
|
400
|
+
],
|
401
|
+
"name": "Transfer",
|
402
|
+
"type": "event"
|
403
|
+
}
|
404
|
+
],
|
405
|
+
"bin": "0x606060405260008054600160a060020a0319163317905561016f806100246000396000f3606060405260e060020a6000350463867904b48114610034578063a9059cbb1461005e578063f8b2cb4f14610092575b610002565b34610002576100bc600435602435600054600160a060020a0390811633909116146100be57610002565b34610002576100bc60043560243533600160a060020a03166000908152600160205260409020548190101561010f57610002565b3461000257600160a060020a03600435166000908152600160205260409020546060908152602090f35b005b600160a060020a03821660008181526001602052604090819020805484019055606091825260808390527fc65a3f767206d2fdcede0b094a4840e01c0dd0be1888b5ba800346eaa0123c1691a15050565b6040600081812080548490039055600160a060020a03808516808352929091208054840190553316606090815260809190915260a08290527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9080a1505056",
|
406
|
+
"devdoc": {
|
407
|
+
"methods": {
|
408
|
+
}
|
409
|
+
},
|
410
|
+
"userdoc": {
|
411
|
+
"methods": {
|
412
|
+
}
|
413
|
+
}
|
414
|
+
},
|
415
|
+
"address": "0x3a020580345e79e223580d8d6a50e063667f19b5"
|
416
|
+
}
|
417
|
+
```
|
418
|
+
Another is `temp/migrations/Token.js`
|
419
|
+
```javascript
|
420
|
+
var TokenContract = web3.eth.contract([{"constant":false, "inputs":[{"name":"account", "type":"address"}, {"name":"amount", "type":"uint256"}], "name":"issue", "outputs":[], "payable":false, "type":"function"}, {"constant":false, "inputs":[{"name":"to", "type":"address"}, {"name":"amount", "type":"uint256"}], "name":"transfer", "outputs":[], "payable":false, "type":"function"}, {"constant":true, "inputs":[{"name":"account", "type":"address"}], "name":"getBalance", "outputs":[{"name":"", "type":"uint256"}], "payable":false, "type":"function"}, {"inputs":[], "type":"constructor"}, {"anonymous":false, "inputs":[{"indexed":false, "name":"account", "type":"address"}, {"indexed":false, "name":"amount", "type":"uint256"}], "name":"Issue", "type":"event"}, {"anonymous":false, "inputs":[{"indexed":false, "name":"from", "type":"address"}, {"indexed":false, "name":"to", "type":"address"}, {"indexed":false, "name":"amount", "type":"uint256"}], "name":"Transfer", "type":"event"}]);
|
421
|
+
|
422
|
+
var Token = TokenContract.at('0x80d29fb7f81d2ccd77c708b6135389c9c08653dc');
|
423
|
+
|
424
|
+
```
|
425
|
+
Deploy all contracts if no contract name provided.
|
426
|
+
|
427
|
+
#### Write your own javascript test
|
428
|
+
`gtests/Token_test.js`
|
429
|
+
```javascript
|
430
|
+
loadScript('temp/migrations/Token.js');
|
431
|
+
|
432
|
+
var balance = Token.getBalance.call(web3.eth.accounts[0], { from: web3.eth.accounts[0] })
|
433
|
+
|
434
|
+
console.log("balance is: ", balance);
|
435
|
+
|
436
|
+
Token.issue.sendTransaction(web3.eth.accounts[0], 10000, { from: web3.eth.accounts[0] }, function(err, tx){
|
437
|
+
if(err){
|
438
|
+
console.log("issue error!");
|
439
|
+
} else {
|
440
|
+
console.log("issue success. tx: ", tx);
|
441
|
+
}
|
442
|
+
})
|
443
|
+
|
444
|
+
miner.start();admin.sleepBlocks(2);miner.stop();
|
445
|
+
|
446
|
+
balance = Token.getBalance.call(web3.eth.accounts[0], { from: web3.eth.accounts[0] })
|
447
|
+
|
448
|
+
console.log("balance is: ", balance);
|
449
|
+
|
450
|
+
```
|
451
|
+
|
452
|
+
#### Run gtest
|
453
|
+
```
|
454
|
+
$ teth gt token
|
455
|
+
***** Using geth at: geth
|
456
|
+
Testing contract Token...
|
457
|
+
balance is: 0
|
458
|
+
issue success. tx: 0x7fd24d1903345d4f70208c41fc3a1bd71be63f8dd7db7c654f2d3a7c176b4031
|
459
|
+
balance is: 10000
|
460
|
+
true
|
461
|
+
Done.
|
462
|
+
```
|
463
|
+
|
464
|
+
:beers:
|
465
|
+
|
466
|
+
## Frequently Asked Questions
|
467
|
+
|
468
|
+
#### Error: Account does not exist or account balance too low while `teth migrate token`
|
469
|
+
This is just low balance for your accoutns. Just mining for a while will be ok.
|
470
|
+
```shell
|
471
|
+
$ teth c
|
472
|
+
***** Using geth at: geth
|
473
|
+
Starting geth attach...
|
474
|
+
Welcome to the Geth JavaScript console!
|
475
|
+
|
476
|
+
instance: Geth/v1.4.11-stable-fed692f6/darwin/go1.7
|
477
|
+
coinbase: 0x3ae88fe370c39384fc16da2c9e768cf5d2495b48
|
478
|
+
at block: 3 (Sat, 17 Sep 2016 17:42:50 CST)
|
479
|
+
datadir: data
|
480
|
+
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
|
481
|
+
|
482
|
+
> miner.start()
|
483
|
+
true
|
484
|
+
> web3.eth.getBalance(web3.eth.accounts[0])
|
485
|
+
506406250000000000000
|
486
|
+
>
|
487
|
+
|
488
|
+
```
|
489
|
+
|
490
|
+
|
491
|
+
## TODO:
|
492
|
+
|
493
|
+
- Add chai for js test
|
data/bin/teth
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'erb'
|
4
4
|
require 'json'
|
5
5
|
require 'fileutils'
|
6
|
+
require 'ethereum'
|
6
7
|
|
7
8
|
ARGV << "--help" if ARGV.empty?
|
8
9
|
|
@@ -16,7 +17,7 @@ ALIASES = {
|
|
16
17
|
"m" => "migrate",
|
17
18
|
"s" => "server",
|
18
19
|
"c" => "console",
|
19
|
-
"gt" => "
|
20
|
+
"gt" => "gtest"
|
20
21
|
}
|
21
22
|
|
22
23
|
command = ARGV.shift
|
@@ -26,25 +27,27 @@ HELP_MESSAGE = <<-EOF
|
|
26
27
|
Usage: teth COMMAND [ARGS]
|
27
28
|
The most common teth commands are:
|
28
29
|
new Create a new Smart Contract application. "teth new my_app" creates a
|
29
|
-
new application called
|
30
|
-
generate Generate new
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
new application called my_app in "./my_app" (short-cut alias: "n")
|
31
|
+
generate Generate new solidity smart contract and test files. "teth generate token"
|
32
|
+
creates Token contract and corresponding test files. (short-cut alias: "g")
|
33
|
+
test Run your ruby tests in ruby evm. (short-cut alias: "t")
|
34
|
+
init Bootstraps and initialises a new genesis block. "teth init" creates data directory
|
35
|
+
for private chain.(short-cut alias: "i")
|
36
|
+
import_keys Import keys to private chain (short-cut alias: "ik")
|
34
37
|
build Build contract (short-cut alias: "b")
|
35
|
-
migrate Deploy contract on chain (short-cut alias: "m")
|
36
|
-
server Start
|
37
|
-
console
|
38
|
-
|
38
|
+
migrate Deploy contract on private chain (short-cut alias: "m")
|
39
|
+
server Start geth server (short-cut alias: "s")
|
40
|
+
console Start geth attach (short-cut alias: "c")
|
41
|
+
gtest Run your javascript tests on geth (short-cut alias: "gt")
|
39
42
|
|
40
43
|
All commands can be run with -h (or --help) for more information.
|
41
44
|
EOF
|
42
45
|
|
43
|
-
KEYS_TEMPLATE =
|
44
|
-
"3ae88fe370c39384fc16da2c9e768cf5d2495b48"
|
45
|
-
"81063419f13cab5ac090cd8329d8fff9feead4a0"
|
46
|
-
"9da26fc2e1d6ad9fdd46138906b0104ae68a65d8"
|
47
|
-
|
46
|
+
KEYS_TEMPLATE = [
|
47
|
+
["3ae88fe370c39384fc16da2c9e768cf5d2495b48", "095e53c9c20e23fd01eaad953c01da9e9d3ed9bebcfed8e5b2c2fce94037d963"],
|
48
|
+
["81063419f13cab5ac090cd8329d8fff9feead4a0", "5bc505a123a695176a9688ffe22798cfd40424c5b91c818e985574ea8ebda167"],
|
49
|
+
["9da26fc2e1d6ad9fdd46138906b0104ae68a65d8", "b6a03207128827eaae0d31d97a7a6243de31f2baf99eabd764e33389ecf436fc"]
|
50
|
+
]
|
48
51
|
|
49
52
|
def gem_dir
|
50
53
|
spec = Gem::Specification.find_by_name("teth")
|
@@ -55,22 +58,25 @@ def new
|
|
55
58
|
name = ARGV.shift
|
56
59
|
if name
|
57
60
|
puts "Creating project #{name}..."
|
58
|
-
|
59
|
-
|
61
|
+
dirs_commands = %w{ private_keys builds temp contracts tests gtests}.map{ |d| "mkdir #{d}" }.join(" && ")
|
62
|
+
system("mkdir #{name} && cd #{name} && #{dirs_commands}")
|
63
|
+
system("cd #{name} && cd temp && mkdir db && mkdir migrations")
|
60
64
|
gemfile = File.read("#{gem_dir}/lib/teth/erbs/Gemfile")
|
61
65
|
|
62
66
|
File.open("#{name}/Gemfile", "w+") {|f| f.write(gemfile) }
|
63
67
|
system("cd #{name} && bundle install")
|
64
68
|
|
65
|
-
KEYS_TEMPLATE.each do |k
|
66
|
-
File.open("#{name}/private_keys/#{k}.key", "w+") { |f| f.write(
|
69
|
+
KEYS_TEMPLATE.each do |k|
|
70
|
+
File.open("#{name}/private_keys/#{k[0]}.key", "w+") { |f| f.write(k[1]) }
|
67
71
|
end
|
68
72
|
|
69
73
|
FileUtils.cp("#{gem_dir}/lib/teth/templates/private_keys/import.sh", "#{name}/private_keys/import.sh")
|
70
74
|
FileUtils.chmod 0700, "#{name}/private_keys/import.sh"
|
71
75
|
|
72
|
-
|
73
|
-
|
76
|
+
%w{genesis.json rakefile gitignore}.each do |f|
|
77
|
+
FileUtils.cp("#{gem_dir}/lib/teth/templates/#{f}", "#{name}/#{f}")
|
78
|
+
end
|
79
|
+
FileUtils.mv("#{name}/gitignore", "#{name}/.gitignore")
|
74
80
|
|
75
81
|
FileUtils.cp_r("#{gem_dir}/lib/teth/templates/bin/", "#{name}")
|
76
82
|
FileUtils.chmod_R 0700, "#{name}/bin/"
|
@@ -83,15 +89,28 @@ end
|
|
83
89
|
def generate
|
84
90
|
name = ARGV.shift
|
85
91
|
if name
|
92
|
+
puts "Creating #{name.capitalize} contract file..."
|
86
93
|
contract_template = ERB.new File.read("#{gem_dir}/lib/teth/erbs/contract.sol")
|
87
94
|
contract = contract_template.result(binding)
|
95
|
+
|
96
|
+
version = Ethereum::Tester::Language.get(:solidity).compiler_version
|
97
|
+
semver = version.split('-').first
|
98
|
+
major, minor, patch = semver.split('.')
|
99
|
+
unless major.to_i < 1 && minor.to_i < 4
|
100
|
+
pragma = "pragma solidity ^#{major}.#{minor}.0;" # always set patch to 0
|
101
|
+
contract = "#{pragma}\n\n#{contract}"
|
102
|
+
end
|
103
|
+
|
88
104
|
puts "Create #{name.capitalize}.sol contract file..."
|
89
105
|
File.open("contracts/#{name.capitalize}.sol", "w+") { |f| f.write(contract) }
|
90
106
|
|
107
|
+
puts "Creating #{name} test files..."
|
91
108
|
test_template = ERB.new File.read("#{gem_dir}/lib/teth/erbs/contract_test.rb")
|
92
109
|
test = test_template.result(binding)
|
93
|
-
|
94
|
-
File.open("
|
110
|
+
File.open("tests/#{name}_test.rb", "w+") {|f| f.write(test) }
|
111
|
+
File.open("gtests/#{name.capitalize}_test.js", "w") do |f|
|
112
|
+
f.write("loadScript('temp/migrations/#{name.capitalize}.js');")
|
113
|
+
end
|
95
114
|
puts "Done."
|
96
115
|
else
|
97
116
|
puts "Need contract name"
|
@@ -111,10 +130,12 @@ def test
|
|
111
130
|
end
|
112
131
|
|
113
132
|
def init
|
133
|
+
puts "Initialising a new genesis block..."
|
114
134
|
system("./bin/init.sh")
|
115
135
|
end
|
116
136
|
|
117
137
|
def import_keys
|
138
|
+
puts "Importing keys, this will take a while, please be patient..."
|
118
139
|
system("./bin/import_keys.sh")
|
119
140
|
end
|
120
141
|
|
@@ -129,15 +150,43 @@ end
|
|
129
150
|
|
130
151
|
def migrate
|
131
152
|
name = ARGV.shift
|
132
|
-
|
153
|
+
if name
|
154
|
+
puts "Migrating contract #{name}"
|
155
|
+
else
|
156
|
+
puts "Migrating all contracts"
|
157
|
+
end
|
158
|
+
output = `./bin/migrate.sh #{name}`
|
159
|
+
puts output
|
160
|
+
contract = ""
|
161
|
+
output.split("\n").each do |o|
|
162
|
+
if o.match("Contract Instance")
|
163
|
+
contract = o.split("Contract Instance : ")[1]
|
164
|
+
end
|
165
|
+
if o.match("Contract mined!")
|
166
|
+
address = o.split("Address: ")[1]
|
167
|
+
data = File.read("temp/db/#{contract}.json")
|
168
|
+
data = JSON.parse data
|
169
|
+
data["address"] = address
|
170
|
+
File.open("temp/db/#{contract}.json", "w") do |f|
|
171
|
+
f.write(JSON.pretty_generate data)
|
172
|
+
end
|
173
|
+
File.open("temp/migrations/#{contract}.js", "w") do |f|
|
174
|
+
abi = data[contract]["abi"]
|
175
|
+
template = ERB.new File.read("#{gem_dir}/lib/teth/erbs/migration")
|
176
|
+
migration = template.result(binding)
|
177
|
+
f.write(migration.gsub("=>", ":"))
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
133
181
|
end
|
134
182
|
|
135
183
|
def console
|
136
184
|
system("./bin/attach.sh")
|
137
185
|
end
|
138
186
|
|
139
|
-
def
|
140
|
-
|
187
|
+
def gtest
|
188
|
+
name = ARGV.shift
|
189
|
+
system("./bin/test.sh #{name}")
|
141
190
|
end
|
142
191
|
|
143
192
|
def help
|
data/lib/teth/erbs/Gemfile
CHANGED
@@ -1 +1 @@
|
|
1
|
-
gem 'teth', '>= 0.
|
1
|
+
gem 'teth', '>= 0.2.0'
|
data/lib/teth/minitest.rb
CHANGED
@@ -30,7 +30,7 @@ module Teth
|
|
30
30
|
when /\.sol\z/
|
31
31
|
type = :solidity
|
32
32
|
when /\.se\z/
|
33
|
-
|
33
|
+
type = :serpent
|
34
34
|
else
|
35
35
|
raise "Unknown contract source type: #{path}"
|
36
36
|
end
|
@@ -41,7 +41,7 @@ module Teth
|
|
41
41
|
if self.class.print_logs
|
42
42
|
topics = log.topics.map {|t| heuristic_prettify Utils.int_to_big_endian(t) }
|
43
43
|
data = heuristic_prettify(log.data)
|
44
|
-
puts "[Log] #{Utils.encode_hex(log.address)} >>> topics=#{topics} data=#{data}"
|
44
|
+
puts "[Log] #{Utils.encode_hex(log.address)} >>> topics=#{topics} data=#{data.inspect}"
|
45
45
|
end
|
46
46
|
else # user defined event
|
47
47
|
if self.class.print_logs && self.class.print_events
|
@@ -108,22 +108,15 @@ module Teth
|
|
108
108
|
end
|
109
109
|
|
110
110
|
def privkey
|
111
|
-
|
111
|
+
privkeys[0]
|
112
112
|
end
|
113
113
|
|
114
114
|
def pubkey
|
115
|
-
|
115
|
+
pubkeys[0]
|
116
116
|
end
|
117
117
|
|
118
118
|
def address
|
119
|
-
|
120
|
-
end
|
121
|
-
|
122
|
-
def account
|
123
|
-
return @account if @account
|
124
|
-
|
125
|
-
i = rand(self.class.account_num)
|
126
|
-
@account = [privkeys[i], pubkeys[i], addresses[i]]
|
119
|
+
addresses[0]
|
127
120
|
end
|
128
121
|
|
129
122
|
def privkeys
|
@@ -1,30 +1,25 @@
|
|
1
1
|
#!/bin/bash
|
2
2
|
if [ -z "$1" ]
|
3
3
|
then
|
4
|
-
echo "
|
4
|
+
echo "Building all contracts ..."
|
5
5
|
for sol in `find ./contracts -name '*.sol'`
|
6
6
|
do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
jsfile="${filename:12:len}_compiled.js"
|
12
|
-
echo $jsfile
|
13
|
-
./bin/solc_helper.rb $sol $jsfile
|
7
|
+
echo "Building contract ${sol}"
|
8
|
+
let len=${#sol}-16
|
9
|
+
jsfile="${sol:12:len}_compiled.js"
|
10
|
+
./bin/solc_helper.rb ${sol} $jsfile
|
14
11
|
mv $jsfile builds/
|
15
12
|
done
|
16
13
|
echo "Done."
|
17
14
|
else
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
jsfile
|
26
|
-
echo "Build $foo to $jsfile"
|
27
|
-
./bin/solc_helper.rb $sol $jsfile
|
15
|
+
sol=$1
|
16
|
+
sol="$(tr '[:lower:]' '[:upper:]' <<< ${sol:0:1})${sol:1}"
|
17
|
+
echo "Building contract ${sol}"
|
18
|
+
|
19
|
+
file="contracts/${sol}.sol"
|
20
|
+
len=${#sol}-16
|
21
|
+
jsfile="${sol}_compiled.js"
|
22
|
+
./bin/solc_helper.rb $file $jsfile
|
28
23
|
mv $jsfile builds/
|
29
24
|
echo "Done."
|
30
25
|
fi
|
@@ -1,12 +1,14 @@
|
|
1
1
|
#!/bin/sh
|
2
2
|
|
3
3
|
geth=${GETH:-geth}
|
4
|
+
|
4
5
|
echo "***** Using geth at: $geth"
|
5
6
|
|
6
7
|
echo "***** Import all pre-funded private keys"
|
7
8
|
|
8
9
|
for key in `find ./private_keys -name '*.key'`
|
9
10
|
do
|
11
|
+
echo "Notice: No need to input your password. The default password is 123456"
|
10
12
|
./private_keys/import.sh $key $geth
|
11
13
|
done
|
12
14
|
|
@@ -2,21 +2,22 @@
|
|
2
2
|
|
3
3
|
geth=${GETH:-geth}
|
4
4
|
|
5
|
+
echo "***** Using geth at: $geth"
|
6
|
+
|
5
7
|
if [ -z "$1" ]
|
6
8
|
then
|
7
|
-
echo "Migrate all contract ..."
|
8
9
|
scripts=""
|
9
10
|
for file in `find ./builds -name '*compiled.js'`
|
10
11
|
do
|
11
12
|
scripts="${scripts};loadScript('$file')"
|
12
13
|
done
|
13
14
|
scripts="${scripts};miner.start();admin.sleepBlocks(2);miner.stop()"
|
14
|
-
# echo "$scripts"
|
15
15
|
$geth --exec "$scripts" attach ipc:data/geth.ipc
|
16
16
|
else
|
17
17
|
file=$1
|
18
18
|
file="$(tr '[:lower:]' '[:upper:]' <<< ${file:0:1})${file:1}"
|
19
19
|
file+="_compiled.js"
|
20
|
-
echo "Migrate $file ..."
|
21
20
|
$geth --exec "loadScript('builds/$file');miner.start();admin.sleepBlocks(2);miner.stop()" attach ipc:data/geth.ipc
|
22
21
|
fi
|
22
|
+
|
23
|
+
echo "Done."
|
@@ -1,5 +1,8 @@
|
|
1
1
|
#!/bin/bash
|
2
|
-
|
3
2
|
geth=${GETH:-geth}
|
4
3
|
|
4
|
+
echo "***** Using geth at: $geth"
|
5
|
+
|
6
|
+
echo "Start geth server..."
|
7
|
+
|
5
8
|
$geth --datadir data --networkid 31415926 --rpc --rpccorsdomain "*" --nodiscover --unlock 3ae88fe370c39384fc16da2c9e768cf5d2495b48 --password <(echo -n 123456)
|
@@ -99,6 +99,6 @@ File.open(javascript_file_name, 'w') do |f|
|
|
99
99
|
f.write("console.log('Contract Instance : #{contract_instance_variable_name}');")
|
100
100
|
end
|
101
101
|
|
102
|
-
File.open("db/#{current_contract}.json", "w") do |f|
|
103
|
-
f.write(compiled_object
|
102
|
+
File.open("temp/db/#{current_contract}.json", "w") do |f|
|
103
|
+
f.write(JSON.pretty_generate compiled_object)
|
104
104
|
end
|
@@ -2,17 +2,25 @@
|
|
2
2
|
|
3
3
|
geth=${GETH:-geth}
|
4
4
|
|
5
|
+
echo "***** Using geth at: $geth"
|
6
|
+
|
5
7
|
scripts=""
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
if [ -z "$1" ]
|
10
|
+
then
|
11
|
+
echo "Testing all contracts on geth..."
|
12
|
+
for file in `find ./gtests -name '*.js'`
|
13
|
+
do
|
14
|
+
scripts="${scripts};loadScript('$file');"
|
15
|
+
done
|
16
|
+
else
|
17
|
+
file=$1
|
18
|
+
file="$(tr '[:lower:]' '[:upper:]' <<< ${file:0:1})${file:1}"
|
19
|
+
echo "Testing contract $file..."
|
20
|
+
file+="_test.js"
|
21
|
+
scripts="loadScript('gtests/$file');"
|
22
|
+
fi
|
11
23
|
|
12
|
-
|
13
|
-
do
|
14
|
-
scripts="${scripts};loadScript('$file');"
|
15
|
-
done
|
24
|
+
$geth --exec "$scripts" attach ipc:data/geth.ipc
|
16
25
|
|
17
|
-
echo
|
18
|
-
$geth --datadir data --networkid 31415926 --rpc --rpccorsdomain "*" --nodiscover --unlock 3ae88fe370c39384fc16da2c9e768cf5d2495b48 --password <(echo -n 123456) --exec "$scripts" console 2>> ./logfile
|
26
|
+
echo "Done."
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: teth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zhang YaNing
|
@@ -9,22 +9,36 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-09-
|
12
|
+
date: 2016-09-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ruby-ethereum
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version:
|
20
|
+
version: 0.9.6
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - "
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 0.9.6
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: serpent
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.3.0
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
26
40
|
- !ruby/object:Gem::Version
|
27
|
-
version:
|
41
|
+
version: 0.3.0
|
28
42
|
- !ruby/object:Gem::Dependency
|
29
43
|
name: minitest
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -57,6 +71,7 @@ files:
|
|
57
71
|
- lib/teth/erbs/Gemfile
|
58
72
|
- lib/teth/erbs/contract.sol
|
59
73
|
- lib/teth/erbs/contract_test.rb
|
74
|
+
- lib/teth/erbs/migration
|
60
75
|
- lib/teth/minitest.rb
|
61
76
|
- lib/teth/templates/bin/attach.sh
|
62
77
|
- lib/teth/templates/bin/build.sh
|
@@ -67,6 +82,7 @@ files:
|
|
67
82
|
- lib/teth/templates/bin/solc_helper.rb
|
68
83
|
- lib/teth/templates/bin/test.sh
|
69
84
|
- lib/teth/templates/genesis.json
|
85
|
+
- lib/teth/templates/gitignore
|
70
86
|
- lib/teth/templates/private_keys/import.sh
|
71
87
|
- lib/teth/templates/rakefile
|
72
88
|
homepage: https://github.com/cryptape/teth
|