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 }