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,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