Support ECDSA and Schnorr signatures over the SECP256k1 curve in Plutus Core;specifically, allow validation of such signatures as builtins.These builtins work overBuiltinByteString
s.
Signature schemes based on the SECP256k1 curve are common in the blockchainindustry; a notable user of these is Bitcoin. Supporting signature schemes whichare used in other parts of the industry provides an interoperability benefit: wecan verify signatures produced by other systems as they are today, withoutrequiring other people to produce signatures specifically for us. This not onlyprovides us with improved interoperability with systems based on Bitcoin, butalso compatibility with other interoperability systems, such as Wanchain andRenbridge, which use SECP256k1 signatures for verification. Lastly, if we canverify Schnorr signatures, we can also verify Schnorr-compatible multi orthreshold signatures, such asMuSig2 orFrost.
Two new builtin functions would be provided:
These would be based onsecp256k1
,a reference implementation of both kinds of signature scheme in C. Thisimplementation would be called from Haskell using direct bindings to C. Thesebindings would be defined incardano-base
, using its existingDSIGN
interface, with new builtins in Plutus Core on the basis of theDSIGN
interface for both schemes.
The builtins would be costed as follows: ECDSA signature verification hasconstant cost, as the message, verification key and signature are allfixed-width; Schnorr signature verification is instead linear in the messagelength, as this can be arbitrary, but as the length of the verification key andsignature are constant, the costing will be constant in both.
More specifically, Plutus would gain the following primitive operations:
verifyEcdsaSecp256k1Signature :: BuiltinByteString -> BuiltinByteString -> BuiltinByteString -> BuiltinBool
, for verifying 32-byte message hashes signedusing the ECDSA signature scheme on the SECP256k1 curve; andverifySchnorrSecp256k1Signature :: BuiltinByteString -> BuiltinByteString -> BuiltinByteString -> BuiltinBool
, for verifying arbitrary binary messagessigned using the Schnorr signature scheme on the SECP256k1 curve.Both functions take parameters of a specific part of the signature scheme, eventhough they are all encoded asBuiltinByteString
s. In order, for bothfunctions, these are:
The two different schemes handle deserialization internally: specifically, thereis a distinction made between 'external' representations, which are expected asarguments, and 'internal' representations, used only by the implementationsthemselves. This creates different expecations for each argument for both ofthese schemes; we describe these below.
For theECDSA signaturescheme,the requirements are as follows. Note that these differ from the serializationused by Bitcoin, as the serialisation of signatures uses DER-encoding, whichresult in variable size signatures up to 72 bytes (instead of the 64 byte encodingwe describe in this document).
secp256k1_ec_pubkey_serialize
,when given a length argument of 33, and theSECP256K1_EC_COMPRESSED
flag.This implies all of the following:0x02
ify is even, and0x03
otherwise.verifyEcdsaSecp256k1Signature
receives themessage and hashes it, rather than accepting a hash directly: doing socan be dangerous.Typically, the hashing function used would be SHA256; however, this is notrequired, as only the length is checked.secp256k1_ecdsa_serialize_compact
.This implies all of the following: ┏━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━┓ ┃ r <32 bytes> │ s <32 bytes> ┃ ┗━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━┛ <--------- signature ---------->
For the Schnorr signature scheme, we have the following requirements, asdescribed in the requirements forBIP-340:
secp256k1_xonly_pubkey_serialize
.This implies all of the following:s
. ┏━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━┓ ┃ R <32 bytes> │ s <32 bytes> ┃ ┗━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━┛ <--------- signature ---------->
The builtin operations will error with a descriptive message if given inputsthat don't correspond to the constraints above, returnFalse
if the signaturefails to verify the input given the key, andTrue
otherwise.
We consider the implementation trustworthy:secp256k1
is the referenceimplementation for both signature schemes, and is already being used inproduction by Bitcoin. Specifically, ECDSA signatures over the SECP256k1 curvewere used by Bitcoin before Taproot, while Schnorr signatures over the samecurve have been used since Taproot.
An alternative approach could be to provide low-level primitives, which wouldallow any signature scheme (not just the ones under consideration here) to beimplemented by whoever needs them. While this approach is certainly moreflexible, it has two significant drawbacks:
It may be possible that some set of primitive can avoid both of these issues(for example, the suggestions inthisCIP); in the meantime,providing direct support for commonly-used schemes such as these is worthwhile.
At the Plutus Core level, implementing this proposal induces nobackwards-incompatibility: the proposed new primitives do not break any existingfunctionality or affect any other builtins. Likewise, at levels above PlutusCore (such asPlutusTx
), no existing functionality should be affected.
On-chain, this requires a hard fork.
This CIP is licensed underApache-2.0.