btcruby 1.6 → 1.7
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/RELEASE_NOTES.md +6 -0
- data/lib/btcruby/address.rb +1 -1
- data/lib/btcruby/keychain.rb +13 -13
- data/lib/btcruby/mnemonic.rb +1 -1
- data/lib/btcruby/script/script.rb +17 -9
- data/lib/btcruby/script/script_interpreter.rb +19 -19
- data/lib/btcruby/script/script_version.rb +54 -0
- data/lib/btcruby/script/versioned_script.rb +64 -0
- data/lib/btcruby/secp256k1.rb +5 -3
- data/lib/btcruby/transaction_builder.rb +10 -5
- data/lib/btcruby/transaction_builder/errors.rb +6 -1
- data/lib/btcruby/version.rb +1 -1
- data/spec/currency_formatter_spec.rb +0 -2
- data/spec/keychain_spec.rb +5 -0
- data/spec/script_number_spec.rb +4 -0
- data/spec/secp256k1_spec.rb +13 -1
- data/spec/transaction_builder_spec.rb +12 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ffee95a66b5f287235a52944ff6f92e3335b22df
|
4
|
+
data.tar.gz: 550a1d62ecea9b95a15294f3b65477566c9baa90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 517e12449e58e4866826c6f28230f854bc26b04e788f9d7d3bd864dd15aeb60ccc948da70a4c2d69bfdeef84bd1475f5ac5d78aee599ea67ba52674a30703cb0
|
7
|
+
data.tar.gz: e175176b5a85c1eb40e17134179bf29acc04b2077228c26089b308321d17f94e29960a4f66f0c216e124c108173e40f353125b2e596cef0117687f82098b31bb
|
data/RELEASE_NOTES.md
CHANGED
data/lib/btcruby/address.rb
CHANGED
data/lib/btcruby/keychain.rb
CHANGED
@@ -24,7 +24,7 @@ module BTC
|
|
24
24
|
|
25
25
|
PUBLIC_MAINNET_VERSION = 0x0488B21E # xpub
|
26
26
|
PRIVATE_MAINNET_VERSION = 0x0488ADE4 # xprv
|
27
|
-
|
27
|
+
PUBLIC_TESTNET_VERSION = 0x043587CF # tpub
|
28
28
|
PRIVATE_TESTNET_VERSION = 0x04358394 # tprv
|
29
29
|
|
30
30
|
# Instance of BTC::Key that is a "head" of this keychain.
|
@@ -107,9 +107,9 @@ module BTC
|
|
107
107
|
|
108
108
|
def key
|
109
109
|
@key ||= if @private_key
|
110
|
-
BTC::Key.new(private_key: @private_key, public_key_compressed: true)
|
110
|
+
BTC::Key.new(private_key: @private_key, public_key_compressed: true, network: @network)
|
111
111
|
else
|
112
|
-
BTC::Key.new(public_key: @public_key)
|
112
|
+
BTC::Key.new(public_key: @public_key, network: @network)
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
@@ -127,7 +127,7 @@ module BTC
|
|
127
127
|
|
128
128
|
def extended_public_key
|
129
129
|
@extended_public_key ||= begin
|
130
|
-
prefix = _extended_key_prefix(mainnet? ? PUBLIC_MAINNET_VERSION :
|
130
|
+
prefix = _extended_key_prefix(mainnet? ? PUBLIC_MAINNET_VERSION : PUBLIC_TESTNET_VERSION)
|
131
131
|
BTC::Base58.base58check_from_data(prefix + @public_key)
|
132
132
|
end
|
133
133
|
end
|
@@ -205,7 +205,7 @@ module BTC
|
|
205
205
|
elsif _components
|
206
206
|
init_with_components(*_components)
|
207
207
|
else
|
208
|
-
raise ArgumentError, "Either seed or an extended
|
208
|
+
raise ArgumentError, "Either seed or an extended key must be provided"
|
209
209
|
end
|
210
210
|
end
|
211
211
|
|
@@ -251,13 +251,13 @@ module BTC
|
|
251
251
|
@network = Network.testnet
|
252
252
|
end
|
253
253
|
|
254
|
-
elsif version == PUBLIC_MAINNET_VERSION || version ==
|
254
|
+
elsif version == PUBLIC_MAINNET_VERSION || version == PUBLIC_TESTNET_VERSION
|
255
255
|
# Should have a 33-byte public key with non-zero first byte.
|
256
256
|
if keyprefix == 0x00
|
257
257
|
raise BTCError, "Extended public key must have non-zero first byte (received #{keyprefix})"
|
258
258
|
end
|
259
259
|
@public_key = xkeydata[45, 33]
|
260
|
-
if version ==
|
260
|
+
if version == PUBLIC_TESTNET_VERSION
|
261
261
|
@network = Network.testnet
|
262
262
|
end
|
263
263
|
else
|
@@ -452,25 +452,25 @@ module BTC
|
|
452
452
|
end
|
453
453
|
end
|
454
454
|
end
|
455
|
-
|
455
|
+
|
456
456
|
# BIP44 Support
|
457
|
-
|
457
|
+
|
458
458
|
def bip44_keychain(network: Network.mainnet)
|
459
459
|
network_index = network.mainnet? ? 0 : 1
|
460
460
|
derived_keychain(44, hardened: true).derived_keychain(network_index, hardened: true)
|
461
461
|
end
|
462
|
-
|
462
|
+
|
463
463
|
def bip44_account_keychain(account_index)
|
464
464
|
derived_keychain(account_index, hardened: true)
|
465
465
|
end
|
466
|
-
|
466
|
+
|
467
467
|
def bip44_external_keychain
|
468
468
|
derived_keychain(0, hardened: false)
|
469
469
|
end
|
470
|
-
|
470
|
+
|
471
471
|
def bip44_internal_keychain
|
472
472
|
derived_keychain(1, hardened: false)
|
473
473
|
end
|
474
|
-
|
474
|
+
|
475
475
|
end # Keychain
|
476
476
|
end # BTC
|
data/lib/btcruby/mnemonic.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# BTC::Mnemonic implements
|
1
|
+
# BTC::Mnemonic implements BIP39: mnemonic-based hierarchical deterministic wallets.
|
2
2
|
# Currently only supports restoring keychain from words. Generating sentence.
|
3
3
|
require 'openssl'
|
4
4
|
require 'openssl/digest'
|
@@ -89,6 +89,14 @@ module BTC
|
|
89
89
|
public_key_script? ||
|
90
90
|
standard_op_return_script?
|
91
91
|
end
|
92
|
+
|
93
|
+
# Returns true if this is an output script wrapped in a versioned pushdata for segwit softfork.
|
94
|
+
def versioned_script?
|
95
|
+
return chunks.size == 1 &&
|
96
|
+
chunks[0].pushdata? &&
|
97
|
+
chunks[0].canonical? &&
|
98
|
+
chunks[0].pushdata.bytesize > 2
|
99
|
+
end
|
92
100
|
|
93
101
|
# Returns true if the script is a pay-to-pubkey script:
|
94
102
|
# "<pubkey> OP_CHECKSIG"
|
@@ -248,7 +256,7 @@ module BTC
|
|
248
256
|
# and a new script instance is returned.
|
249
257
|
def without_dropped_prefix_data
|
250
258
|
if dropped_prefix_data
|
251
|
-
return
|
259
|
+
return Script.new << @chunks[2..-1]
|
252
260
|
end
|
253
261
|
self
|
254
262
|
end
|
@@ -288,7 +296,7 @@ module BTC
|
|
288
296
|
|
289
297
|
# Complete copy of a script.
|
290
298
|
def dup
|
291
|
-
|
299
|
+
Script.new(data: self.data)
|
292
300
|
end
|
293
301
|
|
294
302
|
def ==(other)
|
@@ -324,7 +332,7 @@ module BTC
|
|
324
332
|
# Wraps the recipient into an output P2SH script
|
325
333
|
# (OP_HASH160 <20-byte hash of the recipient> OP_EQUAL).
|
326
334
|
def p2sh_script
|
327
|
-
|
335
|
+
Script.new << OP_HASH160 << BTC.hash160(self.data) << OP_EQUAL
|
328
336
|
end
|
329
337
|
|
330
338
|
|
@@ -336,18 +344,18 @@ module BTC
|
|
336
344
|
def simulated_signature_script(strict: true)
|
337
345
|
if public_key_hash_script?
|
338
346
|
# assuming non-compressed pubkeys to be conservative
|
339
|
-
return
|
347
|
+
return Script.new << Script.simulated_signature(hashtype: SIGHASH_ALL) << Script.simulated_uncompressed_pubkey
|
340
348
|
|
341
349
|
elsif public_key_script?
|
342
|
-
return
|
350
|
+
return Script.new << Script.simulated_signature(hashtype: SIGHASH_ALL)
|
343
351
|
|
344
352
|
elsif script_hash_script? && !strict
|
345
353
|
# This is a wild approximation, but works well if most p2sh scripts are 2-of-3 multisig scripts.
|
346
354
|
# If you have a very particular smart contract scheme you should not use TransactionBuilder which estimates fees this way.
|
347
|
-
return
|
355
|
+
return Script.new << OP_0 << [Script.simulated_signature(hashtype: SIGHASH_ALL)]*2 << Script.simulated_multisig_script(2,3).data
|
348
356
|
|
349
357
|
elsif multisig_script?
|
350
|
-
return
|
358
|
+
return Script.new << OP_0 << [Script.simulated_signature(hashtype: SIGHASH_ALL)]*self.multisig_signatures_required
|
351
359
|
else
|
352
360
|
return nil
|
353
361
|
end
|
@@ -463,7 +471,7 @@ module BTC
|
|
463
471
|
|
464
472
|
# Same arguments as with Array#[].
|
465
473
|
def subscript(*args)
|
466
|
-
|
474
|
+
Script.new << chunks[*args]
|
467
475
|
end
|
468
476
|
alias_method :[], :subscript
|
469
477
|
|
@@ -478,7 +486,7 @@ module BTC
|
|
478
486
|
subscriptsize = subscript.chunks.size
|
479
487
|
buf = []
|
480
488
|
i = 0
|
481
|
-
result =
|
489
|
+
result = Script.new
|
482
490
|
chunks.each do |chunk|
|
483
491
|
if chunk == subscript.chunks[i]
|
484
492
|
buf << chunk
|
@@ -36,7 +36,7 @@ module BTC
|
|
36
36
|
# (required if the scripts use signature-checking opcodes).
|
37
37
|
# Checker can be transaction checker or block checker
|
38
38
|
def initialize(flags: SCRIPT_VERIFY_NONE,
|
39
|
-
extensions:
|
39
|
+
extensions: nil,
|
40
40
|
signature_checker: nil,
|
41
41
|
raise_on_failure: false,
|
42
42
|
max_pushdata_size: MAX_SCRIPT_ELEMENT_SIZE,
|
@@ -144,8 +144,8 @@ module BTC
|
|
144
144
|
@altstack = []
|
145
145
|
@run_script_chunks = script.chunks.to_a.dup # can be modified by `insert_script`
|
146
146
|
|
147
|
-
number_zero = ScriptNumber.new(integer: 0)
|
148
|
-
number_one = ScriptNumber.new(integer: 1)
|
147
|
+
number_zero = BTC::ScriptNumber.new(integer: 0)
|
148
|
+
number_one = BTC::ScriptNumber.new(integer: 1)
|
149
149
|
zero_value = "".b
|
150
150
|
false_value = "".b
|
151
151
|
true_value = "\x01".b
|
@@ -216,7 +216,7 @@ module BTC
|
|
216
216
|
case opcode
|
217
217
|
when OP_1NEGATE, OP_1..OP_16
|
218
218
|
# ( -- value)
|
219
|
-
num = ScriptNumber.new(integer: opcode - (OP_1 - 1))
|
219
|
+
num = BTC::ScriptNumber.new(integer: opcode - (OP_1 - 1))
|
220
220
|
stack_push(num.data)
|
221
221
|
# The result of these opcodes should always be the minimal way to push the data
|
222
222
|
# they push, so no need for a CheckMinimalPush here.
|
@@ -408,7 +408,7 @@ module BTC
|
|
408
408
|
|
409
409
|
when OP_DEPTH
|
410
410
|
# -- stacksize
|
411
|
-
sn = ScriptNumber.new(integer: @stack.size)
|
411
|
+
sn = BTC::ScriptNumber.new(integer: @stack.size)
|
412
412
|
stack_push(sn.data)
|
413
413
|
|
414
414
|
when OP_DROP
|
@@ -489,7 +489,7 @@ module BTC
|
|
489
489
|
if @stack.size < 1
|
490
490
|
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
491
491
|
end
|
492
|
-
sn = ScriptNumber.new(integer: @stack.last.size)
|
492
|
+
sn = BTC::ScriptNumber.new(integer: @stack.last.size)
|
493
493
|
stack_push(sn.data)
|
494
494
|
|
495
495
|
|
@@ -548,9 +548,9 @@ module BTC
|
|
548
548
|
when OP_ABS
|
549
549
|
bn = -bn if bn < 0
|
550
550
|
when OP_NOT
|
551
|
-
bn = ScriptNumber.new(boolean: (bn == 0))
|
551
|
+
bn = BTC::ScriptNumber.new(boolean: (bn == 0))
|
552
552
|
when OP_0NOTEQUAL
|
553
|
-
bn = ScriptNumber.new(boolean: (bn != 0))
|
553
|
+
bn = BTC::ScriptNumber.new(boolean: (bn != 0))
|
554
554
|
else
|
555
555
|
raise "invalid opcode"
|
556
556
|
end
|
@@ -566,7 +566,7 @@ module BTC
|
|
566
566
|
|
567
567
|
bn1 = cast_to_number(@stack[-2])
|
568
568
|
bn2 = cast_to_number(@stack[-1])
|
569
|
-
bn = ScriptNumber.new(integer: 0)
|
569
|
+
bn = BTC::ScriptNumber.new(integer: 0)
|
570
570
|
|
571
571
|
case opcode
|
572
572
|
when OP_ADD
|
@@ -574,21 +574,21 @@ module BTC
|
|
574
574
|
when OP_SUB
|
575
575
|
bn = bn1 - bn2
|
576
576
|
when OP_BOOLAND
|
577
|
-
bn = ScriptNumber.new(boolean: ((bn1 != 0) && (bn2 != 0)))
|
577
|
+
bn = BTC::ScriptNumber.new(boolean: ((bn1 != 0) && (bn2 != 0)))
|
578
578
|
when OP_BOOLOR
|
579
|
-
bn = ScriptNumber.new(boolean: ((bn1 != 0) || (bn2 != 0)))
|
579
|
+
bn = BTC::ScriptNumber.new(boolean: ((bn1 != 0) || (bn2 != 0)))
|
580
580
|
when OP_NUMEQUAL, OP_NUMEQUALVERIFY
|
581
|
-
bn = ScriptNumber.new(boolean: (bn1 == bn2))
|
581
|
+
bn = BTC::ScriptNumber.new(boolean: (bn1 == bn2))
|
582
582
|
when OP_NUMNOTEQUAL
|
583
|
-
bn = ScriptNumber.new(boolean: (bn1 != bn2))
|
583
|
+
bn = BTC::ScriptNumber.new(boolean: (bn1 != bn2))
|
584
584
|
when OP_LESSTHAN
|
585
|
-
bn = ScriptNumber.new(boolean: (bn1 < bn2))
|
585
|
+
bn = BTC::ScriptNumber.new(boolean: (bn1 < bn2))
|
586
586
|
when OP_GREATERTHAN
|
587
|
-
bn = ScriptNumber.new(boolean: (bn1 > bn2))
|
587
|
+
bn = BTC::ScriptNumber.new(boolean: (bn1 > bn2))
|
588
588
|
when OP_LESSTHANOREQUAL
|
589
|
-
bn = ScriptNumber.new(boolean: (bn1 <= bn2))
|
589
|
+
bn = BTC::ScriptNumber.new(boolean: (bn1 <= bn2))
|
590
590
|
when OP_GREATERTHANOREQUAL
|
591
|
-
bn = ScriptNumber.new(boolean: (bn1 >= bn2))
|
591
|
+
bn = BTC::ScriptNumber.new(boolean: (bn1 >= bn2))
|
592
592
|
when OP_MIN
|
593
593
|
bn = (bn1 < bn2 ? bn1 : bn2)
|
594
594
|
when OP_MAX
|
@@ -815,7 +815,7 @@ module BTC
|
|
815
815
|
end
|
816
816
|
|
817
817
|
return true
|
818
|
-
rescue ScriptNumberError => e
|
818
|
+
rescue BTC::ScriptNumberError => e
|
819
819
|
return set_error(SCRIPT_ERR_UNKNOWN_ERROR, e.message)
|
820
820
|
end # run_script
|
821
821
|
|
@@ -914,7 +914,7 @@ module BTC
|
|
914
914
|
def cast_to_number(data,
|
915
915
|
require_minimal: flag?(SCRIPT_VERIFY_MINIMALDATA),
|
916
916
|
max_size: @integer_max_size)
|
917
|
-
ScriptNumber.new(data: data, require_minimal: require_minimal, max_size: max_size)
|
917
|
+
BTC::ScriptNumber.new(data: data, require_minimal: require_minimal, max_size: max_size)
|
918
918
|
end
|
919
919
|
|
920
920
|
def cast_to_bool(data)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module BTC
|
2
|
+
class ScriptVersion
|
3
|
+
VERSION_DEFAULT = 0
|
4
|
+
VERSION_P2SH = 1
|
5
|
+
|
6
|
+
attr_reader :version # binary string containing the version
|
7
|
+
attr_reader :sighash_version
|
8
|
+
|
9
|
+
def initialize(version)
|
10
|
+
@version = version
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns a matching signature hash version for the given script version.
|
14
|
+
# All currently known script versions use sighash version 1.
|
15
|
+
def sighash_version
|
16
|
+
return 1
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns true if version is known.
|
20
|
+
# Unknown versions are supported too, but scripts are not even parsed and interpreted as "anyone can spend".
|
21
|
+
def known?
|
22
|
+
default? ||
|
23
|
+
p2sh?
|
24
|
+
end
|
25
|
+
|
26
|
+
def default?
|
27
|
+
@version == VERSION_DEFAULT
|
28
|
+
end
|
29
|
+
|
30
|
+
def p2sh?
|
31
|
+
@version == VERSION_P2SH
|
32
|
+
end
|
33
|
+
|
34
|
+
def name
|
35
|
+
case version
|
36
|
+
when VERSION_DEFAULT
|
37
|
+
"Default script"
|
38
|
+
when VERSION_P2SH
|
39
|
+
"P2SH v1"
|
40
|
+
else
|
41
|
+
"Unknown script version"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_i
|
46
|
+
@version
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
"v#{@version} (#{name})"
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module BTC
|
2
|
+
# Versioned script is a wrapper around a regular script with a version prefix.
|
3
|
+
# Note that we support version of any length, but do not parse and interpret unknown versions.
|
4
|
+
class VersionedScript
|
5
|
+
|
6
|
+
attr_reader :version # Integer
|
7
|
+
attr_reader :data # Raw data representing script wrapped in VersionedScript. For 256-bit P2SH it's just a hash, not a script.
|
8
|
+
attr_reader :wrapper_script # BTC::Script that wraps version + data
|
9
|
+
attr_reader :script_version # BTC::ScriptVersion
|
10
|
+
attr_reader :inner_script # BTC::Script derived from data, if it makes sense for the given version.
|
11
|
+
|
12
|
+
# Initializers:
|
13
|
+
# - `VersionedScript.new(wrapper_script:)` that wraps the version and inner script data.
|
14
|
+
# - `VersionedScript.new(version:, data:)` where version is one of ScriptVersion::VERSION_* and data is a raw inner script data (hash for p2sh256).
|
15
|
+
# - `VersionedScript.new(p2sh_redeem_script:)` creates a versioned script with p2sh hash of the redeem script.
|
16
|
+
def initialize(wrapper_script: nil,
|
17
|
+
version: nil, data: nil,
|
18
|
+
script: nil,
|
19
|
+
p2sh_redeem_script: nil,
|
20
|
+
)
|
21
|
+
if wrapper_script
|
22
|
+
if !wrapper_script.versioned_script?
|
23
|
+
raise ArgumentError, "Script is not a canonical versioned script with minimal pushdata encoding"
|
24
|
+
end
|
25
|
+
@wrapper_script = wrapper_script
|
26
|
+
fulldata = BTC::Data.ensure_binary_encoding(wrapper_script.chunks.first.pushdata)
|
27
|
+
@version = fulldata.bytes[0]
|
28
|
+
@data = fulldata[1..-1]
|
29
|
+
elsif script
|
30
|
+
@version = ScriptVersion::VERSION_DEFAULT
|
31
|
+
@data = script.data
|
32
|
+
elsif p2sh_redeem_script
|
33
|
+
@version = ScriptVersion::VERSION_P2SH256
|
34
|
+
@data = BTC.hash256(p2sh_redeem_script.data)
|
35
|
+
else
|
36
|
+
@version = version or raise ArgumentError, "Version is missing"
|
37
|
+
@data = data
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def wrapper_script
|
42
|
+
@wrapper_script ||= BTC::Script.new << (@version.chr.b + @data)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns inner BTC::Script if it's a default script
|
46
|
+
def inner_script
|
47
|
+
if script_version.default?
|
48
|
+
BTC::Script.new(data: data)
|
49
|
+
else
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def script_version
|
55
|
+
@script_version ||= ScriptVersion.new(@version)
|
56
|
+
end
|
57
|
+
|
58
|
+
def known_version?
|
59
|
+
script_version.known?
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
data/lib/btcruby/secp256k1.rb
CHANGED
@@ -10,8 +10,10 @@ module BTC
|
|
10
10
|
|
11
11
|
ffi_lib 'secp256k1'
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
SECP256K1_FLAGS_TYPE_CONTEXT = (1 << 0)
|
14
|
+
SECP256K1_FLAGS_BIT_CONTEXT_VERIFY = (1 << 8)
|
15
|
+
SECP256K1_FLAGS_BIT_CONTEXT_SIGN = (1 << 9)
|
16
|
+
SECP256K1_CONTEXT_SIGN = (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
|
15
17
|
|
16
18
|
# Note: this struct is opaque, but public. Its size will eventually be guaranteed.
|
17
19
|
# See https://github.com/bitcoin/secp256k1/issues/288
|
@@ -42,7 +44,7 @@ module BTC
|
|
42
44
|
privkey_buf = FFI::MemoryPointer.new(:uchar, privkey.bytesize)
|
43
45
|
privkey_buf.put_bytes(0, privkey)
|
44
46
|
|
45
|
-
if secp256k1_ecdsa_sign(ctx,
|
47
|
+
if secp256k1_ecdsa_sign(ctx, sig.pointer, hash_buf, privkey_buf, nil, nil) == 1
|
46
48
|
# Serialize an ECDSA signature in DER format.
|
47
49
|
bufsize = 72
|
48
50
|
output_pointer = FFI::MemoryPointer.new(:uint8, bufsize)
|
@@ -60,6 +60,10 @@ module BTC
|
|
60
60
|
# Default is Transaction::DEFAULT_FEE_RATE
|
61
61
|
attr_accessor :fee_rate
|
62
62
|
|
63
|
+
# Miner's fee for this transaction.
|
64
|
+
# If `fee` is not nil, fee_rate is ignored and transaction uses the specified fee.
|
65
|
+
attr_accessor :fee
|
66
|
+
|
63
67
|
# Minimum amount of change below which transaction is not composed.
|
64
68
|
# If change amount is non-zero and below this value, more unspent outputs are used.
|
65
69
|
# If change amount is zero, change output is not even created and this attribute is not used.
|
@@ -92,10 +96,10 @@ module BTC
|
|
92
96
|
# A total size of all outputs in bytes (including change output).
|
93
97
|
attr_reader :outputs_size
|
94
98
|
|
95
|
-
|
96
99
|
# Implementation of the attributes declared above
|
97
100
|
# ===============================================
|
98
101
|
|
102
|
+
|
99
103
|
def network
|
100
104
|
@network ||= Network.default
|
101
105
|
end
|
@@ -223,7 +227,7 @@ module BTC
|
|
223
227
|
|
224
228
|
# Check if inputs cover the fees
|
225
229
|
if result.outputs_amount < 0
|
226
|
-
raise InsufficientFundsError
|
230
|
+
raise InsufficientFundsError.new(result)
|
227
231
|
end
|
228
232
|
|
229
233
|
# Warn if the output amount is relatively small.
|
@@ -282,7 +286,7 @@ module BTC
|
|
282
286
|
|
283
287
|
while true
|
284
288
|
if (sorted_utxos.size + mandatory_utxos.size) == 0
|
285
|
-
raise InsufficientFundsError
|
289
|
+
raise InsufficientFundsError.new(result)
|
286
290
|
end
|
287
291
|
|
288
292
|
utxo = nil
|
@@ -361,7 +365,7 @@ module BTC
|
|
361
365
|
else
|
362
366
|
# Change is negative, we need more funds for this transaction.
|
363
367
|
# Try adding more utxos on the next cycle.
|
364
|
-
|
368
|
+
result.fee = fee
|
365
369
|
end
|
366
370
|
|
367
371
|
end # if inputs_amount >= outputs_amount
|
@@ -374,6 +378,8 @@ module BTC
|
|
374
378
|
# Helper to compute total fee for a given transaction.
|
375
379
|
# Simulates signatures to estimate final size.
|
376
380
|
def compute_fee_for_transaction(tx, fee_rate)
|
381
|
+
# Return mining fee if set manually
|
382
|
+
return fee if fee
|
377
383
|
# Compute fees for this tx by composing a tx with properly sized dummy signatures.
|
378
384
|
simulated_tx = tx.dup
|
379
385
|
simulated_tx.inputs.each do |txin|
|
@@ -518,4 +524,3 @@ if $0 == __FILE__
|
|
518
524
|
|
519
525
|
|
520
526
|
end
|
521
|
-
|
@@ -10,6 +10,11 @@ module BTC
|
|
10
10
|
class MissingUnspentOutputsError < Error; end
|
11
11
|
|
12
12
|
# Unspent outputs are not sufficient to build the transaction.
|
13
|
-
class InsufficientFundsError < Error
|
13
|
+
class InsufficientFundsError < Error
|
14
|
+
attr_accessor :result
|
15
|
+
def initialize(result = nil)
|
16
|
+
@result = result
|
17
|
+
end
|
18
|
+
end
|
14
19
|
end
|
15
20
|
end
|
data/lib/btcruby/version.rb
CHANGED
@@ -14,7 +14,6 @@ describe BTC::CurrencyFormatter do
|
|
14
14
|
fm.string_from_number(42000*BTC::COIN + BTC::COIN/2).must_equal("42000.5")
|
15
15
|
|
16
16
|
fm.number_from_string("1").must_equal 1*BTC::COIN
|
17
|
-
fm.number_from_string("1.").must_equal 1*BTC::COIN
|
18
17
|
fm.number_from_string("1.0").must_equal 1*BTC::COIN
|
19
18
|
fm.number_from_string("42").must_equal 42*BTC::COIN
|
20
19
|
fm.number_from_string("42.123").must_equal 42*BTC::COIN + 12300000
|
@@ -35,7 +34,6 @@ describe BTC::CurrencyFormatter do
|
|
35
34
|
fm.string_from_number(42000*BTC::COIN + BTC::COIN/2).must_equal("42000.50000000")
|
36
35
|
|
37
36
|
fm.number_from_string("1").must_equal 1*BTC::COIN
|
38
|
-
fm.number_from_string("1.").must_equal 1*BTC::COIN
|
39
37
|
fm.number_from_string("1.0").must_equal 1*BTC::COIN
|
40
38
|
fm.number_from_string("42").must_equal 42*BTC::COIN
|
41
39
|
fm.number_from_string("42.123").must_equal 42*BTC::COIN + 12300000
|
data/spec/keychain_spec.rb
CHANGED
@@ -258,4 +258,9 @@ describe BTC::Keychain do
|
|
258
258
|
m0pub.extended_public_key.must_equal "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"
|
259
259
|
m0pub.extended_private_key.must_equal nil
|
260
260
|
end
|
261
|
+
it "should raise the ArgumentError when arguments are not passed" do
|
262
|
+
assert_raises ArgumentError do
|
263
|
+
BTC::Keychain.new
|
264
|
+
end
|
265
|
+
end
|
261
266
|
end
|
data/spec/script_number_spec.rb
CHANGED
@@ -22,6 +22,10 @@ describe BTC::ScriptNumber do
|
|
22
22
|
BTC::ScriptNumber.new(data: "\x01").to_i.must_equal 1
|
23
23
|
end
|
24
24
|
|
25
|
+
it "should parse 0x27 as 39" do
|
26
|
+
BTC::ScriptNumber.new(data: "\x27").to_i.must_equal 39
|
27
|
+
end
|
28
|
+
|
25
29
|
it "should parse 0xff as -127" do
|
26
30
|
BTC::ScriptNumber.new(data: "\xff").to_i.must_equal -127
|
27
31
|
end
|
data/spec/secp256k1_spec.rb
CHANGED
@@ -9,22 +9,34 @@ describe BTC::Secp256k1 do
|
|
9
9
|
sig.to_hex.must_equal sighex
|
10
10
|
end
|
11
11
|
|
12
|
-
it "should produce deterministic ECDSA signatures Bitcoin-canonical using nonce from RFC6979" do
|
12
|
+
it "should produce deterministic ECDSA signatures Bitcoin-canonical using nonce from RFC6979 01" do
|
13
13
|
verify_rfc6979_signature("cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50",
|
14
14
|
"sample",
|
15
15
|
"3045022100af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b384202205009fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should produce deterministic ECDSA signatures Bitcoin-canonical using nonce from RFC6979 02" do
|
16
19
|
verify_rfc6979_signature("0000000000000000000000000000000000000000000000000000000000000001",
|
17
20
|
"Satoshi Nakamoto",
|
18
21
|
"3045022100934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d802202442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should produce deterministic ECDSA signatures Bitcoin-canonical using nonce from RFC6979 03" do
|
19
25
|
verify_rfc6979_signature("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
|
20
26
|
"Satoshi Nakamoto",
|
21
27
|
"3045022100fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d002206b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5")
|
28
|
+
end
|
29
|
+
it "should produce deterministic ECDSA signatures Bitcoin-canonical using nonce from RFC6979 04" do
|
22
30
|
verify_rfc6979_signature("f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181",
|
23
31
|
"Alan Turing",
|
24
32
|
"304402207063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c022058dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea")
|
33
|
+
end
|
34
|
+
it "should produce deterministic ECDSA signatures Bitcoin-canonical using nonce from RFC6979 05" do
|
25
35
|
verify_rfc6979_signature("0000000000000000000000000000000000000000000000000000000000000001",
|
26
36
|
"All those moments will be lost in time, like tears in rain. Time to die...",
|
27
37
|
"30450221008600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b0220547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21")
|
38
|
+
end
|
39
|
+
it "should produce deterministic ECDSA signatures Bitcoin-canonical using nonce from RFC6979 06" do
|
28
40
|
verify_rfc6979_signature("e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2",
|
29
41
|
"There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!",
|
30
42
|
"3045022100b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b0220279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6")
|
@@ -68,6 +68,12 @@ describe BTC::TransactionBuilder do
|
|
68
68
|
result.fee.must_be :<, 0.01 * BTC::COIN
|
69
69
|
end
|
70
70
|
|
71
|
+
it "should be able to set mining fee" do
|
72
|
+
@builder.fee =50690
|
73
|
+
result = @builder.build()
|
74
|
+
result.fee.must_equal 50690
|
75
|
+
end
|
76
|
+
|
71
77
|
it "should have valid amounts" do
|
72
78
|
result = @builder.build
|
73
79
|
result.inputs_amount.must_equal mock_utxos.inject(0){|sum, out| sum + out.value}
|
@@ -190,6 +196,12 @@ describe BTC::TransactionBuilder do
|
|
190
196
|
result.fee.must_be :<, 0.01 * BTC::COIN
|
191
197
|
end
|
192
198
|
|
199
|
+
it "should be able to set mining fee" do
|
200
|
+
@builder.fee =50690
|
201
|
+
result = @builder.build()
|
202
|
+
result.fee.must_equal 50690
|
203
|
+
end
|
204
|
+
|
193
205
|
it "should have valid amounts" do
|
194
206
|
result = @builder.build
|
195
207
|
result.inputs_amount.must_equal 11*1000_00
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: btcruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.7'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oleg Andreev
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2017-09-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
@@ -115,10 +115,12 @@ files:
|
|
115
115
|
- lib/btcruby/script/script_interpreter.rb
|
116
116
|
- lib/btcruby/script/script_interpreter_extension.rb
|
117
117
|
- lib/btcruby/script/script_number.rb
|
118
|
+
- lib/btcruby/script/script_version.rb
|
118
119
|
- lib/btcruby/script/signature_checker.rb
|
119
120
|
- lib/btcruby/script/signature_hashtype.rb
|
120
121
|
- lib/btcruby/script/test_signature_checker.rb
|
121
122
|
- lib/btcruby/script/transaction_signature_checker.rb
|
123
|
+
- lib/btcruby/script/versioned_script.rb
|
122
124
|
- lib/btcruby/secp256k1.rb
|
123
125
|
- lib/btcruby/ssss.rb
|
124
126
|
- lib/btcruby/transaction.rb
|