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,97 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+
5
+ ##
6
+ # Collection of indexes.
7
+ #
8
+ # * children - needed to get the uncles of a block
9
+ # * blocknumbers - needed to mark the longest chain (path to top)
10
+ # * transactions - optional to resolve txhash to block:tx
11
+ #
12
+ class Index
13
+
14
+ attr :db
15
+
16
+ def initialize(env, index_transactions=true)
17
+ @env = env
18
+ @db = env.db
19
+ @index_transactions = index_transactions
20
+ end
21
+
22
+ def add_block(blk)
23
+ add_child blk.prevhash, blk.full_hash
24
+ add_transactions blk if @index_transactions
25
+ end
26
+
27
+ def add_child(parent_hash, child_hash)
28
+ children = (get_children(parent_hash) + [child_hash]).uniq
29
+ @db.put_temporarily child_db_key(parent_hash), RLP.encode(children)
30
+ end
31
+
32
+ # start from head and update until the existing indices match the block
33
+ def update_blocknumbers(blk)
34
+ loop do
35
+ if blk.number > 0
36
+ @db.put_temporarily block_by_number_key(blk.number), blk.full_hash
37
+ else
38
+ @db.put block_by_number_key(blk.number), blk.full_hash
39
+ end
40
+ @db.commit_refcount_changes blk.number
41
+
42
+ break if blk.number == 0
43
+
44
+ blk = blk.get_parent()
45
+ break if has_block_by_number(blk.number) && get_block_by_number(blk.number) == blk.full_hash
46
+ end
47
+ end
48
+
49
+ def has_block_by_number(number)
50
+ @db.has_key? block_by_number_key(number)
51
+ end
52
+
53
+ def get_block_by_number(number)
54
+ @db.get block_by_number_key(number)
55
+ end
56
+
57
+ def get_children(blk_hash)
58
+ key = child_db_key blk_hash
59
+ @db.has_key?(key) ? RLP.decode(@db.get(key)) : []
60
+ end
61
+
62
+ ##
63
+ # @param txhash [String] transaction hash
64
+ #
65
+ # @return [[Transaction, Block, Integer]] transaction, block, and tx number
66
+ #
67
+ def get_transaction(txhash)
68
+ blockhash, tx_num_enc = RLP.decode @db.get(txhash)
69
+ blk = RLP.decode(@db.get(blockhash), sedes: Block, env: @env)
70
+
71
+ num = Utils.decode_int tx_num_enc
72
+ tx_data = blk.get_transaction num
73
+
74
+ [tx_data, blk, num]
75
+ end
76
+
77
+ private
78
+
79
+ def child_db_key(blk_hash)
80
+ "ci:#{blk_hash}"
81
+ end
82
+
83
+ def add_transactions(blk)
84
+ blk.get_transactions.each_with_index do |tx, i|
85
+ @db.put_temporarily tx.full_hash, RLP.encode([blk.full_hash, i])
86
+ end
87
+
88
+ @db.commit_refcount_changes blk.number
89
+ end
90
+
91
+ def block_by_number_key(number)
92
+ "blocknumber:#{number}"
93
+ end
94
+
95
+ end
96
+
97
+ end
@@ -0,0 +1,43 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ class Log
5
+ include RLP::Sedes::Serializable
6
+
7
+ set_serializable_fields(
8
+ address: Sedes.address,
9
+ topics: RLP::Sedes::CountableList.new(Sedes.int32),
10
+ data: Sedes.binary
11
+ )
12
+
13
+ def initialize(*args)
14
+ h = parse_field_args args
15
+
16
+ address = h[:address]
17
+ raise ArgumentError, "invalid address: #{address}" unless address.size == 20 || address.size == 40
18
+
19
+ address = Utils.decode_hex(address) if address.size == 40
20
+
21
+ h[:address] = address
22
+ super(h)
23
+ end
24
+
25
+ def bloomables
26
+ topics.map {|t| Sedes.int32.serialize(t) }.unshift(address)
27
+ end
28
+
29
+ def to_h
30
+ { bloom: Utils.encode_hex(Bloom.b256(Bloom.from_array(bloomables))),
31
+ address: Utils.encode_hex(address),
32
+ data: "0x#{Utils.encode_hex(data)}",
33
+ topics: topics.map {|t| Utils.encode_hex(Sedes.int32.serialize(t)) }
34
+ }
35
+ end
36
+
37
+ def to_s
38
+ "#<#{self.class.name}:#{object_id} address=#{Utils.encode_hex(address)} topics=#{topics} data=#{data}>"
39
+ end
40
+ alias :inspect :to_s
41
+
42
+ end
43
+ end
@@ -0,0 +1,84 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ class Miner
5
+
6
+ class <<self
7
+ def check_pow(block_number, header_hash, mixhash, nonce, difficulty)
8
+ Logger.new('eth.miner').debug "checking pow", block_number: block_number
9
+
10
+ return false if mixhash.size != 32 || header_hash.size != 32 || nonce.size != 8
11
+
12
+ cache = Ethash.get_cache block_number
13
+ mining_output = hashimoto_light block_number, cache, header_hash, nonce
14
+
15
+ return false if mining_output[:mixhash] != mixhash
16
+ return Utils.big_endian_to_int(mining_output[:result]) <= (Constant::TT256 / difficulty)
17
+ end
18
+ lru_cache :check_pow, 32
19
+
20
+ def hashimoto_light(*args)
21
+ Ethash.hashimoto_light(*args)
22
+ end
23
+ end
24
+
25
+
26
+ ##
27
+ # Mines on the current head. Stores received transactions.
28
+ #
29
+ # The process of finalising a block involves four stages:
30
+ #
31
+ # 1. validate (or, if mining, determine) uncles;
32
+ # 2. validate (or, if mining, determine) transactions;
33
+ # 3. apply rewards;
34
+ # 4. verify (or, if mining, compute a valid) state and nonce.
35
+ #
36
+ def initialize(block)
37
+ @nonce = 0
38
+ @block = block
39
+
40
+ logger.debug "mining", block_number: @block.number, block_hash: Utils.encode_hex(@block.full_hash), block_difficulty: @block.difficulty
41
+ end
42
+
43
+ def mine(rounds=1000, start_nonce=0)
44
+ blk = @block
45
+ bin_nonce, mixhash = _mine(blk.number, blk.difficulty, blk.mining_hash, start_nonce, rounds)
46
+
47
+ if bin_nonce.true?
48
+ blk.mixhash = mixhash
49
+ blk.nonce = bin_nonce
50
+ return blk
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def logger
57
+ @logger ||= Logger.new 'eth.miner'
58
+ end
59
+
60
+ def _mine(block_number, difficulty, mining_hash, start_nonce=0, rounds=1000)
61
+ raise AssertError, "start nonce must be an integer" unless start_nonce.is_a?(Integer)
62
+
63
+ cache = Ethash.get_cache block_number
64
+ nonce = start_nonce
65
+ difficulty ||= 1
66
+ target = Utils.zpad Utils.int_to_big_endian(Constant::TT256 / difficulty), 32
67
+
68
+ (1..rounds).each do |i|
69
+ bin_nonce = Utils.zpad Utils.int_to_big_endian((nonce+i) & Constant::TT64M1), 8
70
+ o = Miner.hashimoto_light block_number, cache, mining_hash, bin_nonce
71
+
72
+ if o[:result] <= target
73
+ logger.debug "nonce found"
74
+ raise AssertError, "nonce must be 8 bytes long" unless bin_nonce.size == 8
75
+ raise AssertError, "mishash must be 32 bytes long" unless o[:mixhash].size == 32
76
+ return bin_nonce, o[:mixhash]
77
+ end
78
+ end
79
+
80
+ return nil, nil
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,131 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ class Opcodes
5
+
6
+ TABLE = {
7
+ # schema: [op, ins, outs, gas]
8
+ 0x00 => [:STOP, 0, 0, 0],
9
+ 0x01 => [:ADD, 2, 1, 3],
10
+ 0x02 => [:MUL, 2, 1, 5],
11
+ 0x03 => [:SUB, 2, 1, 3],
12
+ 0x04 => [:DIV, 2, 1, 5],
13
+ 0x05 => [:SDIV, 2, 1, 5],
14
+ 0x06 => [:MOD, 2, 1, 5],
15
+ 0x07 => [:SMOD, 2, 1, 5],
16
+ 0x08 => [:ADDMOD, 3, 1, 8],
17
+ 0x09 => [:MULMOD, 3, 1, 8],
18
+ 0x0a => [:EXP, 2, 1, 10],
19
+ 0x0b => [:SIGNEXTEND, 2, 1, 5],
20
+ 0x10 => [:LT, 2, 1, 3],
21
+ 0x11 => [:GT, 2, 1, 3],
22
+ 0x12 => [:SLT, 2, 1, 3],
23
+ 0x13 => [:SGT, 2, 1, 3],
24
+ 0x14 => [:EQ, 2, 1, 3],
25
+ 0x15 => [:ISZERO, 1, 1, 3],
26
+ 0x16 => [:AND, 2, 1, 3],
27
+ 0x17 => [:OR, 2, 1, 3],
28
+ 0x18 => [:XOR, 2, 1, 3],
29
+ 0x19 => [:NOT, 1, 1, 3],
30
+ 0x1a => [:BYTE, 2, 1, 3],
31
+ 0x20 => [:SHA3, 2, 1, 30],
32
+ 0x30 => [:ADDRESS, 0, 1, 2],
33
+ 0x31 => [:BALANCE, 1, 1, 20],
34
+ 0x32 => [:ORIGIN, 0, 1, 2],
35
+ 0x33 => [:CALLER, 0, 1, 2],
36
+ 0x34 => [:CALLVALUE, 0, 1, 2],
37
+ 0x35 => [:CALLDATALOAD, 1, 1, 3],
38
+ 0x36 => [:CALLDATASIZE, 0, 1, 2],
39
+ 0x37 => [:CALLDATACOPY, 3, 0, 3],
40
+ 0x38 => [:CODESIZE, 0, 1, 2],
41
+ 0x39 => [:CODECOPY, 3, 0, 3],
42
+ 0x3a => [:GASPRICE, 0, 1, 2],
43
+ 0x3b => [:EXTCODESIZE, 1, 1, 20],
44
+ 0x3c => [:EXTCODECOPY, 4, 0, 20],
45
+ 0x40 => [:BLOCKHASH, 1, 1, 20],
46
+ 0x41 => [:COINBASE, 0, 1, 2],
47
+ 0x42 => [:TIMESTAMP, 0, 1, 2],
48
+ 0x43 => [:NUMBER, 0, 1, 2],
49
+ 0x44 => [:DIFFICULTY, 0, 1, 2],
50
+ 0x45 => [:GASLIMIT, 0, 1, 2],
51
+ 0x50 => [:POP, 1, 0, 2],
52
+ 0x51 => [:MLOAD, 1, 1, 3],
53
+ 0x52 => [:MSTORE, 2, 0, 3],
54
+ 0x53 => [:MSTORE8, 2, 0, 3],
55
+ 0x54 => [:SLOAD, 1, 1, 50],
56
+ 0x55 => [:SSTORE, 2, 0, 0],
57
+ 0x56 => [:JUMP, 1, 0, 8],
58
+ 0x57 => [:JUMPI, 2, 0, 10],
59
+ 0x58 => [:PC, 0, 1, 2],
60
+ 0x59 => [:MSIZE, 0, 1, 2],
61
+ 0x5a => [:GAS, 0, 1, 2],
62
+ 0x5b => [:JUMPDEST, 0, 0, 1],
63
+ 0xa0 => [:LOG0, 2, 0, 375],
64
+ 0xa1 => [:LOG1, 3, 0, 750],
65
+ 0xa2 => [:LOG2, 4, 0, 1125],
66
+ 0xa3 => [:LOG3, 5, 0, 1500],
67
+ 0xa4 => [:LOG4, 6, 0, 1875],
68
+ 0xf0 => [:CREATE, 3, 1, 32000],
69
+ 0xf1 => [:CALL, 7, 1, 40],
70
+ 0xf2 => [:CALLCODE, 7, 1, 40],
71
+ 0xf3 => [:RETURN, 2, 0, 0],
72
+ 0xf4 => [:DELEGATECALL, 6, 0, 40],
73
+ 0xff => [:SUICIDE, 1, 0, 0],
74
+ }
75
+
76
+ PREFIX_LOG = 'LOG'.freeze
77
+ PREFIX_PUSH = 'PUSH'.freeze
78
+ PREFIX_DUP = 'DUP'.freeze
79
+ PREFIX_SWAP = 'SWAP'.freeze
80
+
81
+ 32.times do |i|
82
+ TABLE[0x60+i] = [:"#{PREFIX_PUSH}#{i+1}", 0, 1, 3]
83
+ end
84
+
85
+ 16.times do |i|
86
+ TABLE[0X80+i] = [:"#{PREFIX_DUP}#{i+1}", i+1, i+2, 3]
87
+ TABLE[0x90+i] = [:"#{PREFIX_SWAP}#{i+1}", i+2, i+2, 3]
88
+ end
89
+
90
+ REVERSE_TABLE = {}
91
+ TABLE.each do |code, defn|
92
+ const_set defn[0], defn
93
+ REVERSE_TABLE[defn[0]] = code
94
+ end
95
+
96
+ TABLE.freeze
97
+ REVERSE_TABLE.freeze
98
+
99
+ # Non-opcode gas prices
100
+ GDEFAULT = 1
101
+ GMEMORY = 3
102
+ GQUADRATICMEMDENOM = 512 # 1 gas per 512 quadwords
103
+ GSTORAGEREFUND = 15000
104
+ GSTORAGEKILL = 5000
105
+ GSTORAGEMOD = 5000
106
+ GSTORAGEADD = 20000
107
+ GEXPONENTBYTE = 10 # cost of EXP exponent per byte
108
+ GCOPY = 3 # cost to copy one 32 byte word
109
+ GCONTRACTBYTE = 200 # one byte of code in contract creation
110
+ GCALLVALUETRANSFER = 9000 # non-zero-valued call
111
+ GLOGBYTE = 8 # cost of a byte of logdata
112
+
113
+ GTXCOST = 21000 # TX BASE GAS COST
114
+ GTXDATAZERO = 4 # TX DATA ZERO BYTE GAS COST
115
+ GTXDATANONZERO = 68 # TX DATA NON ZERO BYTE GAS COST
116
+ GSHA3WORD = 6 # Cost of SHA3 per word
117
+ GSHA256BASE = 60 # Base c of SHA256
118
+ GSHA256WORD = 12 # Cost of SHA256 per word
119
+ GRIPEMD160BASE = 600 # Base cost of RIPEMD160
120
+ GRIPEMD160WORD = 120 # Cost of RIPEMD160 per word
121
+ GIDENTITYBASE = 15 # Base cost of indentity
122
+ GIDENTITYWORD = 3 # Cost of identity per word
123
+ GECRECOVER = 3000 # Cost of ecrecover op
124
+
125
+ GSTIPEND = 2300
126
+
127
+ GCALLNEWACCOUNT = 25000
128
+ GSUICIDEREFUND = 24000
129
+
130
+ end
131
+ end
@@ -0,0 +1,92 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ class PrivateKey
5
+
6
+ attr :raw
7
+
8
+ def initialize(raw)
9
+ @raw = raw
10
+ end
11
+
12
+ def encode(fmt, vbyte=0)
13
+ return self.class.new(value).encode(fmt, vbyte) unless raw.is_a?(Numeric)
14
+
15
+ case fmt
16
+ when :decimal
17
+ raw
18
+ when :bin
19
+ BaseConvert.encode(raw, 256, 32)
20
+ when :bin_compressed
21
+ "#{BaseConvert.encode(raw, 256, 32)}\x01"
22
+ when :hex
23
+ BaseConvert.encode(raw, 16, 64)
24
+ when :hex_compressed
25
+ "#{BaseConvert.encode(raw, 16, 64)}01"
26
+ when :wif
27
+ Utils.bytes_to_base58_check(encode(:bin), 128+vbyte)
28
+ when :wif_compressed
29
+ Utils.bytes_to_base58_check(encode(:bin_compressed), 128+vbyte)
30
+ else
31
+ raise ArgumentError, "invalid format: #{fmt}"
32
+ end
33
+ end
34
+
35
+ def decode(fmt=nil)
36
+ fmt ||= format
37
+
38
+ case fmt
39
+ when :decimal
40
+ raw
41
+ when :bin
42
+ BaseConvert.decode(raw, 256)
43
+ when :bin_compressed
44
+ BaseConvert.decode(raw[0,32], 256)
45
+ when :hex
46
+ BaseConvert.decode(raw, 16)
47
+ when :hex_compressed
48
+ BaseConvert.decode(raw[0,64], 16)
49
+ when :wif
50
+ BaseConvert.decode Utils.base58_check_to_bytes(raw), 256
51
+ when :wif_compressed
52
+ BaseConvert.decode Utils.base58_check_to_bytes(raw)[0,32], 256
53
+ else
54
+ raise ArgumentError, "WIF does not represent privkey"
55
+ end
56
+ end
57
+
58
+ def value
59
+ @value ||= decode
60
+ end
61
+
62
+ def format
63
+ return :decimal if raw.is_a?(Numeric)
64
+ return :bin if raw.size == 32
65
+ return :bin_compressed if raw.size == 33
66
+ return :hex if raw.size == 64
67
+ return :hex_compressed if raw.size == 66
68
+
69
+ bytes = Utils.base58_check_to_bytes raw
70
+ return :wif if bytes.size == 32
71
+ return :wif_compressed if bytes.size == 33
72
+
73
+ raise FormatError, "WIF does not represent privkey"
74
+ end
75
+
76
+ def to_pubkey
77
+ raise ValidationError, "Invalid private key" if value >= Secp256k1::N
78
+
79
+ fmt = format.to_s.sub(/wif/, 'hex').to_sym
80
+ PublicKey.new(Secp256k1.priv_to_pub(encode(:bin))).encode(fmt)
81
+ end
82
+
83
+ def to_bitcoin_address
84
+ PublicKey.new(to_pubkey).to_bitcoin_address
85
+ end
86
+
87
+ def to_address(extended=false)
88
+ PublicKey.new(to_pubkey).to_address(extended)
89
+ end
90
+
91
+ end
92
+ end