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,28 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ class PruningTrie < Trie
5
+ # TODO: pruning trie implementation
6
+
7
+ def clear_all(node=nil)
8
+ if node.nil?
9
+ node = @root_node
10
+ delete_node_storage node
11
+ end
12
+
13
+ return if node == BLANK_NODE
14
+
15
+ node_type = get_node_type node
16
+ delete_node_storage node
17
+
18
+ if NODE_KV_TYPE.include?(node_type)
19
+ value_is_node = node_type == :extension
20
+ clear_all decode_to_node(node[1]) if value_is_node
21
+ elsif node_type == :branch
22
+ 16.times do |i|
23
+ clear_all decode_to_node(node[i])
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,88 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ class PublicKey
5
+
6
+ attr :raw
7
+
8
+ def initialize(raw)
9
+ @raw = raw
10
+ end
11
+
12
+ def encode(fmt)
13
+ case fmt
14
+ when :decimal
15
+ value
16
+ when :bin
17
+ "\x04#{BaseConvert.encode(value[0], 256, 32)}#{BaseConvert.encode(value[1], 256, 32)}"
18
+ when :bin_compressed
19
+ "#{(2+(value[1]%2)).chr}#{BaseConvert.encode(value[0], 256, 32)}"
20
+ when :hex
21
+ "04#{BaseConvert.encode(value[0], 16, 64)}#{BaseConvert.encode(value[1], 16, 64)}"
22
+ when :hex_compressed
23
+ "0#{2+(value[1]%2)}#{BaseConvert.encode(value[0], 16, 64)}"
24
+ when :bin_electrum
25
+ "#{BaseConvert.encode(value[0], 256, 32)}#{BaseConvert.encode(value[1], 256, 32)}"
26
+ when :hex_electrum
27
+ "#{BaseConvert.encode(value[0], 16, 64)}#{BaseConvert.encode(value[1], 16, 64)}"
28
+ else
29
+ raise FormatError, "Invalid format!"
30
+ end
31
+ end
32
+
33
+ def decode(fmt=nil)
34
+ fmt ||= format
35
+
36
+ case fmt
37
+ when :decimal
38
+ raw
39
+ when :bin
40
+ [BaseConvert.decode(raw[1,32], 256), BaseConvert.decode(raw[33,32], 256)]
41
+ when :bin_compressed
42
+ x = BaseConvert.decode raw[1,32], 256
43
+ m = x*x*x + Secp256k1::A*x + Secp256k1::B
44
+ n = Utils.mod_exp(m, (Secp256k1::P+1)/4, Secp256k1::P)
45
+ q = (n + raw[0].ord) % 2
46
+ y = q == 1 ? (Secp256k1::P - n) : n
47
+ [x, y]
48
+ when :hex
49
+ [BaseConvert.decode(raw[2,64], 16), BaseConvert.decode(raw[66,64], 16)]
50
+ when :hex_compressed
51
+ PublicKey.new(Utils.decode_hex(raw)).decode :bin_compressed
52
+ when :bin_electrum
53
+ [BaseConvert.decode(raw[0,32], 256), BaseConvert.decode(raw[32,32], 256)]
54
+ when :hex_electrum
55
+ [BaseConvert.decode(raw[0,64], 16), BaseConvert.decode(raw[64,128], 16)]
56
+ else
57
+ raise FormatError, "Invalid format!"
58
+ end
59
+ end
60
+
61
+ def value
62
+ @value ||= decode
63
+ end
64
+
65
+ def format
66
+ return :decimal if raw.is_a?(Array)
67
+ return :bin if raw.size == 65 && raw[0] == "\x04"
68
+ return :hex if raw.size == 130 && raw[0, 2] == '04'
69
+ return :bin_compressed if raw.size == 33 && "\x02\x03".include?(raw[0])
70
+ return :hex_compressed if raw.size == 66 && %w(02 03).include?(raw[0,2])
71
+ return :bin_electrum if raw.size == 64
72
+ return :hex_electrum if raw.size == 128
73
+
74
+ raise FormatError, "Pubkey is not in recognized format"
75
+ end
76
+
77
+ def to_bitcoin_address(magicbyte=0)
78
+ bytes = encode(:bin)
79
+ Utils.bytes_to_base58_check Utils.hash160(bytes), magicbyte
80
+ end
81
+
82
+ def to_address(extended=false)
83
+ bytes = Utils.keccak256(encode(:bin)[1..-1])[-20..-1]
84
+ Address.new(bytes).to_bytes(extended)
85
+ end
86
+
87
+ end
88
+ end
@@ -0,0 +1,53 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ class Receipt
5
+ include RLP::Sedes::Serializable
6
+
7
+ extend Sedes
8
+
9
+ set_serializable_fields(
10
+ state_root: trie_root,
11
+ gas_used: big_endian_int,
12
+ bloom: int256,
13
+ logs: RLP::Sedes::CountableList.new(Log)
14
+ )
15
+
16
+ # initialize(state_root, gas_used, logs, bloom: nil)
17
+ def initialize(*args)
18
+ h = normalize_args args
19
+ super(h)
20
+ raise ArgumentError, "Invalid bloom filter" if h[:bloom] && h[:bloom] != self.bloom
21
+ end
22
+
23
+ def bloom
24
+ bloomables = logs.map {|l| l.bloomables }
25
+ Bloom.from_array bloomables.flatten
26
+ end
27
+
28
+ private
29
+
30
+ def normalize_args(args)
31
+ options = args.last.is_a?(Hash) ? args.pop : {}
32
+ field_set = %i(state_root gas_used logs bloom) # different order than serializable fields
33
+
34
+ h = {}
35
+ fields = field_set[0,args.size]
36
+ fields.zip(args).each do |(field, arg)|
37
+ h[field] = arg
38
+ field_set.delete field
39
+ end
40
+
41
+ options.each do |field, value|
42
+ if field_set.include?(field)
43
+ h[field] = value
44
+ field_set.delete field
45
+ end
46
+ end
47
+
48
+ field_set.each {|field| h[field] = nil }
49
+ h
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,58 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ require 'base64'
4
+ require 'secp256k1'
5
+
6
+ module Ethereum
7
+ module Secp256k1
8
+
9
+ # Elliptic curve parameters
10
+ P = 2**256 - 2**32 - 977
11
+ N = 115792089237316195423570985008687907852837564279074904382605163141518161494337
12
+ A = 0
13
+ B = 7
14
+ Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
15
+ Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424
16
+ G = [Gx, Gy].freeze
17
+
18
+ class InvalidPrivateKey < StandardError; end
19
+
20
+ class <<self # extensions
21
+ def priv_to_pub(priv)
22
+ priv = PrivateKey.new(priv)
23
+ privkey = ::Secp256k1::PrivateKey.new privkey: priv.encode(:bin), raw: true
24
+ pubkey = privkey.pubkey
25
+ PublicKey.new(pubkey.serialize).encode(priv.format)
26
+ end
27
+
28
+ def recoverable_sign(msg, privkey)
29
+ pk = ::Secp256k1::PrivateKey.new privkey: privkey, raw: true
30
+ signature = pk.ecdsa_recoverable_serialize pk.ecdsa_sign_recoverable(msg, raw: true)
31
+
32
+ v = signature[1] + 27
33
+ r = Utils.big_endian_to_int signature[0][0,32]
34
+ s = Utils.big_endian_to_int signature[0][32,32]
35
+
36
+ [v,r,s]
37
+ end
38
+
39
+ def signature_verify(msg, vrs, pubkey)
40
+ pk = ::Secp256k1::PublicKey.new(pubkey: pubkey)
41
+ raw_sig = Utils.zpad_int(vrs[1]) + Utils.zpad_int(vrs[2])
42
+
43
+ sig = ::Secp256k1::C::ECDSASignature.new
44
+ sig[:data].to_ptr.write_bytes(raw_sig)
45
+
46
+ pk.ecdsa_verify(msg, sig)
47
+ end
48
+
49
+ def recover_pubkey(msg, vrs, compressed: false)
50
+ pk = ::Secp256k1::PublicKey.new(flags: ::Secp256k1::ALL_FLAGS)
51
+ sig = Utils.zpad_int(vrs[1]) + Utils.zpad_int(vrs[2])
52
+ recsig = pk.ecdsa_recoverable_deserialize(sig, vrs[0]-27)
53
+ pk.public_key = pk.ecdsa_recover msg, recsig, raw: true
54
+ pk.serialize compressed: compressed
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,49 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ require 'forwardable'
4
+
5
+ module Ethereum
6
+ class SecureTrie
7
+
8
+ extend Forwardable
9
+ def_delegators :@trie, :root_hash, :set_root_hash, :root_hash_valid?, :process_epoch, :commit_death_row, :revert_epoch, :has_key?, :include?, :size, :to_h, :db
10
+
11
+ def initialize(trie)
12
+ @trie = trie
13
+ @db = trie.db
14
+ end
15
+
16
+ def [](k)
17
+ @trie[Utils.keccak256(k)]
18
+ end
19
+ alias :get :[]
20
+
21
+ def []=(k, v)
22
+ h = Utils.keccak256 k
23
+ @db.put h, k
24
+ @trie[h] = v
25
+ end
26
+ alias :set :[]=
27
+
28
+ def delete(k)
29
+ @trie.delete Utils.keccak256(k)
30
+ end
31
+
32
+ def to_h
33
+ o = {}
34
+ @trie.to_h.each do |h, v|
35
+ k = @db.get h
36
+ o[k] = v
37
+ end
38
+ o
39
+ end
40
+
41
+ def each(&block)
42
+ @trie.each do |h, v|
43
+ k = @db.get h
44
+ block.call k, v
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,42 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ module Sedes
5
+ include RLP::Sedes
6
+
7
+ extend self
8
+
9
+ def address
10
+ Binary.fixed_length(20, allow_empty: true)
11
+ end
12
+
13
+ def int20
14
+ BigEndianInt.new(20)
15
+ end
16
+
17
+ def int32
18
+ BigEndianInt.new(32)
19
+ end
20
+
21
+ def int256
22
+ BigEndianInt.new(256)
23
+ end
24
+
25
+ def hash32
26
+ Binary.fixed_length(32)
27
+ end
28
+
29
+ def trie_root
30
+ Binary.fixed_length(32, allow_empty: true)
31
+ end
32
+
33
+ def big_endian_int
34
+ RLP::Sedes.big_endian_int
35
+ end
36
+
37
+ def binary
38
+ RLP::Sedes.binary
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,95 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ class SpecialContract
5
+
6
+ class ECRecover
7
+ def call(ext, msg)
8
+ gas_cost = Opcodes::GECRECOVER
9
+ return 0, 0, [] if msg.gas < gas_cost
10
+
11
+ message_hash_ints = [0] * 32
12
+ msg.data.extract_copy(message_hash_ints, 0, 0, 32)
13
+ message_hash = Utils.int_array_to_bytes message_hash_ints
14
+
15
+ v = msg.data.extract32(32)
16
+ r = msg.data.extract32(64)
17
+ s = msg.data.extract32(96)
18
+
19
+ if r >= Secp256k1::N || s >= Secp256k1::N || v < 27 || v > 28
20
+ return 1, msg.gas - gas_cost, []
21
+ end
22
+
23
+ signature_ints = [0] * 64
24
+ msg.data.extract_copy signature_ints, 0, 64, 32 # r
25
+ msg.data.extract_copy signature_ints, 32, 96, 32 # s
26
+ signature = Utils.int_array_to_bytes signature_ints
27
+
28
+ pub = nil
29
+ begin
30
+ pub = Secp256k1.recover_pubkey(message_hash, [v,r,s])
31
+ rescue
32
+ return 1, msg.gas - gas_cost, []
33
+ end
34
+
35
+ pubhash = Utils.keccak256(pub[1..-1])[-20..-1]
36
+ o = Utils.bytes_to_int_array Utils.zpad(pubhash, 32)
37
+
38
+ return 1, msg.gas - gas_cost, o
39
+ end
40
+ end
41
+
42
+ class SHA256
43
+ def call(ext, msg)
44
+ gas_cost = Opcodes::GSHA256BASE +
45
+ (Utils.ceil32(msg.data.size) / 32) * Opcodes::GSHA256WORD
46
+ return 0, 0, [] if msg.gas < gas_cost
47
+
48
+ d = msg.data.extract_all
49
+ o = Utils.bytes_to_int_array Utils.sha256(d)
50
+
51
+ return 1, msg.gas - gas_cost, o
52
+ end
53
+ end
54
+
55
+ class RIPEMD160
56
+ def call(ext, msg)
57
+ gas_cost = Opcodes::GRIPEMD160BASE +
58
+ (Utils.ceil32(msg.data.size) / 32) * Opcodes::GRIPEMD160WORD
59
+ return 0, 0, [] if msg.gas < gas_cost
60
+
61
+ d = msg.data.extract_all
62
+ o = Utils.bytes_to_int_array Utils.zpad(Utils.ripemd160(d), 32)
63
+
64
+ return 1, msg.gas - gas_cost, o
65
+ end
66
+ end
67
+
68
+ class Identity
69
+ def call(ext, msg)
70
+ gas_cost = Opcodes::GIDENTITYBASE +
71
+ (Utils.ceil32(msg.data.size) / 32) * Opcodes::GIDENTITYWORD
72
+ return 0, 0, [] if msg.gas < gas_cost
73
+
74
+ o = []
75
+ msg.data.extract_copy(o, 0, 0, msg.data.size)
76
+
77
+ return 1, msg.gas - gas_cost, o
78
+ end
79
+ end
80
+
81
+ DEPLOY = {
82
+ '0000000000000000000000000000000000000001' => ECRecover.new,
83
+ '0000000000000000000000000000000000000002' => SHA256.new,
84
+ '0000000000000000000000000000000000000003' => RIPEMD160.new,
85
+ '0000000000000000000000000000000000000004' => Identity.new
86
+ }.map {|k,v| [Utils.decode_hex(k), v] }.to_h.freeze
87
+
88
+ class <<self
89
+ def [](address)
90
+ DEPLOY[address]
91
+ end
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,79 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ require 'ethereum/spv/proof'
4
+ require 'ethereum/spv/proof_constructor'
5
+ require 'ethereum/spv/proof_verifier'
6
+
7
+ module Ethereum
8
+ module SPV
9
+
10
+ class <<self
11
+
12
+ def proofs
13
+ @proofs ||= []
14
+ end
15
+
16
+ def proof
17
+ proofs.last
18
+ end
19
+
20
+ def grabbing(node)
21
+ proof.grabbing node if proof
22
+ end
23
+
24
+ def store(node)
25
+ proof.store node if proof
26
+ end
27
+
28
+ def record
29
+ proofs.push ProofConstructor.new
30
+ result = yield
31
+ nodes = proof.decoded_nodes
32
+ [result, nodes]
33
+ ensure
34
+ proofs.pop
35
+ end
36
+
37
+ def verify(nodes)
38
+ proofs.push ProofVerifier.new(nodes: nodes)
39
+ yield
40
+ ensure
41
+ proofs.pop
42
+ end
43
+
44
+ def mode
45
+ case proof
46
+ when ProofConstructor
47
+ :record
48
+ when ProofVerifier
49
+ :verify
50
+ else
51
+ nil
52
+ end
53
+ end
54
+
55
+ def make_transaction_proof(block, tx)
56
+ result, nodes = record do
57
+ block.apply_transaction(tx)
58
+ end
59
+
60
+ nodes
61
+ .map {|x| RLP.encode(x) }
62
+ .uniq
63
+ .map {|x| RLP.decode(x) }
64
+ end
65
+
66
+ def verify_transaction_proof(block, tx, nodes)
67
+ verify do
68
+ block.apply_transaction(tx)
69
+ end
70
+ true
71
+ rescue
72
+ puts $!
73
+ false
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+ end