teth 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +77 -0
- data/bin/teth +170 -0
- data/lib/teth/configurable.rb +15 -0
- data/lib/teth/erbs/Gemfile +1 -0
- data/lib/teth/erbs/contract.sol +2 -0
- data/lib/teth/erbs/contract_test.rb +6 -0
- data/lib/teth/minitest.rb +164 -0
- data/lib/teth/templates/bin/attach.sh +4 -0
- data/lib/teth/templates/bin/build.sh +30 -0
- data/lib/teth/templates/bin/import_keys.sh +13 -0
- data/lib/teth/templates/bin/init.sh +2 -0
- data/lib/teth/templates/bin/migrate.sh +22 -0
- data/lib/teth/templates/bin/private_blockchain.sh +5 -0
- data/lib/teth/templates/bin/solc_helper.rb +104 -0
- data/lib/teth/templates/bin/test.sh +18 -0
- data/lib/teth/templates/genesis.json +10 -0
- data/lib/teth/templates/private_keys/import.sh +11 -0
- data/lib/teth/templates/rakefile +9 -0
- metadata +97 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7635b781f5fd78703f5bbddb1dcea9fd0804eb0c
|
4
|
+
data.tar.gz: 47c054a1f298a4e8ff2645af025a1cc347a40945
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c8582ab1a8eb8152f4d61e7d6203cdacf6eac0d0182fe2b02db75af672f518de50a86fab6b4b96ab09908506ca9d3a984de40c5665e5772c688e3eabaf0f0a62
|
7
|
+
data.tar.gz: 411a9aee288dfb9c0d84f247afee074d02026277eec33dea92187fd8096b6d1c6262a9eab93490c988d40fe76d97629569468376d216cb9472a9cf77e68d1615
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016 YaNing Zhang, Jan Xie
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
## Introduction
|
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
|
+
|
4
|
+
## Dependencies
|
5
|
+
- Solidity ~> 0.3.6
|
6
|
+
- Ruby ~> 2.2.2
|
7
|
+
- Go-ethereum ~> 1.4.11
|
8
|
+
|
9
|
+
## Install
|
10
|
+
```shell
|
11
|
+
bundle install teth
|
12
|
+
```
|
13
|
+
|
14
|
+
## How to use
|
15
|
+
|
16
|
+
Help command:
|
17
|
+
```
|
18
|
+
$ teth
|
19
|
+
```
|
20
|
+
Create a new Smart Contract application
|
21
|
+
```
|
22
|
+
$ teth n project
|
23
|
+
$ cd project
|
24
|
+
```
|
25
|
+
Generate new Smart Contract and test file
|
26
|
+
```
|
27
|
+
$ teth g game
|
28
|
+
```
|
29
|
+
Run tests
|
30
|
+
```
|
31
|
+
$ teth t game
|
32
|
+
```
|
33
|
+
|
34
|
+
## Unit tests
|
35
|
+
You can wirte fast, simple tests.
|
36
|
+
```ruby
|
37
|
+
class TokenTest < Minitest::Test
|
38
|
+
include Ethereum
|
39
|
+
|
40
|
+
def setup
|
41
|
+
@state = Tester::State.new
|
42
|
+
@solidity_code = File.read('./contracts/Token.sol')
|
43
|
+
@c = @state.abi_contract @solidity_code, language: :solidity
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_issue_balance
|
47
|
+
assert_equal 0, @c.getBalance(Tester::Fixture.accounts[2])
|
48
|
+
@c.issue Tester::Fixture.accounts[2], 100
|
49
|
+
assert_equal 100, @c.getBalance(Tester::Fixture.accounts[2])
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_issue_exception
|
53
|
+
assert_raises(TransactionFailed) { @c.issue Tester::Fixture.accounts[3], 100, sender: Tester::Fixture.keys[4] }
|
54
|
+
assert_equal 0, @c.getBalance(Tester::Fixture.accounts[3])
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_token_transfer
|
58
|
+
@c.issue Tester::Fixture.accounts[2], 100
|
59
|
+
@c.transfer Tester::Fixture.accounts[3], 90, sender: Tester::Fixture.keys[2]
|
60
|
+
assert_equal 90, @c.getBalance(Tester::Fixture.accounts[3])
|
61
|
+
|
62
|
+
assert_raises(TransactionFailed) { @c.transfer Tester::Fixture.accounts[3], 90, sender: Tester::Fixture.keys[2] }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
```
|
67
|
+
More details:
|
68
|
+
https://github.com/seattlerb/minitest
|
69
|
+
|
70
|
+
## TODO
|
71
|
+
- Extract test file require and others
|
72
|
+
- Save migrate address
|
73
|
+
- Migrate ARGV
|
74
|
+
- Script for preload
|
75
|
+
- Easy way to load contract on chain
|
76
|
+
- Easy way to test on chain
|
77
|
+
- ES6
|
data/bin/teth
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
require 'json'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
ARGV << "--help" if ARGV.empty?
|
8
|
+
|
9
|
+
ALIASES = {
|
10
|
+
"n" => "new",
|
11
|
+
"g" => "generate",
|
12
|
+
"t" => "test",
|
13
|
+
"i" => "init",
|
14
|
+
"ik" => "import_keys",
|
15
|
+
"b" => "build",
|
16
|
+
"m" => "migrate",
|
17
|
+
"s" => "server",
|
18
|
+
"c" => "console",
|
19
|
+
"gt" => "geth_test"
|
20
|
+
}
|
21
|
+
|
22
|
+
command = ARGV.shift
|
23
|
+
command = ALIASES[command] || command
|
24
|
+
|
25
|
+
HELP_MESSAGE = <<-EOF
|
26
|
+
Usage: teth COMMAND [ARGS]
|
27
|
+
The most common teth commands are:
|
28
|
+
new Create a new Smart Contract application. "teth new my_app" creates a
|
29
|
+
new application called MyApp in "./my_app" (short-cut alias: "n")
|
30
|
+
generate Generate new Smart Contract and test file (short-cut alias: "g")
|
31
|
+
test Run tests (short-cut alias: "t")
|
32
|
+
init Init private geth chain (short-cut alias: "i")
|
33
|
+
import_keys Import keys to chain (short-cut alias: "ik")
|
34
|
+
build Build contract (short-cut alias: "b")
|
35
|
+
migrate Deploy contract on chain (short-cut alias: "m")
|
36
|
+
server Start chain server (short-cut alias: "s")
|
37
|
+
console Open Chain console (short-cut alias: "c")
|
38
|
+
geth_test Test on chain (short-cut alias: "gt")
|
39
|
+
|
40
|
+
All commands can be run with -h (or --help) for more information.
|
41
|
+
EOF
|
42
|
+
|
43
|
+
KEYS_TEMPLATE = {
|
44
|
+
"3ae88fe370c39384fc16da2c9e768cf5d2495b48" => "095e53c9c20e23fd01eaad953c01da9e9d3ed9bebcfed8e5b2c2fce94037d963",
|
45
|
+
"81063419f13cab5ac090cd8329d8fff9feead4a0" => "5bc505a123a695176a9688ffe22798cfd40424c5b91c818e985574ea8ebda167",
|
46
|
+
"9da26fc2e1d6ad9fdd46138906b0104ae68a65d8" => "b6a03207128827eaae0d31d97a7a6243de31f2baf99eabd764e33389ecf436fc"
|
47
|
+
}
|
48
|
+
|
49
|
+
def gem_dir
|
50
|
+
spec = Gem::Specification.find_by_name("teth")
|
51
|
+
spec.gem_dir
|
52
|
+
end
|
53
|
+
|
54
|
+
def new
|
55
|
+
name = ARGV.shift
|
56
|
+
if name
|
57
|
+
puts "Creating project #{name}..."
|
58
|
+
system("mkdir #{name} && cd #{name} && mkdir private_keys && mkdir builds && mkdir db && mkdir contracts && mkdir tests")
|
59
|
+
|
60
|
+
gemfile = File.read("#{gem_dir}/lib/teth/erbs/Gemfile")
|
61
|
+
|
62
|
+
File.open("#{name}/Gemfile", "w+") {|f| f.write(gemfile) }
|
63
|
+
system("cd #{name} && bundle install")
|
64
|
+
|
65
|
+
KEYS_TEMPLATE.each do |k, v|
|
66
|
+
File.open("#{name}/private_keys/#{k}.key", "w+") { |f| f.write(v) }
|
67
|
+
end
|
68
|
+
|
69
|
+
FileUtils.cp("#{gem_dir}/lib/teth/templates/private_keys/import.sh", "#{name}/private_keys/import.sh")
|
70
|
+
FileUtils.chmod 0700, "#{name}/private_keys/import.sh"
|
71
|
+
|
72
|
+
FileUtils.cp("#{gem_dir}/lib/teth/templates/genesis.json", "#{name}/genesis.json")
|
73
|
+
FileUtils.cp("#{gem_dir}/lib/teth/templates/rakefile", "#{name}/rakefile")
|
74
|
+
|
75
|
+
FileUtils.cp_r("#{gem_dir}/lib/teth/templates/bin/", "#{name}")
|
76
|
+
FileUtils.chmod_R 0700, "#{name}/bin/"
|
77
|
+
puts "Done."
|
78
|
+
else
|
79
|
+
puts "Need project name"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def generate
|
84
|
+
name = ARGV.shift
|
85
|
+
if name
|
86
|
+
contract_template = ERB.new File.read("#{gem_dir}/lib/teth/erbs/contract.sol")
|
87
|
+
contract = contract_template.result(binding)
|
88
|
+
puts "Create #{name.capitalize}.sol contract file..."
|
89
|
+
File.open("contracts/#{name.capitalize}.sol", "w+") { |f| f.write(contract) }
|
90
|
+
|
91
|
+
test_template = ERB.new File.read("#{gem_dir}/lib/teth/erbs/contract_test.rb")
|
92
|
+
test = test_template.result(binding)
|
93
|
+
puts "Create #{name}_test.rb test file..."
|
94
|
+
File.open("tests/#{name.capitalize}_test.rb", "w+") {|f| f.write(test) }
|
95
|
+
puts "Done."
|
96
|
+
else
|
97
|
+
puts "Need contract name"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def test
|
102
|
+
name = ARGV.shift
|
103
|
+
if name
|
104
|
+
puts "Test #{name.capitalize} contract..."
|
105
|
+
system("bundle exec ruby -Ilib:test tests/#{name}_test.rb")
|
106
|
+
else
|
107
|
+
puts "Test all contracts..."
|
108
|
+
system("bundle exec rake")
|
109
|
+
end
|
110
|
+
puts "Done."
|
111
|
+
end
|
112
|
+
|
113
|
+
def init
|
114
|
+
system("./bin/init.sh")
|
115
|
+
end
|
116
|
+
|
117
|
+
def import_keys
|
118
|
+
system("./bin/import_keys.sh")
|
119
|
+
end
|
120
|
+
|
121
|
+
def build
|
122
|
+
name = ARGV.shift
|
123
|
+
system("./bin/build.sh #{name}")
|
124
|
+
end
|
125
|
+
|
126
|
+
def server
|
127
|
+
system("./bin/private_blockchain.sh")
|
128
|
+
end
|
129
|
+
|
130
|
+
def migrate
|
131
|
+
name = ARGV.shift
|
132
|
+
system("./bin/migrate.sh #{name}")
|
133
|
+
end
|
134
|
+
|
135
|
+
def console
|
136
|
+
system("./bin/attach.sh")
|
137
|
+
end
|
138
|
+
|
139
|
+
def geth_test
|
140
|
+
system("./bin/test.sh")
|
141
|
+
end
|
142
|
+
|
143
|
+
def help
|
144
|
+
write_help_message
|
145
|
+
end
|
146
|
+
|
147
|
+
def write_help_message
|
148
|
+
puts HELP_MESSAGE
|
149
|
+
end
|
150
|
+
|
151
|
+
def parse_command(command)
|
152
|
+
case command
|
153
|
+
when "--help", "-h"
|
154
|
+
"help"
|
155
|
+
else
|
156
|
+
command
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def run_command!(command)
|
161
|
+
command = parse_command(command)
|
162
|
+
|
163
|
+
if ALIASES.values.include?(command)
|
164
|
+
send(command)
|
165
|
+
else
|
166
|
+
help
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
run_command!(command)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Teth
|
2
|
+
module Configurable
|
3
|
+
def option(name, default=nil)
|
4
|
+
singleton_class.send(:define_method, name) do |*args|
|
5
|
+
if args.empty?
|
6
|
+
v = instance_variable_get("@#{name}")
|
7
|
+
return default if v.nil?
|
8
|
+
v
|
9
|
+
else
|
10
|
+
instance_variable_set("@#{name}", args[0])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
gem 'teth', '>= 0.1.0'
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'ethereum'
|
3
|
+
|
4
|
+
require 'teth/configurable'
|
5
|
+
|
6
|
+
module Teth
|
7
|
+
class Minitest < ::Minitest::Test
|
8
|
+
extend Configurable
|
9
|
+
include Ethereum
|
10
|
+
|
11
|
+
option :contract_dir_name, 'contracts'
|
12
|
+
option :account_num, 10
|
13
|
+
option :print_events, false
|
14
|
+
option :print_logs, true
|
15
|
+
|
16
|
+
def setup
|
17
|
+
path = find_contract_source
|
18
|
+
raise "Cannot find corresponding contract source" unless path
|
19
|
+
setup_contract(path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def teardown
|
23
|
+
@state = nil
|
24
|
+
@account = nil
|
25
|
+
@contract = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def setup_contract(path)
|
29
|
+
case path
|
30
|
+
when /\.sol\z/
|
31
|
+
type = :solidity
|
32
|
+
when /\.se\z/
|
33
|
+
raise NotImplemented, "Serpent not supported yet"
|
34
|
+
else
|
35
|
+
raise "Unknown contract source type: #{path}"
|
36
|
+
end
|
37
|
+
|
38
|
+
code = File.read path
|
39
|
+
log_listener = ->(log) do
|
40
|
+
if log.instance_of?(Log) # unrecognized event
|
41
|
+
if self.class.print_logs
|
42
|
+
topics = log.topics.map {|t| heuristic_prettify Utils.int_to_big_endian(t) }
|
43
|
+
data = heuristic_prettify(log.data)
|
44
|
+
puts "[Log] #{Utils.encode_hex(log.address)} >>> topics=#{topics} data=#{data}"
|
45
|
+
end
|
46
|
+
else # user defined event
|
47
|
+
if self.class.print_logs && self.class.print_events
|
48
|
+
from = log.delete '_from'
|
49
|
+
name = log.delete '_event_type'
|
50
|
+
s = log.keys.map {|k| "#{k}=#{log[k]}" }.join(' ')
|
51
|
+
puts "[Event] #{from} #{name} >>> #{s}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
@contract = state.abi_contract code,
|
56
|
+
language: type, sender: privkey, log_listener: log_listener
|
57
|
+
end
|
58
|
+
|
59
|
+
def heuristic_prettify(bytes)
|
60
|
+
dry_bytes = bytes.gsub(/\A(\x00)+/, '')
|
61
|
+
dry_bytes = dry_bytes.gsub(/(\x00)+\z/, '')
|
62
|
+
if (bytes.size - dry_bytes.size) > 3
|
63
|
+
# there's many ZERO bytes in the head or tail of bytes, it must be padded
|
64
|
+
if dry_bytes.size == 20 # address
|
65
|
+
Utils.encode_hex(dry_bytes)
|
66
|
+
else
|
67
|
+
dry_bytes
|
68
|
+
end
|
69
|
+
else
|
70
|
+
Utils.encode_hex(bytes)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def contract
|
75
|
+
@contract
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_contract_source
|
79
|
+
name = self.class.name[0...-4]
|
80
|
+
dir = find_contracts_directory
|
81
|
+
find_source(dir, name, 'sol') || find_source(dir, name, 'se')
|
82
|
+
end
|
83
|
+
|
84
|
+
def find_contracts_directory
|
85
|
+
last = nil
|
86
|
+
cur = ENV['PWD']
|
87
|
+
while cur != last
|
88
|
+
path = File.join cur, self.class.contract_dir_name
|
89
|
+
return path if File.directory?(path)
|
90
|
+
last = cur
|
91
|
+
cur = File.dirname cur
|
92
|
+
end
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def find_source(dir, name, ext)
|
97
|
+
name = "#{name}.#{ext}"
|
98
|
+
list = Dir.glob File.join(dir, "**/*.#{ext}")
|
99
|
+
list.find {|fn| File.basename(fn) =~ /\A#{name}\z/i }
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Fixtures
|
104
|
+
#
|
105
|
+
|
106
|
+
@@privkeys = account_num.times.map do |i|
|
107
|
+
Utils.keccak256 rand(Constant::TT256).to_s
|
108
|
+
end
|
109
|
+
|
110
|
+
def privkey
|
111
|
+
account[0]
|
112
|
+
end
|
113
|
+
|
114
|
+
def pubkey
|
115
|
+
account[1]
|
116
|
+
end
|
117
|
+
|
118
|
+
def address
|
119
|
+
account[2]
|
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]]
|
127
|
+
end
|
128
|
+
|
129
|
+
def privkeys
|
130
|
+
@privkeys ||= @@privkeys
|
131
|
+
end
|
132
|
+
|
133
|
+
def pubkeys
|
134
|
+
@pubkeys ||= privkeys.map {|k| PrivateKey.new(k).to_pubkey }
|
135
|
+
end
|
136
|
+
|
137
|
+
def addresses
|
138
|
+
@addresses ||= privkeys.map {|k| PrivateKey.new(k).to_address }
|
139
|
+
end
|
140
|
+
|
141
|
+
%w(alice bob carol chuck dave eve mallet oscar sybil).each_with_index do |n, i|
|
142
|
+
class_eval <<-METHOD
|
143
|
+
def #{n}
|
144
|
+
addresses[#{i}]
|
145
|
+
end
|
146
|
+
def #{n}_pubkey
|
147
|
+
pubkeys[#{i}]
|
148
|
+
end
|
149
|
+
def #{n}_privkey
|
150
|
+
privkeys[#{i}]
|
151
|
+
end
|
152
|
+
METHOD
|
153
|
+
end
|
154
|
+
|
155
|
+
def state
|
156
|
+
@state ||= Tester::State.new privkeys: privkeys
|
157
|
+
end
|
158
|
+
|
159
|
+
def head
|
160
|
+
state.head
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
if [ -z "$1" ]
|
3
|
+
then
|
4
|
+
echo "Build all contracts ..."
|
5
|
+
for sol in `find ./contracts -name '*.sol'`
|
6
|
+
do
|
7
|
+
filename="${sol}"
|
8
|
+
echo "Build ${filename}"
|
9
|
+
let len=${#filename}-16
|
10
|
+
# echo $len
|
11
|
+
jsfile="${filename:12:len}_compiled.js"
|
12
|
+
echo $jsfile
|
13
|
+
./bin/solc_helper.rb $sol $jsfile
|
14
|
+
mv $jsfile builds/
|
15
|
+
done
|
16
|
+
echo "Done."
|
17
|
+
else
|
18
|
+
foo=$1
|
19
|
+
foo="$(tr '[:lower:]' '[:upper:]' <<< ${foo:0:1})${foo:1}"
|
20
|
+
sol="contracts/$1.sol"
|
21
|
+
filename="${sol}"
|
22
|
+
echo "Build ${filename}"
|
23
|
+
let len=${#filename}-16
|
24
|
+
# echo $len
|
25
|
+
jsfile="${foo}_compiled.js"
|
26
|
+
echo "Build $foo to $jsfile"
|
27
|
+
./bin/solc_helper.rb $sol $jsfile
|
28
|
+
mv $jsfile builds/
|
29
|
+
echo "Done."
|
30
|
+
fi
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
geth=${GETH:-geth}
|
4
|
+
|
5
|
+
if [ -z "$1" ]
|
6
|
+
then
|
7
|
+
echo "Migrate all contract ..."
|
8
|
+
scripts=""
|
9
|
+
for file in `find ./builds -name '*compiled.js'`
|
10
|
+
do
|
11
|
+
scripts="${scripts};loadScript('$file')"
|
12
|
+
done
|
13
|
+
scripts="${scripts};miner.start();admin.sleepBlocks(2);miner.stop()"
|
14
|
+
# echo "$scripts"
|
15
|
+
$geth --exec "$scripts" attach ipc:data/geth.ipc
|
16
|
+
else
|
17
|
+
file=$1
|
18
|
+
file="$(tr '[:lower:]' '[:upper:]' <<< ${file:0:1})${file:1}"
|
19
|
+
file+="_compiled.js"
|
20
|
+
echo "Migrate $file ..."
|
21
|
+
$geth --exec "loadScript('builds/$file');miner.start();admin.sleepBlocks(2);miner.stop()" attach ipc:data/geth.ipc
|
22
|
+
fi
|
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
def library_code
|
5
|
+
'function create(abiDefinition) { return web3.eth.contract(abiDefinition);}/*function deploy(account, value, gas, contract, code, input) */function deploy() { var account = arguments[0]; var value = arguments[1]; var gas = arguments[2]; var contract = arguments[3]; var code = arguments[4]; var codeString = "contract.new(inputMarker,{from:\'accountMarker\', value: valueMarker, data: \'codeMarker\', gas: gasMarker}, function (e, contract) { if(!e) { if(!contract.address) { console.log(\"Contract transaction send: TransactionHash: \" + contract.transactionHash + \" waiting to be mined...\"); } else { console.log(\"Contract mined! Address: \" + contract.address); } } else { console.log(e) } })"; codeString = codeString.replace("accountMarker", account); codeString = codeString.replace("valueMarker", value); codeString = codeString.replace("codeMarker", code); codeString = codeString.replace("gasMarker", gas); input = "null"; if (arguments.length > 5) { if (arguments[5] != null) { var args = []; for (var i = 5;i < arguments.length; i++) { var val = arguments[i]; if (typeof(val) === \'string\') { val = "\"" + val + "\""; } args.push(val); } input = args.join(","); } } codeString = codeString.replace("inputMarker", input); console.log(input); var instance = eval(codeString); return instance;}function watcher(error, result) { if (!error) { console.log("Result"); console.log(JSON.stringify(result)); return; } console.log("Error" + error);}/*function call(account, gas, func, input) */function call() { var account = "eth.accounts["+arguments[0]+"]"; var gas = arguments[1]; var func = arguments[2]; input = "null"; if (arguments.length > 3) { if (arguments[3] != null) { var args = Array.prototype.slice.call(arguments, 3); input = args.join(","); } } codeString = "func.sendTransaction(inputMarker, gasMarker, {from:accountMarker}, watcher);"; codeString = codeString.replace("accountMarker",account); codeString = codeString.replace("gasMarker",gas); codeString = codeString.replace("inputMarker",input); eval(codeString);}function send(from_index, to, value, gas){ return eth.sendTransaction({from:eth.accounts[from_index], to:to, value:web3.toWei(value,\'ether\'), gas:gas});}function bal() { for (var i = 0; i < eth.accounts.length; i++) { account = eth.accounts[i]; balance = web3.fromWei(eth.getBalance(eth.accounts[i]), \'ether\'); console.log("Index : " + i); console.log("Account : "+ account); console.log("Balance : "+ balance); console.log("\n"); }}'
|
6
|
+
end
|
7
|
+
|
8
|
+
def compile_solidity(file)
|
9
|
+
json_string = `solc --add-std --optimize --combined-json abi,bin,userdoc,devdoc #{file}`
|
10
|
+
json_string = json_string.gsub("\\n","")
|
11
|
+
begin
|
12
|
+
json_object = JSON.parse(json_string)
|
13
|
+
throw if json_object.nil?
|
14
|
+
puts `solc --optimize --gas #{file}`
|
15
|
+
puts "\n\n"
|
16
|
+
puts "-------------------------------------"
|
17
|
+
json_object["contracts"]
|
18
|
+
rescue
|
19
|
+
puts "Failed to Compile."
|
20
|
+
abort
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_code(contracts)
|
25
|
+
contracts.keys.each.with_index do |key, i|
|
26
|
+
contracts[key]["bin"] = "0x" + contracts[key]["bin"]
|
27
|
+
contracts[key]["abi"] = JSON.parse(contracts[key]["abi"])
|
28
|
+
contracts[key]["devdoc"] = JSON.parse(contracts[key]["devdoc"])
|
29
|
+
contracts[key]["userdoc"] = JSON.parse(contracts[key]["userdoc"])
|
30
|
+
end
|
31
|
+
return contracts
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def javascript_file_name(file_name)
|
36
|
+
file_name = file_name.split('/')[-1]
|
37
|
+
file_name.split('.')[0] + '_compiled.js'
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_contract_to_deploy(compiled_object)
|
41
|
+
return compiled_object.keys[0] if compiled_object.keys.count == 1
|
42
|
+
puts "Which contract do you want to deploy?"
|
43
|
+
choice = 0
|
44
|
+
while choice <= 0 || choice > compiled_object.keys.count
|
45
|
+
compiled_object.keys.each.with_index do |key, i|
|
46
|
+
puts "#{(i+1)}. "+key
|
47
|
+
end
|
48
|
+
choice = $stdin.gets.to_i
|
49
|
+
end
|
50
|
+
return compiled_object.keys[choice - 1]
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_input
|
54
|
+
puts "Enter Input: "
|
55
|
+
input = $stdin.gets
|
56
|
+
input.strip.chomp == "" ? "null" : input
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_gas
|
60
|
+
gas = 0
|
61
|
+
while gas == 0
|
62
|
+
puts "Enter Gas: "
|
63
|
+
gas = $stdin.gets.to_i
|
64
|
+
end
|
65
|
+
gas
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_value
|
69
|
+
gas = -1
|
70
|
+
while gas < 0
|
71
|
+
puts "Enter Value To Be Transferred: "
|
72
|
+
gas = $stdin.gets.to_i
|
73
|
+
end
|
74
|
+
gas
|
75
|
+
end
|
76
|
+
|
77
|
+
file_name = ARGV[0]
|
78
|
+
|
79
|
+
compiled_object = compile_solidity(file_name)
|
80
|
+
compiled_object = process_code(compiled_object)
|
81
|
+
javascript_file_name = javascript_file_name(file_name)
|
82
|
+
|
83
|
+
current_contract = get_contract_to_deploy(compiled_object)
|
84
|
+
|
85
|
+
compiled_variable_name = "#{current_contract}Compiled"
|
86
|
+
contract_variable_name = "#{current_contract}Contract"
|
87
|
+
contract_instance_variable_name = "#{current_contract}"
|
88
|
+
|
89
|
+
gas = get_gas
|
90
|
+
value = get_value
|
91
|
+
input = get_input
|
92
|
+
|
93
|
+
File.open(javascript_file_name, 'w') do |f|
|
94
|
+
f.write("#{library_code};\nvar #{compiled_variable_name} = #{compiled_object.to_json};")
|
95
|
+
f.write("#{contract_variable_name} = create(#{compiled_variable_name}.#{current_contract}.abi);")
|
96
|
+
f.write("#{contract_instance_variable_name} = deploy(eth.coinbase,#{value},#{gas},#{contract_variable_name},#{compiled_variable_name}.#{current_contract}.bin,#{input});")
|
97
|
+
f.write("console.log('Compiled Object : #{compiled_variable_name}');")
|
98
|
+
f.write("console.log('Contract : #{contract_variable_name}');")
|
99
|
+
f.write("console.log('Contract Instance : #{contract_instance_variable_name}');")
|
100
|
+
end
|
101
|
+
|
102
|
+
File.open("db/#{current_contract}.json", "w") do |f|
|
103
|
+
f.write(compiled_object.to_json)
|
104
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
geth=${GETH:-geth}
|
4
|
+
|
5
|
+
scripts=""
|
6
|
+
|
7
|
+
for file in `find ./builds -name '*compiled.js'`
|
8
|
+
do
|
9
|
+
scripts="${scripts};loadScript('$file')"
|
10
|
+
done
|
11
|
+
|
12
|
+
for file in `find ./test -name '*.js'`
|
13
|
+
do
|
14
|
+
scripts="${scripts};loadScript('$file');"
|
15
|
+
done
|
16
|
+
|
17
|
+
echo $scripts
|
18
|
+
$geth --datadir data --networkid 31415926 --rpc --rpccorsdomain "*" --nodiscover --unlock 3ae88fe370c39384fc16da2c9e768cf5d2495b48 --password <(echo -n 123456) --exec "$scripts" console 2>> ./logfile
|
@@ -0,0 +1,10 @@
|
|
1
|
+
{
|
2
|
+
"nonce": "0x0000000000000042",
|
3
|
+
"difficulty": "0x020000",
|
4
|
+
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
5
|
+
"coinbase": "0x0000000000000000000000000000000000000000",
|
6
|
+
"timestamp": "0x00",
|
7
|
+
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
8
|
+
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
|
9
|
+
"gasLimit": "0x4c4b40"
|
10
|
+
}
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: teth
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Zhang YaNing
|
8
|
+
- Jan Xie
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-09-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ruby-ethereum
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0.9'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0.9'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: minitest
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '5.8'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '5.8'
|
42
|
+
description: 'Teth is a Ethereum smart contract test framework in ruby. It provides
|
43
|
+
two testing environments: testing in ruby EVM and testing in geth. You don''t need
|
44
|
+
to understand ruby grammar, just enjoy syntactic sugar.'
|
45
|
+
email:
|
46
|
+
- zhangyaning1985@gmail.com
|
47
|
+
- jan.h.xie@gmail.com
|
48
|
+
executables:
|
49
|
+
- teth
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- LICENSE
|
54
|
+
- README.md
|
55
|
+
- bin/teth
|
56
|
+
- lib/teth/configurable.rb
|
57
|
+
- lib/teth/erbs/Gemfile
|
58
|
+
- lib/teth/erbs/contract.sol
|
59
|
+
- lib/teth/erbs/contract_test.rb
|
60
|
+
- lib/teth/minitest.rb
|
61
|
+
- lib/teth/templates/bin/attach.sh
|
62
|
+
- lib/teth/templates/bin/build.sh
|
63
|
+
- lib/teth/templates/bin/import_keys.sh
|
64
|
+
- lib/teth/templates/bin/init.sh
|
65
|
+
- lib/teth/templates/bin/migrate.sh
|
66
|
+
- lib/teth/templates/bin/private_blockchain.sh
|
67
|
+
- lib/teth/templates/bin/solc_helper.rb
|
68
|
+
- lib/teth/templates/bin/test.sh
|
69
|
+
- lib/teth/templates/genesis.json
|
70
|
+
- lib/teth/templates/private_keys/import.sh
|
71
|
+
- lib/teth/templates/rakefile
|
72
|
+
homepage: https://github.com/cryptape/teth
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.4.5.1
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: Testing and deployment framework for Ethereum smart contracts.
|
96
|
+
test_files: []
|
97
|
+
has_rdoc:
|