From f47f1c8f0a012287a7f1e33355545b2811b026f2 Mon Sep 17 00:00:00 2001 From: Lazar Date: Wed, 4 Dec 2024 16:47:53 +0100 Subject: [PATCH] adds unsafeSignEOTS --- eotsmanager/client/rpcclient.go | 19 ++++++++++ eotsmanager/eotsmanager.go | 3 ++ eotsmanager/localmanager.go | 19 ++++++++++ eotsmanager/proto/eotsmanager.pb.go | 46 +++++++++++++----------- eotsmanager/proto/eotsmanager.proto | 4 +++ eotsmanager/proto/eotsmanager_grpc.pb.go | 39 ++++++++++++++++++++ eotsmanager/service/rpcserver.go | 13 +++++++ finality-provider/service/fp_instance.go | 12 ++++++- itest/e2e_test.go | 13 +++---- 9 files changed, 139 insertions(+), 29 deletions(-) diff --git a/eotsmanager/client/rpcclient.go b/eotsmanager/client/rpcclient.go index 821ecb18..c306e830 100644 --- a/eotsmanager/client/rpcclient.go +++ b/eotsmanager/client/rpcclient.go @@ -118,6 +118,25 @@ func (c *EOTSManagerGRpcClient) SignEOTS(uid, chaiID, msg []byte, height uint64, return &s, nil } +func (c *EOTSManagerGRpcClient) UnsafeSignEOTS(uid, chaiID, msg []byte, height uint64, passphrase string) (*btcec.ModNScalar, error) { + req := &proto.SignEOTSRequest{ + Uid: uid, + ChainId: chaiID, + Msg: msg, + Height: height, + Passphrase: passphrase, + } + res, err := c.client.UnsafeSignEOTS(context.Background(), req) + if err != nil { + return nil, err + } + + var s btcec.ModNScalar + s.SetByteSlice(res.Sig) + + return &s, nil +} + func (c *EOTSManagerGRpcClient) SignSchnorrSig(uid, msg []byte, passphrase string) (*schnorr.Signature, error) { req := &proto.SignSchnorrSigRequest{Uid: uid, Msg: msg, Passphrase: passphrase} res, err := c.client.SignSchnorrSig(context.Background(), req) diff --git a/eotsmanager/eotsmanager.go b/eotsmanager/eotsmanager.go index 04d02316..d7056340 100644 --- a/eotsmanager/eotsmanager.go +++ b/eotsmanager/eotsmanager.go @@ -31,6 +31,9 @@ type EOTSManager interface { // or passPhrase is incorrect SignEOTS(uid []byte, chainID []byte, msg []byte, height uint64, passphrase string) (*btcec.ModNScalar, error) + // UnsafeSignEOTS should only be used in e2e tests for demonstration purposes. Use SignEOTS for real operations + UnsafeSignEOTS(uid []byte, chainID []byte, msg []byte, height uint64, passphrase string) (*btcec.ModNScalar, error) + // SignSchnorrSig signs a Schnorr signature using the private key of the finality provider // It fails if the finality provider does not exist or the message size is not 32 bytes // or passPhrase is incorrect diff --git a/eotsmanager/localmanager.go b/eotsmanager/localmanager.go index 45c57a28..7bf31b24 100644 --- a/eotsmanager/localmanager.go +++ b/eotsmanager/localmanager.go @@ -235,6 +235,25 @@ func (lm *LocalEOTSManager) SignEOTS(fpPk []byte, chainID []byte, msg []byte, he return signedBytes, nil } +// UnsafeSignEOTS should only be used in e2e test to demonstrate double sign +func (lm *LocalEOTSManager) UnsafeSignEOTS(fpPk []byte, chainID []byte, msg []byte, height uint64, passphrase string) (*btcec.ModNScalar, error) { + privRand, _, err := lm.getRandomnessPair(fpPk, chainID, height, passphrase) + if err != nil { + return nil, fmt.Errorf("failed to get private randomness: %w", err) + } + + privKey, err := lm.getEOTSPrivKey(fpPk, passphrase) + if err != nil { + return nil, fmt.Errorf("failed to get EOTS private key: %w", err) + } + + // Update metrics + lm.metrics.IncrementEotsFpTotalEotsSignCounter(hex.EncodeToString(fpPk)) + lm.metrics.SetEotsFpLastEotsSignHeight(hex.EncodeToString(fpPk), float64(height)) + + return eots.Sign(privKey, privRand, msg) +} + func (lm *LocalEOTSManager) SignSchnorrSig(fpPk []byte, msg []byte, passphrase string) (*schnorr.Signature, error) { privKey, err := lm.getEOTSPrivKey(fpPk, passphrase) if err != nil { diff --git a/eotsmanager/proto/eotsmanager.pb.go b/eotsmanager/proto/eotsmanager.pb.go index eaf5ac07..d1fa3a55 100644 --- a/eotsmanager/proto/eotsmanager.pb.go +++ b/eotsmanager/proto/eotsmanager.pb.go @@ -760,7 +760,7 @@ var file_eotsmanager_proto_rawDesc = []byte{ 0x0a, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x22, 0x2a, 0x0a, 0x16, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x63, 0x68, 0x6e, 0x6f, 0x72, 0x72, 0x53, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x32, 0xb7, 0x03, 0x0a, 0x0b, 0x45, 0x4f, 0x54, 0x53, + 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x32, 0xfa, 0x03, 0x0a, 0x0b, 0x45, 0x4f, 0x54, 0x53, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x2f, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69, 0x6e, 0x67, @@ -783,16 +783,20 @@ var file_eotsmanager_proto_rawDesc = []byte{ 0x53, 0x12, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x45, 0x4f, 0x54, 0x53, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x45, 0x4f, 0x54, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x63, 0x68, 0x6e, 0x6f, 0x72, - 0x72, 0x53, 0x69, 0x67, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, - 0x6e, 0x53, 0x63, 0x68, 0x6e, 0x6f, 0x72, 0x72, 0x53, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x53, - 0x63, 0x68, 0x6e, 0x6f, 0x72, 0x72, 0x53, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x62, 0x61, 0x62, 0x79, 0x6c, 0x6f, 0x6e, 0x6c, 0x61, 0x62, 0x73, 0x2d, 0x69, 0x6f, 0x2f, 0x66, - 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x2f, 0x65, 0x6f, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x55, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x53, 0x69, 0x67, 0x6e, + 0x45, 0x4f, 0x54, 0x53, 0x12, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, + 0x6e, 0x45, 0x4f, 0x54, 0x53, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x45, 0x4f, 0x54, 0x53, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x63, 0x68, + 0x6e, 0x6f, 0x72, 0x72, 0x53, 0x69, 0x67, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x53, 0x69, 0x67, 0x6e, 0x53, 0x63, 0x68, 0x6e, 0x6f, 0x72, 0x72, 0x53, 0x69, 0x67, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, + 0x67, 0x6e, 0x53, 0x63, 0x68, 0x6e, 0x6f, 0x72, 0x72, 0x53, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x62, 0x79, 0x6c, 0x6f, 0x6e, 0x6c, 0x61, 0x62, 0x73, 0x2d, 0x69, + 0x6f, 0x2f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2d, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x2f, 0x65, 0x6f, 0x74, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -828,15 +832,17 @@ var file_eotsmanager_proto_depIdxs = []int32{ 4, // 2: proto.EOTSManager.CreateRandomnessPairList:input_type -> proto.CreateRandomnessPairListRequest 6, // 3: proto.EOTSManager.KeyRecord:input_type -> proto.KeyRecordRequest 8, // 4: proto.EOTSManager.SignEOTS:input_type -> proto.SignEOTSRequest - 10, // 5: proto.EOTSManager.SignSchnorrSig:input_type -> proto.SignSchnorrSigRequest - 1, // 6: proto.EOTSManager.Ping:output_type -> proto.PingResponse - 3, // 7: proto.EOTSManager.CreateKey:output_type -> proto.CreateKeyResponse - 5, // 8: proto.EOTSManager.CreateRandomnessPairList:output_type -> proto.CreateRandomnessPairListResponse - 7, // 9: proto.EOTSManager.KeyRecord:output_type -> proto.KeyRecordResponse - 9, // 10: proto.EOTSManager.SignEOTS:output_type -> proto.SignEOTSResponse - 11, // 11: proto.EOTSManager.SignSchnorrSig:output_type -> proto.SignSchnorrSigResponse - 6, // [6:12] is the sub-list for method output_type - 0, // [0:6] is the sub-list for method input_type + 8, // 5: proto.EOTSManager.UnsafeSignEOTS:input_type -> proto.SignEOTSRequest + 10, // 6: proto.EOTSManager.SignSchnorrSig:input_type -> proto.SignSchnorrSigRequest + 1, // 7: proto.EOTSManager.Ping:output_type -> proto.PingResponse + 3, // 8: proto.EOTSManager.CreateKey:output_type -> proto.CreateKeyResponse + 5, // 9: proto.EOTSManager.CreateRandomnessPairList:output_type -> proto.CreateRandomnessPairListResponse + 7, // 10: proto.EOTSManager.KeyRecord:output_type -> proto.KeyRecordResponse + 9, // 11: proto.EOTSManager.SignEOTS:output_type -> proto.SignEOTSResponse + 9, // 12: proto.EOTSManager.UnsafeSignEOTS:output_type -> proto.SignEOTSResponse + 11, // 13: proto.EOTSManager.SignSchnorrSig:output_type -> proto.SignSchnorrSigResponse + 7, // [7:14] is the sub-list for method output_type + 0, // [0:7] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name diff --git a/eotsmanager/proto/eotsmanager.proto b/eotsmanager/proto/eotsmanager.proto index cc7efec0..ad32b192 100644 --- a/eotsmanager/proto/eotsmanager.proto +++ b/eotsmanager/proto/eotsmanager.proto @@ -23,6 +23,10 @@ service EOTSManager { rpc SignEOTS (SignEOTSRequest) returns (SignEOTSResponse); + // UnsafeSignEOTS used only for testing purpose. Use SignEOTS for real operations + rpc UnsafeSignEOTS (SignEOTSRequest) + returns (SignEOTSResponse); + // SignSchnorrSig signs a Schnorr sig with the EOTS private key rpc SignSchnorrSig (SignSchnorrSigRequest) returns (SignSchnorrSigResponse); diff --git a/eotsmanager/proto/eotsmanager_grpc.pb.go b/eotsmanager/proto/eotsmanager_grpc.pb.go index 0a8814ad..3028009f 100644 --- a/eotsmanager/proto/eotsmanager_grpc.pb.go +++ b/eotsmanager/proto/eotsmanager_grpc.pb.go @@ -24,6 +24,7 @@ const ( EOTSManager_CreateRandomnessPairList_FullMethodName = "/proto.EOTSManager/CreateRandomnessPairList" EOTSManager_KeyRecord_FullMethodName = "/proto.EOTSManager/KeyRecord" EOTSManager_SignEOTS_FullMethodName = "/proto.EOTSManager/SignEOTS" + EOTSManager_UnsafeSignEOTS_FullMethodName = "/proto.EOTSManager/UnsafeSignEOTS" EOTSManager_SignSchnorrSig_FullMethodName = "/proto.EOTSManager/SignSchnorrSig" ) @@ -40,6 +41,8 @@ type EOTSManagerClient interface { KeyRecord(ctx context.Context, in *KeyRecordRequest, opts ...grpc.CallOption) (*KeyRecordResponse, error) // SignEOTS signs an EOTS with the EOTS private key and the relevant randomness SignEOTS(ctx context.Context, in *SignEOTSRequest, opts ...grpc.CallOption) (*SignEOTSResponse, error) + // UnsafeSignEOTS used only for testing purpose. Use SignEOTS for real operations + UnsafeSignEOTS(ctx context.Context, in *SignEOTSRequest, opts ...grpc.CallOption) (*SignEOTSResponse, error) // SignSchnorrSig signs a Schnorr sig with the EOTS private key SignSchnorrSig(ctx context.Context, in *SignSchnorrSigRequest, opts ...grpc.CallOption) (*SignSchnorrSigResponse, error) } @@ -97,6 +100,15 @@ func (c *eOTSManagerClient) SignEOTS(ctx context.Context, in *SignEOTSRequest, o return out, nil } +func (c *eOTSManagerClient) UnsafeSignEOTS(ctx context.Context, in *SignEOTSRequest, opts ...grpc.CallOption) (*SignEOTSResponse, error) { + out := new(SignEOTSResponse) + err := c.cc.Invoke(ctx, EOTSManager_UnsafeSignEOTS_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *eOTSManagerClient) SignSchnorrSig(ctx context.Context, in *SignSchnorrSigRequest, opts ...grpc.CallOption) (*SignSchnorrSigResponse, error) { out := new(SignSchnorrSigResponse) err := c.cc.Invoke(ctx, EOTSManager_SignSchnorrSig_FullMethodName, in, out, opts...) @@ -119,6 +131,8 @@ type EOTSManagerServer interface { KeyRecord(context.Context, *KeyRecordRequest) (*KeyRecordResponse, error) // SignEOTS signs an EOTS with the EOTS private key and the relevant randomness SignEOTS(context.Context, *SignEOTSRequest) (*SignEOTSResponse, error) + // UnsafeSignEOTS used only for testing purpose. Use SignEOTS for real operations + UnsafeSignEOTS(context.Context, *SignEOTSRequest) (*SignEOTSResponse, error) // SignSchnorrSig signs a Schnorr sig with the EOTS private key SignSchnorrSig(context.Context, *SignSchnorrSigRequest) (*SignSchnorrSigResponse, error) mustEmbedUnimplementedEOTSManagerServer() @@ -143,6 +157,9 @@ func (UnimplementedEOTSManagerServer) KeyRecord(context.Context, *KeyRecordReque func (UnimplementedEOTSManagerServer) SignEOTS(context.Context, *SignEOTSRequest) (*SignEOTSResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SignEOTS not implemented") } +func (UnimplementedEOTSManagerServer) UnsafeSignEOTS(context.Context, *SignEOTSRequest) (*SignEOTSResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UnsafeSignEOTS not implemented") +} func (UnimplementedEOTSManagerServer) SignSchnorrSig(context.Context, *SignSchnorrSigRequest) (*SignSchnorrSigResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SignSchnorrSig not implemented") } @@ -249,6 +266,24 @@ func _EOTSManager_SignEOTS_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _EOTSManager_UnsafeSignEOTS_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignEOTSRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EOTSManagerServer).UnsafeSignEOTS(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: EOTSManager_UnsafeSignEOTS_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EOTSManagerServer).UnsafeSignEOTS(ctx, req.(*SignEOTSRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _EOTSManager_SignSchnorrSig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(SignSchnorrSigRequest) if err := dec(in); err != nil { @@ -294,6 +329,10 @@ var EOTSManager_ServiceDesc = grpc.ServiceDesc{ MethodName: "SignEOTS", Handler: _EOTSManager_SignEOTS_Handler, }, + { + MethodName: "UnsafeSignEOTS", + Handler: _EOTSManager_UnsafeSignEOTS_Handler, + }, { MethodName: "SignSchnorrSig", Handler: _EOTSManager_SignSchnorrSig_Handler, diff --git a/eotsmanager/service/rpcserver.go b/eotsmanager/service/rpcserver.go index e13f44b5..7827bbee 100644 --- a/eotsmanager/service/rpcserver.go +++ b/eotsmanager/service/rpcserver.go @@ -98,6 +98,19 @@ func (r *rpcServer) SignEOTS(_ context.Context, req *proto.SignEOTSRequest) ( return &proto.SignEOTSResponse{Sig: sigBytes[:]}, nil } +// UnsafeSignEOTS only used for testing purposes. Doesn't offer slashing protection! +func (r *rpcServer) UnsafeSignEOTS(_ context.Context, req *proto.SignEOTSRequest) ( + *proto.SignEOTSResponse, error) { + sig, err := r.em.UnsafeSignEOTS(req.Uid, req.ChainId, req.Msg, req.Height, req.Passphrase) + if err != nil { + return nil, err + } + + sigBytes := sig.Bytes() + + return &proto.SignEOTSResponse{Sig: sigBytes[:]}, nil +} + // SignSchnorrSig signs a Schnorr sig with the EOTS private key func (r *rpcServer) SignSchnorrSig(_ context.Context, req *proto.SignSchnorrSigRequest) ( *proto.SignSchnorrSigResponse, error) { diff --git a/finality-provider/service/fp_instance.go b/finality-provider/service/fp_instance.go index 6b7883ca..fe04dc3a 100644 --- a/finality-provider/service/fp_instance.go +++ b/finality-provider/service/fp_instance.go @@ -695,8 +695,18 @@ func (fp *FinalityProviderInstance) TestSubmitFinalitySignatureAndExtractPrivKey return nil, nil, fmt.Errorf("failed to get public randomness inclusion proof: %w", err) } + unsafeSign := func(b *types.BlockInfo) (*bbntypes.SchnorrEOTSSig, error) { + msgToSign := getMsgToSignForVote(b.Height, b.Hash) + sig, err := fp.em.UnsafeSignEOTS(fp.btcPk.MustMarshal(), fp.GetChainID(), msgToSign, b.Height, fp.passphrase) + if err != nil { + return nil, fmt.Errorf("failed to sign EOTS: %w", err) + } + + return bbntypes.NewSchnorrEOTSSigFromModNScalar(sig), nil + } + // sign block - eotsSig, err := fp.signFinalitySig(b) + eotsSig, err := unsafeSign(b) if err != nil { return nil, nil, err } diff --git a/itest/e2e_test.go b/itest/e2e_test.go index 364dff18..4ac22810 100644 --- a/itest/e2e_test.go +++ b/itest/e2e_test.go @@ -98,14 +98,11 @@ func TestDoubleSigning(t *testing.T) { Height: finalizedBlocks[0].Height, Hash: datagen.GenRandomByteArray(r, 32), } - _, _, err = fpIns.TestSubmitFinalitySignatureAndExtractPrivKey(b) - require.Error(t, err) - - // todo(lazar): implement UnsafeSignEOTS, containing logic without double sign protection - //require.NoError(t, err) - //require.NotNil(t, extractedKey) - //localKey := tm.GetFpPrivKey(t, fpIns.GetBtcPkBIP340().MustMarshal()) - //require.True(t, localKey.Key.Equals(&extractedKey.Key) || localKey.Key.Negate().Equals(&extractedKey.Key)) + _, extractedKey, err = fpIns.TestSubmitFinalitySignatureAndExtractPrivKey(b) + require.NoError(t, err) + require.NotNil(t, extractedKey) + localKey := tm.GetFpPrivKey(t, fpIns.GetBtcPkBIP340().MustMarshal()) + require.True(t, localKey.Key.Equals(&extractedKey.Key) || localKey.Key.Negate().Equals(&extractedKey.Key)) t.Logf("the equivocation attack is successful") }