tapyrus 0.2.4 → 0.2.9
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 +4 -4
- data/.github/workflows/ruby.yml +37 -0
- data/.prettierignore +3 -0
- data/.prettierrc.yaml +3 -0
- data/CODE_OF_CONDUCT.md +7 -7
- data/README.md +14 -17
- data/Rakefile +3 -3
- data/lib/openassets.rb +0 -2
- data/lib/openassets/marker_output.rb +0 -4
- data/lib/openassets/payload.rb +4 -10
- data/lib/schnorr.rb +14 -9
- data/lib/schnorr/sign_to_contract.rb +51 -0
- data/lib/schnorr/signature.rb +3 -6
- data/lib/tapyrus.rb +8 -30
- data/lib/tapyrus/base58.rb +7 -6
- data/lib/tapyrus/bip175.rb +67 -0
- data/lib/tapyrus/block.rb +1 -2
- data/lib/tapyrus/block_header.rb +15 -9
- data/lib/tapyrus/bloom_filter.rb +5 -3
- data/lib/tapyrus/chain_params.rb +1 -4
- data/lib/tapyrus/chainparams/dev.yml +3 -2
- data/lib/tapyrus/chainparams/prod.yml +3 -2
- data/lib/tapyrus/constants.rb +29 -23
- data/lib/tapyrus/errors.rb +1 -3
- data/lib/tapyrus/ext.rb +1 -1
- data/lib/tapyrus/ext/ecdsa.rb +4 -4
- data/lib/tapyrus/ext/json_parser.rb +1 -4
- data/lib/tapyrus/ext_key.rb +44 -32
- data/lib/tapyrus/key.rb +31 -35
- data/lib/tapyrus/key_path.rb +15 -12
- data/lib/tapyrus/logger.rb +20 -16
- data/lib/tapyrus/merkle_tree.rb +19 -20
- data/lib/tapyrus/message.rb +14 -16
- data/lib/tapyrus/message/addr.rb +1 -7
- data/lib/tapyrus/message/base.rb +0 -3
- data/lib/tapyrus/message/block.rb +2 -9
- data/lib/tapyrus/message/block_transaction_request.rb +3 -6
- data/lib/tapyrus/message/block_transactions.rb +2 -6
- data/lib/tapyrus/message/block_txn.rb +0 -4
- data/lib/tapyrus/message/cmpct_block.rb +1 -7
- data/lib/tapyrus/message/error.rb +1 -4
- data/lib/tapyrus/message/fee_filter.rb +1 -4
- data/lib/tapyrus/message/filter_add.rb +0 -4
- data/lib/tapyrus/message/filter_clear.rb +0 -4
- data/lib/tapyrus/message/filter_load.rb +2 -5
- data/lib/tapyrus/message/get_addr.rb +0 -4
- data/lib/tapyrus/message/get_block_txn.rb +0 -4
- data/lib/tapyrus/message/get_blocks.rb +0 -3
- data/lib/tapyrus/message/get_data.rb +1 -4
- data/lib/tapyrus/message/get_headers.rb +1 -3
- data/lib/tapyrus/message/header_and_short_ids.rb +3 -9
- data/lib/tapyrus/message/headers.rb +0 -4
- data/lib/tapyrus/message/headers_parser.rb +3 -8
- data/lib/tapyrus/message/inv.rb +1 -4
- data/lib/tapyrus/message/inventories_parser.rb +2 -7
- data/lib/tapyrus/message/inventory.rb +12 -5
- data/lib/tapyrus/message/mem_pool.rb +0 -4
- data/lib/tapyrus/message/merkle_block.rb +4 -9
- data/lib/tapyrus/message/network_addr.rb +7 -6
- data/lib/tapyrus/message/not_found.rb +0 -3
- data/lib/tapyrus/message/ping.rb +0 -3
- data/lib/tapyrus/message/pong.rb +0 -3
- data/lib/tapyrus/message/prefilled_tx.rb +0 -4
- data/lib/tapyrus/message/reject.rb +0 -3
- data/lib/tapyrus/message/send_cmpct.rb +1 -3
- data/lib/tapyrus/message/send_headers.rb +0 -3
- data/lib/tapyrus/message/tx.rb +0 -4
- data/lib/tapyrus/message/ver_ack.rb +1 -5
- data/lib/tapyrus/message/version.rb +2 -5
- data/lib/tapyrus/mnemonic.rb +17 -15
- data/lib/tapyrus/network.rb +0 -2
- data/lib/tapyrus/network/connection.rb +0 -3
- data/lib/tapyrus/network/message_handler.rb +61 -60
- data/lib/tapyrus/network/peer.rb +13 -12
- data/lib/tapyrus/network/peer_discovery.rb +3 -5
- data/lib/tapyrus/network/pool.rb +12 -12
- data/lib/tapyrus/node.rb +1 -1
- data/lib/tapyrus/node/cli.rb +12 -14
- data/lib/tapyrus/node/configuration.rb +1 -3
- data/lib/tapyrus/node/spv.rb +2 -3
- data/lib/tapyrus/opcodes.rb +9 -7
- data/lib/tapyrus/out_point.rb +5 -5
- data/lib/tapyrus/rpc.rb +1 -0
- data/lib/tapyrus/rpc/http_server.rb +21 -22
- data/lib/tapyrus/rpc/request_handler.rb +42 -44
- data/lib/tapyrus/rpc/tapyrus_core_client.rb +67 -25
- data/lib/tapyrus/script/color.rb +20 -2
- data/lib/tapyrus/script/multisig.rb +13 -12
- data/lib/tapyrus/script/script.rb +104 -67
- data/lib/tapyrus/script/script_error.rb +1 -4
- data/lib/tapyrus/script/script_interpreter.rb +439 -399
- data/lib/tapyrus/script/tx_checker.rb +20 -10
- data/lib/tapyrus/secp256k1.rb +0 -4
- data/lib/tapyrus/secp256k1/native.rb +14 -15
- data/lib/tapyrus/secp256k1/rfc6979.rb +7 -4
- data/lib/tapyrus/secp256k1/ruby.rb +10 -12
- data/lib/tapyrus/slip39.rb +20 -5
- data/lib/tapyrus/slip39/share.rb +41 -29
- data/lib/tapyrus/slip39/sss.rb +101 -57
- data/lib/tapyrus/store.rb +1 -3
- data/lib/tapyrus/store/chain_entry.rb +0 -4
- data/lib/tapyrus/store/db.rb +0 -2
- data/lib/tapyrus/store/db/level_db.rb +5 -9
- data/lib/tapyrus/store/spv_chain.rb +11 -17
- data/lib/tapyrus/tx.rb +45 -37
- data/lib/tapyrus/tx_builder.rb +158 -0
- data/lib/tapyrus/tx_in.rb +1 -6
- data/lib/tapyrus/tx_out.rb +2 -7
- data/lib/tapyrus/util.rb +20 -7
- data/lib/tapyrus/validation.rb +12 -11
- data/lib/tapyrus/version.rb +1 -1
- data/lib/tapyrus/wallet/account.rb +22 -18
- data/lib/tapyrus/wallet/base.rb +12 -9
- data/lib/tapyrus/wallet/db.rb +6 -9
- data/lib/tapyrus/wallet/master_key.rb +2 -4
- data/tapyrusrb.gemspec +13 -16
- metadata +22 -31
- data/.travis.yml +0 -12
data/lib/tapyrus/base58.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Tapyrus
|
2
|
-
|
3
2
|
# Base58Check encoding
|
4
3
|
# https://en.bitcoin.it/wiki/Base58Check_encoding
|
5
4
|
module Base58
|
@@ -23,16 +22,18 @@ module Tapyrus
|
|
23
22
|
# decode base58 string to hex value.
|
24
23
|
def decode(base58_val)
|
25
24
|
int_val = 0
|
26
|
-
base58_val
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
base58_val
|
26
|
+
.reverse
|
27
|
+
.split(//)
|
28
|
+
.each_with_index do |char, index|
|
29
|
+
raise ArgumentError, 'Value passed not a valid Base58 String.' if (char_index = ALPHABET.index(char)).nil?
|
30
|
+
int_val += char_index * (SIZE**index)
|
31
|
+
end
|
30
32
|
s = int_val.to_even_length_hex
|
31
33
|
s = '' if s == '00'
|
32
34
|
leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : '').size
|
33
35
|
s = ('00' * leading_zero_bytes) + s if leading_zero_bytes > 0
|
34
36
|
s
|
35
37
|
end
|
36
|
-
|
37
38
|
end
|
38
39
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Tapyrus
|
2
|
+
# Key generation based on BIP-175
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
#
|
6
|
+
# master = Tapyrus::ExtKey.from_base58('xprv9s21ZrQH143K2JF8RafpqtKiTbsbaxEeUaMnNHsm5o6wCW3z8ySyH4UxFVSfZ8n7ESu7fgir8imbZKLYVBxFPND1pniTZ81vKfd45EHKX73')
|
7
|
+
# bip175 = Tapyrus::BIP175.from_private_key(master)
|
8
|
+
# bip175 << "foo"
|
9
|
+
# bip175 << "bar"
|
10
|
+
# bip175.addr
|
11
|
+
# > 1C7f322izqMqLzZzfzkPAjxBzprxDi47Yf
|
12
|
+
#
|
13
|
+
# @see https://github.com/bitcoin/bips/blob/master/bip-0175.mediawiki
|
14
|
+
class BIP175
|
15
|
+
PURPOSE_TYPE = 175
|
16
|
+
|
17
|
+
attr_accessor :payment_base
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@contracts = []
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param key [Tapyrus::ExtKey] master private extended key
|
24
|
+
def self.from_ext_key(key)
|
25
|
+
raise ArgumentError, 'key should be Tapyrus::ExtKey' unless key.is_a?(Tapyrus::ExtKey)
|
26
|
+
raise ArgumentError, 'key should be master private extended key' unless key.master?
|
27
|
+
new.tap do |bip175|
|
28
|
+
bip175.payment_base =
|
29
|
+
key.derive(PURPOSE_TYPE, true).derive(Tapyrus.chain_params.bip44_coin_type, true).ext_pubkey
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param key [Tapyrus::ExtPubkey] contract base public key
|
34
|
+
def self.from_ext_pubkey(key)
|
35
|
+
raise ArgumentError, 'key should be Tapyrus::ExtPubkey' unless key.is_a?(Tapyrus::ExtPubkey)
|
36
|
+
new.tap { |bip175| bip175.payment_base = key }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Add value of hashed contract
|
40
|
+
# @param contract [String] contract information
|
41
|
+
def add(contract)
|
42
|
+
@contracts << Tapyrus.sha256(contract)
|
43
|
+
self
|
44
|
+
end
|
45
|
+
alias << add
|
46
|
+
|
47
|
+
# Return combined hash consist of payment_base and contract hashes
|
48
|
+
# @return [String] contract_hash
|
49
|
+
def combined_hash
|
50
|
+
hashes = @contracts.map { |c| c.bth }.sort
|
51
|
+
concatenated_hash = [payment_base.to_base58].concat(hashes).join
|
52
|
+
Tapyrus.sha256(concatenated_hash)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Return pay-to-contract extended public key
|
56
|
+
# @return [Tapyrus::ExtPubkey] extended public key
|
57
|
+
def pubkey
|
58
|
+
# Split every 2 bytes
|
59
|
+
paths = combined_hash.unpack('S>*')
|
60
|
+
paths.inject(payment_base) { |key, p| key.derive(p) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def addr
|
64
|
+
pubkey.addr
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/tapyrus/block.rb
CHANGED
data/lib/tapyrus/block_header.rb
CHANGED
@@ -1,18 +1,17 @@
|
|
1
1
|
module Tapyrus
|
2
|
-
|
3
2
|
# Block Header
|
4
3
|
class BlockHeader
|
5
4
|
include Tapyrus::HexConverter
|
6
5
|
extend Tapyrus::Util
|
7
6
|
include Tapyrus::Util
|
8
7
|
|
9
|
-
X_FILED_TYPES = {none: 0, aggregate_pubkey: 1}
|
8
|
+
X_FILED_TYPES = { none: 0, aggregate_pubkey: 1 }
|
10
9
|
|
11
10
|
attr_accessor :features
|
12
11
|
attr_accessor :prev_hash
|
13
12
|
attr_accessor :merkle_root
|
14
13
|
attr_accessor :im_merkle_root # merkel root of immulable merkle tree which consist of immutable txid.
|
15
|
-
attr_accessor :time
|
14
|
+
attr_accessor :time # unix timestamp
|
16
15
|
attr_accessor :x_field_type
|
17
16
|
attr_accessor :x_field
|
18
17
|
attr_accessor :proof
|
@@ -33,7 +32,16 @@ module Tapyrus
|
|
33
32
|
features, prev_hash, merkle_root, im_merkle_root, time, x_filed_type = buf.read(105).unpack('Va32a32a32Vc')
|
34
33
|
x_field = buf.read(unpack_var_int_from_io(buf)) unless x_filed_type == X_FILED_TYPES[:none]
|
35
34
|
proof = buf.read(unpack_var_int_from_io(buf))
|
36
|
-
new(
|
35
|
+
new(
|
36
|
+
features,
|
37
|
+
prev_hash.bth,
|
38
|
+
merkle_root.bth,
|
39
|
+
im_merkle_root.bth,
|
40
|
+
time,
|
41
|
+
x_filed_type,
|
42
|
+
x_field ? x_field.bth : x_field,
|
43
|
+
proof.bth
|
44
|
+
)
|
37
45
|
end
|
38
46
|
|
39
47
|
def to_payload(skip_proof = false)
|
@@ -87,9 +95,9 @@ module Tapyrus
|
|
87
95
|
# @return [Boolean] if valid return true, otherwise false
|
88
96
|
def valid_x_field?
|
89
97
|
case x_field_type
|
90
|
-
when X_FILED_TYPES[:none]
|
98
|
+
when X_FILED_TYPES[:none]
|
91
99
|
x_field.nil?
|
92
|
-
when X_FILED_TYPES[:aggregate_pubkey]
|
100
|
+
when X_FILED_TYPES[:aggregate_pubkey]
|
93
101
|
Tapyrus::Key.new(pubkey: x_field).fully_valid_pubkey?
|
94
102
|
else
|
95
103
|
false
|
@@ -111,7 +119,5 @@ module Tapyrus
|
|
111
119
|
def size
|
112
120
|
to_payload.bytesize
|
113
121
|
end
|
114
|
-
|
115
122
|
end
|
116
|
-
|
117
|
-
end
|
123
|
+
end
|
data/lib/tapyrus/bloom_filter.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'murmurhash3'
|
2
2
|
module Tapyrus
|
3
3
|
class BloomFilter
|
4
4
|
LN2_SQUARED = 0.4804530139182014246671025263266649717305529515945455 # log(2) ** 2
|
@@ -23,6 +23,7 @@ module Tapyrus
|
|
23
23
|
# The size S of the filter in bytes is given by (-1 / pow(log(2), 2) * N * log(P)) / 8
|
24
24
|
len = [[(-elements_length * Math.log(fp_rate) / (LN2_SQUARED * 8)).to_i, MAX_BLOOM_FILTER_SIZE].min, 1].max
|
25
25
|
filter = Array.new(len, 0)
|
26
|
+
|
26
27
|
# The number of hash functions required is given by S * 8 / N * log(2)
|
27
28
|
hash_funcs = [[(filter.size * 8 * LN2 / elements_length).to_i, MAX_HASH_FUNCS].min, 1].max
|
28
29
|
BloomFilter.new(filter, hash_funcs, tweak)
|
@@ -59,8 +60,9 @@ module Tapyrus
|
|
59
60
|
end
|
60
61
|
|
61
62
|
private
|
63
|
+
|
62
64
|
def to_hash(data, i)
|
63
|
-
MurmurHash3::V32.str_hash(data, (i * 0xfba4c795 + tweak) & 0xffffffff)
|
65
|
+
MurmurHash3::V32.str_hash(data, (i * 0xfba4c795 + tweak) & 0xffffffff) % (filter.length * 8)
|
64
66
|
end
|
65
67
|
|
66
68
|
def set_bit(data)
|
@@ -72,7 +74,7 @@ module Tapyrus
|
|
72
74
|
end
|
73
75
|
|
74
76
|
def full?
|
75
|
-
@full |= filter.all? {|byte| byte == 0xff}
|
77
|
+
@full |= filter.all? { |byte| byte == 0xff }
|
76
78
|
end
|
77
79
|
end
|
78
80
|
end
|
data/lib/tapyrus/chain_params.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
|
3
3
|
module Tapyrus
|
4
|
-
|
5
4
|
# Network parameter class
|
6
5
|
class ChainParams
|
7
|
-
|
8
6
|
attr_reader :network
|
9
7
|
attr_reader :magic_head
|
10
8
|
attr_reader :message_magic
|
@@ -63,5 +61,4 @@ module Tapyrus
|
|
63
61
|
|
64
62
|
private_class_method :init
|
65
63
|
end
|
66
|
-
|
67
|
-
end
|
64
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
|
-
---
|
1
|
+
---
|
2
|
+
!ruby/object:Tapyrus::ChainParams
|
2
3
|
network: "dev"
|
3
4
|
magic_head: "0b110907"
|
4
5
|
message_magic: "Tapyrus Signed Message:\n"
|
@@ -26,4 +27,4 @@ target_spacing: 600 # block interval
|
|
26
27
|
max_money: 21000000
|
27
28
|
bip34_height: 227931
|
28
29
|
dns_seeds:
|
29
|
-
bip44_coin_type: 1
|
30
|
+
bip44_coin_type: 1
|
@@ -1,4 +1,5 @@
|
|
1
|
-
---
|
1
|
+
---
|
2
|
+
!ruby/object:Tapyrus::ChainParams
|
2
3
|
network: "prod"
|
3
4
|
magic_head: "f9beb4d9"
|
4
5
|
message_magic: "Tapyrus Signed Message:\n"
|
@@ -26,4 +27,4 @@ target_spacing: 600 # block interval
|
|
26
27
|
max_money: 21000000
|
27
28
|
bip34_height: 227931
|
28
29
|
dns_seeds:
|
29
|
-
bip44_coin_type: 0
|
30
|
+
bip44_coin_type: 0
|
data/lib/tapyrus/constants.rb
CHANGED
@@ -1,20 +1,23 @@
|
|
1
1
|
module Tapyrus
|
2
|
-
|
3
2
|
COIN = 100_000_000
|
4
3
|
MAX_MONEY = 21_000_000 * COIN
|
5
4
|
|
6
5
|
# The maximum allowed size for a serialized block, in bytes (only for buffer size limits)
|
7
6
|
MAX_BLOCK_SERIALIZED_SIZE = 4_000_000
|
7
|
+
|
8
8
|
# The maximum allowed weight for a block, see BIP 141 (network rule)
|
9
9
|
MAX_BLOCK_WEIGHT = 4_000_000
|
10
|
+
|
10
11
|
# The maximum allowed number of signature check operations in a block (network rule)
|
11
12
|
MAX_BLOCK_SIGOPS_COST = 80_000
|
13
|
+
|
12
14
|
# Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
|
13
15
|
COINBASE_MATURITY = 100
|
14
16
|
WITNESS_SCALE_FACTOR = 4
|
15
17
|
|
16
18
|
# 60 is the lower bound for the size of a valid serialized Tx
|
17
19
|
MIN_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * 60
|
20
|
+
|
18
21
|
# 10 is the lower bound for the size of a serialized Tx
|
19
22
|
MIN_SERIALIZABLE_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * 10
|
20
23
|
|
@@ -26,11 +29,11 @@ module Tapyrus
|
|
26
29
|
DUST_RELAY_TX_FEE = 3000
|
27
30
|
|
28
31
|
# script verify flags
|
29
|
-
SCRIPT_VERIFY_NONE
|
30
|
-
SCRIPT_VERIFY_P2SH
|
32
|
+
SCRIPT_VERIFY_NONE = 0
|
33
|
+
SCRIPT_VERIFY_P2SH = (1 << 0)
|
31
34
|
SCRIPT_VERIFY_STRICTENC = (1 << 1)
|
32
|
-
SCRIPT_VERIFY_DERSIG
|
33
|
-
SCRIPT_VERIFY_LOW_S
|
35
|
+
SCRIPT_VERIFY_DERSIG = (1 << 2)
|
36
|
+
SCRIPT_VERIFY_LOW_S = (1 << 3)
|
34
37
|
SCRIPT_VERIFY_NULLDUMMY = (1 << 4)
|
35
38
|
SCRIPT_VERIFY_SIGPUSHONLY = (1 << 5)
|
36
39
|
SCRIPT_VERIFY_MINIMALDATA = (1 << 6)
|
@@ -45,24 +48,27 @@ module Tapyrus
|
|
45
48
|
MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH
|
46
49
|
|
47
50
|
# Standard script verification flags that standard transactions will comply with.
|
48
|
-
STANDARD_SCRIPT_VERIFY_FLAGS =
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
51
|
+
STANDARD_SCRIPT_VERIFY_FLAGS =
|
52
|
+
[
|
53
|
+
MANDATORY_SCRIPT_VERIFY_FLAGS,
|
54
|
+
SCRIPT_VERIFY_DERSIG,
|
55
|
+
SCRIPT_VERIFY_STRICTENC,
|
56
|
+
SCRIPT_VERIFY_MINIMALDATA,
|
57
|
+
SCRIPT_VERIFY_NULLDUMMY,
|
58
|
+
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS,
|
59
|
+
SCRIPT_VERIFY_CLEANSTACK,
|
60
|
+
SCRIPT_VERIFY_MINIMALIF,
|
61
|
+
SCRIPT_VERIFY_NULLFAIL,
|
62
|
+
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY,
|
63
|
+
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY,
|
64
|
+
SCRIPT_VERIFY_LOW_S,
|
65
|
+
SCRIPT_VERIFY_CONST_SCRIPTCODE
|
66
|
+
].inject(SCRIPT_VERIFY_NONE) { |flags, f| flags |= f }
|
61
67
|
|
62
68
|
# for script
|
63
69
|
|
64
70
|
# Maximum script length in bytes
|
65
|
-
MAX_SCRIPT_SIZE =
|
71
|
+
MAX_SCRIPT_SIZE = 10_000
|
66
72
|
|
67
73
|
# Maximum number of public keys per multisig
|
68
74
|
MAX_PUBKEYS_PER_MULTISIG = 20
|
@@ -77,7 +83,7 @@ module Tapyrus
|
|
77
83
|
MAX_STACK_SIZE = 1000
|
78
84
|
|
79
85
|
# Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
|
80
|
-
LOCKTIME_THRESHOLD =
|
86
|
+
LOCKTIME_THRESHOLD = 500_000_000
|
81
87
|
|
82
88
|
# Signature hash types/flags
|
83
89
|
SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
|
@@ -153,7 +159,7 @@ module Tapyrus
|
|
153
159
|
ERRCODES_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [const_get(c), c.to_s] }.flatten]
|
154
160
|
NAME_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [c.to_s, const_get(c)] }.flatten]
|
155
161
|
|
156
|
-
COINBASE_WTXID = '00'* 32
|
162
|
+
COINBASE_WTXID = '00' * 32
|
157
163
|
|
158
164
|
# for message
|
159
165
|
MESSAGE_HEADER_SIZE = 24
|
@@ -169,5 +175,5 @@ module Tapyrus
|
|
169
175
|
|
170
176
|
BIP32_EXTKEY_WITH_VERSION_SIZE = 78
|
171
177
|
|
172
|
-
HARDENED_THRESHOLD =
|
173
|
-
end
|
178
|
+
HARDENED_THRESHOLD = 2_147_483_648 # 2**31
|
179
|
+
end
|
data/lib/tapyrus/errors.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module Tapyrus
|
2
2
|
module Errors
|
3
3
|
module Messages
|
4
|
-
|
5
4
|
INVALID_PUBLIC_KEY = 'Invalid public key.'
|
6
5
|
INVALID_BIP32_PRIV_PREFIX = 'Invalid BIP32 private key prefix. prefix must be 0x00.'
|
7
6
|
INVALID_BIP32_FINGERPRINT = 'Invalid parent fingerprint.'
|
@@ -11,7 +10,6 @@ module Tapyrus
|
|
11
10
|
|
12
11
|
INVALID_PRIV_KEY = 'Private key is not in range [1..n-1].'
|
13
12
|
INVALID_CHECKSUM = 'Invalid checksum.'
|
14
|
-
|
15
13
|
end
|
16
14
|
end
|
17
|
-
end
|
15
|
+
end
|
data/lib/tapyrus/ext.rb
CHANGED
data/lib/tapyrus/ext/ecdsa.rb
CHANGED
@@ -12,7 +12,6 @@ class ::ECDSA::Point
|
|
12
12
|
end
|
13
13
|
|
14
14
|
module ::ECDSA::Format::PointOctetString
|
15
|
-
|
16
15
|
def self.decode(string, group, allow_hybrid: false)
|
17
16
|
string = string.dup.force_encoding('BINARY')
|
18
17
|
|
@@ -29,11 +28,12 @@ module ::ECDSA::Format::PointOctetString
|
|
29
28
|
when 4
|
30
29
|
decode_uncompressed string, group
|
31
30
|
when 6..7
|
32
|
-
|
31
|
+
unless allow_hybrid
|
32
|
+
raise ECDSA::Format::DecodeError, 'Unrecognized start byte for point octet string: 0x%x' % string[0].ord
|
33
|
+
end
|
33
34
|
decode_uncompressed string, group if allow_hybrid
|
34
35
|
else
|
35
36
|
raise ECDSA::Format::DecodeError, 'Unrecognized start byte for point octet string: 0x%x' % string[0].ord
|
36
37
|
end
|
37
38
|
end
|
38
|
-
|
39
|
-
end
|
39
|
+
end
|
@@ -2,11 +2,9 @@ require 'json/pure'
|
|
2
2
|
|
3
3
|
module Tapyrus
|
4
4
|
module Ext
|
5
|
-
|
6
5
|
# Extension of JSON::Pure::Parser.
|
7
6
|
# This class convert Float value to String value.
|
8
7
|
class JsonParser < JSON::Pure::Parser
|
9
|
-
|
10
8
|
def parse_value
|
11
9
|
case
|
12
10
|
when scan(FLOAT)
|
@@ -41,7 +39,6 @@ module Tapyrus
|
|
41
39
|
UNPARSED
|
42
40
|
end
|
43
41
|
end
|
44
|
-
|
45
42
|
end
|
46
43
|
end
|
47
|
-
end
|
44
|
+
end
|
data/lib/tapyrus/ext_key.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Tapyrus
|
2
|
-
|
3
2
|
# Integers modulo the order of the curve(secp256k1)
|
4
3
|
CURVE_ORDER = ECDSA::Group::Secp256k1.order
|
5
4
|
|
@@ -45,8 +44,8 @@ module Tapyrus
|
|
45
44
|
|
46
45
|
# serialize extended private key
|
47
46
|
def to_payload
|
48
|
-
version.htb << [depth].pack('C') << parent_fingerprint.htb <<
|
49
|
-
|
47
|
+
version.htb << [depth].pack('C') << parent_fingerprint.htb << [number].pack('N') << chain_code <<
|
48
|
+
[0x00].pack('C') << key.priv_key.htb
|
50
49
|
end
|
51
50
|
|
52
51
|
# Base58 encoded extended private key
|
@@ -108,8 +107,7 @@ module Tapyrus
|
|
108
107
|
raise 'invalid key' if left >= CURVE_ORDER
|
109
108
|
child_priv = (left + key.priv_key.to_i(16)) % CURVE_ORDER
|
110
109
|
raise 'invalid key ' if child_priv >= CURVE_ORDER
|
111
|
-
new_key.key = Tapyrus::Key.new(
|
112
|
-
priv_key: child_priv.to_even_length_hex.rjust(64, '0'), key_type: key_type)
|
110
|
+
new_key.key = Tapyrus::Key.new(priv_key: child_priv.to_even_length_hex.rjust(64, '0'), key_type: key_type)
|
113
111
|
new_key.chain_code = l[32..-1]
|
114
112
|
new_key.ver = version
|
115
113
|
new_key
|
@@ -147,10 +145,14 @@ module Tapyrus
|
|
147
145
|
ext_key.parent_fingerprint = buf.read(4).bth
|
148
146
|
ext_key.number = buf.read(4).unpack('N').first
|
149
147
|
if ext_key.depth == 0
|
150
|
-
|
148
|
+
unless ext_key.parent_fingerprint == ExtKey::MASTER_FINGERPRINT
|
149
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_FINGERPRINT
|
150
|
+
end
|
151
151
|
raise ArgumentError, Errors::Messages::INVALID_BIP32_ZERO_INDEX if ext_key.number > 0
|
152
152
|
end
|
153
|
-
|
153
|
+
if ext_key.parent_fingerprint == ExtKey::MASTER_FINGERPRINT && ext_key.depth > 0
|
154
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_ZERO_DEPTH
|
155
|
+
end
|
154
156
|
ext_key.chain_code = buf.read(32)
|
155
157
|
raise ArgumentError, Errors::Messages::INVALID_BIP32_PRIV_PREFIX unless buf.read(1).bth == '00' # 0x00
|
156
158
|
ext_key.key = Tapyrus::Key.new(priv_key: buf.read(32).bth, key_type: Tapyrus::Key::TYPES[:compressed])
|
@@ -166,33 +168,38 @@ module Tapyrus
|
|
166
168
|
def self.version_from_purpose(purpose)
|
167
169
|
v = purpose - Tapyrus::HARDENED_THRESHOLD
|
168
170
|
case v
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
171
|
+
when 49
|
172
|
+
Tapyrus.chain_params.bip49_privkey_p2wpkh_p2sh_version
|
173
|
+
when 84
|
174
|
+
Tapyrus.chain_params.bip84_privkey_p2wpkh_version
|
175
|
+
else
|
176
|
+
Tapyrus.chain_params.extended_privkey_version
|
175
177
|
end
|
176
178
|
end
|
177
179
|
|
178
180
|
# check whether +version+ is supported version bytes.
|
179
181
|
def self.support_version?(version)
|
180
182
|
p = Tapyrus.chain_params
|
181
|
-
[p.bip49_privkey_p2wpkh_p2sh_version, p.bip84_privkey_p2wpkh_version, p.extended_privkey_version].include?(
|
183
|
+
[p.bip49_privkey_p2wpkh_p2sh_version, p.bip84_privkey_p2wpkh_version, p.extended_privkey_version].include?(
|
184
|
+
version
|
185
|
+
)
|
182
186
|
end
|
183
187
|
|
184
188
|
# convert privkey version to pubkey version
|
185
189
|
def priv_ver_to_pub_ver
|
186
190
|
case version
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
191
|
+
when Tapyrus.chain_params.bip49_privkey_p2wpkh_p2sh_version
|
192
|
+
Tapyrus.chain_params.bip49_pubkey_p2wpkh_p2sh_version
|
193
|
+
when Tapyrus.chain_params.bip84_privkey_p2wpkh_version
|
194
|
+
Tapyrus.chain_params.bip84_pubkey_p2wpkh_version
|
195
|
+
else
|
196
|
+
Tapyrus.chain_params.extended_pubkey_version
|
193
197
|
end
|
194
198
|
end
|
195
199
|
|
200
|
+
def master?
|
201
|
+
depth == 0 && number == 0 && parent_fingerprint == '00000000'
|
202
|
+
end
|
196
203
|
end
|
197
204
|
|
198
205
|
# BIP-32 Extended public key
|
@@ -208,8 +215,7 @@ module Tapyrus
|
|
208
215
|
|
209
216
|
# serialize extended pubkey
|
210
217
|
def to_payload
|
211
|
-
version.htb << [depth].pack('C') <<
|
212
|
-
parent_fingerprint.htb << [number].pack('N') << chain_code << pub.htb
|
218
|
+
version.htb << [depth].pack('C') << parent_fingerprint.htb << [number].pack('N') << chain_code << pub.htb
|
213
219
|
end
|
214
220
|
|
215
221
|
def pub
|
@@ -269,7 +275,7 @@ module Tapyrus
|
|
269
275
|
l = Tapyrus.hmac_sha512(chain_code, data)
|
270
276
|
left = l[0..31].bth.to_i(16)
|
271
277
|
raise 'invalid key' if left >= CURVE_ORDER
|
272
|
-
p1 = Tapyrus::
|
278
|
+
p1 = Tapyrus::Key.new(priv_key: left.to_s(16), key_type: Tapyrus::Key::TYPES[:uncompressed]).to_point
|
273
279
|
p2 = Tapyrus::Key.new(pubkey: pubkey, key_type: key_type).to_point
|
274
280
|
new_key.pubkey = (p1 + p2).to_hex
|
275
281
|
new_key.chain_code = l[32..-1]
|
@@ -309,10 +315,14 @@ module Tapyrus
|
|
309
315
|
ext_pubkey.parent_fingerprint = buf.read(4).bth
|
310
316
|
ext_pubkey.number = buf.read(4).unpack('N').first
|
311
317
|
if ext_pubkey.depth == 0
|
312
|
-
|
318
|
+
unless ext_pubkey.parent_fingerprint == ExtKey::MASTER_FINGERPRINT
|
319
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_FINGERPRINT
|
320
|
+
end
|
313
321
|
raise ArgumentError, Errors::Messages::INVALID_BIP32_ZERO_INDEX if ext_pubkey.number > 0
|
314
322
|
end
|
315
|
-
|
323
|
+
if ext_pubkey.parent_fingerprint == ExtKey::MASTER_FINGERPRINT && ext_pubkey.depth > 0
|
324
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_ZERO_DEPTH
|
325
|
+
end
|
316
326
|
ext_pubkey.chain_code = buf.read(32)
|
317
327
|
ext_pubkey.pubkey = Tapyrus::Key.new(pubkey: buf.read(33).bth).pubkey
|
318
328
|
ext_pubkey
|
@@ -336,12 +346,12 @@ module Tapyrus
|
|
336
346
|
def self.version_from_purpose(purpose)
|
337
347
|
v = purpose - Tapyrus::HARDENED_THRESHOLD
|
338
348
|
case v
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
349
|
+
when 49
|
350
|
+
Tapyrus.chain_params.bip49_pubkey_p2wpkh_p2sh_version
|
351
|
+
when 84
|
352
|
+
Tapyrus.chain_params.bip84_pubkey_p2wpkh_version
|
353
|
+
else
|
354
|
+
Tapyrus.chain_params.extended_pubkey_version
|
345
355
|
end
|
346
356
|
end
|
347
357
|
|
@@ -351,6 +361,8 @@ module Tapyrus
|
|
351
361
|
[p.bip49_pubkey_p2wpkh_p2sh_version, p.bip84_pubkey_p2wpkh_version, p.extended_pubkey_version].include?(version)
|
352
362
|
end
|
353
363
|
|
364
|
+
def master?
|
365
|
+
depth == 0 && number == 0 && parent_fingerprint == '00000000'
|
366
|
+
end
|
354
367
|
end
|
355
|
-
|
356
368
|
end
|