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