ruby-ethereum 0.9.0

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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +40 -0
  4. data/lib/ethereum.rb +53 -0
  5. data/lib/ethereum/abi.rb +333 -0
  6. data/lib/ethereum/abi/contract_translator.rb +174 -0
  7. data/lib/ethereum/abi/type.rb +117 -0
  8. data/lib/ethereum/account.rb +72 -0
  9. data/lib/ethereum/address.rb +60 -0
  10. data/lib/ethereum/base_convert.rb +53 -0
  11. data/lib/ethereum/block.rb +1311 -0
  12. data/lib/ethereum/block_header.rb +211 -0
  13. data/lib/ethereum/bloom.rb +83 -0
  14. data/lib/ethereum/cached_block.rb +36 -0
  15. data/lib/ethereum/chain.rb +400 -0
  16. data/lib/ethereum/constant.rb +26 -0
  17. data/lib/ethereum/core_ext/array/safe_slice.rb +18 -0
  18. data/lib/ethereum/core_ext/module/lru_cache.rb +20 -0
  19. data/lib/ethereum/core_ext/numeric/denominations.rb +45 -0
  20. data/lib/ethereum/core_ext/object/truth.rb +47 -0
  21. data/lib/ethereum/db.rb +6 -0
  22. data/lib/ethereum/db/base_db.rb +9 -0
  23. data/lib/ethereum/db/ephem_db.rb +63 -0
  24. data/lib/ethereum/db/overlay_db.rb +72 -0
  25. data/lib/ethereum/db/refcount_db.rb +188 -0
  26. data/lib/ethereum/env.rb +64 -0
  27. data/lib/ethereum/ethash.rb +78 -0
  28. data/lib/ethereum/ethash_ruby.rb +38 -0
  29. data/lib/ethereum/ethash_ruby/cache.rb +47 -0
  30. data/lib/ethereum/ethash_ruby/hashimoto.rb +75 -0
  31. data/lib/ethereum/ethash_ruby/utils.rb +53 -0
  32. data/lib/ethereum/exceptions.rb +28 -0
  33. data/lib/ethereum/external_call.rb +173 -0
  34. data/lib/ethereum/fast_rlp.rb +81 -0
  35. data/lib/ethereum/fast_vm.rb +625 -0
  36. data/lib/ethereum/fast_vm/call_data.rb +44 -0
  37. data/lib/ethereum/fast_vm/message.rb +29 -0
  38. data/lib/ethereum/fast_vm/state.rb +25 -0
  39. data/lib/ethereum/ffi/openssl.rb +390 -0
  40. data/lib/ethereum/index.rb +97 -0
  41. data/lib/ethereum/log.rb +43 -0
  42. data/lib/ethereum/miner.rb +84 -0
  43. data/lib/ethereum/opcodes.rb +131 -0
  44. data/lib/ethereum/private_key.rb +92 -0
  45. data/lib/ethereum/pruning_trie.rb +28 -0
  46. data/lib/ethereum/public_key.rb +88 -0
  47. data/lib/ethereum/receipt.rb +53 -0
  48. data/lib/ethereum/secp256k1.rb +58 -0
  49. data/lib/ethereum/secure_trie.rb +49 -0
  50. data/lib/ethereum/sedes.rb +42 -0
  51. data/lib/ethereum/special_contract.rb +95 -0
  52. data/lib/ethereum/spv.rb +79 -0
  53. data/lib/ethereum/spv/proof.rb +31 -0
  54. data/lib/ethereum/spv/proof_constructor.rb +19 -0
  55. data/lib/ethereum/spv/proof_verifier.rb +24 -0
  56. data/lib/ethereum/tester.rb +14 -0
  57. data/lib/ethereum/tester/abi_contract.rb +65 -0
  58. data/lib/ethereum/tester/fixture.rb +31 -0
  59. data/lib/ethereum/tester/language.rb +30 -0
  60. data/lib/ethereum/tester/log_recorder.rb +13 -0
  61. data/lib/ethereum/tester/solidity_wrapper.rb +144 -0
  62. data/lib/ethereum/tester/state.rb +194 -0
  63. data/lib/ethereum/transaction.rb +196 -0
  64. data/lib/ethereum/transient_trie.rb +28 -0
  65. data/lib/ethereum/trie.rb +549 -0
  66. data/lib/ethereum/trie/nibble_key.rb +184 -0
  67. data/lib/ethereum/utils.rb +191 -0
  68. data/lib/ethereum/version.rb +5 -0
  69. data/lib/ethereum/vm.rb +606 -0
  70. data/lib/ethereum/vm/call_data.rb +40 -0
  71. data/lib/ethereum/vm/message.rb +30 -0
  72. data/lib/ethereum/vm/state.rb +25 -0
  73. metadata +284 -0
@@ -0,0 +1,174 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ require 'json'
4
+
5
+ module Ethereum
6
+ module ABI
7
+ class ContractTranslator
8
+
9
+ def initialize(full_signature)
10
+ @v = {
11
+ function_data: {},
12
+ event_data: {}
13
+ }
14
+
15
+ if full_signature.instance_of?(String)
16
+ full_signature = JSON.parse full_signature
17
+ end
18
+
19
+ full_signature.each do |sig_item|
20
+ next if sig_item['type'] == 'constructor'
21
+
22
+ encode_types = sig_item['inputs'].map {|f| f['type'] }
23
+ signature = sig_item['inputs'].map {|f| [f['type'], f['name']] }
24
+ name = sig_item['name']
25
+
26
+ if name =~ /\(/
27
+ name = name[0, name.index('(')]
28
+ end
29
+
30
+ # TODO: removable?
31
+ #if @v.has_key?(name.to_sym)
32
+ # i = 2
33
+ # i += 1 while @v.has_key?(:"#{name}#{i}")
34
+ # name += i.to_s
35
+
36
+ # logger.warn "multiple methods with the same name. Use #{name} to call #{sig_item['name']} with types #{encode_types}"
37
+ #end
38
+
39
+ if sig_item['type'] == 'function'
40
+ decode_types = sig_item['outputs'].map {|f| f['type'] }
41
+ is_unknown_type = sig_item['outputs'].size.true? && sig_item['outputs'][0]['name'] == 'unknown_out'
42
+ function_data[name.to_sym] = {
43
+ prefix: method_id(name, encode_types),
44
+ encode_types: encode_types,
45
+ decode_types: decode_types,
46
+ is_unknown_type: is_unknown_type,
47
+ is_constant: sig_item.fetch('constant', false),
48
+ signature: signature
49
+ }
50
+ elsif sig_item['type'] == 'event'
51
+ indexed = sig_item['inputs'].map {|f| f['indexed'] }
52
+ names = sig_item['inputs'].map {|f| f['name'] }
53
+
54
+ event_data[event_id(name, encode_types)] = {
55
+ types: encode_types,
56
+ name: name,
57
+ names: names,
58
+ indexed: indexed,
59
+ anonymous: sig_item.fetch('anonymous', false)
60
+ }
61
+ end
62
+ end
63
+ end
64
+
65
+ def encode(name, args)
66
+ fdata = function_data[name.to_sym]
67
+ id = Utils.zpad(Utils.encode_int(fdata[:prefix]), 4)
68
+ calldata = ABI.encode_abi fdata[:encode_types], args
69
+ "#{id}#{calldata}"
70
+ end
71
+
72
+ def decode(name, data)
73
+ fdata = function_data[name.to_sym]
74
+
75
+ if fdata[:is_unknown_type]
76
+ i = 0
77
+ o = []
78
+
79
+ while i < data.size
80
+ o.push Utils.to_signed(Utils.big_endian_to_int(data[i,32]))
81
+ i += 32
82
+ end
83
+
84
+ return 0 if o.empty?
85
+ o.size == 1 ? o[0] : o
86
+ else
87
+ ABI.decode_abi fdata[:decode_types], data
88
+ end
89
+ end
90
+
91
+ def function_data
92
+ @v[:function_data]
93
+ end
94
+
95
+ def event_data
96
+ @v[:event_data]
97
+ end
98
+
99
+ def function(name)
100
+ function_data[name.to_sym]
101
+ end
102
+
103
+ def event(name, encode_types)
104
+ event_data[event_id(name, encode_types)]
105
+ end
106
+
107
+ def is_unknown_type(name)
108
+ function_data[name.to_sym][:is_unknown_type]
109
+ end
110
+
111
+ def listen(log, noprint=false)
112
+ return if log.topics.size == 0 || !event_data.has_key?(log.topics[0])
113
+
114
+ data = event_data[log.topics[0]]
115
+ types = data[:types]
116
+ name = data[:name]
117
+ names = data[:names]
118
+ indexed = data[:indexed]
119
+ indexed_types = types.zip(indexed).select {|(t, i)| i.true? }.map(&:first)
120
+ unindexed_types = types.zip(indexed).select {|(t, i)| i.false? }.map(&:first)
121
+
122
+ deserialized_args = ABI.decode_abi unindexed_types, log.data
123
+
124
+ o = {}
125
+ c1, c2 = 0, 0
126
+ names.each_with_index do |n, i|
127
+ if indexed[i].true?
128
+ topic_bytes = Utils.zpad_int log.topics[c1+1]
129
+ o[n] = ABI.decode_primitive_type ABI::Type.parse(indexed_types[c1]), topic_bytes
130
+ c1 += 1
131
+ else
132
+ o[n] = deserialized_args[c2]
133
+ c2 += 1
134
+ end
135
+ end
136
+
137
+ o['_event_type'] = name
138
+ p o unless noprint
139
+
140
+ o
141
+ end
142
+
143
+ private
144
+
145
+ def logger
146
+ @logger ||= Logger.new 'eth.abi.contract_translator'
147
+ end
148
+
149
+ def method_id(name, encode_types)
150
+ Utils.big_endian_to_int Utils.keccak256(get_sig(name, encode_types))[0,4]
151
+ end
152
+
153
+ def event_id(name, encode_types)
154
+ Utils.big_endian_to_int Utils.keccak256(get_sig(name, encode_types))
155
+ end
156
+
157
+ def get_sig(name, encode_types)
158
+ "#{name}(#{encode_types.map {|x| canonical_name(x) }.join(',')})"
159
+ end
160
+
161
+ def canonical_name(x)
162
+ case x
163
+ when /\A(uint|int)(\[.*\])?\z/
164
+ "#{$1}256#{$2}"
165
+ when /\A(real|ureal|fixed|ufixed)(\[.*\])?\z/
166
+ "#{$1}128x128#{$2}"
167
+ else
168
+ x
169
+ end
170
+ end
171
+
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,117 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ module ABI
5
+ class Type
6
+
7
+ class ParseError < StandardError; end
8
+
9
+ class <<self
10
+ ##
11
+ # Crazy regexp to seperate out base type component (eg. uint), size (eg.
12
+ # 256, 128x128, nil), array component (eg. [], [45], nil)
13
+ #
14
+ def parse(type)
15
+ _, base, sub, dimension = /([a-z]*)([0-9]*x?[0-9]*)((\[[0-9]*\])*)/.match(type).to_a
16
+
17
+ dims = dimension.scan(/\[[0-9]*\]/)
18
+ raise ParseError, "Unknown characters found in array declaration" if dims.join != dimension
19
+
20
+ case base
21
+ when 'string'
22
+ raise ParseError, "String type must have no suffix or numerical suffix" unless sub.empty?
23
+ when 'bytes'
24
+ raise ParseError, "Maximum 32 bytes for fixed-length string or bytes" unless sub.empty? || sub.to_i <= 32
25
+ when 'uint', 'int'
26
+ raise ParseError, "Integer type must have numerical suffix" unless sub =~ /\A[0-9]+\z/
27
+
28
+ size = sub.to_i
29
+ raise ParseError, "Integer size out of bounds" unless size >= 8 && size <= 256
30
+ raise ParseError, "Integer size must be multiple of 8" unless size % 8 == 0
31
+ when 'ureal', 'real', 'fixed', 'ufixed'
32
+ raise ParseError, "Real type must have suffix of form <high>x<low>, e.g. 128x128" unless sub =~ /\A[0-9]+x[0-9]+\z/
33
+
34
+ high, low = sub.split('x').map(&:to_i)
35
+ total = high + low
36
+
37
+ raise ParseError, "Real size out of bounds (max 32 bytes)" unless total >= 8 && total <= 256
38
+ raise ParseError, "Real high/low sizes must be multiples of 8" unless high % 8 == 0 && low % 8 == 0
39
+ when 'hash'
40
+ raise ParseError, "Hash type must have numerical suffix" unless sub =~ /\A[0-9]+\z/
41
+ when 'address'
42
+ raise ParseError, "Address cannot have suffix" unless sub.empty?
43
+ when 'bool'
44
+ raise ParseError, "Bool cannot have suffix" unless sub.empty?
45
+ else
46
+ raise ParseError, "Unrecognized type base: #{base}"
47
+ end
48
+
49
+ new(base, sub, dims.map {|x| x[1...-1].to_i })
50
+ end
51
+
52
+ def size_type
53
+ @size_type ||= new('uint', 256, [])
54
+ end
55
+ end
56
+
57
+ attr :base, :sub, :dims
58
+
59
+ ##
60
+ # @param base [String] base name of type, e.g. uint for uint256[4]
61
+ # @param sub [String] subscript of type, e.g. 256 for uint256[4]
62
+ # @param dims [Array[Integer]] dimensions of array type, e.g. [1,2,0]
63
+ # for uint256[1][2][], [] for non-array type
64
+ #
65
+ def initialize(base, sub, dims)
66
+ @base = base
67
+ @sub = sub
68
+ @dims = dims
69
+ end
70
+
71
+ def ==(another_type)
72
+ base == another_type.base &&
73
+ sub == another_type.sub &&
74
+ dims == another_type.dims
75
+ end
76
+
77
+ ##
78
+ # Get the static size of a type, or nil if dynamic.
79
+ #
80
+ # @return [Integer, NilClass] size of static type, or nil for dynamic
81
+ # type
82
+ #
83
+ def size
84
+ @size ||= if dims.empty?
85
+ if %w(string bytes).include?(base) && sub.empty?
86
+ nil
87
+ else
88
+ 32
89
+ end
90
+ else
91
+ if dims.last == 0 # 0 for dynamic array []
92
+ nil
93
+ else
94
+ subtype.dynamic? ? nil : dims.last * subtype.size
95
+ end
96
+ end
97
+ end
98
+
99
+ def dynamic?
100
+ size.nil?
101
+ end
102
+
103
+ ##
104
+ # Type with one dimension lesser.
105
+ #
106
+ # @example
107
+ # Type.parse("uint256[2][]").subtype # => Type.new('uint', 256, [2])
108
+ #
109
+ # @return [Ethereum::ABI::Type]
110
+ #
111
+ def subtype
112
+ @subtype ||= self.class.new(base, sub, dims[0...-1])
113
+ end
114
+
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,72 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+
5
+ ##
6
+ # An Ethereum account.
7
+ #
8
+ # * `@nonce`: the account's nonce (the number of transactions sent by the
9
+ # account)
10
+ # * `@balance`: the account's balance in wei
11
+ # * `@storage`: the root of the account's storage trie
12
+ # * `@code_hash`: the SHA3 hash of the code associated with the account
13
+ # * `@db`: the database in which the account's code is stored
14
+ #
15
+ class Account
16
+ include RLP::Sedes::Serializable
17
+
18
+ set_serializable_fields(
19
+ nonce: Sedes.big_endian_int,
20
+ balance: Sedes.big_endian_int,
21
+ storage: Sedes.trie_root,
22
+ code_hash: Sedes.hash32
23
+ )
24
+
25
+ class <<self
26
+ ##
27
+ # Create a blank account.
28
+ #
29
+ # The returned account will have zero nonce and balance, a blank storage
30
+ # trie and empty code.
31
+ #
32
+ # @param db [BaseDB] the db in which the account will store its code
33
+ #
34
+ # @return [Account] the created blank account
35
+ #
36
+ def build_blank(db, initial_nonce=0)
37
+ code_hash = Utils.keccak256 Constant::BYTE_EMPTY
38
+ db.put code_hash, Constant::BYTE_EMPTY
39
+
40
+ new initial_nonce, 0, Trie::BLANK_ROOT, code_hash, db
41
+ end
42
+ end
43
+
44
+ def initialize(*args)
45
+ @db = args.pop if args.size == 5 # (nonce, balance, storage, code_hash, db)
46
+ @db = args.last.delete(:db) if args.last.is_a?(Hash)
47
+ raise ArgumentError, "No database object given" unless @db.is_a?(DB::BaseDB)
48
+
49
+ super(*args)
50
+ end
51
+
52
+ ##
53
+ # The EVM code of the account.
54
+ #
55
+ # This property will be read from or written to the db at each access, with
56
+ # `code_hash` used as key.
57
+ #
58
+ def code
59
+ @db.get code_hash
60
+ end
61
+
62
+ def code=(value)
63
+ self.code_hash = Utils.keccak256 value
64
+
65
+ # Technically a db storage leak, but doesn't really matter; the only
66
+ # thing that fails to get garbage collected is when code disappears due
67
+ # to a suicide.
68
+ @db.inc_refcount(code_hash, value)
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,60 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ class Address
5
+
6
+ BLANK = ''.freeze
7
+ ZERO = ("\x00"*20).freeze
8
+
9
+ CREATE_CONTRACT = BLANK
10
+
11
+ def initialize(s)
12
+ @bytes = parse s
13
+ end
14
+
15
+ def to_bytes(extended=false)
16
+ extended ? "#{@bytes}#{checksum}" : @bytes
17
+ end
18
+
19
+ def to_hex(extended=false)
20
+ Utils.encode_hex to_bytes(extended)
21
+ end
22
+
23
+ def checksum(bytes=nil)
24
+ Utils.keccak256(bytes||@bytes)[0,4]
25
+ end
26
+
27
+ def blank?
28
+ @bytes == BLANK
29
+ end
30
+
31
+ private
32
+
33
+ def parse(s)
34
+ case s.size
35
+ when 0
36
+ s
37
+ when 40
38
+ Utils.decode_hex s
39
+ when 42
40
+ raise FormatError, "Invalid address format!" unless s[0,2] == '0x'
41
+ parse s[2..-1]
42
+ when 48
43
+ bytes = Utils.decode_hex s
44
+ parse bytes
45
+ when 50
46
+ raise FormatError, "Invalid address format!" unless s[0,2] == '0x'
47
+ parse s[2..-1]
48
+ when 20
49
+ s
50
+ when 24
51
+ bytes = s[0...-4]
52
+ raise ChecksumError, "Invalid address checksum!" unless s[-4..-1] == checksum(bytes)
53
+ bytes
54
+ else
55
+ raise FormatError, "Invalid address format!"
56
+ end
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,53 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ module BaseConvert
5
+
6
+ BaseSymbols = {
7
+ 2 => '01'.freeze,
8
+ 10 => '0123456789'.freeze,
9
+ 16 => '0123456789abcdef'.freeze,
10
+ 32 => 'abcdefghijklmnopqrstuvwxyz234567'.freeze,
11
+ 58 => '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'.freeze,
12
+ 256 => 256.times.map {|i| i.chr }.join.freeze
13
+ }.freeze
14
+
15
+ extend self
16
+
17
+ def symbols(base)
18
+ BaseSymbols[base] or raise ArgumentError, "invalid base!"
19
+ end
20
+
21
+ def convert(s, from, to, minlen=0)
22
+ return Utils.lpad(s, symbols(from)[0], minlen) if from == to
23
+ encode decode(s, from), to, minlen
24
+ end
25
+
26
+ def encode(v, base, minlen)
27
+ syms = symbols(base)
28
+
29
+ result = ''
30
+ while v > 0
31
+ result = syms[v % base] + result
32
+ v /= base
33
+ end
34
+
35
+ Utils.lpad result, syms[0], minlen
36
+ end
37
+
38
+ def decode(s, base)
39
+ syms = symbols(base)
40
+ s = s.downcase if base == 16
41
+
42
+ result = 0
43
+ while s.size > 0
44
+ result *= base
45
+ result += syms.index(s[0])
46
+ s = s[1..-1]
47
+ end
48
+
49
+ result
50
+ end
51
+
52
+ end
53
+ end