teth 0.1.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|