From 8b61918d437d39526a7a164d8b6d60f14d693423 Mon Sep 17 00:00:00 2001 From: petricadaipegsp <155911522+petricadaipegsp@users.noreply.github.com> Date: Thu, 28 Nov 2024 01:13:57 +0100 Subject: [PATCH] Add transaction request validation (#390) --- client/cmd/merge.go | 32 +- client/cmd/proverPause.go | 10 +- client/cmd/split.go | 35 +- client/cmd/transfer.go | 47 +- node/consensus/data/broadcast_messaging.go | 20 +- .../data/data_clock_consensus_engine.go | 30 +- node/consensus/data/main_data_loop.go | 66 +- node/consensus/data/message_handler.go | 67 +- node/consensus/data/message_validators.go | 21 +- node/consensus/data/peer_messaging.go | 15 +- .../token/application/token_application.go | 12 +- .../token/application/token_handle_merge.go | 21 +- .../token/application/token_handle_mint.go | 6 +- .../token_handle_prover_announce.go | 28 +- .../application/token_handle_prover_join.go | 29 +- .../token_handle_prover_join_test.go | 92 +- .../application/token_handle_prover_leave.go | 19 +- .../application/token_handle_prover_pause.go | 19 +- .../application/token_handle_prover_resume.go | 19 +- .../token/application/token_handle_split.go | 28 +- .../application/token_handle_transfer.go | 17 +- .../token/token_execution_engine.go | 138 +-- node/protobufs/keys.go | 5 + node/protobufs/node.go | 99 +- node/protobufs/validation.go | 700 ++++++++++++++ node/protobufs/validation_internal_test.go | 5 + node/protobufs/validation_test.go | 914 ++++++++++++++++++ node/rpc/node_rpc_server.go | 10 +- node/tries/proof_leaf.go | 21 +- 29 files changed, 1946 insertions(+), 579 deletions(-) create mode 100644 node/protobufs/validation.go create mode 100644 node/protobufs/validation_internal_test.go create mode 100644 node/protobufs/validation_test.go diff --git a/client/cmd/merge.go b/client/cmd/merge.go index b2ddc46..aa8a7da 100644 --- a/client/cmd/merge.go +++ b/client/cmd/merge.go @@ -37,7 +37,7 @@ var mergeCmd = &cobra.Command{ panic(err) } - pub, err := privKey.GetPublic().Raw() + pubKeyBytes, err := privKey.GetPublic().Raw() if err != nil { panic(err) } @@ -48,7 +48,7 @@ var mergeCmd = &cobra.Command{ addrBytes := addr.FillBytes(make([]byte, 32)) - altAddr, err := poseidon.HashBytes([]byte(pub)) + altAddr, err := poseidon.HashBytes([]byte(pubKeyBytes)) if err != nil { panic(err) } @@ -109,34 +109,20 @@ var mergeCmd = &cobra.Command{ } } - // Create payload for merge operation - payload := []byte("merge") - for _, coinRef := range coinaddrs { - payload = append(payload, coinRef.Address...) + merge := &protobufs.MergeCoinRequest{ + Coins: coinaddrs, } - - // Signing process - sig, err := privKey.Sign(payload) - if err != nil { + if err := merge.SignED448(pubKeyBytes, privKey.Sign); err != nil { + panic(err) + } + if err := merge.Validate(); err != nil { panic(err) } // Send merge request _, err = client.SendMessage( context.Background(), - &protobufs.TokenRequest{ - Request: &protobufs.TokenRequest_Merge{ - Merge: &protobufs.MergeCoinRequest{ - Coins: coinaddrs, - Signature: &protobufs.Ed448Signature{ - Signature: sig, - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: pub, - }, - }, - }, - }, - }, + merge.TokenRequest(), ) if err != nil { panic(err) diff --git a/client/cmd/proverPause.go b/client/cmd/proverPause.go index 31e2df2..91b0a4f 100644 --- a/client/cmd/proverPause.go +++ b/client/cmd/proverPause.go @@ -98,19 +98,19 @@ func publishMessage( filter []byte, message proto.Message, ) error { - any := &anypb.Any{} - if err := any.MarshalFrom(message); err != nil { + a := &anypb.Any{} + if err := a.MarshalFrom(message); err != nil { return errors.Wrap(err, "publish message") } - any.TypeUrl = strings.Replace( - any.TypeUrl, + a.TypeUrl = strings.Replace( + a.TypeUrl, "type.googleapis.com", "types.quilibrium.com", 1, ) - payload, err := proto.Marshal(any) + payload, err := proto.Marshal(a) if err != nil { return errors.Wrap(err, "publish message") } diff --git a/client/cmd/split.go b/client/cmd/split.go index 3def31f..1274ce5 100644 --- a/client/cmd/split.go +++ b/client/cmd/split.go @@ -29,7 +29,6 @@ var splitCmd = &cobra.Command{ os.Exit(1) } - payload := []byte("split") coinaddrHex, _ := strings.CutPrefix(args[0], "0x") coinaddr, err := hex.DecodeString(coinaddrHex) if err != nil { @@ -38,7 +37,6 @@ var splitCmd = &cobra.Command{ coin := &protobufs.CoinRef{ Address: coinaddr, } - payload = append(payload, coinaddr...) conversionFactor, _ := new(big.Int).SetString("1DCD65000", 16) amounts := [][]byte{} @@ -51,7 +49,6 @@ var splitCmd = &cobra.Command{ amount = amount.Mul(decimal.NewFromBigInt(conversionFactor, 0)) amountBytes := amount.BigInt().FillBytes(make([]byte, 32)) amounts = append(amounts, amountBytes) - payload = append(payload, amountBytes...) } conn, err := GetGRPCClient() @@ -61,37 +58,29 @@ var splitCmd = &cobra.Command{ defer conn.Close() client := protobufs.NewNodeServiceClient(conn) - key, err := GetPrivKeyFromConfig(NodeConfig) + privKey, err := GetPrivKeyFromConfig(NodeConfig) + if err != nil { + panic(err) + } + pubKeyBytes, err := privKey.GetPublic().Raw() if err != nil { panic(err) } - sig, err := key.Sign(payload) - if err != nil { + split := &protobufs.SplitCoinRequest{ + OfCoin: coin, + Amounts: amounts, + } + if err := split.SignED448(pubKeyBytes, privKey.Sign); err != nil { panic(err) } - - pub, err := key.GetPublic().Raw() - if err != nil { + if err := split.Validate(); err != nil { panic(err) } _, err = client.SendMessage( context.Background(), - &protobufs.TokenRequest{ - Request: &protobufs.TokenRequest_Split{ - Split: &protobufs.SplitCoinRequest{ - OfCoin: coin, - Amounts: amounts, - Signature: &protobufs.Ed448Signature{ - Signature: sig, - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: pub, - }, - }, - }, - }, - }, + split.TokenRequest(), ) if err != nil { panic(err) diff --git a/client/cmd/transfer.go b/client/cmd/transfer.go index 70f533d..f87dbb0 100644 --- a/client/cmd/transfer.go +++ b/client/cmd/transfer.go @@ -31,13 +31,16 @@ var transferCmd = &cobra.Command{ defer conn.Close() client := protobufs.NewNodeServiceClient(conn) - key, err := GetPrivKeyFromConfig(NodeConfig) + privKey, err := GetPrivKeyFromConfig(NodeConfig) + if err != nil { + panic(err) + } + pubKeyBytes, err := privKey.GetPublic().Raw() if err != nil { panic(err) } var coinaddr *protobufs.CoinRef - payload := []byte("transfer") toaddr := []byte{} for i, arg := range args { addrHex, _ := strings.CutPrefix(arg, "0x") @@ -53,42 +56,28 @@ var transferCmd = &cobra.Command{ coinaddr = &protobufs.CoinRef{ Address: addr, } - payload = append(payload, addr...) } - payload = append(payload, toaddr...) - sig, err := key.Sign(payload) - if err != nil { + transfer := &protobufs.TransferCoinRequest{ + OfCoin: coinaddr, + ToAccount: &protobufs.AccountRef{ + Account: &protobufs.AccountRef_ImplicitAccount{ + ImplicitAccount: &protobufs.ImplicitAccount{ + Address: toaddr, + }, + }, + }, + } + if err := transfer.SignED448(pubKeyBytes, privKey.Sign); err != nil { panic(err) } - - pub, err := key.GetPublic().Raw() - if err != nil { + if err := transfer.Validate(); err != nil { panic(err) } _, err = client.SendMessage( context.Background(), - &protobufs.TokenRequest{ - Request: &protobufs.TokenRequest_Transfer{ - Transfer: &protobufs.TransferCoinRequest{ - OfCoin: coinaddr, - ToAccount: &protobufs.AccountRef{ - Account: &protobufs.AccountRef_ImplicitAccount{ - ImplicitAccount: &protobufs.ImplicitAccount{ - Address: toaddr, - }, - }, - }, - Signature: &protobufs.Ed448Signature{ - Signature: sig, - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: pub, - }, - }, - }, - }, - }, + transfer.TokenRequest(), ) if err != nil { panic(err) diff --git a/node/consensus/data/broadcast_messaging.go b/node/consensus/data/broadcast_messaging.go index 8e72cd4..46ae6bd 100644 --- a/node/consensus/data/broadcast_messaging.go +++ b/node/consensus/data/broadcast_messaging.go @@ -102,19 +102,19 @@ func (e *DataClockConsensusEngine) insertTxMessage( filter []byte, message proto.Message, ) error { - any := &anypb.Any{} - if err := any.MarshalFrom(message); err != nil { + a := &anypb.Any{} + if err := a.MarshalFrom(message); err != nil { return errors.Wrap(err, "publish message") } - any.TypeUrl = strings.Replace( - any.TypeUrl, + a.TypeUrl = strings.Replace( + a.TypeUrl, "type.googleapis.com", "types.quilibrium.com", 1, ) - payload, err := proto.Marshal(any) + payload, err := proto.Marshal(a) if err != nil { return errors.Wrap(err, "publish message") } @@ -156,19 +156,19 @@ func (e *DataClockConsensusEngine) publishMessage( filter []byte, message proto.Message, ) error { - any := &anypb.Any{} - if err := any.MarshalFrom(message); err != nil { + a := &anypb.Any{} + if err := a.MarshalFrom(message); err != nil { return errors.Wrap(err, "publish message") } - any.TypeUrl = strings.Replace( - any.TypeUrl, + a.TypeUrl = strings.Replace( + a.TypeUrl, "type.googleapis.com", "types.quilibrium.com", 1, ) - payload, err := proto.Marshal(any) + payload, err := proto.Marshal(a) if err != nil { return errors.Wrap(err, "publish message") } diff --git a/node/consensus/data/data_clock_consensus_engine.go b/node/consensus/data/data_clock_consensus_engine.go index f40fb51..e6bf3ad 100644 --- a/node/consensus/data/data_clock_consensus_engine.go +++ b/node/consensus/data/data_clock_consensus_engine.go @@ -3,7 +3,6 @@ package data import ( "context" "crypto" - "encoding/binary" stderrors "errors" "fmt" @@ -646,29 +645,18 @@ func (e *DataClockConsensusEngine) Stop(force bool) <-chan error { e.stateMx.Unlock() errChan := make(chan error) - msg := []byte("pause") - msg = binary.BigEndian.AppendUint64(msg, e.GetFrame().FrameNumber) - msg = append(msg, e.filter...) - sig, err := e.pubSub.SignMessage(msg) - if err != nil { + pause := &protobufs.AnnounceProverPause{ + Filter: e.filter, + FrameNumber: e.GetFrame().FrameNumber, + } + if err := pause.SignED448(e.pubSub.GetPublicKey(), e.pubSub.SignMessage); err != nil { + panic(err) + } + if err := pause.Validate(); err != nil { panic(err) } - e.publishMessage(e.txFilter, &protobufs.TokenRequest{ - Request: &protobufs.TokenRequest_Pause{ - Pause: &protobufs.AnnounceProverPause{ - Filter: e.filter, - FrameNumber: e.GetFrame().FrameNumber, - PublicKeySignatureEd448: &protobufs.Ed448Signature{ - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: e.pubSub.GetPublicKey(), - }, - Signature: sig, - }, - }, - }, - Timestamp: time.Now().UnixMilli(), - }) + e.publishMessage(e.txFilter, pause.TokenRequest()) wg := sync.WaitGroup{} wg.Add(len(e.executionEngines)) diff --git a/node/consensus/data/main_data_loop.go b/node/consensus/data/main_data_loop.go index 334bfb5..2fb64a1 100644 --- a/node/consensus/data/main_data_loop.go +++ b/node/consensus/data/main_data_loop.go @@ -297,7 +297,7 @@ func (e *DataClockConsensusEngine) processFrame( return latestFrame } modulo := len(outputs) - proofTree, payload, output, err := tries.PackOutputIntoPayloadAndProof( + proofTree, output, err := tries.PackOutputIntoPayloadAndProof( outputs, modulo, latestFrame, @@ -313,11 +313,16 @@ func (e *DataClockConsensusEngine) processFrame( e.previousFrameProven = latestFrame e.previousTree = proofTree - sig, err := e.pubSub.SignMessage( - payload, - ) - if err != nil { - panic(err) + mint := &protobufs.MintCoinRequest{ + Proofs: output, + } + if err := mint.SignED448(e.pubSub.GetPublicKey(), e.pubSub.SignMessage); err != nil { + e.logger.Error("could not sign mint", zap.Error(err)) + return latestFrame + } + if err := mint.Validate(); err != nil { + e.logger.Error("mint validation failed", zap.Error(err)) + return latestFrame } e.logger.Info( @@ -328,20 +333,7 @@ func (e *DataClockConsensusEngine) processFrame( zap.Duration("frame_age", frametime.Since(latestFrame)), ) - e.publishMessage(e.txFilter, &protobufs.TokenRequest{ - Request: &protobufs.TokenRequest_Mint{ - Mint: &protobufs.MintCoinRequest{ - Proofs: output, - Signature: &protobufs.Ed448Signature{ - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: e.pubSub.GetPublicKey(), - }, - Signature: sig, - }, - }, - }, - Timestamp: time.Now().UnixMilli(), - }) + e.publishMessage(e.txFilter, mint.TokenRequest()) if e.config.Engine.AutoMergeCoins { _, addrs, _, err := e.coinStore.GetCoinsForOwner( @@ -356,33 +348,29 @@ func (e *DataClockConsensusEngine) processFrame( } if len(addrs) > 25 { - message := []byte("merge") refs := []*protobufs.CoinRef{} for _, addr := range addrs { - message = append(message, addr...) refs = append(refs, &protobufs.CoinRef{ Address: addr, }) } - sig, _ := e.pubSub.SignMessage( - message, - ) + merge := &protobufs.MergeCoinRequest{ + Coins: refs, + } + if err := merge.SignED448( + e.pubSub.GetPublicKey(), + e.pubSub.SignMessage, + ); err != nil { + e.logger.Error("could not sign merge", zap.Error(err)) + return latestFrame + } + if err := merge.Validate(); err != nil { + e.logger.Error("merge validation failed", zap.Error(err)) + return latestFrame + } - e.publishMessage(e.txFilter, &protobufs.TokenRequest{ - Request: &protobufs.TokenRequest_Merge{ - Merge: &protobufs.MergeCoinRequest{ - Coins: refs, - Signature: &protobufs.Ed448Signature{ - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: e.pubSub.GetPublicKey(), - }, - Signature: sig, - }, - }, - }, - Timestamp: time.Now().UnixMilli(), - }) + e.publishMessage(e.txFilter, merge.TokenRequest()) } } } diff --git a/node/consensus/data/message_handler.go b/node/consensus/data/message_handler.go index d8355ac..7123fc9 100644 --- a/node/consensus/data/message_handler.go +++ b/node/consensus/data/message_handler.go @@ -34,31 +34,18 @@ func (e *DataClockConsensusEngine) runFrameMessageHandler() { continue } - any := &anypb.Any{} - if err := proto.Unmarshal(msg.Payload, any); err != nil { + a := &anypb.Any{} + if err := proto.Unmarshal(msg.Payload, a); err != nil { e.logger.Error("error while unmarshaling", zap.Error(err)) continue } - accepted := false - switch any.TypeUrl { - //expand for future message types - case protobufs.ClockFrameType: - accepted = true - default: - } - - if !accepted { - e.pubSub.AddPeerScore(message.From, -100000) - continue - } - - switch any.TypeUrl { + switch a.TypeUrl { case protobufs.ClockFrameType: if err := e.handleClockFrameData( message.From, msg.Address, - any, + a, false, ); err != nil { e.logger.Debug("could not handle clock frame data", zap.Error(err)) @@ -83,21 +70,8 @@ func (e *DataClockConsensusEngine) runTxMessageHandler() { continue } - any := &anypb.Any{} - if err := proto.Unmarshal(msg.Payload, any); err != nil { - continue - } - - accepted := false - switch any.TypeUrl { - //expand for future message types - case protobufs.TokenRequestType: - accepted = true - default: - } - - if !accepted { - e.pubSub.AddPeerScore(message.From, -100000) + a := &anypb.Any{} + if err := proto.Unmarshal(msg.Payload, a); err != nil { continue } @@ -122,8 +96,8 @@ func (e *DataClockConsensusEngine) runTxMessageHandler() { } for _, appMessage := range messages { - appMsg := &anypb.Any{} - err := proto.Unmarshal(appMessage.Payload, appMsg) + a := &anypb.Any{} + err := proto.Unmarshal(appMessage.Payload, a) if err != nil { e.logger.Error( "could not unmarshal app message", @@ -133,10 +107,10 @@ func (e *DataClockConsensusEngine) runTxMessageHandler() { continue } - switch appMsg.TypeUrl { + switch a.TypeUrl { case protobufs.TokenRequestType: t := &protobufs.TokenRequest{} - err := proto.Unmarshal(appMsg.Value, t) + err := proto.Unmarshal(a.Value, t) if err != nil { e.logger.Debug("could not unmarshal token request", zap.Error(err)) continue @@ -172,31 +146,18 @@ func (e *DataClockConsensusEngine) runInfoMessageHandler() { continue } - any := &anypb.Any{} - if err := proto.Unmarshal(msg.Payload, any); err != nil { + a := &anypb.Any{} + if err := proto.Unmarshal(msg.Payload, a); err != nil { e.logger.Error("error while unmarshaling", zap.Error(err)) continue } - accepted := false - switch any.TypeUrl { - //expand for future message types - case protobufs.DataPeerListAnnounceType: - accepted = true - default: - } - - if !accepted { - e.pubSub.AddPeerScore(message.From, -100000) - continue - } - - switch any.TypeUrl { + switch a.TypeUrl { case protobufs.DataPeerListAnnounceType: if err := e.handleDataPeerListAnnounce( message.From, msg.Address, - any, + a, ); err != nil { e.logger.Debug("could not handle data peer list announce", zap.Error(err)) } diff --git a/node/consensus/data/message_validators.go b/node/consensus/data/message_validators.go index 4834a07..582bdb1 100644 --- a/node/consensus/data/message_validators.go +++ b/node/consensus/data/message_validators.go @@ -52,6 +52,9 @@ func (e *DataClockConsensusEngine) validateTxMessage(peerID peer.ID, message *pb if err := proto.Unmarshal(a.Value, tx); err != nil { return p2p.ValidationResultReject } + if err := tx.Validate(); err != nil { + return p2p.ValidationResultReject + } if mint := tx.GetMint(); mint != nil { if len(mint.Proofs) < 3 { return p2p.ValidationResultReject @@ -62,16 +65,6 @@ func (e *DataClockConsensusEngine) validateTxMessage(peerID peer.ID, message *pb if len(mint.Proofs[2]) != 8 { return p2p.ValidationResultReject } - if mint.Signature == nil || - mint.Signature.PublicKey == nil || - mint.Signature.PublicKey.KeyValue == nil || - len(mint.Signature.PublicKey.KeyValue) > 114 { - return p2p.ValidationResultReject - } - head, err := e.dataTimeReel.Head() - if err != nil { - panic(err) - } // cheap hack for handling protobuf trickery: because protobufs can be // serialized in infinite ways, message ids can be regenerated simply by @@ -89,9 +82,14 @@ func (e *DataClockConsensusEngine) validateTxMessage(peerID peer.ID, message *pb e.validationFilter[id] = struct{}{} e.validationFilterMx.Unlock() if ok { - e.pubSub.AddPeerScore([]byte(message.From), -1000000) + e.pubSub.AddPeerScore(message.From, -1000000) return p2p.ValidationResultIgnore } + + head, err := e.dataTimeReel.Head() + if err != nil { + panic(err) + } if frameNumber+2 < head.FrameNumber { return p2p.ValidationResultIgnore } @@ -99,7 +97,6 @@ func (e *DataClockConsensusEngine) validateTxMessage(peerID peer.ID, message *pb if tx.Timestamp == 0 { // NOTE: The timestamp was added in later versions of the protocol, // and as such it is possible to receive requests without it. - // We avoid logging due to this reason. return p2p.ValidationResultAccept } if ts := time.UnixMilli(tx.Timestamp); time.Since(ts) > 10*time.Minute { diff --git a/node/consensus/data/peer_messaging.go b/node/consensus/data/peer_messaging.go index ed4cb34..ccfe157 100644 --- a/node/consensus/data/peer_messaging.go +++ b/node/consensus/data/peer_messaging.go @@ -186,6 +186,10 @@ func (e *DataClockConsensusEngine) handleMint( return nil, errors.Wrap(errors.New("wrong destination"), "handle mint") } + if err := t.Validate(); err != nil { + return nil, errors.Wrap(application.ErrInvalidStateTransition, "handle mint") + } + returnAddr := []byte{} e.preMidnightMintMx.Lock() if _, active := e.preMidnightMint[string( @@ -211,17 +215,6 @@ func (e *DataClockConsensusEngine) handleMint( return nil, errors.Wrap(errors.New("busy"), "handle mint") } - if t == nil || t.Proofs == nil { - return nil, errors.Wrap(application.ErrInvalidStateTransition, "handle mint") - } - - payload := []byte("mint") - for _, p := range t.Proofs { - payload = append(payload, p...) - } - if err := t.Signature.Verify(payload); err != nil { - return nil, errors.Wrap(application.ErrInvalidStateTransition, "handle mint") - } pk, err := pcrypto.UnmarshalEd448PublicKey( t.Signature.PublicKey.KeyValue, ) diff --git a/node/execution/intrinsics/token/application/token_application.go b/node/execution/intrinsics/token/application/token_application.go index 49d1703..d7c57ca 100644 --- a/node/execution/intrinsics/token/application/token_application.go +++ b/node/execution/intrinsics/token/application/token_application.go @@ -161,15 +161,12 @@ func (a *TokenApplication) ApplyTransitions( i := i switch t := transition.Request.(type) { case *protobufs.TokenRequest_Mint: - if t == nil || t.Mint.Proofs == nil || t.Mint.Signature == nil { + if t == nil { + fails[i] = transition continue } - - payload := []byte("mint") - for _, p := range t.Mint.Proofs { - payload = append(payload, p...) - } - if err := t.Mint.Signature.Verify(payload); err != nil { + if err := t.Mint.Validate(); err != nil { + fails[i] = transition continue } @@ -177,6 +174,7 @@ func (a *TokenApplication) ApplyTransitions( t.Mint.Signature.PublicKey.KeyValue, ) if err != nil { + fails[i] = transition continue } diff --git a/node/execution/intrinsics/token/application/token_handle_merge.go b/node/execution/intrinsics/token/application/token_handle_merge.go index 18821d2..6595661 100644 --- a/node/execution/intrinsics/token/application/token_handle_merge.go +++ b/node/execution/intrinsics/token/application/token_handle_merge.go @@ -16,19 +16,16 @@ func (a *TokenApplication) handleMerge( lockMap map[string]struct{}, t *protobufs.MergeCoinRequest, ) ([]*protobufs.TokenOutput, error) { + if err := t.Validate(); err != nil { + return nil, errors.Wrap(ErrInvalidStateTransition, "handle merge") + } + newCoin := &protobufs.Coin{} newTotal := new(big.Int) newIntersection := make([]byte, 1024) - payload := []byte("merge") - if t == nil || t.Coins == nil || t.Signature == nil { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle merge") - } + addresses := [][]byte{} for _, c := range t.Coins { - if c.Address == nil || len(c.Address) != 32 { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle merge") - } - if _, touched := lockMap[string(c.Address)]; touched { return nil, errors.Wrap(ErrInvalidStateTransition, "handle merge") } @@ -40,14 +37,6 @@ func (a *TokenApplication) handleMerge( } addresses = append(addresses, c.Address) - payload = append(payload, c.Address...) - } - if t.Signature.PublicKey == nil || - t.Signature.Signature == nil { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle merge") - } - if err := t.Signature.Verify(payload); err != nil { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle merge") } addr, err := poseidon.HashBytes(t.Signature.PublicKey.KeyValue) diff --git a/node/execution/intrinsics/token/application/token_handle_mint.go b/node/execution/intrinsics/token/application/token_handle_mint.go index c2f7a93..870396b 100644 --- a/node/execution/intrinsics/token/application/token_handle_mint.go +++ b/node/execution/intrinsics/token/application/token_handle_mint.go @@ -31,7 +31,7 @@ func (a *TokenApplication) handleMint( frame *protobufs.ClockFrame, parallelismMap map[int]uint64, ) ([]*protobufs.TokenOutput, error) { - if t == nil || t.Proofs == nil || t.Signature == nil { + if err := t.Validate(); err != nil { return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint") } @@ -39,9 +39,7 @@ func (a *TokenApplication) handleMint( for _, p := range t.Proofs { payload = append(payload, p...) } - if err := t.Signature.Verify(payload); err != nil { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle mint") - } + pk, err := pcrypto.UnmarshalEd448PublicKey( t.Signature.PublicKey.KeyValue, ) diff --git a/node/execution/intrinsics/token/application/token_handle_prover_announce.go b/node/execution/intrinsics/token/application/token_handle_prover_announce.go index 518f0ce..0f70678 100644 --- a/node/execution/intrinsics/token/application/token_handle_prover_announce.go +++ b/node/execution/intrinsics/token/application/token_handle_prover_announce.go @@ -13,36 +13,14 @@ func (a *TokenApplication) handleAnnounce( []*protobufs.TokenOutput, error, ) { - var primary *protobufs.Ed448Signature - payload := []byte{} - - if t == nil || t.PublicKeySignaturesEd448 == nil || - len(t.PublicKeySignaturesEd448) == 0 { + if err := t.Validate(); err != nil { return nil, errors.Wrap(ErrInvalidStateTransition, "handle announce") } - for i, p := range t.PublicKeySignaturesEd448 { + + for _, p := range t.PublicKeySignaturesEd448 { if _, touched := lockMap[string(p.PublicKey.KeyValue)]; touched { return nil, errors.Wrap(ErrInvalidStateTransition, "handle announce") } - - if p.PublicKey == nil || p.Signature == nil || - p.PublicKey.KeyValue == nil { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle announce") - } - if i == 0 { - primary = p - } else { - payload = append(payload, p.PublicKey.KeyValue...) - if err := p.Verify(primary.PublicKey.KeyValue); err != nil { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle announce") - } - } - } - if primary == nil { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle announce") - } - if err := primary.Verify(payload); err != nil { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle announce") } for _, p := range t.PublicKeySignaturesEd448[1:] { diff --git a/node/execution/intrinsics/token/application/token_handle_prover_join.go b/node/execution/intrinsics/token/application/token_handle_prover_join.go index 3ee825f..c3ee972 100644 --- a/node/execution/intrinsics/token/application/token_handle_prover_join.go +++ b/node/execution/intrinsics/token/application/token_handle_prover_join.go @@ -1,8 +1,6 @@ package application import ( - "encoding/binary" - "github.com/iden3/go-iden3-crypto/poseidon" pcrypto "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" @@ -50,25 +48,7 @@ func (a *TokenApplication) handleDataAnnounceProverJoin( return nil, errors.Wrap(ErrInvalidStateTransition, "handle join") } - payload := []byte("join") - - if t == nil || t.PublicKeySignatureEd448 == nil { - a.Logger.Debug("invalid data for join") - - return nil, errors.Wrap(ErrInvalidStateTransition, "handle join") - } - - if t.PublicKeySignatureEd448.PublicKey == nil || - t.PublicKeySignatureEd448.Signature == nil || - t.PublicKeySignatureEd448.PublicKey.KeyValue == nil || - t.Filter == nil || len(t.Filter) != 32 { - a.Logger.Debug( - "bad payload", - zap.Uint64("given_frame_number", t.FrameNumber), - zap.Uint64("current_frame_number", currentFrameNumber), - zap.Int("filter_length", len(t.Filter)), - ) - + if err := t.Validate(); err != nil { return nil, errors.Wrap(ErrInvalidStateTransition, "handle join") } @@ -79,13 +59,6 @@ func (a *TokenApplication) handleDataAnnounceProverJoin( return nil, errors.Wrap(ErrInvalidStateTransition, "handle join") } - payload = binary.BigEndian.AppendUint64(payload, t.FrameNumber) - payload = append(payload, t.Filter...) - - if err := t.PublicKeySignatureEd448.Verify(payload); err != nil { - a.Logger.Debug("can't verify signature") - return nil, errors.Wrap(ErrInvalidStateTransition, "handle join") - } address, err := a.getAddressFromSignature(t.PublicKeySignatureEd448) if err != nil { diff --git a/node/execution/intrinsics/token/application/token_handle_prover_join_test.go b/node/execution/intrinsics/token/application/token_handle_prover_join_test.go index 0d40c49..0b3bcff 100644 --- a/node/execution/intrinsics/token/application/token_handle_prover_join_test.go +++ b/node/execution/intrinsics/token/application/token_handle_prover_join_test.go @@ -70,10 +70,6 @@ func TestHandleProverJoin(t *testing.T) { } addr := addrBI.FillBytes(make([]byte, 32)) - payload := []byte("join") - payload = binary.BigEndian.AppendUint64(payload, 0) - payload = append(payload, bytes.Repeat([]byte{0xff}, 32)...) - sig, _ := privKey.Sign(payload) wprover := qcrypto.NewWesolowskiFrameProver(app.Logger) gen, _, err := wprover.CreateDataGenesisFrame( p2p.GetBloomFilter(application.TOKEN_ADDRESS, 256, 3), @@ -87,24 +83,17 @@ func TestHandleProverJoin(t *testing.T) { app.ClockStore.StageDataClockFrame(selbi.FillBytes(make([]byte, 32)), gen, txn) app.ClockStore.CommitDataClockFrame(gen.Filter, 0, selbi.FillBytes(make([]byte, 32)), app.Tries, txn, false) txn.Commit() + join := &protobufs.AnnounceProverJoin{ + Filter: bytes.Repeat([]byte{0xff}, 32), + FrameNumber: 0, + } + assert.NoError(t, join.SignED448(pubkey, privKey.Sign)) + assert.NoError(t, join.Validate()) app, success, fail, err := app.ApplyTransitions( 1, &protobufs.TokenRequests{ Requests: []*protobufs.TokenRequest{ - &protobufs.TokenRequest{ - Request: &protobufs.TokenRequest_Join{ - Join: &protobufs.AnnounceProverJoin{ - Filter: bytes.Repeat([]byte{0xff}, 32), - FrameNumber: 0, - PublicKeySignatureEd448: &protobufs.Ed448Signature{ - Signature: sig, - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: pubkey, - }, - }, - }, - }, - }, + join.TokenRequest(), }, }, false, @@ -121,24 +110,17 @@ func TestHandleProverJoin(t *testing.T) { app.ClockStore.StageDataClockFrame(selbi.FillBytes(make([]byte, 32)), frame1, txn) app.ClockStore.CommitDataClockFrame(frame1.Filter, 1, selbi.FillBytes(make([]byte, 32)), app.Tries, txn, false) txn.Commit() + join = &protobufs.AnnounceProverJoin{ + Filter: bytes.Repeat([]byte{0xff}, 32), + FrameNumber: 0, + } + assert.NoError(t, join.SignED448(pubkey, privKey.Sign)) + assert.NoError(t, join.Validate()) _, success, fail, err = app.ApplyTransitions( 2, &protobufs.TokenRequests{ Requests: []*protobufs.TokenRequest{ - &protobufs.TokenRequest{ - Request: &protobufs.TokenRequest_Join{ - Join: &protobufs.AnnounceProverJoin{ - Filter: bytes.Repeat([]byte{0xff}, 32), - FrameNumber: 0, - PublicKeySignatureEd448: &protobufs.Ed448Signature{ - Signature: sig, - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: pubkey, - }, - }, - }, - }, - }, + join.TokenRequest(), }, }, false, @@ -175,7 +157,7 @@ func TestHandleProverJoin(t *testing.T) { fmt.Printf("%x\n", individualChallenge) out2, _ := wprover.CalculateChallengeProof(individualChallenge, 10000) - proofTree, payload, output, err := tries.PackOutputIntoPayloadAndProof( + proofTree, output, err := tries.PackOutputIntoPayloadAndProof( []merkletree.DataBlock{tries.NewProofLeaf(out1), tries.NewProofLeaf(out2)}, 2, frame2, @@ -183,22 +165,15 @@ func TestHandleProverJoin(t *testing.T) { ) assert.NoError(t, err) - sig, _ = privKey.Sign(payload) + mint := &protobufs.MintCoinRequest{ + Proofs: output, + } + assert.NoError(t, mint.SignED448(pubkey, privKey.Sign)) + assert.NoError(t, mint.Validate()) + app, success, _, err = app.ApplyTransitions(2, &protobufs.TokenRequests{ Requests: []*protobufs.TokenRequest{ - &protobufs.TokenRequest{ - Request: &protobufs.TokenRequest_Mint{ - Mint: &protobufs.MintCoinRequest{ - Proofs: output, - Signature: &protobufs.Ed448Signature{ - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: pubkey, - }, - Signature: sig, - }, - }, - }, - }, + mint.TokenRequest(), }, }, false) @@ -240,7 +215,7 @@ func TestHandleProverJoin(t *testing.T) { app.ClockStore.CommitDataClockFrame(frame3.Filter, 3, selbi.FillBytes(make([]byte, 32)), app.Tries, txn, false) txn.Commit() - proofTree, payload, output, err = tries.PackOutputIntoPayloadAndProof( + proofTree, output, err = tries.PackOutputIntoPayloadAndProof( []merkletree.DataBlock{tries.NewProofLeaf(out1), tries.NewProofLeaf(out2)}, 2, frame3, @@ -248,22 +223,15 @@ func TestHandleProverJoin(t *testing.T) { ) assert.NoError(t, err) - sig, _ = privKey.Sign(payload) + mint = &protobufs.MintCoinRequest{ + Proofs: output, + } + assert.NoError(t, mint.SignED448(pubkey, privKey.Sign)) + assert.NoError(t, mint.Validate()) + app, success, _, err = app.ApplyTransitions(3, &protobufs.TokenRequests{ Requests: []*protobufs.TokenRequest{ - &protobufs.TokenRequest{ - Request: &protobufs.TokenRequest_Mint{ - Mint: &protobufs.MintCoinRequest{ - Proofs: output, - Signature: &protobufs.Ed448Signature{ - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: pubkey, - }, - Signature: sig, - }, - }, - }, - }, + mint.TokenRequest(), }, }, false) assert.NoError(t, err) diff --git a/node/execution/intrinsics/token/application/token_handle_prover_leave.go b/node/execution/intrinsics/token/application/token_handle_prover_leave.go index f39afe6..0480a77 100644 --- a/node/execution/intrinsics/token/application/token_handle_prover_leave.go +++ b/node/execution/intrinsics/token/application/token_handle_prover_leave.go @@ -1,8 +1,6 @@ package application import ( - "encoding/binary" - "github.com/pkg/errors" "source.quilibrium.com/quilibrium/monorepo/node/protobufs" ) @@ -19,17 +17,11 @@ func (a *TokenApplication) handleDataAnnounceProverLeave( return nil, errors.Wrap(ErrInvalidStateTransition, "handle leave") } - payload := []byte("leave") - - if t == nil || t.PublicKeySignatureEd448 == nil { + if err := t.Validate(); err != nil { return nil, errors.Wrap(ErrInvalidStateTransition, "handle leave") } - if t.PublicKeySignatureEd448.PublicKey == nil || - t.PublicKeySignatureEd448.Signature == nil || - t.PublicKeySignatureEd448.PublicKey.KeyValue == nil || - t.Filter == nil || len(t.Filter) != 32 || - t.FrameNumber > currentFrameNumber { + if t.FrameNumber > currentFrameNumber { return nil, errors.Wrap(ErrInvalidStateTransition, "handle leave") } @@ -39,13 +31,6 @@ func (a *TokenApplication) handleDataAnnounceProverLeave( return nil, errors.Wrap(ErrInvalidStateTransition, "handle leave") } - payload = binary.BigEndian.AppendUint64(payload, t.FrameNumber) - payload = append(payload, t.Filter...) - - if err := t.PublicKeySignatureEd448.Verify(payload); err != nil { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle leave") - } - address, err := a.getAddressFromSignature(t.PublicKeySignatureEd448) if err != nil { return nil, errors.Wrap(err, "handle leave") diff --git a/node/execution/intrinsics/token/application/token_handle_prover_pause.go b/node/execution/intrinsics/token/application/token_handle_prover_pause.go index 859fd14..8dd6a56 100644 --- a/node/execution/intrinsics/token/application/token_handle_prover_pause.go +++ b/node/execution/intrinsics/token/application/token_handle_prover_pause.go @@ -1,8 +1,6 @@ package application import ( - "encoding/binary" - "github.com/pkg/errors" "source.quilibrium.com/quilibrium/monorepo/node/protobufs" ) @@ -19,17 +17,11 @@ func (a *TokenApplication) handleDataAnnounceProverPause( return nil, errors.Wrap(ErrInvalidStateTransition, "handle pause") } - payload := []byte("pause") - - if t == nil || t.PublicKeySignatureEd448 == nil { + if err := t.Validate(); err != nil { return nil, errors.Wrap(ErrInvalidStateTransition, "handle pause") } - if t.PublicKeySignatureEd448.PublicKey == nil || - t.PublicKeySignatureEd448.Signature == nil || - t.PublicKeySignatureEd448.PublicKey.KeyValue == nil || - t.Filter == nil || len(t.Filter) != 32 || - t.FrameNumber > currentFrameNumber { + if t.FrameNumber > currentFrameNumber { return nil, errors.Wrap(ErrInvalidStateTransition, "handle pause") } if _, touched := lockMap[string( @@ -38,13 +30,6 @@ func (a *TokenApplication) handleDataAnnounceProverPause( return nil, errors.Wrap(ErrInvalidStateTransition, "handle pause") } - payload = binary.BigEndian.AppendUint64(payload, t.FrameNumber) - payload = append(payload, t.Filter...) - - if err := t.PublicKeySignatureEd448.Verify(payload); err != nil { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle pause") - } - address, err := a.getAddressFromSignature(t.PublicKeySignatureEd448) if err != nil { return nil, errors.Wrap(err, "handle pause") diff --git a/node/execution/intrinsics/token/application/token_handle_prover_resume.go b/node/execution/intrinsics/token/application/token_handle_prover_resume.go index 0b7003c..4ff7f31 100644 --- a/node/execution/intrinsics/token/application/token_handle_prover_resume.go +++ b/node/execution/intrinsics/token/application/token_handle_prover_resume.go @@ -1,8 +1,6 @@ package application import ( - "encoding/binary" - "github.com/pkg/errors" "source.quilibrium.com/quilibrium/monorepo/node/protobufs" ) @@ -19,17 +17,11 @@ func (a *TokenApplication) handleDataAnnounceProverResume( return nil, errors.Wrap(ErrInvalidStateTransition, "handle resume") } - payload := []byte("resume") - - if t == nil || t.PublicKeySignatureEd448 == nil { + if err := t.Validate(); err != nil { return nil, errors.Wrap(ErrInvalidStateTransition, "handle resume") } - if t.PublicKeySignatureEd448.PublicKey == nil || - t.PublicKeySignatureEd448.Signature == nil || - t.PublicKeySignatureEd448.PublicKey.KeyValue == nil || - t.Filter == nil || len(t.Filter) != 32 || - t.FrameNumber > currentFrameNumber { + if t.FrameNumber > currentFrameNumber { return nil, errors.Wrap(ErrInvalidStateTransition, "handle resume") } @@ -39,13 +31,6 @@ func (a *TokenApplication) handleDataAnnounceProverResume( return nil, errors.Wrap(ErrInvalidStateTransition, "handle resume") } - payload = binary.BigEndian.AppendUint64(payload, t.FrameNumber) - payload = append(payload, t.Filter...) - - if err := t.PublicKeySignatureEd448.Verify(payload); err != nil { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle resume") - } - address, err := a.getAddressFromSignature(t.PublicKeySignatureEd448) if err != nil { return nil, errors.Wrap(err, "handle resume") diff --git a/node/execution/intrinsics/token/application/token_handle_split.go b/node/execution/intrinsics/token/application/token_handle_split.go index aa32102..e4a35b5 100644 --- a/node/execution/intrinsics/token/application/token_handle_split.go +++ b/node/execution/intrinsics/token/application/token_handle_split.go @@ -16,13 +16,13 @@ func (a *TokenApplication) handleSplit( lockMap map[string]struct{}, t *protobufs.SplitCoinRequest, ) ([]*protobufs.TokenOutput, error) { - newCoins := []*protobufs.Coin{} - newAmounts := []*big.Int{} - payload := []byte{} - if t.Signature == nil || t.OfCoin == nil || t.OfCoin.Address == nil || - len(t.OfCoin.Address) != 32 { + if err := t.Validate(); err != nil { return nil, errors.Wrap(ErrInvalidStateTransition, "handle split") } + + newCoins := []*protobufs.Coin{} + newAmounts := []*big.Int{} + coin, err := a.CoinStore.GetCoinByAddress(nil, t.OfCoin.Address) if err != nil { return nil, errors.Wrap(ErrInvalidStateTransition, "handle split") @@ -32,24 +32,6 @@ func (a *TokenApplication) handleSplit( return nil, errors.Wrap(ErrInvalidStateTransition, "handle split") } - payload = append(payload, []byte("split")...) - payload = append(payload, t.OfCoin.Address...) - - if len(t.Amounts) > 100 { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle split") - } - - for _, a := range t.Amounts { - if len(a) > 32 { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle split") - } - payload = append(payload, a...) - } - - if err := t.Signature.Verify(payload); err != nil { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle split") - } - addr, err := poseidon.HashBytes(t.Signature.PublicKey.KeyValue) if err != nil { return nil, errors.Wrap(ErrInvalidStateTransition, "handle split") diff --git a/node/execution/intrinsics/token/application/token_handle_transfer.go b/node/execution/intrinsics/token/application/token_handle_transfer.go index 52a4150..150566b 100644 --- a/node/execution/intrinsics/token/application/token_handle_transfer.go +++ b/node/execution/intrinsics/token/application/token_handle_transfer.go @@ -16,12 +16,7 @@ func (a *TokenApplication) handleTransfer( lockMap map[string]struct{}, t *protobufs.TransferCoinRequest, ) ([]*protobufs.TokenOutput, error) { - payload := []byte("transfer") - if t == nil || t.Signature == nil || t.OfCoin == nil || - t.OfCoin.Address == nil || len(t.OfCoin.Address) != 32 || - t.ToAccount == nil || t.ToAccount.GetImplicitAccount() == nil || - t.ToAccount.GetImplicitAccount().Address == nil || - len(t.ToAccount.GetImplicitAccount().Address) != 32 { + if err := t.Validate(); err != nil { return nil, errors.Wrap(ErrInvalidStateTransition, "handle transfer") } @@ -34,16 +29,6 @@ func (a *TokenApplication) handleTransfer( return nil, errors.Wrap(ErrInvalidStateTransition, "handle transfer") } - payload = append(payload, t.OfCoin.Address...) - payload = append( - payload, - t.ToAccount.GetImplicitAccount().Address..., - ) - - if err := t.Signature.Verify(payload); err != nil { - return nil, errors.Wrap(ErrInvalidStateTransition, "handle transfer") - } - addr, err := poseidon.HashBytes(t.Signature.PublicKey.KeyValue) if err != nil { return nil, errors.Wrap(ErrInvalidStateTransition, "handle transfer") diff --git a/node/execution/intrinsics/token/token_execution_engine.go b/node/execution/intrinsics/token/token_execution_engine.go index 5deb1a3..107ab3c 100644 --- a/node/execution/intrinsics/token/token_execution_engine.go +++ b/node/execution/intrinsics/token/token_execution_engine.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "crypto" - "encoding/binary" "encoding/hex" "math/big" "slices" @@ -364,11 +363,14 @@ func NewTokenExecutionEngine( } if shouldResume { - msg := []byte("resume") - msg = binary.BigEndian.AppendUint64(msg, f.FrameNumber) - msg = append(msg, e.intrinsicFilter...) - sig, err := e.pubSub.SignMessage(msg) - if err != nil { + resume := &protobufs.AnnounceProverResume{ + Filter: e.intrinsicFilter, + FrameNumber: f.FrameNumber, + } + if err := resume.SignED448(e.pubSub.GetPublicKey(), e.pubSub.SignMessage); err != nil { + panic(err) + } + if err := resume.Validate(); err != nil { panic(err) } @@ -391,21 +393,7 @@ func NewTokenExecutionEngine( } e.publishMessage( append([]byte{0x00}, e.intrinsicFilter...), - &protobufs.TokenRequest{ - Request: &protobufs.TokenRequest_Resume{ - Resume: &protobufs.AnnounceProverResume{ - Filter: e.intrinsicFilter, - FrameNumber: f.FrameNumber, - PublicKeySignatureEd448: &protobufs.Ed448Signature{ - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: e.pubSub.GetPublicKey(), - }, - Signature: sig, - }, - }, - }, - Timestamp: gotime.Now().UnixMilli(), - }, + resume.TokenRequest(), ) } }() @@ -473,20 +461,20 @@ func (e *TokenExecutionEngine) ProcessMessage( message *protobufs.Message, ) ([]*protobufs.Message, error) { if bytes.Equal(address, e.GetSupportedApplications()[0].Address) { - any := &anypb.Any{} - if err := proto.Unmarshal(message.Payload, any); err != nil { + a := &anypb.Any{} + if err := proto.Unmarshal(message.Payload, a); err != nil { return nil, errors.Wrap(err, "process message") } e.logger.Debug( "processing execution message", - zap.String("type", any.TypeUrl), + zap.String("type", a.TypeUrl), ) - switch any.TypeUrl { + switch a.TypeUrl { case protobufs.TokenRequestType: if e.clock.IsInProverTrie(e.proverPublicKey) { - payload, err := proto.Marshal(any) + payload, err := proto.Marshal(a) if err != nil { return nil, errors.Wrap(err, "process message") } @@ -1114,19 +1102,19 @@ func (e *TokenExecutionEngine) publishMessage( filter []byte, message proto.Message, ) error { - any := &anypb.Any{} - if err := any.MarshalFrom(message); err != nil { + a := &anypb.Any{} + if err := a.MarshalFrom(message); err != nil { return errors.Wrap(err, "publish message") } - any.TypeUrl = strings.Replace( - any.TypeUrl, + a.TypeUrl = strings.Replace( + a.TypeUrl, "type.googleapis.com", "types.quilibrium.com", 1, ) - payload, err := proto.Marshal(any) + payload, err := proto.Marshal(a) if err != nil { return errors.Wrap(err, "publish message") } @@ -1378,8 +1366,14 @@ func (e *TokenExecutionEngine) AnnounceProverMerge() *protobufs.AnnounceProverRe currentHead.FrameNumber < application.PROOF_FRAME_CUTOFF { return nil } - keys := [][]byte{} - ksigs := [][]byte{} + + var helpers []protobufs.ED448SignHelper = []protobufs.ED448SignHelper{ + { + PublicKey: e.pubSub.GetPublicKey(), + Sign: e.pubSub.SignMessage, + }, + } + if len(e.engineConfig.MultisigProverEnrollmentPaths) != 0 && e.GetSeniority().Cmp(GetAggregatedSeniority( []string{peer.ID(e.pubSub.GetPeerID()).String()}, @@ -1406,86 +1400,46 @@ func (e *TokenExecutionEngine) AnnounceProverMerge() *protobufs.AnnounceProverRe panic(errors.Wrap(err, "error unmarshaling peerkey")) } - keys = append(keys, pubBytes) - sig, err := privKey.Sign(e.pubSub.GetPublicKey()) - if err != nil { - panic(errors.Wrap(err, "error unmarshaling peerkey")) - } - ksigs = append(ksigs, sig) + helpers = append(helpers, protobufs.ED448SignHelper{ + PublicKey: pubBytes, + Sign: privKey.Sign, + }) } } - keyjoin := []byte{} - for _, k := range keys { - keyjoin = append(keyjoin, k...) - } - - mainsig, err := e.pubSub.SignMessage(keyjoin) - if err != nil { + announce := &protobufs.AnnounceProverRequest{} + if err := announce.SignED448(helpers); err != nil { panic(err) } - - announce := &protobufs.AnnounceProverRequest{ - PublicKeySignaturesEd448: []*protobufs.Ed448Signature{}, - } - - announce.PublicKeySignaturesEd448 = append( - announce.PublicKeySignaturesEd448, - &protobufs.Ed448Signature{ - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: e.pubSub.GetPublicKey(), - }, - Signature: mainsig, - }, - ) - - for i := range keys { - announce.PublicKeySignaturesEd448 = append( - announce.PublicKeySignaturesEd448, - &protobufs.Ed448Signature{ - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: keys[i], - }, - Signature: ksigs[i], - }, - ) + if err := announce.Validate(); err != nil { + panic(err) } return announce } func (e *TokenExecutionEngine) AnnounceProverJoin() { - msg := []byte("join") head := e.GetFrame() if head == nil || head.FrameNumber < application.PROOF_FRAME_CUTOFF { return } - msg = binary.BigEndian.AppendUint64(msg, head.FrameNumber) - msg = append(msg, bytes.Repeat([]byte{0xff}, 32)...) - sig, err := e.pubSub.SignMessage(msg) - if err != nil { + + join := &protobufs.AnnounceProverJoin{ + Filter: bytes.Repeat([]byte{0xff}, 32), + FrameNumber: head.FrameNumber, + Announce: e.AnnounceProverMerge(), + } + if err := join.SignED448(e.pubSub.GetPublicKey(), e.pubSub.SignMessage); err != nil { + panic(err) + } + if err := join.Validate(); err != nil { panic(err) } e.publishMessage( append([]byte{0x00}, e.intrinsicFilter...), - &protobufs.TokenRequest{ - Request: &protobufs.TokenRequest_Join{ - Join: &protobufs.AnnounceProverJoin{ - Filter: bytes.Repeat([]byte{0xff}, 32), - FrameNumber: head.FrameNumber, - PublicKeySignatureEd448: &protobufs.Ed448Signature{ - Signature: sig, - PublicKey: &protobufs.Ed448PublicKey{ - KeyValue: e.pubSub.GetPublicKey(), - }, - }, - Announce: e.AnnounceProverMerge(), - }, - }, - Timestamp: gotime.Now().UnixMilli(), - }, + join.TokenRequest(), ) } diff --git a/node/protobufs/keys.go b/node/protobufs/keys.go index ef2c648..f3dd7d6 100644 --- a/node/protobufs/keys.go +++ b/node/protobufs/keys.go @@ -182,6 +182,11 @@ func (s *Ed448Signature) Verify(msg []byte) error { return errors.Wrap(errors.New("invalid length for signature"), "verify") } + return s.verifyUnsafe(msg) +} + +// verifyUnsafe is used to verify a signature without checking the length of the public key and signature. +func (s *Ed448Signature) verifyUnsafe(msg []byte) error { if !ed448.Verify(s.PublicKey.KeyValue, msg, s.Signature, "") { return errors.Wrap(errors.New("invalid signature for public key"), "verify") } diff --git a/node/protobufs/node.go b/node/protobufs/node.go index 6677052..ace06c6 100644 --- a/node/protobufs/node.go +++ b/node/protobufs/node.go @@ -2,6 +2,7 @@ package protobufs import ( "encoding/binary" + "time" "github.com/iden3/go-iden3-crypto/poseidon" pcrypto "github.com/libp2p/go-libp2p/core/crypto" @@ -22,14 +23,6 @@ func (t *TokenRequest) Priority() uint64 { func (t *MintCoinRequest) RingAndParallelism( ringCalc func(addr []byte) int, ) (int, uint32, error) { - payload := []byte("mint") - for _, p := range t.Proofs { - payload = append(payload, p...) - } - if err := t.Signature.Verify(payload); err != nil { - return -1, 0, errors.New("invalid") - } - pk, err := pcrypto.UnmarshalEd448PublicKey( t.Signature.PublicKey.KeyValue, ) @@ -58,3 +51,93 @@ func (t *MintCoinRequest) RingAndParallelism( return -1, 0, errors.New("invalid") } + +// TokenRequest returns the TokenRequest for the TransferCoinRequest. +func (t *TransferCoinRequest) TokenRequest() *TokenRequest { + return &TokenRequest{ + Request: &TokenRequest_Transfer{ + Transfer: t, + }, + Timestamp: time.Now().UnixMilli(), + } +} + +// TokenRequest returns the TokenRequest for the SplitCoinRequest. +func (t *SplitCoinRequest) TokenRequest() *TokenRequest { + return &TokenRequest{ + Request: &TokenRequest_Split{ + Split: t, + }, + Timestamp: time.Now().UnixMilli(), + } +} + +// TokenRequest returns the TokenRequest for the MergeCoinRequest. +func (t *MergeCoinRequest) TokenRequest() *TokenRequest { + return &TokenRequest{ + Request: &TokenRequest_Merge{ + Merge: t, + }, + Timestamp: time.Now().UnixMilli(), + } +} + +// TokenRequest returns the TokenRequest for the MintCoinRequest. +func (t *MintCoinRequest) TokenRequest() *TokenRequest { + return &TokenRequest{ + Request: &TokenRequest_Mint{ + Mint: t, + }, + Timestamp: time.Now().UnixMilli(), + } +} + +// TokenRequest returns the TokenRequest for the AnnounceProverRequest. +func (t *AnnounceProverRequest) TokenRequest() *TokenRequest { + return &TokenRequest{ + Request: &TokenRequest_Announce{ + Announce: t, + }, + Timestamp: time.Now().UnixMilli(), + } +} + +// TokenRequest returns the TokenRequest for the AnnounceProverJoin. +func (t *AnnounceProverJoin) TokenRequest() *TokenRequest { + return &TokenRequest{ + Request: &TokenRequest_Join{ + Join: t, + }, + Timestamp: time.Now().UnixMilli(), + } +} + +// TokenRequest returns the TokenRequest for the AnnounceProverLeave. +func (t *AnnounceProverLeave) TokenRequest() *TokenRequest { + return &TokenRequest{ + Request: &TokenRequest_Leave{ + Leave: t, + }, + Timestamp: time.Now().UnixMilli(), + } +} + +// TokenRequest returns the TokenRequest for the AnnounceProverPause. +func (t *AnnounceProverPause) TokenRequest() *TokenRequest { + return &TokenRequest{ + Request: &TokenRequest_Pause{ + Pause: t, + }, + Timestamp: time.Now().UnixMilli(), + } +} + +// TokenRequest returns the TokenRequest for the AnnounceProverResume. +func (t *AnnounceProverResume) TokenRequest() *TokenRequest { + return &TokenRequest{ + Request: &TokenRequest_Resume{ + Resume: t, + }, + Timestamp: time.Now().UnixMilli(), + } +} diff --git a/node/protobufs/validation.go b/node/protobufs/validation.go new file mode 100644 index 0000000..f4a7860 --- /dev/null +++ b/node/protobufs/validation.go @@ -0,0 +1,700 @@ +package protobufs + +import ( + "encoding/binary" + + "github.com/pkg/errors" +) + +type signatureMessage interface { + signatureMessage() []byte +} + +var _ signatureMessage = (*TransferCoinRequest)(nil) + +func (t *TransferCoinRequest) signatureMessage() []byte { + payload := []byte("transfer") + payload = append(payload, t.OfCoin.Address...) + payload = append( + payload, + t.ToAccount.GetImplicitAccount().Address..., + ) + return payload +} + +var _ signatureMessage = (*SplitCoinRequest)(nil) + +func (t *SplitCoinRequest) signatureMessage() []byte { + payload := []byte("split") + payload = append(payload, t.OfCoin.Address...) + for _, a := range t.Amounts { + payload = append(payload, a...) + } + return payload +} + +var _ signatureMessage = (*MergeCoinRequest)(nil) + +func (t *MergeCoinRequest) signatureMessage() []byte { + payload := []byte("merge") + for _, c := range t.Coins { + payload = append(payload, c.Address...) + } + return payload +} + +var _ signatureMessage = (*MintCoinRequest)(nil) + +func (t *MintCoinRequest) signatureMessage() []byte { + payload := []byte("mint") + for _, p := range t.Proofs { + payload = append(payload, p...) + } + return payload +} + +// NOTE: AnnounceProverRequest has a non-trivial signature payload. + +var _ signatureMessage = (*AnnounceProverJoin)(nil) + +func (t *AnnounceProverJoin) signatureMessage() []byte { + payload := []byte("join") + payload = binary.BigEndian.AppendUint64(payload, t.FrameNumber) + payload = append(payload, t.Filter...) + return payload +} + +var _ signatureMessage = (*AnnounceProverLeave)(nil) + +func (t *AnnounceProverLeave) signatureMessage() []byte { + payload := []byte("leave") + payload = binary.BigEndian.AppendUint64(payload, t.FrameNumber) + payload = append(payload, t.Filter...) + return payload +} + +var _ signatureMessage = (*AnnounceProverPause)(nil) + +func (t *AnnounceProverPause) signatureMessage() []byte { + payload := []byte("pause") + payload = binary.BigEndian.AppendUint64(payload, t.FrameNumber) + payload = append(payload, t.Filter...) + return payload +} + +var _ signatureMessage = (*AnnounceProverResume)(nil) + +func (t *AnnounceProverResume) signatureMessage() []byte { + payload := []byte("resume") + payload = binary.BigEndian.AppendUint64(payload, t.FrameNumber) + payload = append(payload, t.Filter...) + return payload +} + +// SignedMessage is a message that has a signature. +type SignedMessage interface { + // ValidateSignature checks the signature of the message. + // The message contents are expected to be valid - validation + // of contents must precede validation of the signature. + ValidateSignature() error +} + +var _ SignedMessage = (*TransferCoinRequest)(nil) + +// ValidateSignature checks the signature of the transfer coin request. +func (t *TransferCoinRequest) ValidateSignature() error { + if err := t.Signature.verifyUnsafe(t.signatureMessage()); err != nil { + return errors.Wrap(err, "validate signature") + } + return nil +} + +var _ SignedMessage = (*SplitCoinRequest)(nil) + +// ValidateSignature checks the signature of the split coin request. +func (t *SplitCoinRequest) ValidateSignature() error { + if err := t.Signature.verifyUnsafe(t.signatureMessage()); err != nil { + return errors.Wrap(err, "validate signature") + } + return nil +} + +var _ SignedMessage = (*MergeCoinRequest)(nil) + +// ValidateSignature checks the signature of the merge coin request. +func (t *MergeCoinRequest) ValidateSignature() error { + if err := t.Signature.verifyUnsafe(t.signatureMessage()); err != nil { + return errors.Wrap(err, "validate signature") + } + return nil +} + +var _ SignedMessage = (*MintCoinRequest)(nil) + +// ValidateSignature checks the signature of the mint coin request. +func (t *MintCoinRequest) ValidateSignature() error { + if err := t.Signature.verifyUnsafe(t.signatureMessage()); err != nil { + return errors.Wrap(err, "validate signature") + } + return nil +} + +var _ SignedMessage = (*AnnounceProverRequest)(nil) + +// ValidateSignature checks the signature of the announce prover request. +func (t *AnnounceProverRequest) ValidateSignature() error { + payload := []byte{} + primary := t.PublicKeySignaturesEd448[0] + for _, p := range t.PublicKeySignaturesEd448[1:] { + payload = append(payload, p.PublicKey.KeyValue...) + if err := p.verifyUnsafe(primary.PublicKey.KeyValue); err != nil { + return errors.Wrap(err, "validate signature") + } + } + if err := primary.verifyUnsafe(payload); err != nil { + return errors.Wrap(err, "validate signature") + } + return nil +} + +var _ SignedMessage = (*AnnounceProverJoin)(nil) + +// ValidateSignature checks the signature of the announce prover join. +func (t *AnnounceProverJoin) ValidateSignature() error { + if err := t.PublicKeySignatureEd448.verifyUnsafe(t.signatureMessage()); err != nil { + return errors.Wrap(err, "validate signature") + } + return nil +} + +var _ SignedMessage = (*AnnounceProverLeave)(nil) + +// ValidateSignature checks the signature of the announce prover leave. +func (t *AnnounceProverLeave) ValidateSignature() error { + if err := t.PublicKeySignatureEd448.verifyUnsafe(t.signatureMessage()); err != nil { + return errors.Wrap(err, "validate signature") + } + return nil +} + +var _ SignedMessage = (*AnnounceProverPause)(nil) + +// ValidateSignature checks the signature of the announce prover pause. +func (t *AnnounceProverPause) ValidateSignature() error { + if err := t.PublicKeySignatureEd448.verifyUnsafe(t.signatureMessage()); err != nil { + return errors.Wrap(err, "validate signature") + } + return nil +} + +var _ SignedMessage = (*AnnounceProverResume)(nil) + +// ValidateSignature checks the signature of the announce prover resume. +func (t *AnnounceProverResume) ValidateSignature() error { + if err := t.PublicKeySignatureEd448.verifyUnsafe(t.signatureMessage()); err != nil { + return errors.Wrap(err, "validate signature") + } + return nil +} + +// ValidatableMessage is a message that can be validated. +type ValidatableMessage interface { + // Validate checks the message contents. + // It will also verify signatures if the message is signed. + Validate() error +} + +var _ ValidatableMessage = (*Ed448PublicKey)(nil) + +// Validate checks the Ed448 public key. +func (e *Ed448PublicKey) Validate() error { + if e == nil { + return errors.New("nil Ed448 public key") + } + if len(e.KeyValue) != 57 { + return errors.New("invalid Ed448 public key") + } + return nil +} + +var _ ValidatableMessage = (*Ed448Signature)(nil) + +// Validate checks the Ed448 signature. +func (e *Ed448Signature) Validate() error { + if e == nil { + return errors.New("nil Ed448 signature") + } + if err := e.PublicKey.Validate(); err != nil { + return errors.Wrap(err, "public key") + } + if len(e.Signature) != 114 { + return errors.New("invalid Ed448 signature") + } + return nil +} + +var _ ValidatableMessage = (*ImplicitAccount)(nil) + +// Validate checks the implicit account. +func (i *ImplicitAccount) Validate() error { + if i == nil { + return errors.New("nil implicit account") + } + // TODO: Validate ImplicitType. + if len(i.Address) != 32 { + return errors.New("invalid implicit account") + } + // TODO: Validate Domain. + return nil +} + +var _ ValidatableMessage = (*OriginatedAccountRef)(nil) + +// Validate checks the originated account. +func (o *OriginatedAccountRef) Validate() error { + if o == nil { + return errors.New("nil originated account") + } + if len(o.Address) != 32 { + return errors.New("invalid originated account") + } + return nil +} + +var _ ValidatableMessage = (*AccountRef)(nil) + +// Validate checks the account reference. +func (a *AccountRef) Validate() error { + if a == nil { + return errors.New("nil account reference") + } + switch { + case a.GetImplicitAccount() != nil: + if err := a.GetImplicitAccount().Validate(); err != nil { + return errors.Wrap(err, "implicit account") + } + case a.GetOriginatedAccount() != nil: + if err := a.GetOriginatedAccount().Validate(); err != nil { + return errors.Wrap(err, "originated account") + } + default: + return errors.New("invalid account reference") + } + return nil +} + +var _ ValidatableMessage = (*CoinRef)(nil) + +// Validate checks the coin reference. +func (c *CoinRef) Validate() error { + if c == nil { + return errors.New("nil coin reference") + } + if len(c.Address) != 32 { + return errors.New("invalid coin reference") + } + return nil +} + +var _ ValidatableMessage = (*AccountAllowanceRef)(nil) + +// Validate checks the account allowance reference. +func (a *AccountAllowanceRef) Validate() error { + if a == nil { + return errors.New("nil account allowance reference") + } + if len(a.Address) != 32 { + return errors.New("invalid account allowance reference") + } + return nil +} + +var _ ValidatableMessage = (*CoinAllowanceRef)(nil) + +// Validate checks the coin allowance reference. +func (c *CoinAllowanceRef) Validate() error { + if c == nil { + return errors.New("nil coin allowance reference") + } + if len(c.Address) != 32 { + return errors.New("invalid coin allowance reference") + } + return nil +} + +var _ ValidatableMessage = (*TokenRequest)(nil) + +// Validate checks the token request. +func (t *TokenRequest) Validate() error { + if t == nil { + return errors.New("nil token request") + } + switch { + case t.GetTransfer() != nil: + return t.GetTransfer().Validate() + case t.GetSplit() != nil: + return t.GetSplit().Validate() + case t.GetMerge() != nil: + return t.GetMerge().Validate() + case t.GetMint() != nil: + return t.GetMint().Validate() + case t.GetAnnounce() != nil: + return t.GetAnnounce().Validate() + case t.GetJoin() != nil: + return t.GetJoin().Validate() + case t.GetLeave() != nil: + return t.GetLeave().Validate() + case t.GetPause() != nil: + return t.GetPause().Validate() + case t.GetResume() != nil: + return t.GetResume().Validate() + default: + return nil + } +} + +var _ ValidatableMessage = (*TransferCoinRequest)(nil) + +// Validate checks the transfer coin request. +func (t *TransferCoinRequest) Validate() error { + if t == nil { + return errors.New("nil transfer coin request") + } + if err := t.ToAccount.Validate(); err != nil { + return errors.Wrap(err, "to account") + } + // TODO: Validate RefundAccount. + if err := t.OfCoin.Validate(); err != nil { + return errors.Wrap(err, "of coin") + } + // TODO: Validate Expiry. + // TODO: Validate AccountAllowance. + // TODO: Validate CoinAllowance. + if err := t.Signature.Validate(); err != nil { + return errors.Wrap(err, "signature") + } + if err := t.ValidateSignature(); err != nil { + return errors.Wrap(err, "signature") + } + return nil +} + +var _ ValidatableMessage = (*SplitCoinRequest)(nil) + +// Validate checks the split coin request. +func (t *SplitCoinRequest) Validate() error { + if t == nil { + return errors.New("nil split coin request") + } + if err := t.OfCoin.Validate(); err != nil { + return errors.Wrap(err, "of coin") + } + if n := len(t.Amounts); n == 0 || n > 100 { + return errors.New("invalid amounts") + } + for _, a := range t.Amounts { + if n := len(a); n == 0 || n > 32 { + return errors.New("invalid amount") + } + } + // TODO: Validate AccountAllowance. + // TODO: Validate CoinAllowance. + if err := t.Signature.Validate(); err != nil { + return errors.Wrap(err, "signature") + } + if err := t.ValidateSignature(); err != nil { + return errors.Wrap(err, "signature") + } + return nil +} + +var _ ValidatableMessage = (*MergeCoinRequest)(nil) + +// Validate checks the merge coin request. +func (t *MergeCoinRequest) Validate() error { + if t == nil { + return errors.New("nil merge coin request") + } + if len(t.Coins) == 0 { + return errors.New("invalid coins") + } + for _, c := range t.Coins { + if err := c.Validate(); err != nil { + return errors.Wrap(err, "coin") + } + } + // TODO: Validate AccountAllowance. + // TODO: Validate CoinAllowance. + if err := t.Signature.Validate(); err != nil { + return errors.Wrap(err, "signature") + } + if err := t.ValidateSignature(); err != nil { + return errors.Wrap(err, "signature") + } + return nil +} + +var _ ValidatableMessage = (*MintCoinRequest)(nil) + +// Validate checks the mint coin request. +func (t *MintCoinRequest) Validate() error { + if t == nil { + return errors.New("nil mint coin request") + } + if len(t.Proofs) == 0 { + return errors.New("invalid proofs") + } + // TODO: Validate AccountAllowance. + if err := t.Signature.Validate(); err != nil { + return errors.Wrap(err, "signature") + } + if err := t.ValidateSignature(); err != nil { + return errors.Wrap(err, "signature") + } + return nil +} + +var _ ValidatableMessage = (*AnnounceProverRequest)(nil) + +// Validate checks the announce prover request. +func (t *AnnounceProverRequest) Validate() error { + if t == nil { + return errors.New("nil announce prover request") + } + if len(t.PublicKeySignaturesEd448) == 0 { + return errors.New("invalid public key signatures") + } + for _, p := range t.PublicKeySignaturesEd448 { + if err := p.Validate(); err != nil { + return errors.Wrap(err, "public key signature") + } + } + if err := t.ValidateSignature(); err != nil { + return errors.Wrap(err, "signature") + } + return nil +} + +var _ ValidatableMessage = (*AnnounceProverJoin)(nil) + +// Validate checks the announce prover join. +func (t *AnnounceProverJoin) Validate() error { + if t == nil { + return errors.New("nil announce prover join") + } + if len(t.Filter) != 32 { + return errors.New("invalid filter") + } + if announce := t.Announce; announce != nil { + if err := announce.Validate(); err != nil { + return errors.Wrap(err, "announce") + } + } + if err := t.PublicKeySignatureEd448.Validate(); err != nil { + return errors.Wrap(err, "public key signature") + } + if err := t.ValidateSignature(); err != nil { + return errors.Wrap(err, "signature") + } + return nil +} + +var _ ValidatableMessage = (*AnnounceProverLeave)(nil) + +// Validate checks the announce prover leave. +func (t *AnnounceProverLeave) Validate() error { + if t == nil { + return errors.New("nil announce prover leave") + } + if len(t.Filter) != 32 { + return errors.New("invalid filter") + } + if err := t.PublicKeySignatureEd448.Validate(); err != nil { + return errors.Wrap(err, "public key signature") + } + if err := t.ValidateSignature(); err != nil { + return errors.Wrap(err, "signature") + } + return nil +} + +var _ ValidatableMessage = (*AnnounceProverPause)(nil) + +// Validate checks the announce prover pause. +func (t *AnnounceProverPause) Validate() error { + if t == nil { + return errors.New("nil announce prover pause") + } + if len(t.Filter) != 32 { + return errors.New("invalid filter") + } + if err := t.PublicKeySignatureEd448.Validate(); err != nil { + return errors.Wrap(err, "public key signature") + } + if err := t.ValidateSignature(); err != nil { + return errors.Wrap(err, "signature") + } + return nil +} + +var _ ValidatableMessage = (*AnnounceProverResume)(nil) + +// Validate checks the announce prover resume. +func (t *AnnounceProverResume) Validate() error { + if t == nil { + return errors.New("nil announce prover resume") + } + if len(t.Filter) != 32 { + return errors.New("invalid filter") + } + if err := t.PublicKeySignatureEd448.Validate(); err != nil { + return errors.Wrap(err, "public key signature") + } + if err := t.ValidateSignature(); err != nil { + return errors.Wrap(err, "signature") + } + return nil +} + +// SignableED448Message is a message that can be signed. +type SignableED448Message interface { + // SignED448 signs the message with the given key, modifying the message. + // The message contents are expected to be valid - message + // contents must be validated, or correctly constructed, before signing. + SignED448(publicKey []byte, sign func([]byte) ([]byte, error)) error +} + +func newED448Signature(publicKey, signature []byte) *Ed448Signature { + return &Ed448Signature{ + PublicKey: &Ed448PublicKey{ + KeyValue: publicKey, + }, + Signature: signature, + } +} + +var _ SignableED448Message = (*TransferCoinRequest)(nil) + +// SignED448 signs the transfer coin request with the given key. +func (t *TransferCoinRequest) SignED448(publicKey []byte, sign func([]byte) ([]byte, error)) error { + signature, err := sign(t.signatureMessage()) + if err != nil { + return errors.Wrap(err, "sign") + } + t.Signature = newED448Signature(publicKey, signature) + return nil +} + +var _ SignableED448Message = (*SplitCoinRequest)(nil) + +// SignED448 signs the split coin request with the given key. +func (t *SplitCoinRequest) SignED448(publicKey []byte, sign func([]byte) ([]byte, error)) error { + signature, err := sign(t.signatureMessage()) + if err != nil { + return errors.Wrap(err, "sign") + } + t.Signature = newED448Signature(publicKey, signature) + return nil +} + +var _ SignableED448Message = (*MergeCoinRequest)(nil) + +// SignED448 signs the merge coin request with the given key. +func (t *MergeCoinRequest) SignED448(publicKey []byte, sign func([]byte) ([]byte, error)) error { + signature, err := sign(t.signatureMessage()) + if err != nil { + return errors.Wrap(err, "sign") + } + t.Signature = newED448Signature(publicKey, signature) + return nil +} + +var _ SignableED448Message = (*MintCoinRequest)(nil) + +// SignED448 signs the mint coin request with the given key. +func (t *MintCoinRequest) SignED448(publicKey []byte, sign func([]byte) ([]byte, error)) error { + signature, err := sign(t.signatureMessage()) + if err != nil { + return errors.Wrap(err, "sign") + } + t.Signature = newED448Signature(publicKey, signature) + return nil +} + +type ED448SignHelper struct { + PublicKey []byte + Sign func([]byte) ([]byte, error) +} + +// SignED448 signs the announce prover request with the given keys. +func (t *AnnounceProverRequest) SignED448(helpers []ED448SignHelper) error { + if len(helpers) == 0 { + return errors.New("no keys") + } + payload := []byte{} + primary := helpers[0] + signatures := make([]*Ed448Signature, len(helpers)) + for i, k := range helpers[1:] { + payload = append(payload, k.PublicKey...) + signature, err := k.Sign(primary.PublicKey) + if err != nil { + return errors.Wrap(err, "sign") + } + signatures[i+1] = newED448Signature(k.PublicKey, signature) + } + signature, err := primary.Sign(payload) + if err != nil { + return errors.Wrap(err, "sign") + } + signatures[0] = newED448Signature(primary.PublicKey, signature) + t.PublicKeySignaturesEd448 = signatures + return nil +} + +var _ SignableED448Message = (*AnnounceProverJoin)(nil) + +// SignED448 signs the announce prover join with the given key. +func (t *AnnounceProverJoin) SignED448(publicKey []byte, sign func([]byte) ([]byte, error)) error { + signature, err := sign(t.signatureMessage()) + if err != nil { + return errors.Wrap(err, "sign") + } + t.PublicKeySignatureEd448 = newED448Signature(publicKey, signature) + return nil +} + +var _ SignableED448Message = (*AnnounceProverLeave)(nil) + +// SignED448 signs the announce prover leave with the given key. +func (t *AnnounceProverLeave) SignED448(publicKey []byte, sign func([]byte) ([]byte, error)) error { + signature, err := sign(t.signatureMessage()) + if err != nil { + return errors.Wrap(err, "sign") + } + t.PublicKeySignatureEd448 = newED448Signature(publicKey, signature) + return nil +} + +var _ SignableED448Message = (*AnnounceProverPause)(nil) + +// SignED448 signs the announce prover pause with the given key. +func (t *AnnounceProverPause) SignED448(publicKey []byte, sign func([]byte) ([]byte, error)) error { + signature, err := sign(t.signatureMessage()) + if err != nil { + return errors.Wrap(err, "sign") + } + t.PublicKeySignatureEd448 = newED448Signature(publicKey, signature) + return nil +} + +var _ SignableED448Message = (*AnnounceProverResume)(nil) + +// SignED448 signs the announce prover resume with the given key. +func (t *AnnounceProverResume) SignED448(publicKey []byte, sign func([]byte) ([]byte, error)) error { + signature, err := sign(t.signatureMessage()) + if err != nil { + return errors.Wrap(err, "sign") + } + t.PublicKeySignatureEd448 = newED448Signature(publicKey, signature) + return nil +} diff --git a/node/protobufs/validation_internal_test.go b/node/protobufs/validation_internal_test.go new file mode 100644 index 0000000..b83b74d --- /dev/null +++ b/node/protobufs/validation_internal_test.go @@ -0,0 +1,5 @@ +package protobufs + +func SignatureMessageOf(m any) []byte { + return m.(signatureMessage).signatureMessage() +} diff --git a/node/protobufs/validation_test.go b/node/protobufs/validation_test.go new file mode 100644 index 0000000..a10f9c1 --- /dev/null +++ b/node/protobufs/validation_test.go @@ -0,0 +1,914 @@ +package protobufs_test + +import ( + "bytes" + "crypto/rand" + "testing" + + "github.com/libp2p/go-libp2p/core/crypto" + "source.quilibrium.com/quilibrium/monorepo/node/protobufs" +) + +func newPrivateKey() crypto.PrivKey { + privKey, _, err := crypto.GenerateEd448Key(rand.Reader) + if err != nil { + panic(err) + } + return privKey +} + +func publicKeyBytesOf(privKey crypto.PrivKey) []byte { + b, err := privKey.GetPublic().Raw() + if err != nil { + panic(err) + } + return b +} + +var ( + primaryPrivateKey crypto.PrivKey = newPrivateKey() + primaryPublicKeyBytes []byte = publicKeyBytesOf(primaryPrivateKey) + + secondaryPrivateKey crypto.PrivKey = newPrivateKey() + secondaryPublicKeyBytes []byte = publicKeyBytesOf(secondaryPrivateKey) +) + +func metaAppend[T any](bs ...[]T) []T { + var result []T + for _, b := range bs { + result = append(result, b...) + } + return result +} + +func TestTransferCoinRequestSignatureRoundtrip(t *testing.T) { + t.Parallel() + message := &protobufs.TransferCoinRequest{ + OfCoin: &protobufs.CoinRef{ + Address: bytes.Repeat([]byte{0x01}, 32), + }, + ToAccount: &protobufs.AccountRef{ + Account: &protobufs.AccountRef_ImplicitAccount{ + ImplicitAccount: &protobufs.ImplicitAccount{ + Address: bytes.Repeat([]byte{0x02}, 32), + }, + }, + }, + Signature: &protobufs.Ed448Signature{ + Signature: bytes.Repeat([]byte{0x03}, 114), + PublicKey: &protobufs.Ed448PublicKey{ + KeyValue: bytes.Repeat([]byte{0x04}, 57), + }, + }, + } + if !bytes.Equal( + protobufs.SignatureMessageOf(message), + metaAppend( + []byte("transfer"), + bytes.Repeat([]byte{0x01}, 32), + bytes.Repeat([]byte{0x02}, 32), + ), + ) { + t.Fatal("unexpected signature message") + } + if err := message.ValidateSignature(); err == nil { + t.Fatal("expected error") + } + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if !bytes.Equal(message.Signature.PublicKey.KeyValue, primaryPublicKeyBytes) { + t.Fatal("unexpected public key") + } + if err := message.ValidateSignature(); err != nil { + t.Fatal(err) + } +} + +func TestSplitCoinRequestSignatureRoundtrip(t *testing.T) { + t.Parallel() + message := &protobufs.SplitCoinRequest{ + OfCoin: &protobufs.CoinRef{ + Address: bytes.Repeat([]byte{0x01}, 32), + }, + Amounts: [][]byte{ + bytes.Repeat([]byte{0x02}, 32), + bytes.Repeat([]byte{0x03}, 32), + }, + Signature: &protobufs.Ed448Signature{ + Signature: bytes.Repeat([]byte{0x04}, 114), + PublicKey: &protobufs.Ed448PublicKey{ + KeyValue: bytes.Repeat([]byte{0x05}, 57), + }, + }, + } + if !bytes.Equal( + protobufs.SignatureMessageOf(message), + metaAppend( + []byte("split"), + bytes.Repeat([]byte{0x01}, 32), + bytes.Repeat([]byte{0x02}, 32), + bytes.Repeat([]byte{0x03}, 32), + ), + ) { + t.Fatal("unexpected signature message") + } + if err := message.ValidateSignature(); err == nil { + t.Fatal("expected error") + } + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if !bytes.Equal(message.Signature.PublicKey.KeyValue, primaryPublicKeyBytes) { + t.Fatal("unexpected public key") + } + if err := message.ValidateSignature(); err != nil { + t.Fatal(err) + } +} + +func TestMergeCoinRequestSignatureRoundtrip(t *testing.T) { + t.Parallel() + message := &protobufs.MergeCoinRequest{ + Coins: []*protobufs.CoinRef{ + { + Address: bytes.Repeat([]byte{0x01}, 32), + }, + { + Address: bytes.Repeat([]byte{0x02}, 32), + }, + }, + Signature: &protobufs.Ed448Signature{ + Signature: bytes.Repeat([]byte{0x03}, 114), + PublicKey: &protobufs.Ed448PublicKey{ + KeyValue: bytes.Repeat([]byte{0x04}, 57), + }, + }, + } + if !bytes.Equal( + protobufs.SignatureMessageOf(message), + metaAppend( + []byte("merge"), + bytes.Repeat([]byte{0x01}, 32), + bytes.Repeat([]byte{0x02}, 32), + ), + ) { + t.Fatal("unexpected signature message") + } + if err := message.ValidateSignature(); err == nil { + t.Fatal("expected error") + } + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if !bytes.Equal(message.Signature.PublicKey.KeyValue, primaryPublicKeyBytes) { + t.Fatal("unexpected public key") + } + if err := message.ValidateSignature(); err != nil { + t.Fatal(err) + } +} + +func TestMintCoinRequestSignatureRoundtrip(t *testing.T) { + t.Parallel() + message := &protobufs.MintCoinRequest{ + Proofs: [][]byte{ + bytes.Repeat([]byte{0x01}, 32), + bytes.Repeat([]byte{0x02}, 32), + }, + Signature: &protobufs.Ed448Signature{ + Signature: bytes.Repeat([]byte{0x03}, 114), + PublicKey: &protobufs.Ed448PublicKey{ + KeyValue: bytes.Repeat([]byte{0x04}, 57), + }, + }, + } + if !bytes.Equal( + protobufs.SignatureMessageOf(message), + metaAppend( + []byte("mint"), + bytes.Repeat([]byte{0x01}, 32), + bytes.Repeat([]byte{0x02}, 32), + ), + ) { + t.Fatal("unexpected signature message") + } + if err := message.ValidateSignature(); err == nil { + t.Fatal("expected error") + } + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if !bytes.Equal(message.Signature.PublicKey.KeyValue, primaryPublicKeyBytes) { + t.Fatal("unexpected public key") + } + if err := message.ValidateSignature(); err != nil { + t.Fatal(err) + } +} + +func TestAnnounceProverRequestSignatureRoundtrip(t *testing.T) { + t.Parallel() + message := &protobufs.AnnounceProverRequest{ + PublicKeySignaturesEd448: []*protobufs.Ed448Signature{ + { + Signature: bytes.Repeat([]byte{0x01}, 114), + PublicKey: &protobufs.Ed448PublicKey{ + KeyValue: bytes.Repeat([]byte{0x02}, 57), + }, + }, + { + Signature: bytes.Repeat([]byte{0x03}, 114), + PublicKey: &protobufs.Ed448PublicKey{ + KeyValue: bytes.Repeat([]byte{0x04}, 57), + }, + }, + }, + } + if err := message.ValidateSignature(); err == nil { + t.Fatal("expected error") + } + if err := message.SignED448([]protobufs.ED448SignHelper{ + { + PublicKey: primaryPublicKeyBytes, + Sign: primaryPrivateKey.Sign, + }, + { + PublicKey: secondaryPublicKeyBytes, + Sign: secondaryPrivateKey.Sign, + }, + }); err != nil { + t.Fatal(err) + } + if !bytes.Equal(message.PublicKeySignaturesEd448[0].PublicKey.KeyValue, primaryPublicKeyBytes) { + t.Fatal("unexpected public key") + } + if !bytes.Equal(message.PublicKeySignaturesEd448[1].PublicKey.KeyValue, secondaryPublicKeyBytes) { + t.Fatal("unexpected public key") + } + if err := message.ValidateSignature(); err != nil { + t.Fatal(err) + } + message = &protobufs.AnnounceProverRequest{ + PublicKeySignaturesEd448: []*protobufs.Ed448Signature{ + { + Signature: bytes.Repeat([]byte{0x01}, 114), + PublicKey: &protobufs.Ed448PublicKey{ + KeyValue: bytes.Repeat([]byte{0x02}, 57), + }, + }, + }, + } + if err := message.ValidateSignature(); err == nil { + t.Fatal("expected error") + } + if err := message.SignED448([]protobufs.ED448SignHelper{ + { + PublicKey: primaryPublicKeyBytes, + Sign: primaryPrivateKey.Sign, + }, + }); err != nil { + t.Fatal(err) + } + if !bytes.Equal(message.PublicKeySignaturesEd448[0].PublicKey.KeyValue, primaryPublicKeyBytes) { + t.Fatal("unexpected public key") + } + if err := message.ValidateSignature(); err != nil { + t.Fatal(err) + } +} + +func TestAnnounceProverJoinSignatureRoundtrip(t *testing.T) { + t.Parallel() + message := &protobufs.AnnounceProverJoin{ + Filter: bytes.Repeat([]byte{0x01}, 32), + FrameNumber: 1, + PublicKeySignatureEd448: &protobufs.Ed448Signature{ + Signature: bytes.Repeat([]byte{0x02}, 114), + PublicKey: &protobufs.Ed448PublicKey{ + KeyValue: bytes.Repeat([]byte{0x03}, 57), + }, + }, + } + if !bytes.Equal( + protobufs.SignatureMessageOf(message), + metaAppend( + []byte("join"), + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + bytes.Repeat([]byte{0x01}, 32), + ), + ) { + t.Fatal("unexpected signature message") + } + if err := message.ValidateSignature(); err == nil { + t.Fatal("expected error") + } + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if !bytes.Equal(message.PublicKeySignatureEd448.PublicKey.KeyValue, primaryPublicKeyBytes) { + t.Fatal("unexpected public key") + } + if err := message.ValidateSignature(); err != nil { + t.Fatal(err) + } +} + +func TestAnnounceProverLeaveSignatureRoundtrip(t *testing.T) { + t.Parallel() + message := &protobufs.AnnounceProverLeave{ + Filter: bytes.Repeat([]byte{0x01}, 32), + FrameNumber: 1, + PublicKeySignatureEd448: &protobufs.Ed448Signature{ + Signature: bytes.Repeat([]byte{0x02}, 114), + PublicKey: &protobufs.Ed448PublicKey{ + KeyValue: bytes.Repeat([]byte{0x03}, 57), + }, + }, + } + if !bytes.Equal( + protobufs.SignatureMessageOf(message), + metaAppend( + []byte("leave"), + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + bytes.Repeat([]byte{0x01}, 32), + ), + ) { + t.Fatal("unexpected signature message") + } + if err := message.ValidateSignature(); err == nil { + t.Fatal("expected error") + } + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if !bytes.Equal(message.PublicKeySignatureEd448.PublicKey.KeyValue, primaryPublicKeyBytes) { + t.Fatal("unexpected public key") + } + if err := message.ValidateSignature(); err != nil { + t.Fatal(err) + } +} + +func TestAnnounceProverPauseSignatureRoundtrip(t *testing.T) { + t.Parallel() + message := &protobufs.AnnounceProverPause{ + Filter: bytes.Repeat([]byte{0x01}, 32), + FrameNumber: 1, + PublicKeySignatureEd448: &protobufs.Ed448Signature{ + Signature: bytes.Repeat([]byte{0x02}, 114), + PublicKey: &protobufs.Ed448PublicKey{ + KeyValue: bytes.Repeat([]byte{0x03}, 57), + }, + }, + } + if !bytes.Equal( + protobufs.SignatureMessageOf(message), + metaAppend( + []byte("pause"), + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + bytes.Repeat([]byte{0x01}, 32), + ), + ) { + t.Fatal("unexpected signature message") + } + if err := message.ValidateSignature(); err == nil { + t.Fatal("expected error") + } + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if !bytes.Equal(message.PublicKeySignatureEd448.PublicKey.KeyValue, primaryPublicKeyBytes) { + t.Fatal("unexpected public key") + } + if err := message.ValidateSignature(); err != nil { + t.Fatal(err) + } +} + +func TestAnnounceProverResumeSignatureRoundtrip(t *testing.T) { + t.Parallel() + message := &protobufs.AnnounceProverResume{ + Filter: bytes.Repeat([]byte{0x01}, 32), + FrameNumber: 1, + PublicKeySignatureEd448: &protobufs.Ed448Signature{ + Signature: bytes.Repeat([]byte{0x02}, 114), + PublicKey: &protobufs.Ed448PublicKey{ + KeyValue: bytes.Repeat([]byte{0x03}, 57), + }, + }, + } + if !bytes.Equal( + protobufs.SignatureMessageOf(message), + metaAppend( + []byte("resume"), + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + bytes.Repeat([]byte{0x01}, 32), + ), + ) { + t.Fatal("unexpected signature message") + } + if err := message.ValidateSignature(); err == nil { + t.Fatal("expected error") + } + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if !bytes.Equal(message.PublicKeySignatureEd448.PublicKey.KeyValue, primaryPublicKeyBytes) { + t.Fatal("unexpected public key") + } + if err := message.ValidateSignature(); err != nil { + t.Fatal(err) + } +} + +func TestEd448PublicKeyValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.Ed448PublicKey)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.Ed448PublicKey{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + message.KeyValue = bytes.Repeat([]byte{0x01}, 57) + if err := message.Validate(); err != nil { + t.Fatal(err) + } +} + +func TestEd448SignatureValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.Ed448Signature)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.Ed448Signature{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + message.Signature = bytes.Repeat([]byte{0x01}, 114) + message.PublicKey = &protobufs.Ed448PublicKey{ + KeyValue: bytes.Repeat([]byte{0x02}, 57), + } + if err := message.Validate(); err != nil { + t.Fatal(err) + } +} + +func TestImplicitAccountValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.ImplicitAccount)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.ImplicitAccount{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + message.Address = bytes.Repeat([]byte{0x01}, 32) + if err := message.Validate(); err != nil { + t.Fatal(err) + } +} + +func TestOriginatedAccountRefValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.OriginatedAccountRef)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.OriginatedAccountRef{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + message.Address = bytes.Repeat([]byte{0x01}, 32) + if err := message.Validate(); err != nil { + t.Fatal(err) + } +} + +func TestAccountRefValidate(t *testing.T) { + t.Parallel() + message := &protobufs.AccountRef{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + message.Account = &protobufs.AccountRef_ImplicitAccount{ + ImplicitAccount: &protobufs.ImplicitAccount{ + Address: bytes.Repeat([]byte{0x01}, 32), + }, + } + if err := message.Validate(); err != nil { + t.Fatal(err) + } + message.Account = &protobufs.AccountRef_OriginatedAccount{ + OriginatedAccount: &protobufs.OriginatedAccountRef{ + Address: bytes.Repeat([]byte{0x02}, 32), + }, + } + if err := message.Validate(); err != nil { + t.Fatal(err) + } +} + +func TestCoinRefValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.CoinRef)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.CoinRef{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + message.Address = bytes.Repeat([]byte{0x01}, 32) + if err := message.Validate(); err != nil { + t.Fatal(err) + } +} + +func TestAccountAllowanceRefValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.AccountAllowanceRef)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.AccountAllowanceRef{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + message.Address = bytes.Repeat([]byte{0x01}, 32) + if err := message.Validate(); err != nil { + t.Fatal(err) + } +} + +func TestCoinAllowanceRefValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.CoinAllowanceRef)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.CoinAllowanceRef{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + message.Address = bytes.Repeat([]byte{0x01}, 32) + if err := message.Validate(); err != nil { + t.Fatal(err) + } +} + +func TestTransferCoinRequestValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.TransferCoinRequest)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.TransferCoinRequest{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Transfer{ + Transfer: message, + }, + }).Validate(); err == nil { + t.Fatal("expected error") + } + message.OfCoin = &protobufs.CoinRef{ + Address: bytes.Repeat([]byte{0x01}, 32), + } + message.ToAccount = &protobufs.AccountRef{ + Account: &protobufs.AccountRef_ImplicitAccount{ + ImplicitAccount: &protobufs.ImplicitAccount{ + Address: bytes.Repeat([]byte{0x02}, 32), + }, + }, + } + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if err := message.Validate(); err != nil { + t.Fatal(err) + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Transfer{ + Transfer: message, + }, + }).Validate(); err != nil { + t.Fatal(err) + } +} + +func TestSplitCoinRequestValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.SplitCoinRequest)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.SplitCoinRequest{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Split{ + Split: message, + }, + }).Validate(); err == nil { + t.Fatal("expected error") + } + message.OfCoin = &protobufs.CoinRef{ + Address: bytes.Repeat([]byte{0x01}, 32), + } + message.Amounts = [][]byte{ + bytes.Repeat([]byte{0x02}, 32), + bytes.Repeat([]byte{0x03}, 32), + } + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if err := message.Validate(); err != nil { + t.Fatal(err) + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Split{ + Split: message, + }, + }).Validate(); err != nil { + t.Fatal(err) + } +} + +func TestMergeCoinRequestValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.MergeCoinRequest)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.MergeCoinRequest{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Merge{ + Merge: message, + }, + }).Validate(); err == nil { + t.Fatal("expected error") + } + message.Coins = []*protobufs.CoinRef{ + { + Address: bytes.Repeat([]byte{0x01}, 32), + }, + { + Address: bytes.Repeat([]byte{0x02}, 32), + }, + } + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if err := message.Validate(); err != nil { + t.Fatal(err) + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Merge{ + Merge: message, + }, + }).Validate(); err != nil { + t.Fatal(err) + } +} + +func TestMintCoinRequestValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.MintCoinRequest)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.MintCoinRequest{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Mint{ + Mint: message, + }, + }).Validate(); err == nil { + t.Fatal("expected error") + } + message.Proofs = [][]byte{ + bytes.Repeat([]byte{0x01}, 32), + bytes.Repeat([]byte{0x02}, 32), + } + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if err := message.Validate(); err != nil { + t.Fatal(err) + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Mint{ + Mint: message, + }, + }).Validate(); err != nil { + t.Fatal(err) + } +} + +func TestAnnounceProverRequestValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.AnnounceProverRequest)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.AnnounceProverRequest{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Announce{ + Announce: message, + }, + }).Validate(); err == nil { + t.Fatal("expected error") + } + if err := message.SignED448([]protobufs.ED448SignHelper{ + { + PublicKey: primaryPublicKeyBytes, + Sign: primaryPrivateKey.Sign, + }, + { + PublicKey: secondaryPublicKeyBytes, + Sign: secondaryPrivateKey.Sign, + }, + }); err != nil { + t.Fatal(err) + } + if err := message.Validate(); err != nil { + t.Fatal(err) + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Announce{ + Announce: message, + }, + }).Validate(); err != nil { + t.Fatal("expected error") + } +} + +func TestAnnounceProverJoinValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.AnnounceProverJoin)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.AnnounceProverJoin{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Join{ + Join: message, + }, + }).Validate(); err == nil { + t.Fatal("expected error") + } + message.Filter = bytes.Repeat([]byte{0x01}, 32) + message.FrameNumber = 1 + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if err := message.Validate(); err != nil { + t.Fatal(err) + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Join{ + Join: message, + }, + }).Validate(); err != nil { + t.Fatal(err) + } + announce := &protobufs.AnnounceProverRequest{} + message.Announce = announce + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Join{ + Join: message, + }, + }).Validate(); err == nil { + t.Fatal("expected error") + } + if err := announce.SignED448([]protobufs.ED448SignHelper{ + { + PublicKey: primaryPublicKeyBytes, + Sign: primaryPrivateKey.Sign, + }, + { + PublicKey: secondaryPublicKeyBytes, + Sign: secondaryPrivateKey.Sign, + }, + }); err != nil { + t.Fatal(err) + } + if err := message.Validate(); err != nil { + t.Fatal(err) + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Join{ + Join: message, + }, + }).Validate(); err != nil { + t.Fatal(err) + } +} + +func TestAnnounceProverLeaveValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.AnnounceProverLeave)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.AnnounceProverLeave{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Leave{ + Leave: message, + }, + }).Validate(); err == nil { + t.Fatal("expected error") + } + message.Filter = bytes.Repeat([]byte{0x01}, 32) + message.FrameNumber = 1 + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if err := message.Validate(); err != nil { + t.Fatal(err) + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Leave{ + Leave: message, + }, + }).Validate(); err != nil { + t.Fatal(err) + } +} + +func TestAnnounceProverPauseValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.AnnounceProverPause)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.AnnounceProverPause{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Pause{ + Pause: message, + }, + }).Validate(); err == nil { + t.Fatal("expected error") + } + message.Filter = bytes.Repeat([]byte{0x01}, 32) + message.FrameNumber = 1 + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if err := message.Validate(); err != nil { + t.Fatal(err) + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Pause{ + Pause: message, + }, + }).Validate(); err != nil { + t.Fatal(err) + } +} + +func TestAnnounceProverResumeValidate(t *testing.T) { + t.Parallel() + if err := (*protobufs.AnnounceProverResume)(nil).Validate(); err == nil { + t.Fatal("expected error") + } + message := &protobufs.AnnounceProverResume{} + if err := message.Validate(); err == nil { + t.Fatal("expected error") + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Resume{ + Resume: message, + }, + }).Validate(); err == nil { + t.Fatal("expected error") + } + message.Filter = bytes.Repeat([]byte{0x01}, 32) + message.FrameNumber = 1 + if err := message.SignED448(primaryPublicKeyBytes, primaryPrivateKey.Sign); err != nil { + t.Fatal(err) + } + if err := message.Validate(); err != nil { + t.Fatal(err) + } + if err := (&protobufs.TokenRequest{ + Request: &protobufs.TokenRequest_Resume{ + Resume: message, + }, + }).Validate(); err != nil { + t.Fatal(err) + } +} diff --git a/node/rpc/node_rpc_server.go b/node/rpc/node_rpc_server.go index 5b026e6..568b836 100644 --- a/node/rpc/node_rpc_server.go +++ b/node/rpc/node_rpc_server.go @@ -221,20 +221,20 @@ func (r *RPCServer) SendMessage( ) (*protobufs.SendMessageResponse, error) { req.Timestamp = time.Now().UnixMilli() - any := &anypb.Any{} - if err := any.MarshalFrom(req); err != nil { + a := &anypb.Any{} + if err := a.MarshalFrom(req); err != nil { return nil, errors.Wrap(err, "publish message") } // annoying protobuf any hack - any.TypeUrl = strings.Replace( - any.TypeUrl, + a.TypeUrl = strings.Replace( + a.TypeUrl, "type.googleapis.com", "types.quilibrium.com", 1, ) - payload, err := proto.Marshal(any) + payload, err := proto.Marshal(a) if err != nil { return nil, errors.Wrap(err, "publish message") } diff --git a/node/tries/proof_leaf.go b/node/tries/proof_leaf.go index 998bdc6..087a6d8 100644 --- a/node/tries/proof_leaf.go +++ b/node/tries/proof_leaf.go @@ -31,9 +31,9 @@ func PackOutputIntoPayloadAndProof( modulo int, frame *protobufs.ClockFrame, previousTree *mt.MerkleTree, -) (*mt.MerkleTree, []byte, [][]byte, error) { +) (*mt.MerkleTree, [][]byte, error) { if modulo != len(outputs) { - return nil, nil, nil, errors.Wrap( + return nil, nil, errors.Wrap( errors.New("mismatch of outputs and prover size"), "pack output into payload and proof", ) @@ -50,14 +50,9 @@ func PackOutputIntoPayloadAndProof( outputs, ) if err != nil { - return nil, nil, nil, errors.Wrap(err, "pack output into payload and proof") + return nil, nil, errors.Wrap(err, "pack output into payload and proof") } - payload := []byte("mint") - payload = append(payload, tree.Root...) - payload = binary.BigEndian.AppendUint32(payload, uint32(modulo)) - payload = binary.BigEndian.AppendUint64(payload, frame.FrameNumber) - output := [][]byte{ tree.Root, binary.BigEndian.AppendUint32([]byte{}, uint32(modulo)), @@ -68,19 +63,14 @@ func PackOutputIntoPayloadAndProof( hash := sha3.Sum256(frame.Output) pick := BytesToUnbiasedMod(hash, uint64(modulo)) if uint64(modulo) < pick { - return nil, nil, nil, errors.Wrap( + return nil, nil, errors.Wrap( errors.New("proof size mismatch"), "pack output into payload and proof", ) } for _, sib := range previousTree.Proofs[int(pick)].Siblings { - payload = append(payload, sib...) output = append(output, sib) } - payload = binary.BigEndian.AppendUint32( - payload, - previousTree.Proofs[int(pick)].Path, - ) output = append( output, binary.BigEndian.AppendUint32( @@ -88,10 +78,9 @@ func PackOutputIntoPayloadAndProof( previousTree.Proofs[int(pick)].Path, ), ) - payload = append(payload, previousTree.Leaves[int(pick)]...) output = append(output, previousTree.Leaves[int(pick)]) } - return tree, payload, output, nil + return tree, output, nil } func UnpackAndVerifyOutput(