std::pair<bool, std::string> STTx::checkMultiSign () const { // Make sure the MultiSigners are present. Otherwise they are not // attempting multi-signing and we just have a bad SigningPubKey. if (!isFieldPresent (sfSigners)) return {false, "Empty SigningPubKey."}; // We don't allow both an sfSigners and an sfTxnSignature. Both fields // being present would indicate that the transaction is signed both ways. if (isFieldPresent (sfTxnSignature)) return {false, "Cannot both single- and multi-sign."}; STArray const& signers {getFieldArray (sfSigners)}; // There are well known bounds that the number of signers must be within. if (signers.size() < minMultiSigners || signers.size() > maxMultiSigners) return {false, "Invalid Signers array size."}; // We can ease the computational load inside the loop a bit by // pre-constructing part of the data that we hash. Fill a Serializer // with the stuff that stays constant from signature to signature. Serializer const dataStart {startMultiSigningData (*this)}; // We also use the sfAccount field inside the loop. Get it once. auto const txnAccountID = getAccountID (sfAccount); // Determine whether signatures must be full canonical. bool const fullyCanonical = (getFlags() & tfFullyCanonicalSig); // Signers must be in sorted order by AccountID. AccountID lastAccountID (beast::zero); for (auto const& signer : signers) { auto const accountID = signer.getAccountID (sfAccount); // The account owner may not multisign for themselves. if (accountID == txnAccountID) return {false, "Invalid multisigner."}; // No duplicate signers allowed. if (lastAccountID == accountID) return {false, "Duplicate Signers not allowed."}; // Accounts must be in order by account ID. No duplicates allowed. if (lastAccountID > accountID) return {false, "Unsorted Signers array."}; // The next signature must be greater than this one. lastAccountID = accountID; // Verify the signature. bool validSig = false; try { Serializer s = dataStart; finishMultiSigningData (accountID, s); auto spk = signer.getFieldVL (sfSigningPubKey); if (publicKeyType (makeSlice(spk))) { Blob const signature = signer.getFieldVL (sfTxnSignature); validSig = verify ( PublicKey (makeSlice(spk)), s.slice(), makeSlice(signature), fullyCanonical); } } catch (std::exception const&) { // We assume any problem lies with the signature. validSig = false; } if (!validSig) return {false, std::string("Invalid signature on account ") + toBase58(accountID) + "."}; } // All signatures verified. return {true, ""}; }
bool STTx::checkMultiSign () const { // Make sure the MultiSigners are present. Otherwise they are not // attempting multi-signing and we just have a bad SigningPubKey. if (!isFieldPresent (sfSigners)) return false; STArray const& signers {getFieldArray (sfSigners)}; // There are well known bounds that the number of signers must be within. if (signers.size() < minMultiSigners || signers.size() > maxMultiSigners) return false; // We can ease the computational load inside the loop a bit by // pre-constructing part of the data that we hash. Fill a Serializer // with the stuff that stays constant from signature to signature. Serializer const dataStart {startMultiSigningData (*this)}; // We also use the sfAccount field inside the loop. Get it once. auto const txnAccountID = getAccountID (sfAccount); // Determine whether signatures must be full canonical. ECDSA const fullyCanonical = (getFlags() & tfFullyCanonicalSig) ? ECDSA::strict : ECDSA::not_strict; // Signers must be in sorted order by AccountID. AccountID lastAccountID (beast::zero); for (auto const& signer : signers) { auto const accountID = signer.getAccountID (sfAccount); // The account owner may not multisign for themselves. if (accountID == txnAccountID) return false; // Accounts must be in order by account ID. No duplicates allowed. if (lastAccountID >= accountID) return false; // The next signature must be greater than this one. lastAccountID = accountID; // Verify the signature. bool validSig = false; try { Serializer s = dataStart; finishMultiSigningData (accountID, s); RippleAddress const pubKey = RippleAddress::createAccountPublic ( signer.getFieldVL (sfSigningPubKey)); Blob const signature = signer.getFieldVL (sfTxnSignature); validSig = pubKey.accountPublicVerify ( s.getData(), signature, fullyCanonical); } catch (...) { // We assume any problem lies with the signature. validSig = false; } if (!validSig) return false; } // All signatures verified. return true; }