Yes I did, but I discarded it because of asymmetry. The two variant are suddenly in different "hierarchy depths", which is never the case for CryptoKit. But maybe it is not that bad, maybe it is the way to go, thanks for bring it up! I will consider it some more!
Good question, K1's underlying implementation is Bitcoin Core's libsecp256k1
and (non recovery) ECDSA is declared in secp256k1.h
as secp256k1_ecdsa_sign
, where as the recovery variant is declared in secp256k1_recovery.h
as secp256k1_ecdsa_sign_recoverable
and the source file of recovery is the "modules" folder. Further more they produce different structs, secp256k1_ecdsa_signature *
vs secp256k1_ecdsa_recoverable_signature *
.
A recoverable ECDSA signature can always be normalized to a (non recoverable) ECDSA signature.
Content wise, they only different in size, recoverable signatures contain one single additional byte, sometimes called V
or RecoveryID
(which can be represented as an enum with values 0, 1, 2, 3).
I think what you might be getting at...? Did you perhaps wanna propose using a single namespace for ECDSA, but with either two sign
methods, or a single sign
method but accepting an argument isRecoverable: Bool
? I actually used the same namespace with two different sign
methods earlier. But it became quite confusing because each sign
method has itself 3 variants, see documentation
Thus exposing users with 6 methods instead of 3 is really less than ideal.
So let's explore the ability of using still one sign method - just 3 variants, but letting isRecoverable: Bool
be an argument. Suddenly the recoveryID
becomes an optional in a merge of K1.ECDSA.NonRecoverable.Signature
and K1.ECDSA.Recoverable.Signature
, which makes suddenly the very straightforward serialization and deserialization API's of K1.ECDSA.NonRecoverable.Signature
less easy to understand and use. The reason for this is that only K1.ECDSA.NonRecoverable.Signature
is well defined, with standards.
Recoverable signature have not real standardized serialization format. Content wise, as mentioned above, apart from R
and S
scalars, the contain a single V
byte. Internally, libsecp256k1
stores the signature as leR||leS||v
where leR
and leS
denotes inverted R, meaning little endian (at least on my M1). See documentation of libsecp256k
:
The exact representation of data inside is implementation defined and not guaranteed to be portable between different platforms or versions
So to export it we use secp256k1_ecdsa_recoverable_signature_serialize_compact
, which converts to big endian scalars: R || S || V
. But this is a non standardized (outside of Bitcoin) format, and Ethereum uses V || R || S
.
Oh I see now that I've forgotten to add documentation to K1.ECDSA.Recoverable.Signature.Compact
, which is my swift variant of the output of secp256k1_ecdsa_recoverable_signature_serialize_compact
.
Thus the API of the recoverable Signature is less clear than the API of the non recoverable one. which is an argument for keeping them apart. I could return a protocol
but I think it less than ideal? If I know I do not care about recoverable signatures, I'd prefer it if I get an existential, with the clean API.
Hmm... what do you think?