ruby-ethereum 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/lib/ethereum.rb +53 -0
- data/lib/ethereum/abi.rb +333 -0
- data/lib/ethereum/abi/contract_translator.rb +174 -0
- data/lib/ethereum/abi/type.rb +117 -0
- data/lib/ethereum/account.rb +72 -0
- data/lib/ethereum/address.rb +60 -0
- data/lib/ethereum/base_convert.rb +53 -0
- data/lib/ethereum/block.rb +1311 -0
- data/lib/ethereum/block_header.rb +211 -0
- data/lib/ethereum/bloom.rb +83 -0
- data/lib/ethereum/cached_block.rb +36 -0
- data/lib/ethereum/chain.rb +400 -0
- data/lib/ethereum/constant.rb +26 -0
- data/lib/ethereum/core_ext/array/safe_slice.rb +18 -0
- data/lib/ethereum/core_ext/module/lru_cache.rb +20 -0
- data/lib/ethereum/core_ext/numeric/denominations.rb +45 -0
- data/lib/ethereum/core_ext/object/truth.rb +47 -0
- data/lib/ethereum/db.rb +6 -0
- data/lib/ethereum/db/base_db.rb +9 -0
- data/lib/ethereum/db/ephem_db.rb +63 -0
- data/lib/ethereum/db/overlay_db.rb +72 -0
- data/lib/ethereum/db/refcount_db.rb +188 -0
- data/lib/ethereum/env.rb +64 -0
- data/lib/ethereum/ethash.rb +78 -0
- data/lib/ethereum/ethash_ruby.rb +38 -0
- data/lib/ethereum/ethash_ruby/cache.rb +47 -0
- data/lib/ethereum/ethash_ruby/hashimoto.rb +75 -0
- data/lib/ethereum/ethash_ruby/utils.rb +53 -0
- data/lib/ethereum/exceptions.rb +28 -0
- data/lib/ethereum/external_call.rb +173 -0
- data/lib/ethereum/fast_rlp.rb +81 -0
- data/lib/ethereum/fast_vm.rb +625 -0
- data/lib/ethereum/fast_vm/call_data.rb +44 -0
- data/lib/ethereum/fast_vm/message.rb +29 -0
- data/lib/ethereum/fast_vm/state.rb +25 -0
- data/lib/ethereum/ffi/openssl.rb +390 -0
- data/lib/ethereum/index.rb +97 -0
- data/lib/ethereum/log.rb +43 -0
- data/lib/ethereum/miner.rb +84 -0
- data/lib/ethereum/opcodes.rb +131 -0
- data/lib/ethereum/private_key.rb +92 -0
- data/lib/ethereum/pruning_trie.rb +28 -0
- data/lib/ethereum/public_key.rb +88 -0
- data/lib/ethereum/receipt.rb +53 -0
- data/lib/ethereum/secp256k1.rb +58 -0
- data/lib/ethereum/secure_trie.rb +49 -0
- data/lib/ethereum/sedes.rb +42 -0
- data/lib/ethereum/special_contract.rb +95 -0
- data/lib/ethereum/spv.rb +79 -0
- data/lib/ethereum/spv/proof.rb +31 -0
- data/lib/ethereum/spv/proof_constructor.rb +19 -0
- data/lib/ethereum/spv/proof_verifier.rb +24 -0
- data/lib/ethereum/tester.rb +14 -0
- data/lib/ethereum/tester/abi_contract.rb +65 -0
- data/lib/ethereum/tester/fixture.rb +31 -0
- data/lib/ethereum/tester/language.rb +30 -0
- data/lib/ethereum/tester/log_recorder.rb +13 -0
- data/lib/ethereum/tester/solidity_wrapper.rb +144 -0
- data/lib/ethereum/tester/state.rb +194 -0
- data/lib/ethereum/transaction.rb +196 -0
- data/lib/ethereum/transient_trie.rb +28 -0
- data/lib/ethereum/trie.rb +549 -0
- data/lib/ethereum/trie/nibble_key.rb +184 -0
- data/lib/ethereum/utils.rb +191 -0
- data/lib/ethereum/version.rb +5 -0
- data/lib/ethereum/vm.rb +606 -0
- data/lib/ethereum/vm/call_data.rb +40 -0
- data/lib/ethereum/vm/message.rb +30 -0
- data/lib/ethereum/vm/state.rb +25 -0
- 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
|
data/lib/ethereum/log.rb
ADDED
@@ -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
|