Rework patch. Implement Schnorr signing and undo breaking changes.
This commit is contained in:
parent
7a36c249f8
commit
e75a1e2c82
104
secp256k1.nim
104
secp256k1.nim
|
@ -54,6 +54,9 @@ const
|
|||
SkRawRecoverableSignatureSize* = 65
|
||||
## Size of recoverable signature in octets (bytes)
|
||||
|
||||
SkRawSchnorrSignatureSize* = 64
|
||||
## Size of Schnorr signature in octets (bytes)
|
||||
|
||||
SkRawPublicKeySize* = 65
|
||||
## Size of uncompressed public key in octets (bytes)
|
||||
|
||||
|
@ -80,7 +83,8 @@ type
|
|||
|
||||
SkKeyPair* = object
|
||||
## Representation of private/public keys pair.
|
||||
data: secp256k1_keypair
|
||||
seckey*: SkSecretKey
|
||||
pubkey*: SkPublicKey
|
||||
|
||||
SkSignature* {.requiresInit.} = object
|
||||
## Representation of non-recoverable signature.
|
||||
|
@ -90,6 +94,10 @@ type
|
|||
## Representation of recoverable signature.
|
||||
data: secp256k1_ecdsa_recoverable_signature
|
||||
|
||||
SkSchnorrSignature* {.requiresInit.} = object
|
||||
## Representation of a Schnorr signature.
|
||||
data: array[SkRawSchnorrSignatureSize, byte]
|
||||
|
||||
SkContext = object
|
||||
## Representation of Secp256k1 context object.
|
||||
context: ptr secp256k1_context
|
||||
|
@ -391,58 +399,29 @@ func toRaw*(sig: SkRecoverableSignature): array[SkRawRecoverableSignatureSize, b
|
|||
var recid = cint(0)
|
||||
let res = secp256k1_ecdsa_recoverable_signature_serialize_compact(
|
||||
secp256k1_context_no_precomp, result.baseAddr, addr recid, unsafeAddr sig.data)
|
||||
doAssert res == 1, "can't fail, per documentation"
|
||||
doAssert res == 1, "Can't fail, per documentation"
|
||||
|
||||
result[64] = byte(recid)
|
||||
|
||||
func toHex*(sig: SkRecoverableSignature): string =
|
||||
toHex(toRaw(sig))
|
||||
|
||||
func pubkey*(kp: SkKeyPair): SkPublicKey =
|
||||
var key {.noinit.}: secp256k1_pubkey
|
||||
let res = secp256k1_keypair_pub(secp256k1_context_no_precomp, addr key, addr kp.data)
|
||||
doAssert res == 1, "Can't fail, per documentation"
|
||||
SkPublicKey(data: key)
|
||||
|
||||
func seckey*(kp: SkKeyPair): SkSecretKey =
|
||||
var key {.noinit.}: array[SkRawSecretKeySize, byte]
|
||||
let res = secp256k1_keypair_sec(secp256k1_context_no_precomp, key.baseAddr, addr kp.data)
|
||||
doAssert res == 1, "Can't fail, per documentation"
|
||||
SkSecretKey(data: key)
|
||||
|
||||
func `pubkey=`*(kp: var SkKeyPair, sk: SkPublicKey) {.deprecated: "Set the seckey instead".} = discard
|
||||
|
||||
func `seckey=`*(kp: var SkKeyPair, sk: SkSecretKey) =
|
||||
var newKp: SkKeyPair
|
||||
let res = secp256k1_keypair_create(getContext(), addr newKp.data, sk.data.baseAddr)
|
||||
if res == 1:
|
||||
kp = newKp
|
||||
else:
|
||||
#TODO: Raise an exception? Old behaviour would just set the secret key to an invalid key.
|
||||
discard
|
||||
|
||||
proc random*(T: type SkKeyPair, rng: Rng): SkResult[T] =
|
||||
## Generates new random key pair.
|
||||
let seckey = ? SkSecretKey.random(rng)
|
||||
|
||||
var keypair {.noinit.}: secp256k1_keypair
|
||||
let res = secp256k1_keypair_create(getContext(), addr keypair, addr seckey.data[0])
|
||||
doAssert res == 1, "Can't fail, only fails if secret key is invalid but it was freshly generated."
|
||||
|
||||
ok(T(
|
||||
data: keypair
|
||||
seckey: seckey,
|
||||
pubkey: seckey.toPublicKey()
|
||||
))
|
||||
|
||||
proc random*(T: type SkKeyPair, rng: FoolproofRng): T =
|
||||
## Generates new random key pair.
|
||||
let seckey = SkSecretKey.random(rng)
|
||||
|
||||
var keypair {.noinit.}: secp256k1_keypair
|
||||
let res = secp256k1_keypair_create(getContext(), addr keypair, addr seckey.data[0])
|
||||
doAssert res == 1, "Can't fail, only fails if secret key is invalid but it was freshly generated."
|
||||
|
||||
T(
|
||||
data: keypair
|
||||
seckey: seckey,
|
||||
pubkey: seckey.toPublicKey()
|
||||
)
|
||||
|
||||
func `==`*(lhs, rhs: SkPublicKey): bool =
|
||||
|
@ -457,11 +436,6 @@ func `==`*(lhs, rhs: SkRecoverableSignature): bool =
|
|||
## Compare Secp256k1 `recoverable signature` objects for equality.
|
||||
CT.isEqual(lhs.toRaw(), rhs.toRaw())
|
||||
|
||||
func `==`*(lhs, rhs: SkKeyPair): bool =
|
||||
## Compare Secp256k1 `keypair` objects for equality.
|
||||
lhs.pubkey == rhs.pubkey and
|
||||
lhs.seckey == rhs.seckey
|
||||
|
||||
func sign*(key: SkSecretKey, msg: SkMessage): SkSignature =
|
||||
## Sign message `msg` using private key `key` and return signature object.
|
||||
## It is recommended that `msg` is produced by hashing the input data to
|
||||
|
@ -480,10 +454,54 @@ func signRecoverable*(key: SkSecretKey, msg: SkMessage): SkRecoverableSignature
|
|||
doAssert res == 1, "cannot create recoverable signature, key invalid?"
|
||||
SkRecoverableSignature(data: data)
|
||||
|
||||
func signSchnorr*(key: SkSecretKey, msg: SkMessage): SkSchnorrSignature =
|
||||
## Sign message `msg` using private key `key` and return signature object.
|
||||
var kp: secp256k1_keypair
|
||||
let res = secp256k1_keypair_create(
|
||||
getContext(), addr kp, key.data.baseAddr)
|
||||
doAssert res == 1, "cannot create keypair, key invalid?"
|
||||
|
||||
var data {.noinit.}: array[SkRawSchnorrSignatureSize, byte]
|
||||
let res2 = secp256k1_schnorrsig_sign32(
|
||||
getContext(), data.baseAddr, msg.baseAddr, addr kp, nil)
|
||||
doAssert res2 == 1, "cannot create signature, key invalid?"
|
||||
SkSchnorrSignature(data: data)
|
||||
|
||||
func signSchnorr*(key: SkSecretKey, msg: openArray[byte]): SkSchnorrSignature =
|
||||
## Sign message `msg` using private key `key` and return signature object.
|
||||
var kp: secp256k1_keypair
|
||||
let res = secp256k1_keypair_create(
|
||||
getContext(), addr kp, key.data.baseAddr)
|
||||
doAssert res == 1, "cannot create keypair, key invalid?"
|
||||
|
||||
var data {.noinit.}: array[SkRawSchnorrSignatureSize, byte]
|
||||
let res2 = secp256k1_schnorrsig_sign_custom(
|
||||
getContext(), data.baseAddr, msg.baseAddr, csize_t msg.len, addr kp, nil)
|
||||
doAssert res2 == 1, "cannot create signature, key invalid?"
|
||||
SkSchnorrSignature(data: data)
|
||||
|
||||
func verify*(sig: SkSignature, msg: SkMessage, key: SkPublicKey): bool =
|
||||
secp256k1_ecdsa_verify(
|
||||
getContext(), unsafeAddr sig.data, msg.baseAddr, unsafeAddr key.data) == 1
|
||||
|
||||
func verify*(sig: SkSchnorrSignature, msg: SkMessage | openArray[byte], pubkey: SkPublicKey): bool =
|
||||
var xonlyPk: secp256k1_xonly_pubkey
|
||||
let res = secp256k1_xonly_pubkey_from_pubkey(
|
||||
secp256k1_context_no_precomp, addr xonlyPk, nil, addr pubkey.data)
|
||||
doAssert res == 1, "cannot get xonly pubkey from pubkey, key invalid?"
|
||||
|
||||
secp256k1_schnorrsig_verify(
|
||||
getContext(), addr sig.data[0], msg.baseAddr, csize_t SkMessageSize, addr xonlyPk) == 1
|
||||
|
||||
func verify*(sig: SkSchnorrSignature, msg: openArray[byte], pubkey: SkPublicKey): bool =
|
||||
var xonlyPk: secp256k1_xonly_pubkey
|
||||
let res = secp256k1_xonly_pubkey_from_pubkey(
|
||||
secp256k1_context_no_precomp, addr xonlyPk, nil, addr pubkey.data)
|
||||
doAssert res == 1, "cannot get xonly pubkey from pubkey, key invalid?"
|
||||
|
||||
secp256k1_schnorrsig_verify(
|
||||
getContext(), addr sig.data[0], msg.baseAddr, csize_t msg.len, addr xonlyPk) == 1
|
||||
|
||||
func recover*(sig: SkRecoverableSignature, msg: SkMessage): SkResult[SkPublicKey] =
|
||||
var data {.noinit.}: secp256k1_pubkey
|
||||
if secp256k1_ecdsa_recover(
|
||||
|
@ -519,12 +537,6 @@ func clear*(v: var SkSecretKey) =
|
|||
## result in undefined behaviour or Defect
|
||||
burnMem(v.data)
|
||||
|
||||
func clear*(v: var SkKeyPair) =
|
||||
## Wipe and clear memory of Secp256k1 `keypair`.
|
||||
## After calling this function, the key is invalid and using it elsewhere will
|
||||
## result in undefined behaviour or Defect
|
||||
burnMem(v.data)
|
||||
|
||||
func clear*(v: var SkEcdhSecret) =
|
||||
## Wipe and clear memory of ECDH `shared secret`.
|
||||
## After calling this function, the key is invalid and using it elsewhere will
|
||||
|
|
|
@ -358,28 +358,10 @@ type
|
|||
proc secp256k1_xonly_pubkey_parse*(ctx: ptr secp256k1_context;
|
||||
pubkey: ptr secp256k1_xonly_pubkey;
|
||||
input32: ptr byte): cint {.secp.}
|
||||
## Parse a 32-byte sequence into a xonly_pubkey object.
|
||||
##
|
||||
## Returns: 1 if the public key was fully valid.
|
||||
## 0 if the public key could not be parsed or is invalid.
|
||||
##
|
||||
## Args: ctx: a secp256k1 context object.
|
||||
## Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a
|
||||
## parsed version of input. If not, it's set to an invalid value.
|
||||
## In: input32: pointer to a serialized xonly_pubkey.
|
||||
##
|
||||
|
||||
proc secp256k1_xonly_pubkey_serialize*(ctx: ptr secp256k1_context;
|
||||
output32: ptr byte;
|
||||
pubkey: ptr secp256k1_xonly_pubkey): cint {.secp.}
|
||||
## Serialize an xonly_pubkey object into a 32-byte sequence.
|
||||
##
|
||||
## Returns: 1 always.
|
||||
##
|
||||
## Args: ctx: a secp256k1 context object.
|
||||
## Out: output32: a pointer to a 32-byte array to place the serialized key in.
|
||||
## In: pubkey: a pointer to a secp256k1_xonly_pubkey containing an initialized public key.
|
||||
##
|
||||
|
||||
proc secp256k1_xonly_pubkey_from_pubkey*(ctx: ptr secp256k1_context;
|
||||
xonly_pubkey: ptr secp256k1_xonly_pubkey;
|
||||
|
@ -401,55 +383,12 @@ proc secp256k1_xonly_pubkey_tweak_add*(ctx: ptr secp256k1_context;
|
|||
output_pubkey: ptr secp256k1_pubkey;
|
||||
internal_pubkey: ptr secp256k1_xonly_pubkey;
|
||||
tweak32: ptr byte): cint {.secp.}
|
||||
## Tweak an x-only public key by adding the generator multiplied with tweak32
|
||||
## to it.
|
||||
##
|
||||
## Note that the resulting point can not in general be represented by an x-only
|
||||
## pubkey because it may have an odd Y coordinate. Instead, the output_pubkey
|
||||
## is a normal secp256k1_pubkey.
|
||||
##
|
||||
## Returns: 0 if the arguments are invalid or the resulting public key would be
|
||||
## invalid (only when the tweak is the negation of the corresponding
|
||||
## secret key). 1 otherwise.
|
||||
##
|
||||
## Args: ctx: pointer to a context object initialized for verification.
|
||||
## Out: output_pubkey: pointer to a public key to store the result. Will be set
|
||||
## to an invalid value if this function returns 0.
|
||||
## In: internal_pubkey: pointer to an x-only pubkey to apply the tweak to.
|
||||
## tweak32: pointer to a 32-byte tweak. If the tweak is invalid
|
||||
## according to secp256k1_ec_seckey_verify, this function
|
||||
## returns 0. For uniformly random 32-byte arrays the
|
||||
## chance of being invalid is negligible (around 1 in 2^128).
|
||||
##
|
||||
|
||||
proc secp256k1_xonly_pubkey_tweak_add_check*(ctx: ptr secp256k1_context;
|
||||
tweaked_pubkey32: ptr byte;
|
||||
tweaked_pk_parity: cint;
|
||||
internal_pubkey: ptr secp256k1_xonly_pubkey;
|
||||
tweak32: ptr byte): cint {.secp.}
|
||||
## Checks that a tweaked pubkey is the result of calling
|
||||
## secp256k1_xonly_pubkey_tweak_add with internal_pubkey and tweak32.
|
||||
##
|
||||
## The tweaked pubkey is represented by its 32-byte x-only serialization and
|
||||
## its pk_parity, which can both be obtained by converting the result of
|
||||
## tweak_add to a secp256k1_xonly_pubkey.
|
||||
##
|
||||
## Note that this alone does _not_ verify that the tweaked pubkey is a
|
||||
## commitment. If the tweak is not chosen in a specific way, the tweaked pubkey
|
||||
## can easily be the result of a different internal_pubkey and tweak.
|
||||
##
|
||||
## Returns: 0 if the arguments are invalid or the tweaked pubkey is not the
|
||||
## result of tweaking the internal_pubkey with tweak32. 1 otherwise.
|
||||
## Args: ctx: pointer to a context object initialized for verification.
|
||||
## In: tweaked_pubkey32: pointer to a serialized xonly_pubkey.
|
||||
## tweaked_pk_parity: the parity of the tweaked pubkey (whose serialization
|
||||
## is passed in as tweaked_pubkey32). This must match the
|
||||
## pk_parity value that is returned when calling
|
||||
## secp256k1_xonly_pubkey with the tweaked pubkey, or
|
||||
## this function will fail.
|
||||
## internal_pubkey: pointer to an x-only public key object to apply the tweak to.
|
||||
## tweak32: pointer to a 32-byte tweak.
|
||||
##
|
||||
|
||||
proc secp256k1_keypair_create*(ctx: ptr secp256k1_context;
|
||||
keypair: ptr secp256k1_keypair;
|
||||
|
@ -466,97 +405,24 @@ proc secp256k1_keypair_create*(ctx: ptr secp256k1_context;
|
|||
proc secp256k1_keypair_sec*(ctx: ptr secp256k1_context;
|
||||
seckey: ptr byte;
|
||||
keypair: ptr secp256k1_keypair): cint {.secp.}
|
||||
## Get the secret key from a keypair.
|
||||
##
|
||||
## Returns: 1 always.
|
||||
## Args: ctx: pointer to a context object.
|
||||
## Out: seckey: pointer to a 32-byte buffer for the secret key.
|
||||
## In: keypair: pointer to a keypair.
|
||||
##
|
||||
|
||||
proc secp256k1_keypair_pub*(ctx: ptr secp256k1_context;
|
||||
pubkey: ptr secp256k1_pubkey;
|
||||
keypair: ptr secp256k1_keypair): cint {.secp.}
|
||||
## Get the public key from a keypair.
|
||||
##
|
||||
## Returns: 1 always.
|
||||
## Args: ctx: pointer to a context object.
|
||||
## Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to
|
||||
## the keypair public key. If not, it's set to an invalid value.
|
||||
## In: keypair: pointer to a keypair.
|
||||
##
|
||||
|
||||
proc secp256k1_keypair_xonly_pub*(ctx: ptr secp256k1_context;
|
||||
pubkey: ptr secp256k1_xonly_pubkey;
|
||||
pk_parity: ptr cint;
|
||||
keypair: ptr secp256k1_keypair): cint {.secp.}
|
||||
## Get the x-only public key from a keypair.
|
||||
##
|
||||
## This is the same as calling secp256k1_keypair_pub and then
|
||||
## secp256k1_xonly_pubkey_from_pubkey.
|
||||
##
|
||||
## Returns: 1 always.
|
||||
## Args: ctx: pointer to a context object.
|
||||
## Out: pubkey: pointer to an xonly_pubkey object. If 1 is returned, it is set
|
||||
## to the keypair public key after converting it to an
|
||||
## xonly_pubkey. If not, it's set to an invalid value.
|
||||
## pk_parity: Ignored if NULL. Otherwise, pointer to an integer that will be set to the
|
||||
## pk_parity argument of secp256k1_xonly_pubkey_from_pubkey.
|
||||
## In: keypair: pointer to a keypair.
|
||||
##
|
||||
|
||||
proc secp256k1_keypair_xonly_tweak_add*(ctx: ptr secp256k1_context;
|
||||
keypair: ptr secp256k1_keypair;
|
||||
tweak32: ptr byte): cint {.secp.}
|
||||
## Tweak a keypair by adding tweak32 to the secret key and updating the public
|
||||
## key accordingly.
|
||||
##
|
||||
## Calling this function and then secp256k1_keypair_pub results in the same
|
||||
## public key as calling secp256k1_keypair_xonly_pub and then
|
||||
## secp256k1_xonly_pubkey_tweak_add.
|
||||
##
|
||||
## Returns: 0 if the arguments are invalid or the resulting keypair would be
|
||||
## invalid (only when the tweak is the negation of the keypair's
|
||||
## secret key). 1 otherwise.
|
||||
##
|
||||
## Args: ctx: pointer to a context object initialized for verification.
|
||||
## In/Out: keypair: pointer to a keypair to apply the tweak to. Will be set to
|
||||
## an invalid value if this function returns 0.
|
||||
## In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according
|
||||
## to secp256k1_ec_seckey_verify, this function returns 0. For
|
||||
## uniformly random 32-byte arrays the chance of being invalid
|
||||
## is negligible (around 1 in 2^128).
|
||||
##
|
||||
|
||||
## Schnorrsig interface follows
|
||||
|
||||
type
|
||||
secp256k1_nonce_function_hardened* {.bycopy.} = object
|
||||
## A pointer to a function to deterministically generate a nonce.
|
||||
##
|
||||
## Same as secp256k1_nonce function with the exception of accepting an
|
||||
## additional pubkey argument and not requiring an attempt argument. The pubkey
|
||||
## argument can protect signature schemes with key-prefixed challenge hash
|
||||
## inputs against reusing the nonce when signing with the wrong precomputed
|
||||
## pubkey.
|
||||
##
|
||||
## Returns: 1 if a nonce was successfully generated. 0 will cause signing to
|
||||
## return an error.
|
||||
## Out: nonce32: pointer to a 32-byte array to be filled by the function
|
||||
## In: msg: the message being verified. Is NULL if and only if msglen
|
||||
## is 0.
|
||||
## msglen: the length of the message
|
||||
## key32: pointer to a 32-byte secret key (will not be NULL)
|
||||
## xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32
|
||||
## (will not be NULL)
|
||||
## algo: pointer to an array describing the signature
|
||||
## algorithm (will not be NULL)
|
||||
## algolen: the length of the algo array
|
||||
## data: arbitrary data pointer that is passed through
|
||||
##
|
||||
## Except for test cases, this function should compute some cryptographic hash of
|
||||
## the message, the key, the pubkey, the algorithm description, and data.
|
||||
##
|
||||
nonce32*: ptr byte
|
||||
msg*: ptr byte
|
||||
msglen*: csize_t
|
||||
|
@ -571,19 +437,6 @@ const
|
|||
|
||||
type
|
||||
secp256k1_schnorrsig_extraparams* = object
|
||||
## Data structure that contains additional arguments for schnorrsig_sign_custom.
|
||||
##
|
||||
## Members:
|
||||
## magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization
|
||||
## and has no other function than making sure the object is
|
||||
## initialized.
|
||||
## noncefp: pointer to a nonce generation function. If NULL,
|
||||
## secp256k1_nonce_function_bip340 is used
|
||||
## ndata: pointer to arbitrary data used by the nonce generation function
|
||||
## (can be NULL). If it is non-NULL and
|
||||
## secp256k1_nonce_function_bip340 is used, then ndata must be a
|
||||
## pointer to 32-byte auxiliary randomness as per BIP-340.
|
||||
##
|
||||
magic*: array[4, uint8]
|
||||
noncefp*: secp256k1_nonce_function_hardened
|
||||
ndata*: pointer
|
||||
|
|
|
@ -15,6 +15,13 @@ const
|
|||
1'u8, 0, 0, 0, 0, 0, 0, 0,
|
||||
1'u8, 0, 0, 0, 0, 0, 0, 0,
|
||||
])
|
||||
msg2 = array[40, byte]([
|
||||
1'u8, 0, 0, 0, 0, 0, 0, 0,
|
||||
1'u8, 0, 0, 0, 0, 0, 0, 0,
|
||||
1'u8, 0, 0, 0, 0, 0, 0, 0,
|
||||
1'u8, 0, 0, 0, 0, 0, 0, 0,
|
||||
1'u8, 0, 0, 0, 0, 0, 0, 0,
|
||||
])
|
||||
|
||||
proc workingRng(data: var openArray[byte]): bool =
|
||||
data[0] += 1
|
||||
|
@ -47,6 +54,8 @@ suite "secp256k1":
|
|||
otherPk = SkSecretKey.random(workingRng)[].toPublicKey()
|
||||
sig = sign(sk, msg0)
|
||||
sig2 = signRecoverable(sk, msg0)
|
||||
sig3 = signSchnorr(sk, msg0)
|
||||
sig4 = signSchnorr(sk, msg2)
|
||||
|
||||
check:
|
||||
verify(sig, msg0, pk)
|
||||
|
@ -55,6 +64,8 @@ suite "secp256k1":
|
|||
recover(sig2, msg0)[] == pk
|
||||
recover(sig2, msg1)[] != pk
|
||||
SkSignature.fromDer(sig.toDer())[].toHex() == sig.toHex()
|
||||
verify(sig3, msg0, pk)
|
||||
verify(sig4, msg2, pk)
|
||||
|
||||
test "Message":
|
||||
check:
|
||||
|
|
|
@ -30,33 +30,3 @@ suite "ABI tests":
|
|||
addr aPublicKey,
|
||||
cast[ptr byte](addr bSecretKey[0])) == 1
|
||||
check(data1 == data2)
|
||||
|
||||
test "Schnorr signatures":
|
||||
var keypair: secp256k1_keypair
|
||||
var secretKey: array[32, uint8]
|
||||
var publicKey: secp256k1_xonly_pubkey
|
||||
var data: array[32, byte]
|
||||
var sig: array[64, byte]
|
||||
var sig2: array[64, byte]
|
||||
secretKey[31] = 1'u8
|
||||
let ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN or SECP256K1_CONTEXT_VERIFY)
|
||||
check secp256k1_keypair_create(ctx, addr keypair, cast[ptr byte](addr secretKey[0])) == 1
|
||||
check secp256k1_keypair_xonly_pub(ctx, addr publicKey, nil, addr keypair) == 1
|
||||
check secp256k1_schnorrsig_sign32(ctx, addr sig[0], addr data[0], addr keypair, nil) == 1
|
||||
check secp256k1_schnorrsig_sign_custom(ctx, addr sig2[0], addr data[0], csize_t data.len, addr keypair, nil) == 1
|
||||
check sig == sig2
|
||||
check secp256k1_schnorrsig_verify(ctx, addr sig[0], addr data[0], csize_t data.len, addr publicKey) == 1
|
||||
|
||||
test "Multikeys should be unchanged when serialized":
|
||||
var keypair: secp256k1_keypair
|
||||
var secretKey: array[32, uint8]
|
||||
var publicKey: secp256k1_xonly_pubkey
|
||||
var parsed: array[32, byte]
|
||||
var reflectedPublicKey: secp256k1_xonly_pubkey
|
||||
secretKey[31] = 1'u8
|
||||
let ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN or SECP256K1_CONTEXT_VERIFY)
|
||||
check secp256k1_keypair_create(ctx, addr keypair, cast[ptr byte](addr secretKey[0])) == 1
|
||||
check secp256k1_keypair_xonly_pub(ctx, addr publicKey, nil, addr keypair) == 1
|
||||
check secp256k1_xonly_pubkey_serialize(ctx, addr parsed[0], addr publicKey) == 1
|
||||
check secp256k1_xonly_pubkey_parse(ctx, addr reflectedPublicKey, addr parsed[0]) == 1
|
||||
check publicKey == reflectedPublicKey
|
||||
|
|
Loading…
Reference in New Issue