bitcoinrb 1.5.0 → 1.7.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -1
  3. data/README.md +7 -5
  4. data/lib/bitcoin/block.rb +27 -0
  5. data/lib/bitcoin/descriptor/addr.rb +31 -0
  6. data/lib/bitcoin/descriptor/checksum.rb +74 -0
  7. data/lib/bitcoin/descriptor/combo.rb +30 -0
  8. data/lib/bitcoin/descriptor/expression.rb +122 -0
  9. data/lib/bitcoin/descriptor/key_expression.rb +30 -0
  10. data/lib/bitcoin/descriptor/multi.rb +49 -0
  11. data/lib/bitcoin/descriptor/multi_a.rb +43 -0
  12. data/lib/bitcoin/descriptor/pk.rb +27 -0
  13. data/lib/bitcoin/descriptor/pkh.rb +15 -0
  14. data/lib/bitcoin/descriptor/raw.rb +32 -0
  15. data/lib/bitcoin/descriptor/raw_tr.rb +20 -0
  16. data/lib/bitcoin/descriptor/script_expression.rb +24 -0
  17. data/lib/bitcoin/descriptor/sh.rb +31 -0
  18. data/lib/bitcoin/descriptor/sorted_multi.rb +15 -0
  19. data/lib/bitcoin/descriptor/sorted_multi_a.rb +15 -0
  20. data/lib/bitcoin/descriptor/tr.rb +91 -0
  21. data/lib/bitcoin/descriptor/wpkh.rb +19 -0
  22. data/lib/bitcoin/descriptor/wsh.rb +30 -0
  23. data/lib/bitcoin/descriptor.rb +186 -100
  24. data/lib/bitcoin/key.rb +1 -1
  25. data/lib/bitcoin/message_sign.rb +2 -1
  26. data/lib/bitcoin/script/script.rb +8 -3
  27. data/lib/bitcoin/script_witness.rb +1 -0
  28. data/lib/bitcoin/secp256k1/native.rb +1 -1
  29. data/lib/bitcoin/silent_payment.rb +5 -0
  30. data/lib/bitcoin/sp/addr.rb +55 -0
  31. data/lib/bitcoin/taproot/custom_depth_builder.rb +64 -0
  32. data/lib/bitcoin/taproot/simple_builder.rb +1 -6
  33. data/lib/bitcoin/taproot.rb +1 -0
  34. data/lib/bitcoin/tx.rb +14 -1
  35. data/lib/bitcoin/version.rb +1 -1
  36. data/lib/bitcoin.rb +1 -0
  37. metadata +23 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 900806837ee6cd6b011a50d6a58c37ea233079e8b86a594c6a5031c338f9a490
4
- data.tar.gz: 34b437fee384d630d9b7f1a47faa8175692d9ebe21408d4720828140461b152c
3
+ metadata.gz: 81a899ff49ba8888d3479e7f0c93c2124098c07c68dfd293349881d724c9393b
4
+ data.tar.gz: '052092011357a7abb68ec74bbbd5939b74d75aa83d5561ce088fc80c152b3341'
5
5
  SHA512:
6
- metadata.gz: 87bcfdb59403593475f90b49f9f67299bee3bfd8cfae7089080e95ab8e1ffddcdc21ebbb72f8ca49f1caea9d8df2ea5ca613eb97b698ddab3fb3d3c617611165
7
- data.tar.gz: 55dcbb9ce3f10a5dd822e98605eb00fa74f3034a8852cdcd714b4dff5cd347cbdc55bffd49ae2f74cc4aeaad2bf86d01d635e7146207ccb6557f55ae0849ff0e
6
+ metadata.gz: ce9d912ba951f85c95dd30b29f1f2d0330a168e6fdd119296186fc6e651ba92ab838c90b7d31cf1a99e17def444c6bcb9784eff6ceab0816b210428fbc77b36a
7
+ data.tar.gz: bc9f7c2e34f9f47def957b549e5c33f5a4940fb56ad2fb379f717811aedd0b5f1cc4089c0f67ff7ad1363eb9a7d7b4a4ea4405124a06b20a1674d02802720cfe
@@ -22,7 +22,7 @@ jobs:
22
22
  ruby-version: ['3.0', '3.1', '3.2', '3.3']
23
23
 
24
24
  steps:
25
- - uses: actions/checkout@v2
25
+ - uses: actions/checkout@v4
26
26
  - name: Install leveldb
27
27
  run: sudo apt-get install libleveldb-dev
28
28
  - name: Set up Ruby
data/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  # Bitcoinrb [![Build Status](https://github.com/chaintope/bitcoinrb/actions/workflows/ruby.yml/badge.svg?branch=master)](https://github.com/chaintope/bitcoinrb/actions/workflows/ruby.yml) [![Gem Version](https://badge.fury.io/rb/bitcoinrb.svg)](https://badge.fury.io/rb/bitcoinrb) [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE) <img src="http://segwit.co/static/public/images/logo.png" width="100">
2
2
 
3
-
4
3
  Bitcoinrb is a Ruby implementation of Bitcoin Protocol.
5
4
 
6
5
  NOTE: Bitcoinrb work in progress, and there is a possibility of incompatible change.
@@ -18,10 +17,9 @@ Bitcoinrb supports following feature:
18
17
  * bech32([BIP-173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)) and bech32m([BIP-350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki)) address support
19
18
  * [BIP-174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) PSBT(Partially Signed Bitcoin Transaction) support
20
19
  * [BIP-85](https://github.com/bitcoin/bips/blob/master/bip-0085.mediawiki) Deterministic Entropy From BIP32 Keychains support by `Bitcoin::BIP85Entropy` class.
21
- * Schnorr signature([BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki))
22
- * Taproot consensus([BIP-341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki) and [BIP-342](https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki))
23
- * [WIP] SPV node
24
- * [WIP] 0ff-chain protocol
20
+ * Schnorr signature([BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki))
21
+ * Taproot consensus([BIP-341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki) and [BIP-342](https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki))
22
+ * [Output script descriptor](https://github.com/chaintope/bitcoinrb/wiki/Output-Script-Descriptor) ([BIP-380](https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki), [BIP-381](https://github.com/bitcoin/bips/blob/master/bip-0381.mediawiki), [BIP-382](https://github.com/bitcoin/bips/blob/master/bip-0382.mediawiki), [BIP-383](https://github.com/bitcoin/bips/blob/master/bip-0383.mediawiki), [BIP-384](https://github.com/bitcoin/bips/blob/master/bip-0384.mediawiki), [BIP-385](https://github.com/bitcoin/bips/blob/master/bip-0385.mediawiki), [BIP-386](https://github.com/bitcoin/bips/blob/master/bip-0386.mediawiki), [BIP-387](https://github.com/bitcoin/bips/blob/master/bip-0387.mediawiki))
25
23
 
26
24
  ## Requirements
27
25
 
@@ -112,6 +110,10 @@ Therefore, some tests require this library. In a Linux environment, `spec/lib/li
112
110
  so there is no need to do anything. If you want to test in another environment,
113
111
  please set the library path in the environment variable `TEST_LIBSECP256K1_PATH`.
114
112
 
113
+ In case the supplied linux `spec/lib/libsecp256k1.so` is not working (architecture), you might have to compile it yourself.
114
+ Since if available in the repository, it might not be compiled using the `./configure --enable-module-recovery` option.
115
+ Then `TEST_LIBSECP256K1_PATH=/path/to/secp256k1/.libs/libsecp256k1.so rspec` can be used.
116
+
115
117
  The libsecp256k1 library currently tested for operation with this library is `v0.4.0`.
116
118
 
117
119
  ## Contributing
data/lib/bitcoin/block.rb CHANGED
@@ -4,11 +4,38 @@ module Bitcoin
4
4
  attr_accessor :header
5
5
  attr_accessor :transactions
6
6
 
7
+ # Constructor
8
+ # @param [Bitcoin::BlockHeader] header
9
+ # @param [Array] transactions An array of transaction.
10
+ # @raise [ArgumentError]
7
11
  def initialize(header, transactions = [])
12
+ raise ArgumentError, "header must be Bitcoin::BlockHeader." unless header.is_a?(Bitcoin::BlockHeader)
13
+ raise ArgumentError, "transactions must be an Array." unless transactions.is_a?(Array)
8
14
  @header = header
9
15
  @transactions = transactions
10
16
  end
11
17
 
18
+ # Create genesis block.
19
+ # @param [String] msg Message embedded in coinbase transaction.
20
+ # @param [Bitcoin::Script] script Coinbase transaction scriptPubkey.
21
+ # @param [Integer] time Block time.
22
+ # @param [Integer] nonce nonce.
23
+ # @param [Integer] bits nBits
24
+ # @param [Integer] version nVersion.
25
+ # @param [Integer] rewards Block rewards(satoshi).
26
+ def self.create_genesis(msg, script, time, nonce, bits, version, rewards = 50 * 100000000)
27
+ coinbase = Bitcoin::Tx.create_coinbase(msg, script, rewards)
28
+ header = BlockHeader.new(
29
+ version,
30
+ '00' * 32,
31
+ MerkleTree.build_from_leaf([coinbase.txid]).merkle_root.rhex,
32
+ time,
33
+ bits,
34
+ nonce
35
+ )
36
+ Block.new(header, [coinbase])
37
+ end
38
+
12
39
  def self.parse_from_payload(payload)
13
40
  Bitcoin::Message::Block.parse_from_payload(payload).to_block
14
41
  end
@@ -0,0 +1,31 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ class Addr < Expression
4
+ include Bitcoin::Util
5
+
6
+ attr_reader :addr
7
+
8
+ def initialize(addr)
9
+ raise ArgumentError, "Address must be string." unless addr.is_a?(String)
10
+ raise ArgumentError, "Address is not valid." unless valid_address?(addr)
11
+ @addr = addr
12
+ end
13
+
14
+ def type
15
+ :addr
16
+ end
17
+
18
+ def to_script
19
+ Bitcoin::Script.parse_from_addr(addr)
20
+ end
21
+
22
+ def top_level?
23
+ true
24
+ end
25
+
26
+ def args
27
+ addr
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,74 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ # Descriptor checksum.
4
+ # https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki#checksum
5
+ module Checksum
6
+
7
+ INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ "
8
+ CHECKSUM_CHARSET = Bech32::CHARSET
9
+ GENERATOR = [0xF5DEE51989, 0xA9FDCA3312, 0x1BAB10E32D, 0x3706B1677A, 0x644D626FFD]
10
+
11
+ module_function
12
+
13
+ # Verify that the checksum is correct in a descriptor
14
+ # @param [String] s Descriptor string.
15
+ # @return [Boolean]
16
+ def descsum_check(s)
17
+ return false unless s[-9] == '#'
18
+ s[-8..-1].each_char do |c|
19
+ return false unless CHECKSUM_CHARSET.include?(c)
20
+ end
21
+ symbols = descsum_expand(s[0...-9]) + s[-8..-1].each_char.map{|c|CHECKSUM_CHARSET.index(c)}
22
+ descsum_polymod(symbols) == 1
23
+ end
24
+
25
+ # Add a checksum to a descriptor without
26
+ # @param [String] s Descriptor string without checksum.
27
+ # @return [String] Descriptor string with checksum.
28
+ def descsum_create(s)
29
+ symbols = descsum_expand(s) + [0, 0, 0, 0, 0, 0, 0, 0]
30
+ checksum = descsum_polymod(symbols) ^ 1
31
+ result = 8.times.map do |i|
32
+ CHECKSUM_CHARSET[(checksum >> (5 * (7 - i))) & 31]
33
+ end.join
34
+ "#{s}##{result}"
35
+ end
36
+
37
+ # Internal function that does the character to symbol expansion.
38
+ # @param [String] s Descriptor string without checksum.
39
+ # @return [Array] symbols. An array of integer.
40
+ def descsum_expand(s)
41
+ groups = []
42
+ symbols = []
43
+ s.each_char do |c|
44
+ return nil unless INPUT_CHARSET.include?(c)
45
+ v = INPUT_CHARSET.index(c)
46
+ symbols << (v & 31)
47
+ groups << (v >> 5)
48
+ if groups.length == 3
49
+ symbols << (groups[0] * 9 + groups[1] * 3 + groups[2])
50
+ groups = []
51
+ end
52
+ end
53
+ symbols << groups[0] if groups.length == 1
54
+ symbols << (groups[0] * 3 + groups[1]) if groups.length == 2
55
+ symbols
56
+ end
57
+
58
+ # Internal function that computes the descriptor checksum.
59
+ # @param [Array] symbols
60
+ # @return [Integer]
61
+ def descsum_polymod(symbols)
62
+ chk = 1
63
+ symbols.each do |value|
64
+ top = chk >> 35
65
+ chk = (chk & 0x7FFFFFFFF) << 5 ^ value
66
+ 5.times do |i|
67
+ chk ^= GENERATOR[i] if ((top >> i) & 1) == 1
68
+ end
69
+ end
70
+ chk
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,30 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ # combo() expression
4
+ class Combo < KeyExpression
5
+
6
+ def type
7
+ :combo
8
+ end
9
+
10
+ def to_scripts
11
+ candidates = [Pk.new(key), Pkh.new(key)]
12
+ pubkey = extracted_key
13
+ if pubkey.compressed?
14
+ candidates << Wpkh.new(pubkey.pubkey)
15
+ candidates << Sh.new(candidates.last)
16
+ end
17
+ candidates.map(&:to_script)
18
+ end
19
+
20
+ def ==(other)
21
+ return false unless other.is_a?(Combo)
22
+ type == other.type && to_scripts == other.to_scripts
23
+ end
24
+
25
+ def top_level?
26
+ true
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,122 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ # Expression for descriptor.
4
+ class Expression
5
+
6
+ # Get expression type.
7
+ # @return [Symbol]
8
+ def type
9
+ raise NotImplementedError
10
+ end
11
+
12
+ # Convert to bitcoin script
13
+ # @return [Bitcoin::Script]
14
+ def to_script
15
+ raise NotImplementedError
16
+ end
17
+
18
+ # Whether this is top level or not.
19
+ # @return [Boolean]
20
+ def top_level?
21
+ raise NotImplementedError
22
+ end
23
+
24
+ # Get args for this expression.
25
+ # @return [String] args
26
+ def args
27
+ raise NotImplementedError
28
+ end
29
+
30
+ # Get descriptor string.
31
+ # @param [Boolean] checksum If true, append checksum.
32
+ # @return [String] Descriptor string.
33
+ def to_s(checksum: false)
34
+ desc = "#{type.to_s}(#{args})"
35
+ checksum ? Checksum.descsum_create(desc) : desc
36
+ end
37
+
38
+ # Convert to bitcoin script as hex string.
39
+ # @return [String]
40
+ def to_hex
41
+ to_script.to_hex
42
+ end
43
+
44
+ # Check whether +key+ is compressed public key or not.
45
+ # @return [Boolean]
46
+ def compressed_key?(key)
47
+ %w(02 03).include?(key[0..1]) && [key].pack("H*").bytesize == 33
48
+ end
49
+
50
+ # Extract public key from KEY format.
51
+ # @param [String] key KEY string.
52
+ # @return [Bitcoin::Key] public key.
53
+ def extract_pubkey(key)
54
+ if key.start_with?('[') # BIP32 fingerprint
55
+ raise ArgumentError, "Multiple ']' characters found for a single pubkey." if key.count('[') > 1 || key.count(']') > 1
56
+ info = key[1...key.index(']')]
57
+ fingerprint, *paths = info.split('/')
58
+ raise ArgumentError, "Fingerprint '#{fingerprint}' is not hex." unless fingerprint.valid_hex?
59
+ raise ArgumentError, "Fingerprint '#{fingerprint}' is not 4 bytes." unless fingerprint.size == 8
60
+ key = key[(key.index(']') + 1)..-1]
61
+ else
62
+ raise ArgumentError, 'Invalid key origin.' if key.include?(']')
63
+ end
64
+
65
+ # check BIP32 derivation path
66
+ key, *paths = key.split('/')
67
+
68
+ raise ArgumentError, "No key provided." unless key
69
+
70
+ if key.start_with?('xprv')
71
+ key = Bitcoin::ExtKey.from_base58(key)
72
+ key = derive_path(key, paths) if paths
73
+ elsif key.start_with?('xpub')
74
+ key = Bitcoin::ExtPubkey.from_base58(key)
75
+ key = derive_path(key, paths) if paths
76
+ else
77
+ begin
78
+ key = Bitcoin::Key.from_wif(key)
79
+ rescue ArgumentError
80
+ key = if key.length == 64
81
+ Bitcoin::Key.from_xonly_pubkey(key)
82
+ else
83
+ key_type = compressed_key?(key) ? Bitcoin::Key::TYPES[:compressed] : Bitcoin::Key::TYPES[:uncompressed]
84
+ Bitcoin::Key.new(pubkey: key, key_type: key_type)
85
+ end
86
+ end
87
+ end
88
+ key = key.is_a?(Bitcoin::Key) ? key : key.key
89
+ raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless key.fully_valid_pubkey?
90
+ key
91
+ end
92
+
93
+ # Derive key using +paths+.
94
+ # @param [Bitcoin::ExtKey or Bitcoin::ExtPubkey] key
95
+ # @param [String] paths derivation path.
96
+ # @return [Bitcoin::Key]
97
+ def derive_path(key, paths)
98
+ is_private = key.is_a?(Bitcoin::ExtKey)
99
+ paths.each do |path|
100
+ raise ArgumentError, 'xpub can not derive hardened key.' if !is_private && path.end_with?("'")
101
+ if is_private
102
+ hardened = path.end_with?("'")
103
+ path = hardened ? path[0..-2] : path
104
+ raise ArgumentError, 'Key path value is not a valid value.' unless path =~ /^[0-9]+$/
105
+ raise ArgumentError, 'Key path value is out of range.' if !hardened && path.to_i >= Bitcoin::HARDENED_THRESHOLD
106
+ key = key.derive(path.to_i, hardened)
107
+ else
108
+ raise ArgumentError, 'Key path value is not a valid value.' unless path =~ /^[0-9]+$/
109
+ raise ArgumentError, 'Key path value is out of range.' if path.to_i >= Bitcoin::HARDENED_THRESHOLD
110
+ key = key.derive(path.to_i)
111
+ end
112
+ end
113
+ key
114
+ end
115
+
116
+ def ==(other)
117
+ return false unless other.is_a?(Expression)
118
+ type == other.type && to_script == other.to_script
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,30 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ class KeyExpression < Expression
4
+ attr_reader :key
5
+
6
+ # Constructor
7
+ # @raise [ArgumentError] If +key+ is invalid.
8
+ def initialize(key)
9
+ raise ArgumentError, "Key must be string." unless key.is_a? String
10
+ extract_pubkey(key)
11
+ @key = key
12
+ end
13
+
14
+ def args
15
+ key
16
+ end
17
+
18
+ def top_level?
19
+ false
20
+ end
21
+
22
+ # Get extracted key.
23
+ # @return [Bitcoin::Key] Extracted key.
24
+ def extracted_key
25
+ extract_pubkey(key)
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,49 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ # multi() expression
4
+ class Multi < Expression
5
+
6
+ attr_reader :threshold
7
+ attr_reader :keys
8
+
9
+ def initialize(threshold, keys)
10
+ validate!(threshold, keys)
11
+ @threshold = threshold
12
+ @keys = keys
13
+ end
14
+
15
+ def type
16
+ :multi
17
+ end
18
+
19
+ def to_script
20
+ Script.to_multisig_script(threshold, keys.map{|key| extract_pubkey(key).pubkey }, sort: false)
21
+ end
22
+
23
+ def to_hex
24
+ result = to_script
25
+ pubkey_count = result.get_pubkeys.length
26
+ raise RuntimeError, "Cannot have #{pubkey_count} pubkeys in bare multisig; only at most 3 pubkeys." if pubkey_count > 3
27
+ result.to_hex
28
+ end
29
+
30
+ def args
31
+ "#{threshold},#{keys.join(',')}"
32
+ end
33
+
34
+ def top_level?
35
+ false
36
+ end
37
+
38
+ private
39
+
40
+ def validate!(threshold, keys)
41
+ raise ArgumentError, "Multisig threshold '#{threshold}' is not valid." unless threshold.is_a?(Integer)
42
+ raise ArgumentError, 'Multisig threshold cannot be 0, must be at least 1.' unless threshold > 0
43
+ raise ArgumentError, 'Multisig threshold cannot be larger than the number of keys.' if threshold > keys.size
44
+ raise ArgumentError, "Multisig must have between 1 and #{Bitcoin::MAX_PUBKEYS_PER_MULTISIG} keys, inclusive." if keys.size > Bitcoin::MAX_PUBKEYS_PER_MULTISIG
45
+ keys.each{|key| extract_pubkey(key) }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,43 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ # multi_a() expression
4
+ # @see https://github.com/bitcoin/bips/blob/master/bip-0387.mediawiki
5
+ class MultiA < Multi
6
+ include Bitcoin::Opcodes
7
+
8
+ def type
9
+ :multi_a
10
+ end
11
+
12
+ def to_hex
13
+ raise RuntimeError, "Can only have multi_a/sortedmulti_a inside tr()."
14
+ end
15
+
16
+ def to_script
17
+ multisig_script(keys.map{|k| extract_pubkey(k).xonly_pubkey})
18
+ end
19
+
20
+ private
21
+
22
+ def multisig_script(keys)
23
+ script = Bitcoin::Script.new
24
+ keys.each.with_index do |k, i|
25
+ script << k
26
+ script << (i == 0 ? OP_CHECKSIG : OP_CHECKSIGADD)
27
+ end
28
+ script << threshold << OP_NUMEQUAL
29
+ end
30
+
31
+ def validate!(threshold, keys)
32
+ raise ArgumentError, "Multisig threshold '#{threshold}' is not valid." unless threshold.is_a?(Integer)
33
+ raise ArgumentError, 'Multisig threshold cannot be 0, must be at least 1.' unless threshold > 0
34
+ raise ArgumentError, 'Multisig threshold cannot be larger than the number of keys.' if threshold > keys.size
35
+ raise ArgumentError, "Multisig must have between 1 and 999 keys, inclusive." if keys.size > 999
36
+ keys.each do |key|
37
+ k = extract_pubkey(key)
38
+ raise ArgumentError, "Uncompressed key are not allowed." unless k.compressed?
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,27 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ # pk() expression
4
+ class Pk < KeyExpression
5
+ include Bitcoin::Opcodes
6
+
7
+ attr_accessor :xonly
8
+
9
+ def initialize(key)
10
+ super(key)
11
+ @xonly = false
12
+ end
13
+
14
+ def type
15
+ :pk
16
+ end
17
+
18
+ # Convert to bitcoin script.
19
+ # @return [Bitcoin::Script]
20
+ def to_script
21
+ k = extracted_key
22
+ target_key = xonly ? k.xonly_pubkey : k.pubkey
23
+ Bitcoin::Script.new << target_key << OP_CHECKSIG
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ # pkh() expression
4
+ class Pkh < KeyExpression
5
+
6
+ def type
7
+ :pkh
8
+ end
9
+
10
+ def to_script
11
+ Script.to_p2pkh(extracted_key.hash160)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,32 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ class Raw < Expression
4
+
5
+ attr_reader :hex
6
+
7
+ # Constructor
8
+ # @param [String] hex
9
+ def initialize(hex)
10
+ raise ArgumentError, "Raw script must be string." unless hex.is_a?(String)
11
+ raise ArgumentError, "Raw script is not hex." unless hex.valid_hex?
12
+ @hex = hex
13
+ end
14
+
15
+ def type
16
+ :raw
17
+ end
18
+
19
+ def to_script
20
+ Bitcoin::Script.parse_from_payload(hex.htb)
21
+ end
22
+
23
+ def args
24
+ hex
25
+ end
26
+
27
+ def top_level?
28
+ true
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ # rawtr() expression
4
+ class RawTr < KeyExpression
5
+ include Bitcoin::Opcodes
6
+
7
+ def type
8
+ :rawtr
9
+ end
10
+
11
+ def top_level?
12
+ true
13
+ end
14
+
15
+ def to_script
16
+ Bitcoin::Script.new << OP_1 << extract_pubkey(key).xonly_pubkey
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ class ScriptExpression < Expression
4
+
5
+ attr_reader :script
6
+
7
+ def initialize(script)
8
+ validate!(script)
9
+ @script = script
10
+ end
11
+
12
+ def args
13
+ script.to_s
14
+ end
15
+
16
+ private
17
+
18
+ def validate!(script)
19
+ raise ArgumentError, "Can only have #{script.type.to_s}() at top level." if script.is_a?(Expression) && script.top_level?
20
+ raise ArgumentError, 'Can only have multi_a/sortedmulti_a inside tr().' if script.is_a?(MultiA) || script.is_a?(SortedMultiA)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ # sh expression
4
+ class Sh < ScriptExpression
5
+
6
+ def type
7
+ :sh
8
+ end
9
+
10
+ def to_script
11
+ script.to_script.to_p2sh
12
+ end
13
+
14
+ def top_level?
15
+ true
16
+ end
17
+
18
+ private
19
+
20
+ def validate!(script)
21
+ super(script)
22
+ raise ArgumentError, 'A function is needed within P2SH.' unless script.is_a?(Expression)
23
+ script_size = script.to_script.size
24
+ if script_size > Bitcoin::MAX_SCRIPT_ELEMENT_SIZE
25
+ raise ArgumentError,
26
+ "P2SH script is too large, #{script_size} bytes is larger than #{Bitcoin::MAX_SCRIPT_ELEMENT_SIZE} bytes."
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ # sortedmulti() expression
4
+ class SortedMulti < Multi
5
+
6
+ def type
7
+ :sortedmulti
8
+ end
9
+
10
+ def to_script
11
+ Script.to_multisig_script(threshold, keys.map{|key| extract_pubkey(key).pubkey }, sort: true)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Bitcoin
2
+ module Descriptor
3
+ # sortedmulti_a expression
4
+ # @see https://github.com/bitcoin/bips/blob/master/bip-0387.mediawiki
5
+ class SortedMultiA < MultiA
6
+ def type
7
+ :sortedmulti_a
8
+ end
9
+
10
+ def to_script
11
+ multisig_script( keys.map{|k| extract_pubkey(k).xonly_pubkey}.sort)
12
+ end
13
+ end
14
+ end
15
+ end