secp256k1rb 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +14 -0
- data/README.md +26 -13
- data/lib/secp256k1/c.rb +73 -0
- data/lib/secp256k1/ecdh.rb +37 -0
- data/lib/secp256k1/ellswift.rb +25 -10
- data/lib/secp256k1/key.rb +342 -0
- data/lib/secp256k1/musig/key_agg.rb +74 -0
- data/lib/secp256k1/musig/session.rb +123 -0
- data/lib/secp256k1/musig.rb +203 -0
- data/lib/secp256k1/recovery.rb +38 -9
- data/lib/secp256k1/schnorrsig.rb +73 -11
- data/lib/secp256k1/version.rb +1 -1
- data/lib/secp256k1.rb +113 -33
- metadata +10 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a26cd2beaa9525ea8f7a0db0e411dbb3d049ec976214f4651e787f02ed76f83
|
|
4
|
+
data.tar.gz: 05c9fce673e0f97e48faa60335cb5de37a3be74a12c50f5cfc541621c11526f2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 71dbb792ccefcf0a852ff5ff96173c2f7c784cff9313489782b03159dd0163860938435d66ec974cccf7fe3755b5bd1ded9bbdf20a77a9d4d41bc2a6b65d63f9
|
|
7
|
+
data.tar.gz: b5e36b1dae73beb4c7e2420efb792c9185fa08f96acbbd2090b5858fc305b88bd9b8a42197900bf1dfea9d1ff9c4b754d491719a63754e817c1c1854969b3db9
|
data/.ruby-gemset
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
secp256k1
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ruby-4.0.0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.3.0]
|
|
4
|
+
|
|
5
|
+
- Support secp256k1 v0.7.1.
|
|
6
|
+
- Add FFI bindings for the full v0.7.1 public C API.
|
|
7
|
+
- Add `Secp256k1::Key` module: EC key arithmetic (`tweak_add_seckey`/`tweak_mul_seckey`/`negate_seckey`, `tweak_add_pubkey`/`tweak_mul_pubkey`/`negate_pubkey`, `combine_pubkeys`), x-only public key operations (`xonly_pubkey_from_pubkey`, `xonly_tweak_add_pubkey`, `xonly_tweak_add_check?`) and key pair accessors (`keypair_to_seckey`/`keypair_to_pubkey`/`keypair_to_xonly_pubkey`).
|
|
8
|
+
Also adds `compare_pubkey`/`sort_pubkeys`/`compare_xonly_pubkey` and `keypair_xonly_tweak_add`.
|
|
9
|
+
- Add `Secp256k1::ECDH` module: `ecdh`.
|
|
10
|
+
- Add compact ECDSA signature conversion: `ecdsa_signature_to_compact` / `ecdsa_signature_from_compact`.
|
|
11
|
+
- Add `tagged_sha256` (BIP-340 tagged hash).
|
|
12
|
+
- Add `Secp256k1::EllSwift#ellswift_encode`.
|
|
13
|
+
- Add `Secp256k1::Recover#recoverable_signature_to_ecdsa`.
|
|
14
|
+
- Add `Secp256k1::SchnorrSig#sign_schnorr_custom` / `#verify_schnorr_custom` for variable-length messages.
|
|
15
|
+
- Add `Secp256k1::MuSig#generate_musig_nonce_counter` (deterministic counter-based nonce generation).
|
|
16
|
+
|
|
3
17
|
## [0.1.0] - 2024-05-07
|
|
4
18
|
|
|
5
19
|
- Initial release
|
data/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# secp256k1rb
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
|
3
|
+
This is a Ruby binding for Bitcoin Core's [secp256k1 library](https://github.com/bitcoin-core/secp256k1/).
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
@@ -22,22 +20,37 @@ Or install it yourself as:
|
|
|
22
20
|
|
|
23
21
|
## Usage
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
To use this library, you need to specify the path of the secp256k1 shared library in environment variable
|
|
24
|
+
`SECP256K1_LIB_PATH`, e.g: `$ export SECP256K1_LIB_PATH=/var/local/lib/libsecp256k1.so`.
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
Note: This library also implements the recovery module, so you must have built the secp256k1 library with the
|
|
27
|
+
`--enable-module-recovery` option.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
By including the Secp256k1 module, you can use the features provided by the `libsepc256k1` library. For example:
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
```ruby
|
|
32
|
+
require 'secp256k1'
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
include Secp256k1
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
generate_key_pair
|
|
37
|
+
=> ["e00c2ae99e59b5262be3d507d026081f0e6cf9972ffdd4f2d45a390f7a41b053", "027e0f70b540d627422cf7bb77d86ae1bb6829c80104dd48dc2539e6277ea25624"]
|
|
38
|
+
```
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
See [here](https://www.rubydoc.info/gems/secp256k1rb/Secp256k1) for available methods.
|
|
41
|
+
In addition, the following modules are also included, so you can use them as they are.
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
* [Key](https://www.rubydoc.info/gems/secp256k1rb/Secp256k1/Key)
|
|
44
|
+
* [ECDH](https://www.rubydoc.info/gems/secp256k1rb/Secp256k1/ECDH)
|
|
45
|
+
* [Recover](https://www.rubydoc.info/gems/secp256k1rb/Secp256k1/Recover)
|
|
46
|
+
* [SchnorrSig](https://www.rubydoc.info/gems/secp256k1rb/Secp256k1/SchnorrSig)
|
|
47
|
+
* [MuSig](https://www.rubydoc.info/gems/secp256k1rb/Secp256k1/MuSig)
|
|
48
|
+
* [EllSwift](https://www.rubydoc.info/gems/secp256k1rb/Secp256k1/EllSwift)
|
|
40
49
|
|
|
41
|
-
|
|
50
|
+
### Compatibility
|
|
42
51
|
|
|
43
|
-
|
|
52
|
+
secp256k1 version | secp256k1rb version
|
|
53
|
+
:---:|:---:
|
|
54
|
+
v0.4.0 | v0.1.x
|
|
55
|
+
v0.6.0 | v0.2.x
|
|
56
|
+
v0.7.1 | v0.3.x
|
data/lib/secp256k1/c.rb
CHANGED
|
@@ -11,16 +11,42 @@ module Secp256k1
|
|
|
11
11
|
attach_function(:secp256k1_context_create, [:uint], :pointer)
|
|
12
12
|
attach_function(:secp256k1_context_destroy, [:pointer], :void)
|
|
13
13
|
attach_function(:secp256k1_context_randomize, [:pointer, :pointer], :int)
|
|
14
|
+
attach_function(:secp256k1_context_clone, [:pointer], :pointer)
|
|
15
|
+
attach_function(:secp256k1_context_set_error_callback, [:pointer, :pointer, :pointer], :void)
|
|
16
|
+
attach_function(:secp256k1_context_set_illegal_callback, [:pointer, :pointer, :pointer], :void)
|
|
17
|
+
attach_function(:secp256k1_selftest, [], :void)
|
|
18
|
+
# Preallocated context management
|
|
19
|
+
attach_function(:secp256k1_context_preallocated_size, [:uint], :size_t)
|
|
20
|
+
attach_function(:secp256k1_context_preallocated_create, [:pointer, :uint], :pointer)
|
|
21
|
+
attach_function(:secp256k1_context_preallocated_clone_size, [:pointer], :size_t)
|
|
22
|
+
attach_function(:secp256k1_context_preallocated_clone, [:pointer, :pointer], :pointer)
|
|
23
|
+
attach_function(:secp256k1_context_preallocated_destroy, [:pointer], :void)
|
|
14
24
|
attach_function(:secp256k1_ec_pubkey_create, [:pointer, :pointer, :pointer], :int)
|
|
15
25
|
attach_function(:secp256k1_ec_seckey_verify, [:pointer, :pointer], :int)
|
|
26
|
+
# EC key arithmetic (tweak/negate/combine/compare/sort)
|
|
27
|
+
attach_function(:secp256k1_ec_seckey_negate, [:pointer, :pointer], :int)
|
|
28
|
+
attach_function(:secp256k1_ec_seckey_tweak_add, [:pointer, :pointer, :pointer], :int)
|
|
29
|
+
attach_function(:secp256k1_ec_seckey_tweak_mul, [:pointer, :pointer, :pointer], :int)
|
|
30
|
+
attach_function(:secp256k1_ec_pubkey_negate, [:pointer, :pointer], :int)
|
|
31
|
+
attach_function(:secp256k1_ec_pubkey_tweak_add, [:pointer, :pointer, :pointer], :int)
|
|
32
|
+
attach_function(:secp256k1_ec_pubkey_tweak_mul, [:pointer, :pointer, :pointer], :int)
|
|
33
|
+
attach_function(:secp256k1_ec_pubkey_combine, [:pointer, :pointer, :pointer, :size_t], :int)
|
|
34
|
+
attach_function(:secp256k1_ec_pubkey_cmp, [:pointer, :pointer, :pointer], :int)
|
|
35
|
+
attach_function(:secp256k1_ec_pubkey_sort, [:pointer, :pointer, :size_t], :int)
|
|
36
|
+
# Utilities
|
|
37
|
+
attach_function(:secp256k1_tagged_sha256, [:pointer, :pointer, :pointer, :size_t, :pointer, :size_t], :int)
|
|
16
38
|
attach_function(:secp256k1_ecdsa_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
|
|
17
39
|
attach_function(:secp256k1_ec_pubkey_serialize, [:pointer, :pointer, :pointer, :pointer, :uint], :int)
|
|
18
40
|
attach_function(:secp256k1_ecdsa_signature_serialize_der, [:pointer, :pointer, :pointer, :pointer], :int)
|
|
41
|
+
attach_function(:secp256k1_ecdsa_signature_serialize_compact, [:pointer, :pointer, :pointer], :int)
|
|
19
42
|
attach_function(:secp256k1_ec_pubkey_parse, [:pointer, :pointer, :pointer, :size_t], :int)
|
|
20
43
|
attach_function(:secp256k1_ecdsa_signature_parse_der, [:pointer, :pointer, :pointer, :size_t], :int)
|
|
44
|
+
attach_function(:secp256k1_ecdsa_signature_parse_compact, [:pointer, :pointer, :pointer], :int)
|
|
21
45
|
attach_function(:secp256k1_ecdsa_signature_normalize, [:pointer, :pointer, :pointer], :int)
|
|
22
46
|
attach_function(:secp256k1_ecdsa_verify, [:pointer, :pointer, :pointer, :pointer], :int)
|
|
23
47
|
attach_function(:secp256k1_schnorrsig_sign32, [:pointer, :pointer, :pointer, :pointer, :pointer], :int)
|
|
48
|
+
attach_function(:secp256k1_schnorrsig_sign, [:pointer, :pointer, :pointer, :pointer, :pointer], :int)
|
|
49
|
+
attach_function(:secp256k1_schnorrsig_sign_custom, [:pointer, :pointer, :pointer, :size_t, :pointer, :pointer], :int)
|
|
24
50
|
attach_function(:secp256k1_schnorrsig_verify, [:pointer, :pointer, :pointer, :size_t, :pointer], :int)
|
|
25
51
|
attach_function(:secp256k1_keypair_create, [:pointer, :pointer, :pointer], :int)
|
|
26
52
|
attach_function(:secp256k1_xonly_pubkey_parse, [:pointer, :pointer, :pointer], :int)
|
|
@@ -28,15 +54,62 @@ module Secp256k1
|
|
|
28
54
|
attach_function(:secp256k1_ecdsa_recoverable_signature_serialize_compact, [:pointer, :pointer, :pointer, :pointer], :int)
|
|
29
55
|
attach_function(:secp256k1_ecdsa_recover, [:pointer, :pointer, :pointer, :pointer], :int)
|
|
30
56
|
attach_function(:secp256k1_ecdsa_recoverable_signature_parse_compact, [:pointer, :pointer, :pointer, :int], :int)
|
|
57
|
+
attach_function(:secp256k1_ecdsa_recoverable_signature_convert, [:pointer, :pointer, :pointer], :int)
|
|
58
|
+
# for ECDH module
|
|
59
|
+
attach_function(:secp256k1_ecdh, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
|
|
60
|
+
attach_variable(:secp256k1_ecdh_hash_function_sha256, :pointer)
|
|
61
|
+
attach_variable(:secp256k1_ecdh_hash_function_default, :pointer)
|
|
62
|
+
# for EllSwift module
|
|
31
63
|
attach_function(:secp256k1_ellswift_decode, [:pointer, :pointer, :pointer], :int)
|
|
32
64
|
attach_function(:secp256k1_ellswift_create, [:pointer, :pointer, :pointer, :pointer], :int)
|
|
65
|
+
attach_function(:secp256k1_ellswift_encode, [:pointer, :pointer, :pointer, :pointer], :int)
|
|
33
66
|
attach_variable(:secp256k1_ellswift_xdh_hash_function_bip324, :pointer)
|
|
34
67
|
attach_function(:secp256k1_ellswift_xdh, [:pointer, :pointer, :pointer, :pointer, :pointer, :int, :pointer, :pointer], :int)
|
|
68
|
+
# for ExtraKeys module
|
|
69
|
+
attach_function(:secp256k1_xonly_pubkey_serialize, [:pointer, :pointer, :pointer], :int)
|
|
70
|
+
attach_function(:secp256k1_xonly_pubkey_cmp, [:pointer, :pointer, :pointer], :int)
|
|
71
|
+
attach_function(:secp256k1_xonly_pubkey_from_pubkey, [:pointer, :pointer, :pointer, :pointer], :int)
|
|
72
|
+
attach_function(:secp256k1_xonly_pubkey_tweak_add, [:pointer, :pointer, :pointer, :pointer], :int)
|
|
73
|
+
attach_function(:secp256k1_xonly_pubkey_tweak_add_check, [:pointer, :pointer, :int, :pointer, :pointer], :int)
|
|
74
|
+
attach_function(:secp256k1_keypair_pub, [:pointer, :pointer, :pointer], :int)
|
|
75
|
+
attach_function(:secp256k1_keypair_sec, [:pointer, :pointer, :pointer], :int)
|
|
76
|
+
attach_function(:secp256k1_keypair_xonly_pub, [:pointer, :pointer, :pointer, :pointer], :int)
|
|
77
|
+
attach_function(:secp256k1_keypair_xonly_tweak_add, [:pointer, :pointer, :pointer], :int)
|
|
78
|
+
# for MuSig module
|
|
79
|
+
attach_function(:secp256k1_musig_pubkey_agg, [:pointer, :pointer, :pointer, :pointer, :size_t], :int)
|
|
80
|
+
attach_function(:secp256k1_musig_pubkey_get, [:pointer, :pointer, :pointer], :int)
|
|
81
|
+
attach_function(:secp256k1_musig_pubkey_ec_tweak_add, [:pointer, :pointer, :pointer, :pointer], :int)
|
|
82
|
+
attach_function(:secp256k1_musig_pubkey_xonly_tweak_add, [:pointer, :pointer, :pointer, :pointer], :int)
|
|
83
|
+
attach_function(:secp256k1_musig_nonce_gen, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
|
|
84
|
+
attach_function(:secp256k1_musig_nonce_gen_counter, [:pointer, :pointer, :pointer, :uint64, :pointer, :pointer, :pointer, :pointer], :int)
|
|
85
|
+
attach_function(:secp256k1_musig_pubnonce_parse, [:pointer, :pointer, :pointer], :int)
|
|
86
|
+
attach_function(:secp256k1_musig_pubnonce_serialize, [:pointer, :pointer, :pointer], :int)
|
|
87
|
+
attach_function(:secp256k1_musig_aggnonce_serialize, [:pointer, :pointer, :pointer], :int)
|
|
88
|
+
attach_function(:secp256k1_musig_aggnonce_parse, [:pointer, :pointer, :pointer], :int)
|
|
89
|
+
attach_function(:secp256k1_musig_nonce_agg, [:pointer, :pointer, :pointer, :size_t], :int)
|
|
90
|
+
attach_function(:secp256k1_musig_nonce_process, [:pointer, :pointer, :pointer, :pointer, :pointer], :int)
|
|
91
|
+
attach_function(:secp256k1_musig_partial_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
|
|
92
|
+
attach_function(:secp256k1_musig_partial_sig_serialize, [:pointer, :pointer, :pointer], :int)
|
|
93
|
+
attach_function(:secp256k1_musig_partial_sig_parse, [:pointer, :pointer, :pointer], :int)
|
|
94
|
+
attach_function(:secp256k1_musig_partial_sig_verify, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
|
|
95
|
+
attach_function(:secp256k1_musig_partial_sig_agg, [:pointer, :pointer, :pointer, :pointer, :size_t], :int)
|
|
35
96
|
|
|
36
97
|
# Pointer to secp256k1_ellswift_xdh_hash_function_bip324 constant.
|
|
37
98
|
# @return [FFI::Pointer]
|
|
38
99
|
def self.ellswift_xdh_hash_function_bip324
|
|
39
100
|
FFI::Pointer.new(secp256k1_ellswift_xdh_hash_function_bip324)
|
|
40
101
|
end
|
|
102
|
+
|
|
103
|
+
# Pointer to secp256k1_ecdh_hash_function_sha256 constant (the default ECDH hash function).
|
|
104
|
+
# @return [FFI::Pointer]
|
|
105
|
+
def self.ecdh_hash_function_sha256
|
|
106
|
+
FFI::Pointer.new(secp256k1_ecdh_hash_function_sha256)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Pointer to secp256k1_ecdh_hash_function_default constant.
|
|
110
|
+
# @return [FFI::Pointer]
|
|
111
|
+
def self.ecdh_hash_function_default
|
|
112
|
+
FFI::Pointer.new(secp256k1_ecdh_hash_function_default)
|
|
113
|
+
end
|
|
41
114
|
end
|
|
42
115
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Secp256k1
|
|
2
|
+
# ECDH module.
|
|
3
|
+
# @example
|
|
4
|
+
# include Secp256k1
|
|
5
|
+
#
|
|
6
|
+
# alice_sk, alice_pk = generate_key_pair
|
|
7
|
+
# bob_sk, bob_pk = generate_key_pair
|
|
8
|
+
#
|
|
9
|
+
# # Both parties compute the same shared secret.
|
|
10
|
+
# ecdh(bob_pk, alice_sk) == ecdh(alice_pk, bob_sk)
|
|
11
|
+
#
|
|
12
|
+
module ECDH
|
|
13
|
+
|
|
14
|
+
# Compute an EC Diffie-Hellman shared secret.
|
|
15
|
+
# @param [String] pubkey the other party's public key with hex format.
|
|
16
|
+
# @param [String] private_key your private key with hex format.
|
|
17
|
+
# @param [FFI::Pointer] hash_function (Optional)A pointer to the hash function to use. If omitted, the
|
|
18
|
+
# library default(SHA256 of the compressed point) is used. See {Secp256k1::C.ecdh_hash_function_sha256}.
|
|
19
|
+
# @return [String] 32-byte shared secret with hex format.
|
|
20
|
+
# @raise [Secp256k1::Error] If the secret was invalid or the public key could not be parsed.
|
|
21
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
22
|
+
def ecdh(pubkey, private_key, hash_function: nil)
|
|
23
|
+
raise ArgumentError, "pubkey must be String." unless pubkey.is_a?(String)
|
|
24
|
+
validate_string!("private_key", private_key, 32)
|
|
25
|
+
pubkey = hex2bin(pubkey)
|
|
26
|
+
private_key = hex2bin(private_key)
|
|
27
|
+
with_context do |context|
|
|
28
|
+
internal = parse_pubkey_internal(context, pubkey)
|
|
29
|
+
seckey = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, private_key)
|
|
30
|
+
output = FFI::MemoryPointer.new(:uchar, 32)
|
|
31
|
+
hashfp = hash_function || FFI::Pointer::NULL
|
|
32
|
+
raise Error, 'secp256k1_ecdh failed.' unless secp256k1_ecdh(context, output, internal, seckey, hashfp, nil) == 1
|
|
33
|
+
output.read_string(32).unpack1('H*')
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
data/lib/secp256k1/ellswift.rb
CHANGED
|
@@ -8,9 +8,8 @@ module Secp256k1
|
|
|
8
8
|
# @raise [Secp256k1::Error] If decode failed.
|
|
9
9
|
# @raise [ArgumentError] If invalid arguments specified.
|
|
10
10
|
def ellswift_decode(ell_key, compressed: true)
|
|
11
|
-
|
|
11
|
+
validate_string!("ell_key", ell_key, ELL_SWIFT_KEY_SIZE)
|
|
12
12
|
ell_key = hex2bin(ell_key)
|
|
13
|
-
raise ArgumentError, "ell_key must be 64 bytes." unless ell_key.bytesize == 64
|
|
14
13
|
with_context do |context|
|
|
15
14
|
ell64 = FFI::MemoryPointer.new(:uchar, ell_key.bytesize).put_bytes(0, ell_key)
|
|
16
15
|
internal = FFI::MemoryPointer.new(:uchar, 64)
|
|
@@ -20,15 +19,34 @@ module Secp256k1
|
|
|
20
19
|
end
|
|
21
20
|
end
|
|
22
21
|
|
|
22
|
+
# Encode a public key into an ElligatorSwift public key.
|
|
23
|
+
# @param [String] pubkey public key with hex format.
|
|
24
|
+
# @param [String] rnd (Optional)32-byte randomness with binary format. If omitted, random bytes are used.
|
|
25
|
+
# @return [String] ElligatorSwift public key with hex format(64 bytes).
|
|
26
|
+
# @raise [Secp256k1::Error] If encoding failed.
|
|
27
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
28
|
+
def ellswift_encode(pubkey, rnd = nil)
|
|
29
|
+
raise ArgumentError, "pubkey must be String." unless pubkey.is_a?(String)
|
|
30
|
+
validate_string!("rnd", rnd, 32) if rnd
|
|
31
|
+
pubkey = hex2bin(pubkey)
|
|
32
|
+
rnd = rnd ? hex2bin(rnd) : SecureRandom.random_bytes(32)
|
|
33
|
+
with_context do |context|
|
|
34
|
+
internal = parse_pubkey_internal(context, pubkey)
|
|
35
|
+
ell64 = FFI::MemoryPointer.new(:uchar, 64)
|
|
36
|
+
rnd32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, rnd)
|
|
37
|
+
raise Error, 'Failed to encode ElligatorSwift public key.' unless secp256k1_ellswift_encode(context, ell64, internal, rnd32) == 1
|
|
38
|
+
ell64.read_string(64).unpack1('H*')
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
23
42
|
# Compute an ElligatorSwift public key for a secret key.
|
|
24
43
|
# @param [String] private_key private key with hex format
|
|
25
44
|
# @return [String] ElligatorSwift public key with hex format.
|
|
26
45
|
# @raise [Secp256k1::Error] If failed to create elligattor swhift public key.
|
|
27
46
|
# @raise [ArgumentError] If invalid arguments specified.
|
|
28
47
|
def ellswift_create(private_key)
|
|
29
|
-
|
|
48
|
+
validate_string!("private_key", private_key, 32)
|
|
30
49
|
private_key = hex2bin(private_key)
|
|
31
|
-
raise ArgumentError, "private_key must be 32 bytes." unless private_key.bytesize == 32
|
|
32
50
|
with_context(flags: CONTEXT_SIGN) do |context|
|
|
33
51
|
ell64 = FFI::MemoryPointer.new(:uchar, 64)
|
|
34
52
|
seckey32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, private_key)
|
|
@@ -46,15 +64,12 @@ module Secp256k1
|
|
|
46
64
|
# @return [String] x coordinate with hex format.
|
|
47
65
|
# @raise [Secp256k1::Error] If secret is invalid or hashfp return 0.
|
|
48
66
|
def ellswift_ecdh_xonly(their_ell_pubkey, our_ell_pubkey, private_key, initiating)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
67
|
+
validate_string!("their_ell_pubkey", their_ell_pubkey, ELL_SWIFT_KEY_SIZE)
|
|
68
|
+
validate_string!("our_ell_pubkey", our_ell_pubkey, ELL_SWIFT_KEY_SIZE)
|
|
69
|
+
validate_string!("private_key", private_key, 32)
|
|
52
70
|
their_ell_pubkey = hex2bin(their_ell_pubkey)
|
|
53
71
|
our_ell_pubkey = hex2bin(our_ell_pubkey)
|
|
54
72
|
private_key = hex2bin(private_key)
|
|
55
|
-
raise ArgumentError, "their_ell_pubkey must be #{ELL_SWIFT_KEY_SIZE} bytes." unless their_ell_pubkey.bytesize == ELL_SWIFT_KEY_SIZE
|
|
56
|
-
raise ArgumentError, "our_ell_pubkey must be #{ELL_SWIFT_KEY_SIZE} bytes." unless our_ell_pubkey.bytesize == ELL_SWIFT_KEY_SIZE
|
|
57
|
-
raise ArgumentError, "private_key must be 32 bytes." unless private_key.bytesize == 32
|
|
58
73
|
|
|
59
74
|
with_context(flags: CONTEXT_SIGN) do |context|
|
|
60
75
|
output = FFI::MemoryPointer.new(:uchar, 32)
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
module Secp256k1
|
|
2
|
+
# Key module provides EC key arithmetic (tweak/negate/combine), x-only public key
|
|
3
|
+
# operations used by Taproot, and key pair accessors.
|
|
4
|
+
# @example
|
|
5
|
+
# include Secp256k1
|
|
6
|
+
#
|
|
7
|
+
# sk, pk = generate_key_pair
|
|
8
|
+
# tweak = SecureRandom.bytes(32)
|
|
9
|
+
#
|
|
10
|
+
# # Tweak a private/public key.
|
|
11
|
+
# tweaked_sk = tweak_add_seckey(sk, tweak)
|
|
12
|
+
# tweaked_pk = tweak_add_pubkey(pk, tweak)
|
|
13
|
+
#
|
|
14
|
+
module Key
|
|
15
|
+
|
|
16
|
+
# Negate a private key in place and return the result.
|
|
17
|
+
# @param [String] private_key a private key with hex format.
|
|
18
|
+
# @return [String] negated private key with hex format.
|
|
19
|
+
# @raise [Secp256k1::Error] If the private key is invalid.
|
|
20
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
21
|
+
def negate_seckey(private_key)
|
|
22
|
+
validate_string!("private_key", private_key, 32)
|
|
23
|
+
private_key = hex2bin(private_key)
|
|
24
|
+
with_context do |context|
|
|
25
|
+
seckey = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, private_key)
|
|
26
|
+
raise Error, 'secp256k1_ec_seckey_negate failed.' unless secp256k1_ec_seckey_negate(context, seckey) == 1
|
|
27
|
+
seckey.read_string(32).unpack1('H*')
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Tweak a private key by adding +tweak+ to it.
|
|
32
|
+
# @param [String] private_key a private key with hex format.
|
|
33
|
+
# @param [String] tweak a 32-byte tweak with hex format.
|
|
34
|
+
# @return [String] tweaked private key with hex format.
|
|
35
|
+
# @raise [Secp256k1::Error] If the arguments are invalid or the result is the zero key.
|
|
36
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
37
|
+
def tweak_add_seckey(private_key, tweak)
|
|
38
|
+
tweak_seckey(private_key, tweak, :secp256k1_ec_seckey_tweak_add)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Tweak a private key by multiplying it by +tweak+.
|
|
42
|
+
# @param [String] private_key a private key with hex format.
|
|
43
|
+
# @param [String] tweak a 32-byte tweak with hex format.
|
|
44
|
+
# @return [String] tweaked private key with hex format.
|
|
45
|
+
# @raise [Secp256k1::Error] If the arguments are invalid.
|
|
46
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
47
|
+
def tweak_mul_seckey(private_key, tweak)
|
|
48
|
+
tweak_seckey(private_key, tweak, :secp256k1_ec_seckey_tweak_mul)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Negate a public key.
|
|
52
|
+
# @param [String] pubkey a public key with hex format.
|
|
53
|
+
# @param [Boolean] compressed Whether to return a compressed public key.
|
|
54
|
+
# @return [String] negated public key with hex format.
|
|
55
|
+
# @raise [Secp256k1::Error] If the public key is invalid.
|
|
56
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
57
|
+
def negate_pubkey(pubkey, compressed: true)
|
|
58
|
+
raise ArgumentError, "pubkey must be String." unless pubkey.is_a?(String)
|
|
59
|
+
pubkey = hex2bin(pubkey)
|
|
60
|
+
with_context do |context|
|
|
61
|
+
internal = parse_pubkey_internal(context, pubkey)
|
|
62
|
+
raise Error, 'secp256k1_ec_pubkey_negate failed.' unless secp256k1_ec_pubkey_negate(context, internal) == 1
|
|
63
|
+
serialize_pubkey_internal(context, internal, compressed)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Tweak a public key by adding +tweak+ * G to it.
|
|
68
|
+
# @param [String] pubkey a public key with hex format.
|
|
69
|
+
# @param [String] tweak a 32-byte tweak with hex format.
|
|
70
|
+
# @param [Boolean] compressed Whether to return a compressed public key.
|
|
71
|
+
# @return [String] tweaked public key with hex format.
|
|
72
|
+
# @raise [Secp256k1::Error] If the arguments are invalid or the result is the point at infinity.
|
|
73
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
74
|
+
def tweak_add_pubkey(pubkey, tweak, compressed: true)
|
|
75
|
+
tweak_pubkey(pubkey, tweak, :secp256k1_ec_pubkey_tweak_add, compressed: compressed)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Tweak a public key by multiplying it by +tweak+.
|
|
79
|
+
# @param [String] pubkey a public key with hex format.
|
|
80
|
+
# @param [String] tweak a 32-byte tweak with hex format.
|
|
81
|
+
# @param [Boolean] compressed Whether to return a compressed public key.
|
|
82
|
+
# @return [String] tweaked public key with hex format.
|
|
83
|
+
# @raise [Secp256k1::Error] If the arguments are invalid.
|
|
84
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
85
|
+
def tweak_mul_pubkey(pubkey, tweak, compressed: true)
|
|
86
|
+
tweak_pubkey(pubkey, tweak, :secp256k1_ec_pubkey_tweak_mul, compressed: compressed)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Add a number of public keys together.
|
|
90
|
+
# @param [Array<String>] pubkeys an array of public keys with hex format.
|
|
91
|
+
# @param [Boolean] compressed Whether to return a compressed public key.
|
|
92
|
+
# @return [String] the sum of the public keys with hex format.
|
|
93
|
+
# @raise [Secp256k1::Error] If the sum is the point at infinity.
|
|
94
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
95
|
+
def combine_pubkeys(pubkeys, compressed: true)
|
|
96
|
+
raise ArgumentError, "pubkeys must be Array." unless pubkeys.is_a?(Array)
|
|
97
|
+
raise ArgumentError, "pubkeys must not be empty." if pubkeys.empty?
|
|
98
|
+
with_context do |context|
|
|
99
|
+
internals = pubkeys.map do |pubkey|
|
|
100
|
+
raise ArgumentError, "pubkey must be String." unless pubkey.is_a?(String)
|
|
101
|
+
parse_pubkey_internal(context, hex2bin(pubkey))
|
|
102
|
+
end
|
|
103
|
+
ins = FFI::MemoryPointer.new(:pointer, internals.size)
|
|
104
|
+
ins.write_array_of_pointer(internals)
|
|
105
|
+
combined = FFI::MemoryPointer.new(:uchar, 64)
|
|
106
|
+
raise Error, 'secp256k1_ec_pubkey_combine failed.' unless secp256k1_ec_pubkey_combine(context, combined, ins, internals.size) == 1
|
|
107
|
+
serialize_pubkey_internal(context, combined, compressed)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Convert a public key into an x-only public key.
|
|
112
|
+
# @param [String] pubkey a public key with hex format.
|
|
113
|
+
# @return [Array(String, Integer)] the x-only public key with hex format(32 bytes) and its parity(0 or 1).
|
|
114
|
+
# @raise [Secp256k1::Error] If the public key is invalid.
|
|
115
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
116
|
+
def xonly_pubkey_from_pubkey(pubkey)
|
|
117
|
+
raise ArgumentError, "pubkey must be String." unless pubkey.is_a?(String)
|
|
118
|
+
pubkey = hex2bin(pubkey)
|
|
119
|
+
with_context do |context|
|
|
120
|
+
internal = parse_pubkey_internal(context, pubkey)
|
|
121
|
+
xonly = FFI::MemoryPointer.new(:uchar, 64)
|
|
122
|
+
parity = FFI::MemoryPointer.new(:int)
|
|
123
|
+
raise Error, 'secp256k1_xonly_pubkey_from_pubkey failed.' unless secp256k1_xonly_pubkey_from_pubkey(context, xonly, parity, internal) == 1
|
|
124
|
+
[serialize_xonly_pubkey_internal(context, xonly), parity.read_int]
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Tweak an x-only public key by adding +tweak+ * G to it (used for Taproot output key derivation).
|
|
129
|
+
# @param [String] xonly_pubkey an x-only public key with hex format(32 bytes).
|
|
130
|
+
# @param [String] tweak a 32-byte tweak with hex format.
|
|
131
|
+
# @param [Boolean] compressed Whether to return a compressed public key.
|
|
132
|
+
# @return [Array(String, Integer)] the tweaked(full) public key with hex format and the parity(0 or 1) of the tweaked key.
|
|
133
|
+
# @raise [Secp256k1::Error] If the arguments are invalid.
|
|
134
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
135
|
+
def xonly_tweak_add_pubkey(xonly_pubkey, tweak, compressed: true)
|
|
136
|
+
validate_string!("xonly_pubkey", xonly_pubkey, X_ONLY_PUBKEY_SIZE)
|
|
137
|
+
validate_string!("tweak", tweak, 32)
|
|
138
|
+
xonly_pubkey = hex2bin(xonly_pubkey)
|
|
139
|
+
tweak = hex2bin(tweak)
|
|
140
|
+
with_context do |context|
|
|
141
|
+
xonly = FFI::MemoryPointer.new(:uchar, X_ONLY_PUBKEY_SIZE).put_bytes(0, xonly_pubkey)
|
|
142
|
+
internal = FFI::MemoryPointer.new(:uchar, 64)
|
|
143
|
+
raise Error, 'An invalid x-only public key was specified.' unless secp256k1_xonly_pubkey_parse(context, internal, xonly) == 1
|
|
144
|
+
tweak_ptr = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, tweak)
|
|
145
|
+
output = FFI::MemoryPointer.new(:uchar, 64)
|
|
146
|
+
raise Error, 'secp256k1_xonly_pubkey_tweak_add failed.' unless secp256k1_xonly_pubkey_tweak_add(context, output, internal, tweak_ptr) == 1
|
|
147
|
+
_, parity = xonly_pubkey_from_internal(context, output)
|
|
148
|
+
[serialize_pubkey_internal(context, output, compressed), parity]
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Check that a tweaked x-only public key was computed by tweaking +internal_pubkey+ with +tweak+.
|
|
153
|
+
# @param [String] tweaked_pubkey32 the tweaked x-only public key with hex format(32 bytes).
|
|
154
|
+
# @param [Integer] tweaked_pk_parity the parity(0 or 1) of the tweaked public key.
|
|
155
|
+
# @param [String] internal_pubkey the internal x-only public key with hex format(32 bytes).
|
|
156
|
+
# @param [String] tweak a 32-byte tweak with hex format.
|
|
157
|
+
# @return [Boolean] verification result.
|
|
158
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
159
|
+
def xonly_tweak_add_check?(tweaked_pubkey32, tweaked_pk_parity, internal_pubkey, tweak)
|
|
160
|
+
validate_string!("tweaked_pubkey32", tweaked_pubkey32, X_ONLY_PUBKEY_SIZE)
|
|
161
|
+
validate_string!("internal_pubkey", internal_pubkey, X_ONLY_PUBKEY_SIZE)
|
|
162
|
+
validate_string!("tweak", tweak, 32)
|
|
163
|
+
raise ArgumentError, "tweaked_pk_parity must be 0 or 1." unless [0, 1].include?(tweaked_pk_parity)
|
|
164
|
+
tweaked_pubkey32 = hex2bin(tweaked_pubkey32)
|
|
165
|
+
internal_pubkey = hex2bin(internal_pubkey)
|
|
166
|
+
tweak = hex2bin(tweak)
|
|
167
|
+
with_context do |context|
|
|
168
|
+
internal = FFI::MemoryPointer.new(:uchar, X_ONLY_PUBKEY_SIZE).put_bytes(0, internal_pubkey)
|
|
169
|
+
internal_xonly = FFI::MemoryPointer.new(:uchar, 64)
|
|
170
|
+
return false unless secp256k1_xonly_pubkey_parse(context, internal_xonly, internal) == 1
|
|
171
|
+
tweaked_ptr = FFI::MemoryPointer.new(:uchar, X_ONLY_PUBKEY_SIZE).put_bytes(0, tweaked_pubkey32)
|
|
172
|
+
tweak_ptr = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, tweak)
|
|
173
|
+
secp256k1_xonly_pubkey_tweak_add_check(context, tweaked_ptr, tweaked_pk_parity, internal_xonly, tweak_ptr) == 1
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Get the public key from a key pair.
|
|
178
|
+
# @param [String] keypair a key pair with hex format(96 bytes), created by {#create_keypair}.
|
|
179
|
+
# @param [Boolean] compressed Whether to return a compressed public key.
|
|
180
|
+
# @return [String] public key with hex format.
|
|
181
|
+
# @raise [Secp256k1::Error] If the key pair is invalid.
|
|
182
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
183
|
+
def keypair_to_pubkey(keypair, compressed: true)
|
|
184
|
+
validate_string!("keypair", keypair, 96)
|
|
185
|
+
keypair = hex2bin(keypair)
|
|
186
|
+
with_context do |context|
|
|
187
|
+
keypair_ptr = FFI::MemoryPointer.new(:uchar, 96).put_bytes(0, keypair)
|
|
188
|
+
internal = FFI::MemoryPointer.new(:uchar, 64)
|
|
189
|
+
raise Error, 'secp256k1_keypair_pub failed.' unless secp256k1_keypair_pub(context, internal, keypair_ptr) == 1
|
|
190
|
+
serialize_pubkey_internal(context, internal, compressed)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Get the private key from a key pair.
|
|
195
|
+
# @param [String] keypair a key pair with hex format(96 bytes), created by {#create_keypair}.
|
|
196
|
+
# @return [String] private key with hex format.
|
|
197
|
+
# @raise [Secp256k1::Error] If the key pair is invalid.
|
|
198
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
199
|
+
def keypair_to_seckey(keypair)
|
|
200
|
+
validate_string!("keypair", keypair, 96)
|
|
201
|
+
keypair = hex2bin(keypair)
|
|
202
|
+
with_context do |context|
|
|
203
|
+
keypair_ptr = FFI::MemoryPointer.new(:uchar, 96).put_bytes(0, keypair)
|
|
204
|
+
seckey = FFI::MemoryPointer.new(:uchar, 32)
|
|
205
|
+
raise Error, 'secp256k1_keypair_sec failed.' unless secp256k1_keypair_sec(context, seckey, keypair_ptr) == 1
|
|
206
|
+
seckey.read_string(32).unpack1('H*')
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Get the x-only public key from a key pair.
|
|
211
|
+
# @param [String] keypair a key pair with hex format(96 bytes), created by {#create_keypair}.
|
|
212
|
+
# @return [Array(String, Integer)] the x-only public key with hex format(32 bytes) and its parity(0 or 1).
|
|
213
|
+
# @raise [Secp256k1::Error] If the key pair is invalid.
|
|
214
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
215
|
+
def keypair_to_xonly_pubkey(keypair)
|
|
216
|
+
validate_string!("keypair", keypair, 96)
|
|
217
|
+
keypair = hex2bin(keypair)
|
|
218
|
+
with_context do |context|
|
|
219
|
+
keypair_ptr = FFI::MemoryPointer.new(:uchar, 96).put_bytes(0, keypair)
|
|
220
|
+
xonly = FFI::MemoryPointer.new(:uchar, 64)
|
|
221
|
+
parity = FFI::MemoryPointer.new(:int)
|
|
222
|
+
raise Error, 'secp256k1_keypair_xonly_pub failed.' unless secp256k1_keypair_xonly_pub(context, xonly, parity, keypair_ptr) == 1
|
|
223
|
+
[serialize_xonly_pubkey_internal(context, xonly), parity.read_int]
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Compare two public keys using lexicographic(of compressed serialization) order.
|
|
228
|
+
# @param [String] pubkey1 a public key with hex format.
|
|
229
|
+
# @param [String] pubkey2 a public key with hex format.
|
|
230
|
+
# @return [Integer] negative if pubkey1 < pubkey2, 0 if equal, positive if pubkey1 > pubkey2.
|
|
231
|
+
# @raise [Secp256k1::Error] If a public key is invalid.
|
|
232
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
233
|
+
def compare_pubkey(pubkey1, pubkey2)
|
|
234
|
+
raise ArgumentError, "pubkey1 must be String." unless pubkey1.is_a?(String)
|
|
235
|
+
raise ArgumentError, "pubkey2 must be String." unless pubkey2.is_a?(String)
|
|
236
|
+
with_context do |context|
|
|
237
|
+
a = parse_pubkey_internal(context, hex2bin(pubkey1))
|
|
238
|
+
b = parse_pubkey_internal(context, hex2bin(pubkey2))
|
|
239
|
+
secp256k1_ec_pubkey_cmp(context, a, b)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Sort public keys using lexicographic(of compressed serialization) order.
|
|
244
|
+
# @param [Array<String>] pubkeys an array of public keys with hex format.
|
|
245
|
+
# @param [Boolean] compressed Whether to return compressed public keys.
|
|
246
|
+
# @return [Array<String>] the sorted public keys with hex format.
|
|
247
|
+
# @raise [Secp256k1::Error] If a public key is invalid.
|
|
248
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
249
|
+
def sort_pubkeys(pubkeys, compressed: true)
|
|
250
|
+
raise ArgumentError, "pubkeys must be Array." unless pubkeys.is_a?(Array)
|
|
251
|
+
raise ArgumentError, "pubkeys must not be empty." if pubkeys.empty?
|
|
252
|
+
with_context do |context|
|
|
253
|
+
internals = pubkeys.map do |pubkey|
|
|
254
|
+
raise ArgumentError, "pubkey must be String." unless pubkey.is_a?(String)
|
|
255
|
+
parse_pubkey_internal(context, hex2bin(pubkey))
|
|
256
|
+
end
|
|
257
|
+
arr = FFI::MemoryPointer.new(:pointer, internals.size)
|
|
258
|
+
arr.write_array_of_pointer(internals)
|
|
259
|
+
raise Error, 'secp256k1_ec_pubkey_sort failed.' unless secp256k1_ec_pubkey_sort(context, arr, internals.size) == 1
|
|
260
|
+
arr.read_array_of_pointer(internals.size).map { |ptr| serialize_pubkey_internal(context, ptr, compressed) }
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Compare two x-only public keys using lexicographic order.
|
|
265
|
+
# @param [String] pubkey1 an x-only public key with hex format(32 bytes).
|
|
266
|
+
# @param [String] pubkey2 an x-only public key with hex format(32 bytes).
|
|
267
|
+
# @return [Integer] negative if pubkey1 < pubkey2, 0 if equal, positive if pubkey1 > pubkey2.
|
|
268
|
+
# @raise [Secp256k1::Error] If a public key is invalid.
|
|
269
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
270
|
+
def compare_xonly_pubkey(pubkey1, pubkey2)
|
|
271
|
+
validate_string!("pubkey1", pubkey1, X_ONLY_PUBKEY_SIZE)
|
|
272
|
+
validate_string!("pubkey2", pubkey2, X_ONLY_PUBKEY_SIZE)
|
|
273
|
+
with_context do |context|
|
|
274
|
+
a = parse_xonly_pubkey_internal(context, hex2bin(pubkey1))
|
|
275
|
+
b = parse_xonly_pubkey_internal(context, hex2bin(pubkey2))
|
|
276
|
+
secp256k1_xonly_pubkey_cmp(context, a, b)
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Tweak a key pair by adding +tweak+ to the key pair's x-only context.
|
|
281
|
+
# @param [String] keypair a key pair with hex format(96 bytes), created by {#create_keypair}.
|
|
282
|
+
# @param [String] tweak a 32-byte tweak with hex format.
|
|
283
|
+
# @return [String] the tweaked key pair with hex format(96 bytes).
|
|
284
|
+
# @raise [Secp256k1::Error] If the arguments are invalid.
|
|
285
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
|
286
|
+
def keypair_xonly_tweak_add(keypair, tweak)
|
|
287
|
+
validate_string!("keypair", keypair, 96)
|
|
288
|
+
validate_string!("tweak", tweak, 32)
|
|
289
|
+
keypair = hex2bin(keypair)
|
|
290
|
+
tweak = hex2bin(tweak)
|
|
291
|
+
with_context do |context|
|
|
292
|
+
keypair_ptr = FFI::MemoryPointer.new(:uchar, 96).put_bytes(0, keypair)
|
|
293
|
+
tweak_ptr = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, tweak)
|
|
294
|
+
raise Error, 'secp256k1_keypair_xonly_tweak_add failed.' unless secp256k1_keypair_xonly_tweak_add(context, keypair_ptr, tweak_ptr) == 1
|
|
295
|
+
keypair_ptr.read_string(96).unpack1('H*')
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
private
|
|
300
|
+
|
|
301
|
+
def parse_xonly_pubkey_internal(context, xonly_pubkey)
|
|
302
|
+
xonly = FFI::MemoryPointer.new(:uchar, X_ONLY_PUBKEY_SIZE).put_bytes(0, xonly_pubkey)
|
|
303
|
+
internal = FFI::MemoryPointer.new(:uchar, 64)
|
|
304
|
+
raise Error, 'An invalid x-only public key was specified.' unless secp256k1_xonly_pubkey_parse(context, internal, xonly) == 1
|
|
305
|
+
internal
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def tweak_seckey(private_key, tweak, func)
|
|
309
|
+
validate_string!("private_key", private_key, 32)
|
|
310
|
+
validate_string!("tweak", tweak, 32)
|
|
311
|
+
private_key = hex2bin(private_key)
|
|
312
|
+
tweak = hex2bin(tweak)
|
|
313
|
+
with_context do |context|
|
|
314
|
+
seckey = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, private_key)
|
|
315
|
+
tweak_ptr = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, tweak)
|
|
316
|
+
raise Error, "#{func} failed." unless send(func, context, seckey, tweak_ptr) == 1
|
|
317
|
+
seckey.read_string(32).unpack1('H*')
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def tweak_pubkey(pubkey, tweak, func, compressed: true)
|
|
322
|
+
raise ArgumentError, "pubkey must be String." unless pubkey.is_a?(String)
|
|
323
|
+
validate_string!("tweak", tweak, 32)
|
|
324
|
+
pubkey = hex2bin(pubkey)
|
|
325
|
+
tweak = hex2bin(tweak)
|
|
326
|
+
with_context do |context|
|
|
327
|
+
internal = parse_pubkey_internal(context, pubkey)
|
|
328
|
+
tweak_ptr = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, tweak)
|
|
329
|
+
raise Error, "#{func} failed." unless send(func, context, internal, tweak_ptr) == 1
|
|
330
|
+
serialize_pubkey_internal(context, internal, compressed)
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# Get x-only pubkey and parity from an internal(64 bytes) full pubkey pointer.
|
|
335
|
+
def xonly_pubkey_from_internal(context, internal_pubkey)
|
|
336
|
+
xonly = FFI::MemoryPointer.new(:uchar, 64)
|
|
337
|
+
parity = FFI::MemoryPointer.new(:int)
|
|
338
|
+
raise Error, 'secp256k1_xonly_pubkey_from_pubkey failed.' unless secp256k1_xonly_pubkey_from_pubkey(context, xonly, parity, internal_pubkey) == 1
|
|
339
|
+
[serialize_xonly_pubkey_internal(context, xonly), parity.read_int]
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|