bitcoinrb 0.2.9 → 0.5.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 +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +3 -2
- data/README.md +7 -6
- data/bitcoinrb.gemspec +4 -4
- data/exe/bitcoinrbd +5 -0
- data/lib/bitcoin.rb +33 -1
- data/lib/bitcoin/bip85_entropy.rb +111 -0
- data/lib/bitcoin/block_header.rb +2 -0
- data/lib/bitcoin/chain_params.rb +0 -8
- data/lib/bitcoin/chainparams/regtest.yml +1 -1
- data/lib/bitcoin/chainparams/testnet.yml +1 -1
- data/lib/bitcoin/constants.rb +3 -10
- data/lib/bitcoin/descriptor.rb +147 -0
- data/lib/bitcoin/ext.rb +5 -0
- data/lib/bitcoin/ext/json_parser.rb +46 -0
- data/lib/bitcoin/ext_key.rb +19 -4
- data/lib/bitcoin/key.rb +9 -5
- data/lib/bitcoin/key_path.rb +12 -5
- data/lib/bitcoin/message.rb +7 -0
- data/lib/bitcoin/message/base.rb +1 -0
- data/lib/bitcoin/message/cf_parser.rb +16 -0
- data/lib/bitcoin/message/cfcheckpt.rb +36 -0
- data/lib/bitcoin/message/cfheaders.rb +40 -0
- data/lib/bitcoin/message/cfilter.rb +35 -0
- data/lib/bitcoin/message/get_cfcheckpt.rb +29 -0
- data/lib/bitcoin/message/get_cfheaders.rb +24 -0
- data/lib/bitcoin/message/get_cfilters.rb +25 -0
- data/lib/bitcoin/message/network_addr.rb +31 -12
- data/lib/bitcoin/message/version.rb +14 -22
- data/lib/bitcoin/mnemonic.rb +5 -5
- data/lib/bitcoin/network/peer.rb +12 -11
- data/lib/bitcoin/network/peer_discovery.rb +3 -1
- data/lib/bitcoin/node/cli.rb +14 -10
- data/lib/bitcoin/node/spv.rb +1 -1
- data/lib/bitcoin/out_point.rb +14 -7
- data/lib/bitcoin/payment_code.rb +92 -0
- data/lib/bitcoin/psbt.rb +3 -1
- data/lib/bitcoin/psbt/input.rb +7 -16
- data/lib/bitcoin/psbt/tx.rb +18 -12
- data/lib/bitcoin/rpc/bitcoin_core_client.rb +22 -12
- data/lib/bitcoin/rpc/request_handler.rb +3 -3
- data/lib/bitcoin/script/script.rb +18 -10
- data/lib/bitcoin/script/script_interpreter.rb +3 -5
- data/lib/bitcoin/secp256k1.rb +1 -0
- data/lib/bitcoin/secp256k1/rfc6979.rb +43 -0
- data/lib/bitcoin/secp256k1/ruby.rb +4 -35
- data/lib/bitcoin/slip39.rb +93 -0
- data/lib/bitcoin/slip39/share.rb +122 -0
- data/lib/bitcoin/slip39/sss.rb +245 -0
- data/lib/bitcoin/slip39/wordlist/english.txt +1024 -0
- data/lib/bitcoin/store.rb +2 -1
- data/lib/bitcoin/store/chain_entry.rb +1 -0
- data/lib/bitcoin/store/db/level_db.rb +2 -2
- data/lib/bitcoin/store/utxo_db.rb +226 -0
- data/lib/bitcoin/tx.rb +6 -10
- data/lib/bitcoin/tx_in.rb +4 -5
- data/lib/bitcoin/util.rb +29 -1
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet.rb +1 -0
- data/lib/bitcoin/wallet/account.rb +1 -0
- data/lib/bitcoin/wallet/base.rb +3 -3
- data/lib/bitcoin/wallet/db.rb +1 -1
- data/lib/bitcoin/wallet/master_key.rb +1 -0
- data/lib/bitcoin/wallet/utxo.rb +37 -0
- metadata +45 -26
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
1
3
|
module Bitcoin
|
2
4
|
module Message
|
3
5
|
|
@@ -12,30 +14,47 @@ module Bitcoin
|
|
12
14
|
# The services the node advertised in its version message.
|
13
15
|
attr_accessor :services
|
14
16
|
|
15
|
-
attr_accessor :
|
17
|
+
attr_accessor :ip_addr # IPAddr
|
16
18
|
|
17
19
|
attr_accessor :port
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
attr_reader :skip_time
|
22
|
+
|
23
|
+
def initialize(ip: '127.0.0.1', port: Bitcoin.chain_params.default_port, services: DEFAULT_SERVICE_FLAGS, time: Time.now.to_i)
|
24
|
+
@time = time
|
25
|
+
@ip_addr = IPAddr.new(ip)
|
26
|
+
@port = port
|
27
|
+
@services = services
|
22
28
|
end
|
23
29
|
|
24
30
|
def self.parse_from_payload(payload)
|
25
31
|
buf = payload.is_a?(String) ? StringIO.new(payload) : payload
|
26
|
-
|
27
|
-
addr
|
32
|
+
has_time = buf.size > 26
|
33
|
+
addr = new(time: nil)
|
34
|
+
addr.time = buf.read(4).unpack('V').first if has_time
|
28
35
|
addr.services = buf.read(8).unpack('Q').first
|
29
|
-
|
30
|
-
addr.ip = ip.ipv4_mapped? ? ip.native : ip.to_s
|
36
|
+
addr.ip_addr = IPAddr::new_ntoh(buf.read(16))
|
31
37
|
addr.port = buf.read(2).unpack('n').first
|
32
38
|
addr
|
33
39
|
end
|
34
40
|
|
35
|
-
def
|
36
|
-
|
37
|
-
ip_addr =
|
38
|
-
|
41
|
+
def self.local_addr
|
42
|
+
addr = new
|
43
|
+
addr.ip_addr = IPAddr.new('127.0.0.1')
|
44
|
+
addr.port = Bitcoin.chain_params.default_port
|
45
|
+
addr.services = DEFAULT_SERVICE_FLAGS
|
46
|
+
addr
|
47
|
+
end
|
48
|
+
|
49
|
+
def ip
|
50
|
+
ip_addr.ipv4_mapped? ? ip_addr.native : ip_addr.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_payload(skip_time = false)
|
54
|
+
p = ''
|
55
|
+
p << [time].pack('V') unless skip_time
|
56
|
+
addr = ip_addr.ipv4? ? ip_addr.ipv4_mapped : ip_addr
|
57
|
+
p << [services].pack('Q') << addr.hton << [port].pack('n')
|
39
58
|
end
|
40
59
|
|
41
60
|
end
|
@@ -22,8 +22,8 @@ module Bitcoin
|
|
22
22
|
@version = Bitcoin.chain_params.protocol_version
|
23
23
|
@services = DEFAULT_SERVICE_FLAGS
|
24
24
|
@timestamp = Time.now.to_i
|
25
|
-
@local_addr =
|
26
|
-
@remote_addr =
|
25
|
+
@local_addr = NetworkAddr.local_addr
|
26
|
+
@remote_addr = NetworkAddr.local_addr
|
27
27
|
@nonce = SecureRandom.random_number(0xffffffffffffffff)
|
28
28
|
@user_agent = Bitcoin::Message::USER_AGENT
|
29
29
|
@start_height = 0
|
@@ -32,13 +32,13 @@ module Bitcoin
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def self.parse_from_payload(payload)
|
35
|
-
version, services, timestamp,
|
35
|
+
version, services, timestamp, local_addr, remote_addr, nonce, rest = payload.unpack('VQQa26a26Qa*')
|
36
36
|
v = new
|
37
37
|
v.version = version
|
38
38
|
v.services = services
|
39
39
|
v.timestamp = timestamp
|
40
|
-
v.
|
41
|
-
v.
|
40
|
+
v.local_addr = NetworkAddr.parse_from_payload(local_addr)
|
41
|
+
v.remote_addr = NetworkAddr.parse_from_payload(remote_addr)
|
42
42
|
v.nonce = nonce
|
43
43
|
user_agent, rest = unpack_var_string(rest)
|
44
44
|
start_height, rest = rest.unpack('Va*')
|
@@ -51,8 +51,8 @@ module Bitcoin
|
|
51
51
|
def to_payload
|
52
52
|
[
|
53
53
|
[version, services, timestamp].pack('VQQ'),
|
54
|
-
|
55
|
-
|
54
|
+
local_addr.to_payload(true),
|
55
|
+
remote_addr.to_payload(true),
|
56
56
|
[nonce].pack('Q'),
|
57
57
|
pack_var_string(user_agent),
|
58
58
|
[start_height].pack('V'),
|
@@ -60,25 +60,17 @@ module Bitcoin
|
|
60
60
|
].join
|
61
61
|
end
|
62
62
|
|
63
|
-
def pack_addr(addr)
|
64
|
-
separator = addr.rindex(':')
|
65
|
-
ip = addr[0...separator]
|
66
|
-
port = addr[separator + 1..-1].to_i
|
67
|
-
ip_addr = IPAddr.new(ip)
|
68
|
-
ip_addr = ip_addr.ipv4_mapped if ip_addr.ipv4?
|
69
|
-
[1].pack('Q') << ip_addr.hton << [port].pack('n')
|
70
|
-
# [[1].pack('Q'), "\x00" * 10, "\xFF\xFF", sockaddr[4...8], sockaddr[2...4]].join
|
71
|
-
end
|
72
|
-
|
73
|
-
def unpack_addr(addr)
|
74
|
-
host, port = addr.unpack('x8x12a4n')
|
75
|
-
"#{host.unpack('C*').join('.')}:#{port}"
|
76
|
-
end
|
77
|
-
|
78
63
|
def unpack_relay_field(payload)
|
79
64
|
( version >= 70001 && payload ) ? unpack_boolean(payload) : [ true, nil ]
|
80
65
|
end
|
81
66
|
|
67
|
+
# Check whether +service_flag+ support this version.
|
68
|
+
# @param [Integer] service_flag the service flags.
|
69
|
+
# @return [Boolean] whether support +service_flag+
|
70
|
+
def support?(service_flag)
|
71
|
+
(services & service_flag) != 0
|
72
|
+
end
|
73
|
+
|
82
74
|
end
|
83
75
|
end
|
84
76
|
end
|
data/lib/bitcoin/mnemonic.rb
CHANGED
@@ -6,11 +6,11 @@ module Bitcoin
|
|
6
6
|
|
7
7
|
WORD_DIR = "#{__dir__}/mnemonic/wordlist"
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :language
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
raise ArgumentError, 'specified language is not supported.' unless Mnemonic.word_lists.include?(
|
13
|
-
@
|
11
|
+
def initialize(language)
|
12
|
+
raise ArgumentError, 'specified language is not supported.' unless Mnemonic.word_lists.include?(language)
|
13
|
+
@language = language
|
14
14
|
end
|
15
15
|
|
16
16
|
# get support language list
|
@@ -69,7 +69,7 @@ module Bitcoin
|
|
69
69
|
|
70
70
|
# load word list contents
|
71
71
|
def load_words
|
72
|
-
File.readlines("#{WORD_DIR}/#{
|
72
|
+
File.readlines("#{WORD_DIR}/#{language}.txt").map(&:strip)
|
73
73
|
end
|
74
74
|
|
75
75
|
end
|
data/lib/bitcoin/network/peer.rb
CHANGED
@@ -50,7 +50,8 @@ module Bitcoin
|
|
50
50
|
@bytes_recv = 0
|
51
51
|
@relay = configuration.conf[:relay]
|
52
52
|
current_height = @chain.latest_block.height
|
53
|
-
|
53
|
+
remote_addr = Bitcoin::Message::NetworkAddr.new(ip: host, port: port, time: nil)
|
54
|
+
@local_version = Bitcoin::Message::Version.new(remote_addr: remote_addr, start_height: current_height, relay: @relay)
|
54
55
|
end
|
55
56
|
|
56
57
|
def connect
|
@@ -82,10 +83,15 @@ module Bitcoin
|
|
82
83
|
|
83
84
|
def post_handshake
|
84
85
|
@connected = true
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
86
|
+
if remote_version.support?(Bitcoin::Message::SERVICE_FLAGS[:bloom])
|
87
|
+
pool.handle_new_peer(self)
|
88
|
+
# require remote peer to use headers message instead fo inv message.
|
89
|
+
conn.send_message(Bitcoin::Message::SendHeaders.new)
|
90
|
+
EM.add_periodic_timer(PING_INTERVAL) {send_ping}
|
91
|
+
else
|
92
|
+
close("peer does not support NODE_BLOOM.")
|
93
|
+
pool.pending_peers.delete(self)
|
94
|
+
end
|
89
95
|
end
|
90
96
|
|
91
97
|
# start block header download
|
@@ -164,12 +170,7 @@ module Bitcoin
|
|
164
170
|
# @return [Bitcoin::Message::NetworkAddr]
|
165
171
|
def to_network_addr
|
166
172
|
v = remote_version
|
167
|
-
|
168
|
-
addr.time = v.timestamp
|
169
|
-
addr.services = v.services
|
170
|
-
addr.ip = host
|
171
|
-
addr.port = port
|
172
|
-
addr
|
173
|
+
Bitcoin::Message::NetworkAddr.new(ip: host, port: port, services: v.services, time: v.timestamp)
|
173
174
|
end
|
174
175
|
|
175
176
|
# send +addr+ message to remote peer
|
@@ -30,7 +30,9 @@ module Bitcoin
|
|
30
30
|
logger.debug 'discover peer address from DNS seeds.'
|
31
31
|
dns_seeds.map { |seed|
|
32
32
|
begin
|
33
|
-
|
33
|
+
# x5 is a prefix for finding nodes that support NODE_BLOOM.
|
34
|
+
# https://github.com/bitcoin/bitcoin/pull/8083#issuecomment-221552835
|
35
|
+
Socket.getaddrinfo("x5.#{seed}", Bitcoin.chain_params.default_port).map{|a|a[2]}.uniq
|
34
36
|
rescue SocketError => e
|
35
37
|
logger.error "SocketError occurred when load DNS seed: #{seed}, error: #{e.message}"
|
36
38
|
nil
|
data/lib/bitcoin/node/cli.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'net/http'
|
2
2
|
require 'thor'
|
3
3
|
require 'json'
|
4
4
|
|
@@ -92,15 +92,19 @@ module Bitcoin
|
|
92
92
|
:id => 'jsonrpc'
|
93
93
|
}
|
94
94
|
begin
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
95
|
+
uri = URI.parse(config.server_url)
|
96
|
+
http = Net::HTTP.new(uri.hostname, uri.port)
|
97
|
+
http.use_ssl = uri.scheme === "https"
|
98
|
+
request = Net::HTTP::Post.new('/')
|
99
|
+
request.content_type = 'application/json'
|
100
|
+
request.body = data.to_json
|
101
|
+
response = http.request(request)
|
102
|
+
body = response.body
|
103
|
+
begin
|
104
|
+
json = JSON.parse(body.to_str)
|
105
|
+
puts JSON.pretty_generate(json)
|
106
|
+
rescue Exception
|
107
|
+
puts body.to_str
|
104
108
|
end
|
105
109
|
rescue Exception => e
|
106
110
|
puts e.message
|
data/lib/bitcoin/node/spv.rb
CHANGED
data/lib/bitcoin/out_point.rb
CHANGED
@@ -3,14 +3,16 @@ module Bitcoin
|
|
3
3
|
# outpoint class
|
4
4
|
class OutPoint
|
5
5
|
|
6
|
+
include Bitcoin::HexConverter
|
7
|
+
|
6
8
|
COINBASE_HASH = '0000000000000000000000000000000000000000000000000000000000000000'
|
7
9
|
COINBASE_INDEX = 4294967295
|
8
10
|
|
9
|
-
attr_reader :
|
11
|
+
attr_reader :tx_hash
|
10
12
|
attr_reader :index
|
11
13
|
|
12
|
-
def initialize(
|
13
|
-
@
|
14
|
+
def initialize(tx_hash, index = -1)
|
15
|
+
@tx_hash = tx_hash
|
14
16
|
@index = index
|
15
17
|
end
|
16
18
|
|
@@ -19,11 +21,11 @@ module Bitcoin
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def coinbase?
|
22
|
-
|
24
|
+
tx_hash == COINBASE_HASH && index == COINBASE_INDEX
|
23
25
|
end
|
24
26
|
|
25
27
|
def to_payload
|
26
|
-
[
|
28
|
+
[tx_hash.htb, index].pack('a32V')
|
27
29
|
end
|
28
30
|
|
29
31
|
def self.create_coinbase_outpoint
|
@@ -31,12 +33,17 @@ module Bitcoin
|
|
31
33
|
end
|
32
34
|
|
33
35
|
def valid?
|
34
|
-
index >= 0 && (!coinbase? &&
|
36
|
+
index >= 0 && (!coinbase? && tx_hash != COINBASE_HASH)
|
35
37
|
end
|
36
38
|
|
37
39
|
# convert hash to txid
|
38
40
|
def txid
|
39
|
-
|
41
|
+
tx_hash.rhex
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
return "[#{index}]" unless tx_hash
|
46
|
+
"#{txid}[#{index}]"
|
40
47
|
end
|
41
48
|
|
42
49
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
# BIP47 payment code
|
4
|
+
class PaymentCode < ExtKey
|
5
|
+
|
6
|
+
include Bitcoin::HexConverter
|
7
|
+
|
8
|
+
attr_accessor :x_value
|
9
|
+
attr_accessor :sign
|
10
|
+
|
11
|
+
VERSION_BYTE = '47'
|
12
|
+
SUPPORT_VERSIONS = ['01']
|
13
|
+
SUPPORT_SIGNS = ['02', '03']
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@version = '01'
|
17
|
+
@features_bits = '00'
|
18
|
+
@reserve_field = '0' * 26
|
19
|
+
end
|
20
|
+
|
21
|
+
# generate master key from seed.
|
22
|
+
# @params [String] seed a seed data with hex format.
|
23
|
+
def self.generate_master(seed)
|
24
|
+
master_ext_key = super.derive(47, harden=true).derive(0, harden=true).derive(0, harden=true)
|
25
|
+
compressed_pubkey = master_ext_key.pub
|
26
|
+
|
27
|
+
payment_code = PaymentCode.new
|
28
|
+
payment_code.depth = master_ext_key.depth
|
29
|
+
payment_code.key = master_ext_key.key
|
30
|
+
payment_code.sign = compressed_pubkey[0..1]
|
31
|
+
payment_code.x_value = compressed_pubkey[2..-1]
|
32
|
+
payment_code.chain_code = master_ext_key.chain_code
|
33
|
+
payment_code
|
34
|
+
end
|
35
|
+
|
36
|
+
# Base58 encoded payment code
|
37
|
+
def to_base58
|
38
|
+
payment_code_with_version_byte = VERSION_BYTE + to_hex
|
39
|
+
Bitcoin::Base58.encode(payment_code_with_version_byte + Bitcoin.calc_checksum(payment_code_with_version_byte))
|
40
|
+
end
|
41
|
+
|
42
|
+
# serialize payment code
|
43
|
+
def to_payload
|
44
|
+
@version.htb << @features_bits.htb << @sign.htb << @x_value.htb << @chain_code << @reserve_field.htb
|
45
|
+
end
|
46
|
+
|
47
|
+
# get notification address
|
48
|
+
def notification_address
|
49
|
+
ext_pubkey.derive(0).addr
|
50
|
+
end
|
51
|
+
|
52
|
+
# decode base58 encoded payment code
|
53
|
+
# @params [String] base58_payment_code base58 encoded payment code
|
54
|
+
def self.from_base58(base58_payment_code)
|
55
|
+
hex = Bitcoin::Base58.decode(base58_payment_code)
|
56
|
+
version = hex[2..3]
|
57
|
+
sign = hex[6..7]
|
58
|
+
public_key = hex[8..71]
|
59
|
+
payment_code = hex[0...-8]
|
60
|
+
|
61
|
+
raise ArgumentError, 'invalid version byte' unless hex[0..1] == VERSION_BYTE
|
62
|
+
raise ArgumentError, 'invalid version' unless PaymentCode.support_version?(version)
|
63
|
+
raise ArgumentError, 'invalid sign' unless PaymentCode.support_sign?(sign)
|
64
|
+
raise ArgumentError, 'invalid public key' unless Bitcoin::Key.new(priv_key: nil, pubkey: sign + public_key).fully_valid_pubkey?
|
65
|
+
raise ArgumentError, 'invalid checksum' unless Bitcoin.calc_checksum(payment_code) == hex[-8..-1]
|
66
|
+
|
67
|
+
x_value = payment_code[8..71]
|
68
|
+
chain_code_hex = payment_code[72..135]
|
69
|
+
|
70
|
+
payment_code_pubkey = PaymentCode.new
|
71
|
+
payment_code_pubkey.depth = 3
|
72
|
+
payment_code_pubkey.sign = sign
|
73
|
+
payment_code_pubkey.x_value = x_value
|
74
|
+
payment_code_pubkey.chain_code = [chain_code_hex].pack('H*')
|
75
|
+
|
76
|
+
payment_code_pubkey.to_payload
|
77
|
+
end
|
78
|
+
|
79
|
+
# check whether +version+ is supported version bytes.
|
80
|
+
def self.support_version?(version)
|
81
|
+
SUPPORT_VERSIONS.include?(version)
|
82
|
+
end
|
83
|
+
|
84
|
+
# check whether +sign+ is supported version bytes.
|
85
|
+
def self.support_sign?(sign)
|
86
|
+
SUPPORT_SIGNS.include?(sign)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
data/lib/bitcoin/psbt.rb
CHANGED
@@ -12,13 +12,15 @@ module Bitcoin
|
|
12
12
|
|
13
13
|
# constants for PSBT
|
14
14
|
PSBT_MAGIC_BYTES = 0x70736274
|
15
|
-
PSBT_GLOBAL_TYPES = {unsigned_tx: 0x00, xpub: 0x01}
|
15
|
+
PSBT_GLOBAL_TYPES = {unsigned_tx: 0x00, xpub: 0x01, ver: 0xfb}
|
16
16
|
PSBT_IN_TYPES = {non_witness_utxo: 0x00, witness_utxo: 0x01, partial_sig: 0x02,
|
17
17
|
sighash: 0x03, redeem_script: 0x04, witness_script: 0x05,
|
18
18
|
bip32_derivation: 0x06, script_sig: 0x07, script_witness: 0x08}
|
19
19
|
PSBT_OUT_TYPES = {redeem_script: 0x00, witness_script: 0x01, bip32_derivation: 0x02}
|
20
20
|
PSBT_SEPARATOR = 0x00
|
21
21
|
|
22
|
+
SUPPORT_VERSION = 0
|
23
|
+
|
22
24
|
module_function
|
23
25
|
|
24
26
|
def self.serialize_to_vector(key_type, key: nil, value: nil)
|
data/lib/bitcoin/psbt/input.rb
CHANGED
@@ -93,7 +93,8 @@ module Bitcoin
|
|
93
93
|
|
94
94
|
def to_payload
|
95
95
|
payload = ''
|
96
|
-
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:non_witness_utxo], value:
|
96
|
+
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:non_witness_utxo], value:
|
97
|
+
(witness_utxo && valid_witness_input?) ? non_witness_utxo.serialize_old_format : non_witness_utxo.to_payload) if non_witness_utxo
|
97
98
|
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:witness_utxo], value: witness_utxo.to_payload) if witness_utxo
|
98
99
|
if final_script_sig.nil? && final_script_witness.nil?
|
99
100
|
payload << partial_sigs.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:partial_sig], key: k.htb, value: v)}.join
|
@@ -109,15 +110,6 @@ module Bitcoin
|
|
109
110
|
payload
|
110
111
|
end
|
111
112
|
|
112
|
-
# Sanity check
|
113
|
-
# @return [Boolean]
|
114
|
-
def sane?
|
115
|
-
return false if non_witness_utxo && witness_utxo
|
116
|
-
return false if witness_script && witness_utxo.nil?
|
117
|
-
return false if final_script_witness && witness_utxo.nil?
|
118
|
-
true
|
119
|
-
end
|
120
|
-
|
121
113
|
# Check whether input's scriptPubkey is correct witness.
|
122
114
|
# @return [Boolean]
|
123
115
|
def valid_witness_input?
|
@@ -141,7 +133,6 @@ module Bitcoin
|
|
141
133
|
# @param [Bitcoin::TxOut] utxo utxo object which input refers.
|
142
134
|
# @return [Boolean]
|
143
135
|
def ready_to_sign?(utxo)
|
144
|
-
return false unless sane?
|
145
136
|
return valid_witness_input? if witness_utxo
|
146
137
|
valid_non_witness_input?(utxo) # non_witness_utxo
|
147
138
|
end
|
@@ -156,7 +147,7 @@ module Bitcoin
|
|
156
147
|
# @param [String] pubkey a public key with hex format.
|
157
148
|
# @param [String] sig a signature.
|
158
149
|
def add_sig(pubkey, sig)
|
159
|
-
raise ArgumentError, 'The sighash in signature is invalid.'
|
150
|
+
raise ArgumentError, 'The sighash in signature is invalid.' if sighash_type && sig.unpack('C*')[-1] != sighash_type
|
160
151
|
raise ArgumentError, 'Duplicate Key, input partial signature for pubkey already provided.' if partial_sigs[pubkey]
|
161
152
|
partial_sigs[pubkey] = sig
|
162
153
|
end
|
@@ -168,7 +159,7 @@ module Bitcoin
|
|
168
159
|
raise ArgumentError, 'The argument psbt must be an instance of Bitcoin::PSBT::Input.' unless psbi.is_a?(Bitcoin::PSBT::Input)
|
169
160
|
raise ArgumentError, 'The Partially Signed Input\'s non_witness_utxo are different.' unless non_witness_utxo == psbi.non_witness_utxo
|
170
161
|
raise ArgumentError, 'The Partially Signed Input\'s witness_utxo are different.' unless witness_utxo == psbi.witness_utxo
|
171
|
-
raise ArgumentError, 'The Partially Signed Input\'s sighash_type are different.'
|
162
|
+
raise ArgumentError, 'The Partially Signed Input\'s sighash_type are different.' if sighash_type && psbi.sighash_type && sighash_type != psbi.sighash_type
|
172
163
|
raise ArgumentError, 'The Partially Signed Input\'s redeem_script are different.' unless redeem_script == psbi.redeem_script
|
173
164
|
raise ArgumentError, 'The Partially Signed Input\'s witness_script are different.' unless witness_script == psbi.witness_script
|
174
165
|
combined = Bitcoin::PSBT::Input.new(non_witness_utxo: non_witness_utxo, witness_utxo: witness_utxo)
|
@@ -177,8 +168,8 @@ module Bitcoin
|
|
177
168
|
combined.witness_script = witness_script
|
178
169
|
combined.sighash_type = sighash_type
|
179
170
|
sigs = Hash[partial_sigs.merge(psbi.partial_sigs)]
|
180
|
-
redeem_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if redeem_script
|
181
|
-
witness_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if witness_script
|
171
|
+
redeem_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if redeem_script&.multisig?
|
172
|
+
witness_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if witness_script&.multisig?
|
182
173
|
combined.hd_key_paths = hd_key_paths.merge(psbi.hd_key_paths)
|
183
174
|
combined
|
184
175
|
end
|
@@ -190,7 +181,7 @@ module Bitcoin
|
|
190
181
|
if non_witness_utxo
|
191
182
|
self.final_script_sig = Bitcoin::Script.new << Bitcoin::Opcodes::OP_0 if redeem_script.multisig?
|
192
183
|
partial_sigs.values.each {|sig|final_script_sig << sig}
|
193
|
-
final_script_sig << redeem_script.
|
184
|
+
final_script_sig << redeem_script.to_hex
|
194
185
|
self.partial_sigs = {}
|
195
186
|
self.hd_key_paths = {}
|
196
187
|
self.redeem_script = nil
|