bitcoinrb 1.1.1 → 1.2.1
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/bitcoinrb.gemspec +1 -1
 - data/lib/bitcoin/block_header.rb +5 -1
 - data/lib/bitcoin/constants.rb +2 -0
 - data/lib/bitcoin/key.rb +49 -4
 - data/lib/bitcoin/out_point.rb +9 -0
 - data/lib/bitcoin/psbt/input.rb +44 -0
 - data/lib/bitcoin/psbt/output.rb +18 -0
 - data/lib/bitcoin/psbt.rb +9 -0
 - data/lib/bitcoin/script/script.rb +13 -0
 - data/lib/bitcoin/script/script_interpreter.rb +11 -21
 - data/lib/bitcoin/secp256k1/native.rb +1 -1
 - data/lib/bitcoin/secp256k1/ruby.rb +1 -1
 - data/lib/bitcoin/taproot/control_block.rb +47 -0
 - data/lib/bitcoin/taproot/simple_builder.rb +3 -3
 - data/lib/bitcoin/taproot.rb +1 -0
 - data/lib/bitcoin/tx_in.rb +1 -2
 - data/lib/bitcoin/version.rb +1 -1
 - metadata +8 -7
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 3c9473a6381568e857b55cadaaad942065a2d9189fab7da542248c9e998d528e
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 34b3ebf0c5826edf8241a1294aaa6662bdf1e92973968351ecb2a9d24eeb8402
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: eb63914be0f81eb46e76d6c7fb2bbc9e13db948da8c757fd7c9a7235918faff82b6cd770620a4096480b1d183495f0a930dcdd55aeb014484368b2b1ee91d660
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 696e9d8081863c22c0b3322d32f4cca5200ceaf7553f8a97226b4696a04ee3a091ed384bdef6b28b573f4d9d81b80d37a0d65168d7eb86570df9325232ac1663
         
     | 
    
        data/bitcoinrb.gemspec
    CHANGED
    
    | 
         @@ -23,7 +23,7 @@ Gem::Specification.new do |spec| 
     | 
|
| 
       23 
23 
     | 
    
         
             
              spec.add_runtime_dependency 'ecdsa'
         
     | 
| 
       24 
24 
     | 
    
         
             
              spec.add_runtime_dependency 'eventmachine'
         
     | 
| 
       25 
25 
     | 
    
         
             
              spec.add_runtime_dependency 'murmurhash3'
         
     | 
| 
       26 
     | 
    
         
            -
              spec.add_runtime_dependency 'bech32', ' 
     | 
| 
      
 26 
     | 
    
         
            +
              spec.add_runtime_dependency 'bech32', '>= 1.3.0'
         
     | 
| 
       27 
27 
     | 
    
         
             
              spec.add_runtime_dependency 'daemon-spawn'
         
     | 
| 
       28 
28 
     | 
    
         
             
              spec.add_runtime_dependency 'thor'
         
     | 
| 
       29 
29 
     | 
    
         
             
              spec.add_runtime_dependency 'ffi'
         
     | 
    
        data/lib/bitcoin/block_header.rb
    CHANGED
    
    | 
         @@ -21,8 +21,12 @@ module Bitcoin 
     | 
|
| 
       21 
21 
     | 
    
         
             
                  @nonce = nonce
         
     | 
| 
       22 
22 
     | 
    
         
             
                end
         
     | 
| 
       23 
23 
     | 
    
         | 
| 
      
 24 
     | 
    
         
            +
                # Parse block header from payload.
         
     | 
| 
      
 25 
     | 
    
         
            +
                # @param [String|StringIO] payload block header paylaod
         
     | 
| 
      
 26 
     | 
    
         
            +
                # @return [Bitcoin::BlockHeader]
         
     | 
| 
       24 
27 
     | 
    
         
             
                def self.parse_from_payload(payload)
         
     | 
| 
       25 
     | 
    
         
            -
                   
     | 
| 
      
 28 
     | 
    
         
            +
                  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
         
     | 
| 
      
 29 
     | 
    
         
            +
                  version, prev_hash, merkle_root, time, bits, nonce = buf.read(80).unpack('Va32a32VVV')
         
     | 
| 
       26 
30 
     | 
    
         
             
                  new(version, prev_hash.bth, merkle_root.bth, time, bits, nonce)
         
     | 
| 
       27 
31 
     | 
    
         
             
                end
         
     | 
| 
       28 
32 
     | 
    
         | 
    
        data/lib/bitcoin/constants.rb
    CHANGED
    
    | 
         @@ -11,6 +11,8 @@ module Bitcoin 
     | 
|
| 
       11 
11 
     | 
    
         
             
              HASH160_SIZE = 20
         
     | 
| 
       12 
12 
     | 
    
         
             
              # Byte size of the HASH256 hash
         
     | 
| 
       13 
13 
     | 
    
         
             
              HASH256_SIZE = 32
         
     | 
| 
      
 14 
     | 
    
         
            +
              # Byte size of x-only public key
         
     | 
| 
      
 15 
     | 
    
         
            +
              X_ONLY_PUBKEY_SIZE = 32
         
     | 
| 
       14 
16 
     | 
    
         | 
| 
       15 
17 
     | 
    
         
             
              # The maximum allowed size for a serialized block, in bytes (only for buffer size limits)
         
     | 
| 
       16 
18 
     | 
    
         
             
              MAX_BLOCK_SERIALIZED_SIZE = 4_000_000
         
     | 
    
        data/lib/bitcoin/key.rb
    CHANGED
    
    | 
         @@ -17,7 +17,7 @@ module Bitcoin 
     | 
|
| 
       17 
17 
     | 
    
         
             
                attr_accessor :key_type
         
     | 
| 
       18 
18 
     | 
    
         
             
                attr_reader :secp256k1_module
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                TYPES = {uncompressed: 0x00, compressed: 0x01, p2pkh: 0x10, p2wpkh: 0x11, p2wpkh_p2sh: 0x12}
         
     | 
| 
      
 20 
     | 
    
         
            +
                TYPES = {uncompressed: 0x00, compressed: 0x01, p2pkh: 0x10, p2wpkh: 0x11, p2wpkh_p2sh: 0x12, p2tr: 0x13}
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
                MIN_PRIV_KEY_MOD_ORDER = 0x01
         
     | 
| 
       23 
23 
     | 
    
         
             
                # Order of secp256k1's generator minus 1.
         
     | 
| 
         @@ -81,7 +81,7 @@ module Bitcoin 
     | 
|
| 
       81 
81 
     | 
    
         
             
                # @param [String] xonly_pubkey xonly public key with hex format.
         
     | 
| 
       82 
82 
     | 
    
         
             
                # @return [Bitcoin::Key] key object has public key.
         
     | 
| 
       83 
83 
     | 
    
         
             
                def self.from_xonly_pubkey(xonly_pubkey)
         
     | 
| 
       84 
     | 
    
         
            -
                  raise ArgumentError,  
     | 
| 
      
 84 
     | 
    
         
            +
                  raise ArgumentError, "xonly_pubkey must be #{X_ONLY_PUBKEY_SIZE} bytes" unless xonly_pubkey.htb.bytesize == X_ONLY_PUBKEY_SIZE
         
     | 
| 
       85 
85 
     | 
    
         
             
                  Bitcoin::Key.new(pubkey: "02#{xonly_pubkey}", key_type: TYPES[:compressed])
         
     | 
| 
       86 
86 
     | 
    
         
             
                end
         
     | 
| 
       87 
87 
     | 
    
         | 
| 
         @@ -180,23 +180,68 @@ module Bitcoin 
     | 
|
| 
       180 
180 
     | 
    
         
             
                end
         
     | 
| 
       181 
181 
     | 
    
         | 
| 
       182 
182 
     | 
    
         
             
                # get pay to pubkey hash address
         
     | 
| 
       183 
     | 
    
         
            -
                # @ 
     | 
| 
      
 183 
     | 
    
         
            +
                # @return [String] address
         
     | 
| 
       184 
184 
     | 
    
         
             
                def to_p2pkh
         
     | 
| 
       185 
185 
     | 
    
         
             
                  Bitcoin::Script.to_p2pkh(hash160).to_addr
         
     | 
| 
       186 
186 
     | 
    
         
             
                end
         
     | 
| 
       187 
187 
     | 
    
         | 
| 
       188 
188 
     | 
    
         
             
                # get pay to witness pubkey hash address
         
     | 
| 
       189 
     | 
    
         
            -
                # @ 
     | 
| 
      
 189 
     | 
    
         
            +
                # @return [String] address
         
     | 
| 
       190 
190 
     | 
    
         
             
                def to_p2wpkh
         
     | 
| 
       191 
191 
     | 
    
         
             
                  Bitcoin::Script.to_p2wpkh(hash160).to_addr
         
     | 
| 
       192 
192 
     | 
    
         
             
                end
         
     | 
| 
       193 
193 
     | 
    
         | 
| 
      
 194 
     | 
    
         
            +
                # Get pay to taproot address
         
     | 
| 
      
 195 
     | 
    
         
            +
                # @return [String] address
         
     | 
| 
      
 196 
     | 
    
         
            +
                def to_p2tr
         
     | 
| 
      
 197 
     | 
    
         
            +
                  Bitcoin::Script.to_p2tr(self).to_addr
         
     | 
| 
      
 198 
     | 
    
         
            +
                end
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
       194 
200 
     | 
    
         
             
                # get p2wpkh address nested in p2sh.
         
     | 
| 
       195 
201 
     | 
    
         
             
                # @deprecated
         
     | 
| 
       196 
202 
     | 
    
         
             
                def to_nested_p2wpkh
         
     | 
| 
       197 
203 
     | 
    
         
             
                  Bitcoin::Script.to_p2wpkh(hash160).to_p2sh.to_addr
         
     | 
| 
       198 
204 
     | 
    
         
             
                end
         
     | 
| 
       199 
205 
     | 
    
         | 
| 
      
 206 
     | 
    
         
            +
                # Determine if it is a P2PKH from key_type.
         
     | 
| 
      
 207 
     | 
    
         
            +
                # @return [Boolean]
         
     | 
| 
      
 208 
     | 
    
         
            +
                def p2pkh?
         
     | 
| 
      
 209 
     | 
    
         
            +
                  [TYPES[:uncompressed], TYPES[:compressed], TYPES[:p2pkh]].include?(key_type)
         
     | 
| 
      
 210 
     | 
    
         
            +
                end
         
     | 
| 
      
 211 
     | 
    
         
            +
             
     | 
| 
      
 212 
     | 
    
         
            +
                # Determine if it is a P2WPKH from key_type.
         
     | 
| 
      
 213 
     | 
    
         
            +
                # @return [Boolean]
         
     | 
| 
      
 214 
     | 
    
         
            +
                def p2wpkh?
         
     | 
| 
      
 215 
     | 
    
         
            +
                  key_type == TYPES[:p2wpkh]
         
     | 
| 
      
 216 
     | 
    
         
            +
                end
         
     | 
| 
      
 217 
     | 
    
         
            +
             
     | 
| 
      
 218 
     | 
    
         
            +
                # Determine if it is a nested P2WPKH from key_type.
         
     | 
| 
      
 219 
     | 
    
         
            +
                # @return [Boolean]
         
     | 
| 
      
 220 
     | 
    
         
            +
                def nested_p2wpkh?
         
     | 
| 
      
 221 
     | 
    
         
            +
                  key_type == TYPES[:p2wpkh_p2sh]
         
     | 
| 
      
 222 
     | 
    
         
            +
                end
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
      
 224 
     | 
    
         
            +
                # Determine if it is a nested P2TR from key_type.
         
     | 
| 
      
 225 
     | 
    
         
            +
                # @return [Boolean]
         
     | 
| 
      
 226 
     | 
    
         
            +
                def p2tr?
         
     | 
| 
      
 227 
     | 
    
         
            +
                  key_type == TYPES[:p2tr]
         
     | 
| 
      
 228 
     | 
    
         
            +
                end
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
                # Returns the address corresponding to key_type.
         
     | 
| 
      
 231 
     | 
    
         
            +
                # @return [String] address
         
     | 
| 
      
 232 
     | 
    
         
            +
                def to_addr
         
     | 
| 
      
 233 
     | 
    
         
            +
                  case key_type
         
     | 
| 
      
 234 
     | 
    
         
            +
                  when TYPES[:uncompressed], TYPES[:compressed], TYPES[:p2pkh]
         
     | 
| 
      
 235 
     | 
    
         
            +
                    to_p2pkh
         
     | 
| 
      
 236 
     | 
    
         
            +
                  when TYPES[:p2wpkh]
         
     | 
| 
      
 237 
     | 
    
         
            +
                    to_p2wpkh
         
     | 
| 
      
 238 
     | 
    
         
            +
                  when TYPES[:p2wpkh_p2sh]
         
     | 
| 
      
 239 
     | 
    
         
            +
                    to_nested_p2wpkh
         
     | 
| 
      
 240 
     | 
    
         
            +
                  when TYPES[:p2tr]
         
     | 
| 
      
 241 
     | 
    
         
            +
                    to_p2tr
         
     | 
| 
      
 242 
     | 
    
         
            +
                  end
         
     | 
| 
      
 243 
     | 
    
         
            +
                end
         
     | 
| 
      
 244 
     | 
    
         
            +
             
     | 
| 
       200 
245 
     | 
    
         
             
                def compressed?
         
     | 
| 
       201 
246 
     | 
    
         
             
                  key_type != TYPES[:uncompressed]
         
     | 
| 
       202 
247 
     | 
    
         
             
                end
         
     | 
    
        data/lib/bitcoin/out_point.rb
    CHANGED
    
    | 
         @@ -20,6 +20,15 @@ module Bitcoin 
     | 
|
| 
       20 
20 
     | 
    
         
             
                  self.new(txid.rhex, index)
         
     | 
| 
       21 
21 
     | 
    
         
             
                end
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
      
 23 
     | 
    
         
            +
                # Parse from payload
         
     | 
| 
      
 24 
     | 
    
         
            +
                # @param [String|StringIO] payload prvout payload with binary format
         
     | 
| 
      
 25 
     | 
    
         
            +
                # @return [Bitcoin::OutPoint]
         
     | 
| 
      
 26 
     | 
    
         
            +
                def self.parse_from_payload(payload)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
         
     | 
| 
      
 28 
     | 
    
         
            +
                  hash, index = buf.read(36).unpack('a32V')
         
     | 
| 
      
 29 
     | 
    
         
            +
                  OutPoint.new(hash.bth, index)
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
       23 
32 
     | 
    
         
             
                def coinbase?
         
     | 
| 
       24 
33 
     | 
    
         
             
                  tx_hash == COINBASE_HASH && index == COINBASE_INDEX
         
     | 
| 
       25 
34 
     | 
    
         
             
                end
         
     | 
    
        data/lib/bitcoin/psbt/input.rb
    CHANGED
    
    | 
         @@ -19,6 +19,12 @@ module Bitcoin 
     | 
|
| 
       19 
19 
     | 
    
         
             
                  attr_accessor :hash160_preimages
         
     | 
| 
       20 
20 
     | 
    
         
             
                  attr_accessor :hash256_preimages
         
     | 
| 
       21 
21 
     | 
    
         
             
                  attr_accessor :proprietaries
         
     | 
| 
      
 22 
     | 
    
         
            +
                  attr_accessor :tap_key_sig
         
     | 
| 
      
 23 
     | 
    
         
            +
                  attr_accessor :tap_script_sigs
         
     | 
| 
      
 24 
     | 
    
         
            +
                  attr_accessor :tap_leaf_scripts
         
     | 
| 
      
 25 
     | 
    
         
            +
                  attr_accessor :tap_bip32_derivations
         
     | 
| 
      
 26 
     | 
    
         
            +
                  attr_accessor :tap_internal_key
         
     | 
| 
      
 27 
     | 
    
         
            +
                  attr_accessor :tap_merkle_root
         
     | 
| 
       22 
28 
     | 
    
         
             
                  attr_accessor :unknowns
         
     | 
| 
       23 
29 
     | 
    
         | 
| 
       24 
30 
     | 
    
         
             
                  def initialize(non_witness_utxo: nil, witness_utxo: nil)
         
     | 
| 
         @@ -31,6 +37,9 @@ module Bitcoin 
     | 
|
| 
       31 
37 
     | 
    
         
             
                    @hash160_preimages = {}
         
     | 
| 
       32 
38 
     | 
    
         
             
                    @hash256_preimages = {}
         
     | 
| 
       33 
39 
     | 
    
         
             
                    @proprietaries = []
         
     | 
| 
      
 40 
     | 
    
         
            +
                    @tap_script_sigs = {}
         
     | 
| 
      
 41 
     | 
    
         
            +
                    @tap_leaf_scripts = {}
         
     | 
| 
      
 42 
     | 
    
         
            +
                    @tap_bip32_derivations = {}
         
     | 
| 
       34 
43 
     | 
    
         
             
                    @unknowns = {}
         
     | 
| 
       35 
44 
     | 
    
         
             
                  end
         
     | 
| 
       36 
45 
     | 
    
         | 
| 
         @@ -110,6 +119,35 @@ module Bitcoin 
     | 
|
| 
       110 
119 
     | 
    
         
             
                      when PSBT_IN_TYPES[:proprietary]
         
     | 
| 
       111 
120 
     | 
    
         
             
                        raise ArgumentError, 'Duplicate Key, key for proprietary value already provided.' if input.proprietaries.any?{|p| p.key == key}
         
     | 
| 
       112 
121 
     | 
    
         
             
                        input.proprietaries << Proprietary.new(key, value)
         
     | 
| 
      
 122 
     | 
    
         
            +
                      when PSBT_IN_TYPES[:tap_key_sig]
         
     | 
| 
      
 123 
     | 
    
         
            +
                        raise ArgumentError, 'Size of key was not the expected size for the type tap key sig' unless key_len == 1
         
     | 
| 
      
 124 
     | 
    
         
            +
                        raise ArgumentError, 'Invalid schnorr signature size for the type tap key sig' unless [64, 65].include?(value.bytesize)
         
     | 
| 
      
 125 
     | 
    
         
            +
                        input.tap_key_sig = value.bth
         
     | 
| 
      
 126 
     | 
    
         
            +
                      when PSBT_IN_TYPES[:tap_script_sig]
         
     | 
| 
      
 127 
     | 
    
         
            +
                        raise ArgumentError, 'Duplicate Key, key for tap script sig value already provided.' if input.tap_script_sigs[key.bth]
         
     | 
| 
      
 128 
     | 
    
         
            +
                        raise ArgumentError, 'Size of key was not the expected size for the type tap script sig' unless key.bytesize == (X_ONLY_PUBKEY_SIZE + 32)
         
     | 
| 
      
 129 
     | 
    
         
            +
                        raise ArgumentError, 'Invalid schnorr signature size for the type tap script sig' unless [64, 65].include?(value.bytesize)
         
     | 
| 
      
 130 
     | 
    
         
            +
                        input.tap_script_sigs[key.bth] = value.bth
         
     | 
| 
      
 131 
     | 
    
         
            +
                      when PSBT_IN_TYPES[:tap_leaf_script]
         
     | 
| 
      
 132 
     | 
    
         
            +
                        begin
         
     | 
| 
      
 133 
     | 
    
         
            +
                          cb = Bitcoin::Taproot::ControlBlock.parse_from_payload(key)
         
     | 
| 
      
 134 
     | 
    
         
            +
                          raise ArgumentError, 'Duplicate Key, key for tap leaf script value already provided.' if input.tap_leaf_scripts[cb]
         
     | 
| 
      
 135 
     | 
    
         
            +
                          input.tap_leaf_scripts[cb] = value.bth
         
     | 
| 
      
 136 
     | 
    
         
            +
                        rescue Bitcoin::Taproot::Error => e
         
     | 
| 
      
 137 
     | 
    
         
            +
                          raise ArgumentError, e.message
         
     | 
| 
      
 138 
     | 
    
         
            +
                        end
         
     | 
| 
      
 139 
     | 
    
         
            +
                      when PSBT_IN_TYPES[:tap_bip32_derivation]
         
     | 
| 
      
 140 
     | 
    
         
            +
                        raise ArgumentError, 'Duplicate Key, key for tap bip32 derivation value already provided.' if input.tap_bip32_derivations[key.bth]
         
     | 
| 
      
 141 
     | 
    
         
            +
                        raise ArgumentError, 'Size of key was not the expected size for the type tap bip32 derivation' unless key.bytesize == X_ONLY_PUBKEY_SIZE
         
     | 
| 
      
 142 
     | 
    
         
            +
                        input.tap_bip32_derivations[key.bth] = value.bth
         
     | 
| 
      
 143 
     | 
    
         
            +
                      when PSBT_IN_TYPES[:tap_internal_key]
         
     | 
| 
      
 144 
     | 
    
         
            +
                        raise ArgumentError, 'Size of key was not the expected size for the type tap internal key' unless key_len == 1
         
     | 
| 
      
 145 
     | 
    
         
            +
                        raise ArgumentError, 'Invalid x-only public key size for the type tap internal key' unless value.bytesize == X_ONLY_PUBKEY_SIZE
         
     | 
| 
      
 146 
     | 
    
         
            +
                        input.tap_internal_key = value.bth
         
     | 
| 
      
 147 
     | 
    
         
            +
                      when PSBT_IN_TYPES[:tap_merkle_root]
         
     | 
| 
      
 148 
     | 
    
         
            +
                        raise ArgumentError, 'Size of key was not the expected size for the type tap merkle root' unless key_len == 1
         
     | 
| 
      
 149 
     | 
    
         
            +
                        raise ArgumentError, 'Invalid merkle root hash size for the type tap merkle root' unless value.bytesize == 32
         
     | 
| 
      
 150 
     | 
    
         
            +
                        input.tap_merkle_root = value.bth
         
     | 
| 
       113 
151 
     | 
    
         
             
                      else
         
     | 
| 
       114 
152 
     | 
    
         
             
                        unknown_key = ([key_type].pack('C') + key).bth
         
     | 
| 
       115 
153 
     | 
    
         
             
                        raise ArgumentError, 'Duplicate Key, key for unknown value already provided.' if input.unknowns[unknown_key]
         
     | 
| 
         @@ -135,6 +173,12 @@ module Bitcoin 
     | 
|
| 
       135 
173 
     | 
    
         
             
                      payload << sha256_preimages.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:sha256], key: k.htb, value: v.htb)}.join
         
     | 
| 
       136 
174 
     | 
    
         
             
                      payload << hash160_preimages.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:hash160], key: k.htb, value: v.htb)}.join
         
     | 
| 
       137 
175 
     | 
    
         
             
                      payload << hash256_preimages.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:hash256], key: k.htb, value: v.htb)}.join
         
     | 
| 
      
 176 
     | 
    
         
            +
                      payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_key_sig], value: tap_key_sig.htb) if tap_key_sig
         
     | 
| 
      
 177 
     | 
    
         
            +
                      payload << tap_script_sigs.map{|k, v| PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_script_sig], key: k.htb, value: v.htb)}.join
         
     | 
| 
      
 178 
     | 
    
         
            +
                      payload << tap_leaf_scripts.map{|k, v| PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_leaf_script], key: k.to_payload, value: v.htb)}.join
         
     | 
| 
      
 179 
     | 
    
         
            +
                      payload << tap_bip32_derivations.map{|k, v| PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_bip32_derivation], key: k.htb, value: v.htb)}.join
         
     | 
| 
      
 180 
     | 
    
         
            +
                      payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_internal_key], value: tap_internal_key.htb) if tap_internal_key
         
     | 
| 
      
 181 
     | 
    
         
            +
                      payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_merkle_root], value: tap_merkle_root.htb) if tap_merkle_root
         
     | 
| 
       138 
182 
     | 
    
         
             
                    end
         
     | 
| 
       139 
183 
     | 
    
         
             
                    payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:script_sig], value: final_script_sig.to_payload) if final_script_sig
         
     | 
| 
       140 
184 
     | 
    
         
             
                    payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:script_witness], value: final_script_witness.to_payload) if final_script_witness
         
     | 
    
        data/lib/bitcoin/psbt/output.rb
    CHANGED
    
    | 
         @@ -8,11 +8,15 @@ module Bitcoin 
     | 
|
| 
       8 
8 
     | 
    
         
             
                  attr_accessor :witness_script
         
     | 
| 
       9 
9 
     | 
    
         
             
                  attr_accessor :hd_key_paths
         
     | 
| 
       10 
10 
     | 
    
         
             
                  attr_accessor :proprietaries
         
     | 
| 
      
 11 
     | 
    
         
            +
                  attr_accessor :tap_internal_key
         
     | 
| 
      
 12 
     | 
    
         
            +
                  attr_accessor :tap_tree
         
     | 
| 
      
 13 
     | 
    
         
            +
                  attr_accessor :tap_bip32_derivations
         
     | 
| 
       11 
14 
     | 
    
         
             
                  attr_accessor :unknowns
         
     | 
| 
       12 
15 
     | 
    
         | 
| 
       13 
16 
     | 
    
         
             
                  def initialize
         
     | 
| 
       14 
17 
     | 
    
         
             
                    @hd_key_paths = {}
         
     | 
| 
       15 
18 
     | 
    
         
             
                    @proprietaries = []
         
     | 
| 
      
 19 
     | 
    
         
            +
                    @tap_bip32_derivations = {}
         
     | 
| 
       16 
20 
     | 
    
         
             
                    @unknowns = {}
         
     | 
| 
       17 
21 
     | 
    
         
             
                  end
         
     | 
| 
       18 
22 
     | 
    
         | 
| 
         @@ -46,6 +50,17 @@ module Bitcoin 
     | 
|
| 
       46 
50 
     | 
    
         
             
                      when PSBT_OUT_TYPES[:proprietary]
         
     | 
| 
       47 
51 
     | 
    
         
             
                        raise ArgumentError, 'Duplicate Key, key for proprietary value already provided.' if output.proprietaries.any?{|p| p.key == key}
         
     | 
| 
       48 
52 
     | 
    
         
             
                        output.proprietaries << Proprietary.new(key, value)
         
     | 
| 
      
 53 
     | 
    
         
            +
                      when PSBT_OUT_TYPES[:tap_internal_key]
         
     | 
| 
      
 54 
     | 
    
         
            +
                        raise ArgumentError, 'Invalid output tap internal key typed key.' unless key_len == 1
         
     | 
| 
      
 55 
     | 
    
         
            +
                        raise ArgumentError, 'Invalid x-only public key size for the type tap internal key' unless value.bytesize == X_ONLY_PUBKEY_SIZE
         
     | 
| 
      
 56 
     | 
    
         
            +
                        output.tap_internal_key = value.bth
         
     | 
| 
      
 57 
     | 
    
         
            +
                      when PSBT_OUT_TYPES[:tap_tree]
         
     | 
| 
      
 58 
     | 
    
         
            +
                        raise ArgumentError, 'Invalid output tap tree typed key.' unless key_len == 1
         
     | 
| 
      
 59 
     | 
    
         
            +
                        output.tap_tree = value.bth # TODO implement builder.
         
     | 
| 
      
 60 
     | 
    
         
            +
                      when PSBT_OUT_TYPES[:tap_bip32_derivation]
         
     | 
| 
      
 61 
     | 
    
         
            +
                        raise ArgumentError, 'Duplicate Key, key for tap bip32 derivation value already provided.' if output.tap_bip32_derivations[key.bth]
         
     | 
| 
      
 62 
     | 
    
         
            +
                        raise ArgumentError, 'Size of key was not the expected size for the type tap bip32 derivation' unless key.bytesize == X_ONLY_PUBKEY_SIZE
         
     | 
| 
      
 63 
     | 
    
         
            +
                        output.tap_bip32_derivations[key.bth] = value.bth
         
     | 
| 
       49 
64 
     | 
    
         
             
                      else
         
     | 
| 
       50 
65 
     | 
    
         
             
                        unknown_key = ([key_type].pack('C') + key).bth
         
     | 
| 
       51 
66 
     | 
    
         
             
                        raise ArgumentError, 'Duplicate Key, key for unknown value already provided' if output.unknowns[unknown_key]
         
     | 
| 
         @@ -62,6 +77,9 @@ module Bitcoin 
     | 
|
| 
       62 
77 
     | 
    
         
             
                    payload << PSBT.serialize_to_vector(PSBT_OUT_TYPES[:witness_script], value: witness_script) if witness_script
         
     | 
| 
       63 
78 
     | 
    
         
             
                    payload << hd_key_paths.values.map{|v|v.to_payload(PSBT_OUT_TYPES[:bip32_derivation])}.join
         
     | 
| 
       64 
79 
     | 
    
         
             
                    payload << proprietaries.map(&:to_payload).join
         
     | 
| 
      
 80 
     | 
    
         
            +
                    payload << PSBT.serialize_to_vector(PSBT_OUT_TYPES[:tap_internal_key], value: tap_internal_key.htb) if tap_internal_key
         
     | 
| 
      
 81 
     | 
    
         
            +
                    payload << PSBT.serialize_to_vector(PSBT_OUT_TYPES[:tap_tree], value: tap_tree.htb) if tap_tree
         
     | 
| 
      
 82 
     | 
    
         
            +
                    payload << tap_bip32_derivations.map{|k, v|PSBT.serialize_to_vector(PSBT_OUT_TYPES[:tap_bip32_derivation], key: k.htb, value: v.htb)}.join
         
     | 
| 
       65 
83 
     | 
    
         
             
                    payload << unknowns.map {|k,v|Bitcoin.pack_var_int(k.htb.bytesize) << k.htb << Bitcoin.pack_var_int(v.bytesize) << v}.join
         
     | 
| 
       66 
84 
     | 
    
         
             
                    payload << PSBT_SEPARATOR.itb
         
     | 
| 
       67 
85 
     | 
    
         
             
                    payload
         
     | 
    
        data/lib/bitcoin/psbt.rb
    CHANGED
    
    | 
         @@ -33,12 +33,21 @@ module Bitcoin 
     | 
|
| 
       33 
33 
     | 
    
         
             
                  sha256: 0x0b,
         
     | 
| 
       34 
34 
     | 
    
         
             
                  hash160: 0x0c,
         
     | 
| 
       35 
35 
     | 
    
         
             
                  hash256: 0x0d,
         
     | 
| 
      
 36 
     | 
    
         
            +
                  tap_key_sig: 0x13,
         
     | 
| 
      
 37 
     | 
    
         
            +
                  tap_script_sig: 0x14,
         
     | 
| 
      
 38 
     | 
    
         
            +
                  tap_leaf_script: 0x15,
         
     | 
| 
      
 39 
     | 
    
         
            +
                  tap_bip32_derivation: 0x16,
         
     | 
| 
      
 40 
     | 
    
         
            +
                  tap_internal_key: 0x17,
         
     | 
| 
      
 41 
     | 
    
         
            +
                  tap_merkle_root: 0x18,
         
     | 
| 
       36 
42 
     | 
    
         
             
                  proprietary: 0xfc
         
     | 
| 
       37 
43 
     | 
    
         
             
                }
         
     | 
| 
       38 
44 
     | 
    
         
             
                PSBT_OUT_TYPES = {
         
     | 
| 
       39 
45 
     | 
    
         
             
                  redeem_script: 0x00,
         
     | 
| 
       40 
46 
     | 
    
         
             
                  witness_script: 0x01,
         
     | 
| 
       41 
47 
     | 
    
         
             
                  bip32_derivation: 0x02,
         
     | 
| 
      
 48 
     | 
    
         
            +
                  tap_internal_key: 0x05,
         
     | 
| 
      
 49 
     | 
    
         
            +
                  tap_tree: 0x06,
         
     | 
| 
      
 50 
     | 
    
         
            +
                  tap_bip32_derivation: 0x07,
         
     | 
| 
       42 
51 
     | 
    
         
             
                  proprietary: 0xfc
         
     | 
| 
       43 
52 
     | 
    
         
             
                }
         
     | 
| 
       44 
53 
     | 
    
         
             
                PSBT_SEPARATOR = 0x00
         
     | 
| 
         @@ -15,15 +15,28 @@ module Bitcoin 
     | 
|
| 
       15 
15 
     | 
    
         
             
                end
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
                # generate P2PKH script
         
     | 
| 
      
 18 
     | 
    
         
            +
                # @param [String] pubkey_hash public key hash with hex format
         
     | 
| 
       18 
19 
     | 
    
         
             
                def self.to_p2pkh(pubkey_hash)
         
     | 
| 
       19 
20 
     | 
    
         
             
                  new << OP_DUP << OP_HASH160 << pubkey_hash << OP_EQUALVERIFY << OP_CHECKSIG
         
     | 
| 
       20 
21 
     | 
    
         
             
                end
         
     | 
| 
       21 
22 
     | 
    
         | 
| 
       22 
23 
     | 
    
         
             
                # generate P2WPKH script
         
     | 
| 
      
 24 
     | 
    
         
            +
                # @param [String] pubkey_hash public key hash with hex format
         
     | 
| 
      
 25 
     | 
    
         
            +
                # @return [Bitcoin::Script]
         
     | 
| 
       23 
26 
     | 
    
         
             
                def self.to_p2wpkh(pubkey_hash)
         
     | 
| 
       24 
27 
     | 
    
         
             
                  new << WITNESS_VERSION_V0 << pubkey_hash
         
     | 
| 
       25 
28 
     | 
    
         
             
                end
         
     | 
| 
       26 
29 
     | 
    
         | 
| 
      
 30 
     | 
    
         
            +
                # Generate P2TR script
         
     | 
| 
      
 31 
     | 
    
         
            +
                # @param [String or Bitcoin::Key] xonly_pubkey public key with hex format or Bitcoin::Key
         
     | 
| 
      
 32 
     | 
    
         
            +
                # @return [Bitcoin::Script]
         
     | 
| 
      
 33 
     | 
    
         
            +
                # @raise ArgumentError
         
     | 
| 
      
 34 
     | 
    
         
            +
                def self.to_p2tr(xonly_pubkey)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  xonly_pubkey = xonly_pubkey.xonly_pubkey if xonly_pubkey.is_a?(Bitcoin::Key)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  raise ArgumentError, 'Invalid public key size' unless xonly_pubkey.htb.bytesize == Bitcoin::WITNESS_V1_TAPROOT_SIZE
         
     | 
| 
      
 37 
     | 
    
         
            +
                  new << OP_1 << xonly_pubkey
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
       27 
40 
     | 
    
         
             
                # generate m of n multisig p2sh script
         
     | 
| 
       28 
41 
     | 
    
         
             
                # @param [String] m the number of signatures required for multisig
         
     | 
| 
       29 
42 
     | 
    
         
             
                # @param [Array] pubkeys array of public keys that compose multisig
         
     | 
| 
         @@ -136,16 +136,15 @@ module Bitcoin 
     | 
|
| 
       136 
136 
     | 
    
         
             
                    else
         
     | 
| 
       137 
137 
     | 
    
         
             
                      sig_version = :tapscript
         
     | 
| 
       138 
138 
     | 
    
         
             
                      # Script path spending (stack size is >1 after removing optional annex)
         
     | 
| 
       139 
     | 
    
         
            -
                       
     | 
| 
       140 
     | 
    
         
            -
                       
     | 
| 
       141 
     | 
    
         
            -
                       
     | 
| 
       142 
     | 
    
         
            -
                          (control.bytesize - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE != 0
         
     | 
| 
      
 139 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 140 
     | 
    
         
            +
                      control = Bitcoin::Taproot::ControlBlock.parse_from_payload(stack.pop.htb)
         
     | 
| 
      
 141 
     | 
    
         
            +
                      rescue Bitcoin::Taproot::Error
         
     | 
| 
       143 
142 
     | 
    
         
             
                        return set_error(SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE)
         
     | 
| 
       144 
143 
     | 
    
         
             
                      end
         
     | 
| 
       145 
     | 
    
         
            -
                       
     | 
| 
       146 
     | 
    
         
            -
                      opts[:leaf_hash] = Bitcoin.tagged_hash('TapLeaf', [leaf_ver].pack('C') + Bitcoin.pack_var_string(script_payload))
         
     | 
| 
      
 144 
     | 
    
         
            +
                      script_payload = stack.pop.htb
         
     | 
| 
      
 145 
     | 
    
         
            +
                      opts[:leaf_hash] = Bitcoin.tagged_hash('TapLeaf', [control.leaf_ver].pack('C') + Bitcoin.pack_var_string(script_payload))
         
     | 
| 
       147 
146 
     | 
    
         
             
                      return set_error(SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH) unless valid_taproot_commitment?(control, program, opts[:leaf_hash])
         
     | 
| 
       148 
     | 
    
         
            -
                      if  
     | 
| 
      
 147 
     | 
    
         
            +
                      if control.leaf_ver == TAPROOT_LEAF_TAPSCRIPT
         
     | 
| 
       149 
148 
     | 
    
         
             
                        opts[:weight_left] = witness.to_payload.bytesize + VALIDATION_WEIGHT_OFFSET
         
     | 
| 
       150 
149 
     | 
    
         
             
                        script_pubkey = Bitcoin::Script.parse_from_payload(script_payload)
         
     | 
| 
       151 
150 
     | 
    
         
             
                        script_pubkey.chunks.each do |c|
         
     | 
| 
         @@ -698,19 +697,11 @@ module Bitcoin 
     | 
|
| 
       698 
697 
     | 
    
         
             
                # check whether valid taproot commitment.
         
     | 
| 
       699 
698 
     | 
    
         
             
                def valid_taproot_commitment?(control, program, leaf_hash)
         
     | 
| 
       700 
699 
     | 
    
         
             
                  begin
         
     | 
| 
       701 
     | 
    
         
            -
                     
     | 
| 
       702 
     | 
    
         
            -
                    xonly_pubkey = control[1...TAPROOT_CONTROL_BASE_SIZE]
         
     | 
| 
       703 
     | 
    
         
            -
                    p = Bitcoin::Key.from_xonly_pubkey(xonly_pubkey.bth)
         
     | 
| 
      
 700 
     | 
    
         
            +
                    p = Bitcoin::Key.from_xonly_pubkey(control.internal_key)
         
     | 
| 
       704 
701 
     | 
    
         
             
                    k = leaf_hash
         
     | 
| 
       705 
     | 
    
         
            -
                     
     | 
| 
       706 
     | 
    
         
            -
             
     | 
| 
       707 
     | 
    
         
            -
             
     | 
| 
       708 
     | 
    
         
            -
                      k = Bitcoin.tagged_hash('TapBranch', k.bth < e.bth ? k + e : e + k)
         
     | 
| 
       709 
     | 
    
         
            -
                    end
         
     | 
| 
       710 
     | 
    
         
            -
                    t = Bitcoin.tagged_hash('TapTweak', xonly_pubkey + k)
         
     | 
| 
       711 
     | 
    
         
            -
                    key = Bitcoin::Key.new(priv_key: t.bth, key_type: Key::TYPES[:compressed])
         
     | 
| 
       712 
     | 
    
         
            -
                    q = key.to_point + p.to_point
         
     | 
| 
       713 
     | 
    
         
            -
                    return q.x == program.bti && (control[0].bti & 1) == (q.y % 2)
         
     | 
| 
      
 702 
     | 
    
         
            +
                    control.paths.each { |e| k = Bitcoin.tagged_hash('TapBranch', k.bth < e ? k + e.htb : e.htb + k) }
         
     | 
| 
      
 703 
     | 
    
         
            +
                    q = Bitcoin::Taproot.tweak_public_key(p, k.bth).to_point
         
     | 
| 
      
 704 
     | 
    
         
            +
                    return q.x == program.bti && control.parity == (q.y % 2)
         
     | 
| 
       714 
705 
     | 
    
         
             
                  rescue ArgumentError
         
     | 
| 
       715 
706 
     | 
    
         
             
                    return false
         
     | 
| 
       716 
707 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -751,7 +742,7 @@ module Bitcoin 
     | 
|
| 
       751 
742 
     | 
    
         
             
                  pubkey_size = pubkey.htb.bytesize
         
     | 
| 
       752 
743 
     | 
    
         
             
                  if pubkey_size == 0
         
     | 
| 
       753 
744 
     | 
    
         
             
                    return set_error(SCRIPT_ERR_PUBKEYTYPE)
         
     | 
| 
       754 
     | 
    
         
            -
                  elsif pubkey_size ==  
     | 
| 
      
 745 
     | 
    
         
            +
                  elsif pubkey_size == X_ONLY_PUBKEY_SIZE
         
     | 
| 
       755 
746 
     | 
    
         
             
                    if success
         
     | 
| 
       756 
747 
     | 
    
         
             
                      result = checker.check_schnorr_sig(sig, pubkey, :tapscript, opts)
         
     | 
| 
       757 
748 
     | 
    
         
             
                      return checker.has_error? ? set_error(checker.error_code) : set_error(SCRIPT_ERR_SCHNORR_SIG) unless result
         
     | 
| 
         @@ -760,7 +751,6 @@ module Bitcoin 
     | 
|
| 
       760 
751 
     | 
    
         
             
                    return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE) if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE)
         
     | 
| 
       761 
752 
     | 
    
         
             
                  end
         
     | 
| 
       762 
753 
     | 
    
         
             
                  success
         
     | 
| 
       763 
     | 
    
         
            -
                  # return true
         
     | 
| 
       764 
754 
     | 
    
         
             
                end
         
     | 
| 
       765 
755 
     | 
    
         | 
| 
       766 
756 
     | 
    
         
             
              end
         
     | 
| 
         @@ -232,7 +232,7 @@ module Bitcoin 
     | 
|
| 
       232 
232 
     | 
    
         
             
                  def full_pubkey_from_xonly_pubkey(pub_key)
         
     | 
| 
       233 
233 
     | 
    
         
             
                    with_context do |context|
         
     | 
| 
       234 
234 
     | 
    
         
             
                      pubkey = pub_key.htb
         
     | 
| 
       235 
     | 
    
         
            -
                      raise ArgumentError,  
     | 
| 
      
 235 
     | 
    
         
            +
                      raise ArgumentError, "Pubkey size must be #{X_ONLY_PUBKEY_SIZE} bytes." unless pubkey.bytesize == X_ONLY_PUBKEY_SIZE
         
     | 
| 
       236 
236 
     | 
    
         
             
                      xonly_pubkey = FFI::MemoryPointer.new(:uchar, pubkey.bytesize).put_bytes(0, pubkey)
         
     | 
| 
       237 
237 
     | 
    
         
             
                      full_pubkey = FFI::MemoryPointer.new(:uchar, 64)
         
     | 
| 
       238 
238 
     | 
    
         
             
                      raise ArgumentError, 'An invalid public key was specified.' unless secp256k1_xonly_pubkey_parse(context, full_pubkey, xonly_pubkey) == 1
         
     | 
| 
         @@ -32,7 +32,7 @@ module Bitcoin 
     | 
|
| 
       32 
32 
     | 
    
         
             
                  # @return [Boolean] result.
         
     | 
| 
       33 
33 
     | 
    
         
             
                  def valid_xonly_pubkey?(pub_key)
         
     | 
| 
       34 
34 
     | 
    
         
             
                    pubkey = pub_key.htb
         
     | 
| 
       35 
     | 
    
         
            -
                    return false unless pubkey.bytesize ==  
     | 
| 
      
 35 
     | 
    
         
            +
                    return false unless pubkey.bytesize == X_ONLY_PUBKEY_SIZE
         
     | 
| 
       36 
36 
     | 
    
         
             
                    begin
         
     | 
| 
       37 
37 
     | 
    
         
             
                      ECDSA::Format::PointOctetString.decode(pubkey, ECDSA::Group::Secp256k1)
         
     | 
| 
       38 
38 
     | 
    
         
             
                    rescue Exception
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Bitcoin
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Taproot
         
     | 
| 
      
 3 
     | 
    
         
            +
                class ControlBlock
         
     | 
| 
      
 4 
     | 
    
         
            +
                  include Bitcoin::HexConverter
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  attr_accessor :parity
         
     | 
| 
      
 7 
     | 
    
         
            +
                  attr_accessor :leaf_ver
         
     | 
| 
      
 8 
     | 
    
         
            +
                  attr_accessor :internal_key
         
     | 
| 
      
 9 
     | 
    
         
            +
                  attr_accessor :paths
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  # @param [Integer] parity
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # @param [Integer] leaf_ver
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # @param [String] internal_key public key with hex format.
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # @param [Array[String]] paths array of hash values of sibling nodes in the tree that serve as proof of inclusion
         
     | 
| 
      
 15 
     | 
    
         
            +
                  def initialize(parity, leaf_ver, internal_key, paths = [])
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @parity = parity
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @leaf_ver = leaf_ver
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @internal_key = internal_key
         
     | 
| 
      
 19 
     | 
    
         
            +
                    @paths = paths
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  # parse control block from payload.
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # @raise [Bitcoin::Taproot::Error]
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @return [Bitcoin::Taproot::ControlBlock]
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def self.parse_from_payload(payload)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    raise Bitcoin::Taproot::Error, 'Invalid data length for Control Block' if payload.bytesize > TAPROOT_CONTROL_MAX_SIZE
         
     | 
| 
      
 27 
     | 
    
         
            +
                    raise Bitcoin::Taproot::Error, 'Invalid data length for path in Control Block' unless (payload.bytesize - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE == 0
         
     | 
| 
      
 28 
     | 
    
         
            +
                    control, internal_key, paths = payload.unpack('Ca32a*')
         
     | 
| 
      
 29 
     | 
    
         
            +
                    parity = control & 1
         
     | 
| 
      
 30 
     | 
    
         
            +
                    leaf_ver = control - parity
         
     | 
| 
      
 31 
     | 
    
         
            +
                    raw_paths = StringIO.new(paths)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    paths = (paths.bytesize / 32).times.map { raw_paths.read(32).bth }
         
     | 
| 
      
 33 
     | 
    
         
            +
                    ControlBlock.new(parity, leaf_ver, internal_key.bth, paths)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  # Convert to payload.
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # @return [String] payload with binary format.
         
     | 
| 
      
 38 
     | 
    
         
            +
                  def to_payload
         
     | 
| 
      
 39 
     | 
    
         
            +
                    [parity + leaf_ver].pack("C") + internal_key.htb + paths.map(&:htb).join
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  def ==(other)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    to_payload == other.to_payload
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -16,7 +16,7 @@ module Bitcoin 
     | 
|
| 
       16 
16 
     | 
    
         
             
                  # @raise [Bitcoin::Taproot::Builder] +internal_pubkey+ dose not xonly public key or leaf in +leaves+ does not instance of Bitcoin::Taproot::LeafNode.
         
     | 
| 
       17 
17 
     | 
    
         
             
                  # @return [Bitcoin::Taproot::SimpleBuilder]
         
     | 
| 
       18 
18 
     | 
    
         
             
                  def initialize(internal_key, leaves = [])
         
     | 
| 
       19 
     | 
    
         
            -
                    raise Error,  
     | 
| 
      
 19 
     | 
    
         
            +
                    raise Error, "Internal public key must be #{X_ONLY_PUBKEY_SIZE} bytes" unless internal_key.htb.bytesize == X_ONLY_PUBKEY_SIZE
         
     | 
| 
       20 
20 
     | 
    
         
             
                    raise Error, 'leaf must be Bitcoin::Taproot::LeafNode object' if leaves.find{ |leaf| !leaf.is_a?(Bitcoin::Taproot::LeafNode)}
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
                    @leaves = leaves
         
     | 
| 
         @@ -72,11 +72,11 @@ module Bitcoin 
     | 
|
| 
       72 
72 
     | 
    
         | 
| 
       73 
73 
     | 
    
         
             
                  # Generate control block needed to unlock with script-path.
         
     | 
| 
       74 
74 
     | 
    
         
             
                  # @param [Bitcoin::Taproot::LeafNode] leaf Leaf to use for unlocking.
         
     | 
| 
       75 
     | 
    
         
            -
                  # @return [ 
     | 
| 
      
 75 
     | 
    
         
            +
                  # @return [Bitcoin::Taproot::ControlBlock] control block.
         
     | 
| 
       76 
76 
     | 
    
         
             
                  def control_block(leaf)
         
     | 
| 
       77 
77 
     | 
    
         
             
                    path = inclusion_proof(leaf)
         
     | 
| 
       78 
78 
     | 
    
         
             
                    parity = tweak_public_key.to_point.has_even_y? ? 0 : 1
         
     | 
| 
       79 
     | 
    
         
            -
                     
     | 
| 
      
 79 
     | 
    
         
            +
                    ControlBlock.new(parity, leaf.leaf_ver, internal_key, path.map(&:bth))
         
     | 
| 
       80 
80 
     | 
    
         
             
                  end
         
     | 
| 
       81 
81 
     | 
    
         | 
| 
       82 
82 
     | 
    
         
             
                  # Generate inclusion proof for +leaf+.
         
     | 
    
        data/lib/bitcoin/taproot.rb
    CHANGED
    
    
    
        data/lib/bitcoin/tx_in.rb
    CHANGED
    
    | 
         @@ -34,8 +34,7 @@ module Bitcoin 
     | 
|
| 
       34 
34 
     | 
    
         
             
                def self.parse_from_payload(payload)
         
     | 
| 
       35 
35 
     | 
    
         
             
                  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
         
     | 
| 
       36 
36 
     | 
    
         
             
                  i = new
         
     | 
| 
       37 
     | 
    
         
            -
                   
     | 
| 
       38 
     | 
    
         
            -
                  i.out_point = OutPoint.new(hash.bth, index)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  i.out_point = OutPoint.parse_from_payload(buf)
         
     | 
| 
       39 
38 
     | 
    
         
             
                  sig_length = Bitcoin.unpack_var_int_from_io(buf)
         
     | 
| 
       40 
39 
     | 
    
         
             
                  if sig_length == 0
         
     | 
| 
       41 
40 
     | 
    
         
             
                    i.script_sig = Bitcoin::Script.new
         
     | 
    
        data/lib/bitcoin/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: bitcoinrb
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.2.1
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - azuchi
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: exe
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2022- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2022-10-28 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: ecdsa
         
     | 
| 
         @@ -56,16 +56,16 @@ dependencies: 
     | 
|
| 
       56 
56 
     | 
    
         
             
              name: bech32
         
     | 
| 
       57 
57 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       58 
58 
     | 
    
         
             
                requirements:
         
     | 
| 
       59 
     | 
    
         
            -
                - - " 
     | 
| 
      
 59 
     | 
    
         
            +
                - - ">="
         
     | 
| 
       60 
60 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       61 
     | 
    
         
            -
                    version: 1. 
     | 
| 
      
 61 
     | 
    
         
            +
                    version: 1.3.0
         
     | 
| 
       62 
62 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       63 
63 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       64 
64 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       65 
65 
     | 
    
         
             
                requirements:
         
     | 
| 
       66 
     | 
    
         
            -
                - - " 
     | 
| 
      
 66 
     | 
    
         
            +
                - - ">="
         
     | 
| 
       67 
67 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       68 
     | 
    
         
            -
                    version: 1. 
     | 
| 
      
 68 
     | 
    
         
            +
                    version: 1.3.0
         
     | 
| 
       69 
69 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       70 
70 
     | 
    
         
             
              name: daemon-spawn
         
     | 
| 
       71 
71 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -479,6 +479,7 @@ files: 
     | 
|
| 
       479 
479 
     | 
    
         
             
            - lib/bitcoin/store/spv_chain.rb
         
     | 
| 
       480 
480 
     | 
    
         
             
            - lib/bitcoin/store/utxo_db.rb
         
     | 
| 
       481 
481 
     | 
    
         
             
            - lib/bitcoin/taproot.rb
         
     | 
| 
      
 482 
     | 
    
         
            +
            - lib/bitcoin/taproot/control_block.rb
         
     | 
| 
       482 
483 
     | 
    
         
             
            - lib/bitcoin/taproot/leaf_node.rb
         
     | 
| 
       483 
484 
     | 
    
         
             
            - lib/bitcoin/taproot/simple_builder.rb
         
     | 
| 
       484 
485 
     | 
    
         
             
            - lib/bitcoin/tx.rb
         
     | 
| 
         @@ -516,7 +517,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       516 
517 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       517 
518 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       518 
519 
     | 
    
         
             
            requirements: []
         
     | 
| 
       519 
     | 
    
         
            -
            rubygems_version: 3.3. 
     | 
| 
      
 520 
     | 
    
         
            +
            rubygems_version: 3.3.23
         
     | 
| 
       520 
521 
     | 
    
         
             
            signing_key: 
         
     | 
| 
       521 
522 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       522 
523 
     | 
    
         
             
            summary: The implementation of Bitcoin Protocol for Ruby.
         
     |