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