teth 0.1.1
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 +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:
|