code.vegaprotocol.io/vega@v0.79.0/commands/transaction.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package commands
    17  
    18  import (
    19  	"encoding/hex"
    20  	"fmt"
    21  
    22  	"code.vegaprotocol.io/vega/libs/crypto"
    23  	commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1"
    24  	wcrypto "code.vegaprotocol.io/vega/wallet/crypto"
    25  
    26  	"github.com/golang/protobuf/proto"
    27  )
    28  
    29  const ChainIDDelimiter = '\000'
    30  
    31  func NewTransaction(pubKey string, data []byte, signature *commandspb.Signature) *commandspb.Transaction {
    32  	return &commandspb.Transaction{
    33  		InputData: data,
    34  		Signature: signature,
    35  		From: &commandspb.Transaction_PubKey{
    36  			PubKey: pubKey,
    37  		},
    38  		Version: commandspb.TxVersion_TX_VERSION_V3,
    39  	}
    40  }
    41  
    42  func BundleInputDataForSigning(inputDataBytes []byte, chainID string) []byte {
    43  	return append([]byte(fmt.Sprintf("%s%c", chainID, ChainIDDelimiter)), inputDataBytes...)
    44  }
    45  
    46  func NewInputData(height uint64) *commandspb.InputData {
    47  	return &commandspb.InputData{
    48  		Nonce:       crypto.NewNonce(),
    49  		BlockHeight: height,
    50  	}
    51  }
    52  
    53  func MarshalInputData(inputData *commandspb.InputData) ([]byte, error) {
    54  	data, err := proto.Marshal(inputData)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	return data, nil
    60  }
    61  
    62  func UnmarshalInputData(rawInputData []byte) (*commandspb.InputData, error) {
    63  	inputData := &commandspb.InputData{}
    64  	if err := proto.Unmarshal(rawInputData, inputData); err != nil {
    65  		return nil, fmt.Errorf("couldn't unmarshall input data: %w", err)
    66  	}
    67  	return inputData, nil
    68  }
    69  
    70  func NewSignature(sig []byte, algo string, version uint32) *commandspb.Signature {
    71  	return &commandspb.Signature{
    72  		Value:   hex.EncodeToString(sig),
    73  		Algo:    algo,
    74  		Version: version,
    75  	}
    76  }
    77  
    78  func CheckTransaction(tx *commandspb.Transaction, chainID string) (*commandspb.InputData, error) {
    79  	errs := NewErrors()
    80  
    81  	if tx == nil {
    82  		return nil, errs.FinalAddForProperty("tx", ErrIsRequired)
    83  	}
    84  
    85  	if tx.Version == commandspb.TxVersion_TX_VERSION_UNSPECIFIED {
    86  		return nil, errs.FinalAddForProperty("tx.version", ErrIsRequired)
    87  	}
    88  	if tx.Version != commandspb.TxVersion_TX_VERSION_V2 && tx.Version != commandspb.TxVersion_TX_VERSION_V3 {
    89  		return nil, errs.FinalAddForProperty("tx.version", ErrIsNotSupported)
    90  	}
    91  
    92  	if tx.From == nil {
    93  		errs.AddForProperty("tx.from", ErrIsRequired)
    94  	} else if len(tx.GetPubKey()) == 0 {
    95  		errs.AddForProperty("tx.from.pub_key", ErrIsRequired)
    96  	} else if !IsVegaPublicKey(tx.GetPubKey()) {
    97  		errs.AddForProperty("tx.from.pub_key", ErrShouldBeAValidVegaPublicKey)
    98  	}
    99  
   100  	// We need the above check to pass, so we verify it's all good.
   101  	if !errs.Empty() {
   102  		return nil, errs.ErrorOrNil()
   103  	}
   104  
   105  	inputData, inputErrs := CheckInputData(tx.InputData)
   106  	if !inputErrs.Empty() {
   107  		errs.Merge(inputErrs)
   108  		return nil, errs.ErrorOrNil()
   109  	}
   110  
   111  	inputDataBytes := tx.InputData
   112  	if tx.Version == commandspb.TxVersion_TX_VERSION_V3 {
   113  		inputDataBytes = append([]byte(fmt.Sprintf("%s%c", chainID, ChainIDDelimiter)), inputDataBytes...)
   114  	}
   115  
   116  	errs.Merge(checkSignature(tx.Signature, tx.GetPubKey(), inputDataBytes))
   117  	if !errs.Empty() {
   118  		return nil, errs.ErrorOrNil()
   119  	}
   120  
   121  	return inputData, nil
   122  }
   123  
   124  func checkSignature(signature *commandspb.Signature, pubKey string, rawInputData []byte) Errors {
   125  	errs := NewErrors()
   126  
   127  	if signature == nil {
   128  		return errs.FinalAddForProperty("tx.signature", ErrIsRequired)
   129  	}
   130  
   131  	if len(signature.Value) == 0 {
   132  		errs.AddForProperty("tx.signature.value", ErrIsRequired)
   133  	}
   134  	decodedSig, err := hex.DecodeString(signature.Value)
   135  	if err != nil {
   136  		errs.AddForProperty("tx.signature.value", ErrShouldBeHexEncoded)
   137  	}
   138  
   139  	if len(signature.Algo) == 0 {
   140  		errs.AddForProperty("tx.signature.algo", ErrIsRequired)
   141  	}
   142  	algo, err := wcrypto.NewSignatureAlgorithm(signature.Algo, signature.Version)
   143  	if err != nil {
   144  		errs.AddForProperty("tx.signature.algo", ErrUnsupportedAlgorithm)
   145  		errs.AddForProperty("tx.signature.version", ErrUnsupportedAlgorithm)
   146  	}
   147  
   148  	// We need the above check to pass, so we verify it's all good.
   149  	if !errs.Empty() {
   150  		return errs
   151  	}
   152  
   153  	decodedPubKey := []byte(pubKey)
   154  	if IsVegaPublicKey(pubKey) {
   155  		// We can ignore the error has it should have been checked earlier.
   156  		decodedPubKey, _ = hex.DecodeString(pubKey)
   157  	}
   158  
   159  	isValid, err := algo.Verify(decodedPubKey, rawInputData, decodedSig)
   160  	if err != nil {
   161  		// This shouldn't happen. If it does, we need to add better checks up-hill.
   162  		return errs.FinalAddForProperty("tx.signature.value", ErrSignatureNotVerifiable)
   163  	}
   164  
   165  	if !isValid {
   166  		errs.AddForProperty("tx.signature.value", ErrInvalidSignature)
   167  		return errs
   168  	}
   169  
   170  	return nil
   171  }
   172  
   173  func CheckInputData(rawInputData []byte) (*commandspb.InputData, Errors) {
   174  	errs := NewErrors()
   175  
   176  	if len(rawInputData) == 0 {
   177  		return nil, errs.FinalAddForProperty("tx.input_data", ErrIsRequired)
   178  	}
   179  
   180  	inputData, err := UnmarshalInputData(rawInputData)
   181  	if err != nil {
   182  		return nil, errs.FinalAddForProperty("tx.input_data", err)
   183  	}
   184  
   185  	if inputData.Nonce == 0 {
   186  		errs.AddForProperty("tx.input_data.nonce", ErrMustBePositive)
   187  	}
   188  
   189  	if inputData.Command == nil {
   190  		errs.AddForProperty("tx.input_data.command", ErrIsRequired)
   191  	} else {
   192  		switch cmd := inputData.Command.(type) {
   193  		case *commandspb.InputData_OrderSubmission:
   194  			errs.Merge(checkOrderSubmission(cmd.OrderSubmission))
   195  		case *commandspb.InputData_OrderCancellation:
   196  			break // No verification to be made
   197  		case *commandspb.InputData_OrderAmendment:
   198  			errs.Merge(checkOrderAmendment(cmd.OrderAmendment))
   199  		case *commandspb.InputData_VoteSubmission:
   200  			errs.Merge(checkVoteSubmission(cmd.VoteSubmission))
   201  		case *commandspb.InputData_WithdrawSubmission:
   202  			errs.Merge(checkWithdrawSubmission(cmd.WithdrawSubmission))
   203  		case *commandspb.InputData_LiquidityProvisionSubmission:
   204  			errs.Merge(checkLiquidityProvisionSubmission(cmd.LiquidityProvisionSubmission))
   205  		case *commandspb.InputData_LiquidityProvisionCancellation:
   206  			errs.Merge(checkLiquidityProvisionCancellation(cmd.LiquidityProvisionCancellation))
   207  		case *commandspb.InputData_LiquidityProvisionAmendment:
   208  			errs.Merge(checkLiquidityProvisionAmendment(cmd.LiquidityProvisionAmendment))
   209  		case *commandspb.InputData_ProposalSubmission:
   210  			errs.Merge(checkProposalSubmission(cmd.ProposalSubmission))
   211  		case *commandspb.InputData_BatchProposalSubmission:
   212  			errs.Merge(checkBatchProposalSubmission(cmd.BatchProposalSubmission))
   213  		case *commandspb.InputData_AnnounceNode:
   214  			errs.Merge(checkAnnounceNode(cmd.AnnounceNode))
   215  		case *commandspb.InputData_NodeVote:
   216  			errs.Merge(checkNodeVote(cmd.NodeVote))
   217  		case *commandspb.InputData_NodeSignature:
   218  			errs.Merge(checkNodeSignature(cmd.NodeSignature))
   219  		case *commandspb.InputData_ChainEvent:
   220  			errs.Merge(checkChainEvent(cmd.ChainEvent))
   221  		case *commandspb.InputData_OracleDataSubmission:
   222  			errs.Merge(checkOracleDataSubmission(cmd.OracleDataSubmission))
   223  		case *commandspb.InputData_DelegateSubmission:
   224  			errs.Merge(checkDelegateSubmission(cmd.DelegateSubmission))
   225  		case *commandspb.InputData_UndelegateSubmission:
   226  			errs.Merge(checkUndelegateSubmission(cmd.UndelegateSubmission))
   227  		case *commandspb.InputData_KeyRotateSubmission:
   228  			errs.Merge(checkKeyRotateSubmission(cmd.KeyRotateSubmission))
   229  		case *commandspb.InputData_StateVariableProposal:
   230  			errs.Merge(checkStateVariableProposal(cmd.StateVariableProposal))
   231  		case *commandspb.InputData_Transfer:
   232  			errs.Merge(checkTransfer(cmd.Transfer))
   233  		case *commandspb.InputData_CancelTransfer:
   234  			errs.Merge(checkCancelTransfer(cmd.CancelTransfer))
   235  		case *commandspb.InputData_ValidatorHeartbeat:
   236  			errs.Merge(checkValidatorHeartbeat(cmd.ValidatorHeartbeat))
   237  		case *commandspb.InputData_EthereumKeyRotateSubmission:
   238  			errs.Merge(checkEthereumKeyRotateSubmission(cmd.EthereumKeyRotateSubmission))
   239  		case *commandspb.InputData_ProtocolUpgradeProposal:
   240  			errs.Merge(checkProtocolUpgradeProposal(cmd.ProtocolUpgradeProposal))
   241  		case *commandspb.InputData_IssueSignatures:
   242  			errs.Merge(checkIssueSignatures(cmd.IssueSignatures))
   243  		case *commandspb.InputData_BatchMarketInstructions:
   244  			errs.Merge(checkBatchMarketInstructions(cmd.BatchMarketInstructions))
   245  		case *commandspb.InputData_StopOrdersSubmission:
   246  			errs.Merge(checkStopOrdersSubmission(cmd.StopOrdersSubmission))
   247  		case *commandspb.InputData_StopOrdersCancellation:
   248  			errs.Merge(checkStopOrdersCancellation(cmd.StopOrdersCancellation))
   249  		case *commandspb.InputData_CreateReferralSet:
   250  			errs.Merge(checkCreateReferralSet(cmd.CreateReferralSet))
   251  		case *commandspb.InputData_UpdateReferralSet:
   252  			errs.Merge(checkUpdateReferralSet(cmd.UpdateReferralSet))
   253  		case *commandspb.InputData_ApplyReferralCode:
   254  			errs.Merge(checkApplyReferralCode(cmd.ApplyReferralCode))
   255  		case *commandspb.InputData_UpdateMarginMode:
   256  			errs.Merge(checkUpdateMarginMode(cmd.UpdateMarginMode))
   257  		case *commandspb.InputData_JoinTeam:
   258  			errs.Merge(checkJoinTeam(cmd.JoinTeam))
   259  		case *commandspb.InputData_UpdatePartyProfile:
   260  			errs.Merge(checkUpdatePartyProfile(cmd.UpdatePartyProfile))
   261  		case *commandspb.InputData_SubmitAmm:
   262  			errs.Merge(checkSubmitAMM(cmd.SubmitAmm))
   263  		case *commandspb.InputData_AmendAmm:
   264  			errs.Merge(checkAmendAMM(cmd.AmendAmm))
   265  		case *commandspb.InputData_CancelAmm:
   266  			errs.Merge(checkCancelAMM(cmd.CancelAmm))
   267  		case *commandspb.InputData_DelayedTransactionsWrapper:
   268  			break
   269  		default:
   270  			errs.AddForProperty("tx.input_data.command", ErrIsNotSupported)
   271  		}
   272  	}
   273  
   274  	return inputData, errs
   275  }