ruby-ethereum 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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