bool STTx::checkSingleSign () const { // We don't allow both a non-empty sfSigningPubKey and an sfSigners. // That would allow the transaction to be signed two ways. So if both // fields are present the signature is invalid. if (isFieldPresent (sfSigners)) return false; bool ret = false; try { ECDSA const fullyCanonical = (getFlags() & tfFullyCanonicalSig) ? ECDSA::strict : ECDSA::not_strict; RippleAddress n; n.setAccountPublic (getFieldVL (sfSigningPubKey)); ret = n.accountPublicVerify (getSigningData (*this), getFieldVL (sfTxnSignature), fullyCanonical); } catch (...) { // Assume it was a signature failure. ret = false; } return ret; }
static Rate rippleQuality ( ReadView const& view, AccountID const& destination, AccountID const& source, Currency const& currency, SField const& sfLow, SField const& sfHigh) { if (destination == source) return parityRate; auto const& sfField = destination < source ? sfLow : sfHigh; auto const sle = view.read( keylet::line(destination, source, currency)); if (!sle || !sle->isFieldPresent (sfField)) return parityRate; auto quality = sle->getFieldU32 (sfField); // Avoid divide by zero. NIKB CHECKME: if we // allow zero qualities now, then we shouldn't. if (quality == 0) quality = 1; return Rate{ quality }; }
void nflags::operator()(Env& env) const { auto const sle = env.le(account_); if (sle->isFieldPresent(sfFlags)) env.test.expect((sle->getFieldU32(sfFlags) & mask_) == 0); else env.test.pass(); }
void Transactor::setSeq () { auto const sle = view().peek( keylet::account(account_)); std::uint32_t const t_seq = ctx_.tx.getSequence (); sle->setFieldU32 (sfSequence, t_seq + 1); if (sle->isFieldPresent (sfAccountTxnID)) sle->setFieldH256 (sfAccountTxnID, ctx_.tx.getTransactionID ()); }
NotTEC Transactor::checkSingleSign (PreclaimContext const& ctx) { auto const id = ctx.tx.getAccountID(sfAccount); auto const sle = ctx.view.read( keylet::account(id)); auto const hasAuthKey = sle->isFieldPresent (sfRegularKey); // Consistency: Check signature & verify the transaction's signing // public key is authorized for signing. auto const spk = ctx.tx.getSigningPubKey(); if (!publicKeyType (makeSlice (spk))) { JLOG(ctx.j.trace()) << "checkSingleSign: signing public key type is unknown"; return tefBAD_AUTH; // FIXME: should be better error! } auto const pkAccount = calcAccountID ( PublicKey (makeSlice (spk))); if (pkAccount == id) { // Authorized to continue. if (sle->isFlag(lsfDisableMaster)) return tefMASTER_DISABLED; } else if (hasAuthKey && (pkAccount == sle->getAccountID (sfRegularKey))) { // Authorized to continue. } else if (hasAuthKey) { JLOG(ctx.j.trace()) << "checkSingleSign: Not authorized to use account."; return tefBAD_AUTH; } else { JLOG(ctx.j.trace()) << "checkSingleSign: Not authorized to use account."; return tefBAD_AUTH_MASTER; } return tesSUCCESS; }
std::pair<bool, std::string> STTx::checkSingleSign () const { // We don't allow both a non-empty sfSigningPubKey and an sfSigners. // That would allow the transaction to be signed two ways. So if both // fields are present the signature is invalid. if (isFieldPresent (sfSigners)) return {false, "Cannot both single- and multi-sign."}; bool validSig = false; try { bool const fullyCanonical = (getFlags() & tfFullyCanonicalSig); auto const spk = getFieldVL (sfSigningPubKey); if (publicKeyType (makeSlice(spk))) { Blob const signature = getFieldVL (sfTxnSignature); Blob const data = getSigningData (*this); validSig = verify ( PublicKey (makeSlice(spk)), makeSlice(data), makeSlice(signature), fullyCanonical); } } catch (std::exception const&) { // Assume it was a signature failure. validSig = false; } if (validSig == false) return {false, "Invalid signature."}; return {true, ""}; }
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 STLedgerEntry::isThreaded () const { return isFieldPresent (sfPreviousTxnID); }
bool SerializedLedgerEntry::isThreaded () { return isFieldPresent (sfPreviousTxnID); }
// Based on the meta, send the meta to the streams that are listening. // We need to determine which streams a given meta effects. void OrderBookDB::processTxn ( std::shared_ptr<ReadView const> const& ledger, const AcceptedLedgerTx& alTx) { std::lock_guard <std::recursive_mutex> sl (mLock); Json::Value jvObj; bool bJvObjInitialized = false; if (alTx.getResult () == tesSUCCESS) { std::set<BookListeners::pointer> listenersSet; // Check if this is an offer or an offer cancel or a payment that // consumes an offer. // Check to see what the meta looks like. for (auto& node : alTx.getMeta ()->getNodes ()) { try { if (node.getFieldU16 (sfLedgerEntryType) == ltOFFER) { SField const* field = nullptr; // We need a field that contains the TakerGets and TakerPays // parameters. if (node.getFName () == sfModifiedNode) field = &sfPreviousFields; else if (node.getFName () == sfCreatedNode) field = &sfNewFields; else if (node.getFName () == sfDeletedNode) field = &sfFinalFields; if (field) { auto data = dynamic_cast<const STObject*> ( node.peekAtPField (*field)); if (data && data->isFieldPresent (sfTakerPays) && data->isFieldPresent (sfTakerGets)) { // determine the OrderBook auto listeners = getBookListeners ( {data->getFieldAmount (sfTakerGets).issue(), data->getFieldAmount (sfTakerPays).issue()}); if (listeners) { listenersSet.insert(listeners); } } } } } catch (std::exception const&) { JLOG (j_.info) << "Fields not found in OrderBookDB::processTxn"; } } for (auto& listeners: listenersSet) { if (!bJvObjInitialized) { jvObj = NetworkOPs_transJson (*alTx.getTxn (), alTx.getResult (), true, ledger, app_); jvObj[jss::meta] = alTx.getMeta ()->getJson (0); bJvObjInitialized = true; } listeners->publish (jvObj); } } }
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; }
NotTEC Transactor::checkMultiSign (PreclaimContext const& ctx) { auto const id = ctx.tx.getAccountID(sfAccount); // Get mTxnAccountID's SignerList and Quorum. std::shared_ptr<STLedgerEntry const> sleAccountSigners = ctx.view.read (keylet::signers(id)); // If the signer list doesn't exist the account is not multi-signing. if (!sleAccountSigners) { JLOG(ctx.j.trace()) << "applyTransaction: Invalid: Not a multi-signing account."; return tefNOT_MULTI_SIGNING; } // We have plans to support multiple SignerLists in the future. The // presence and defaulted value of the SignerListID field will enable that. assert (sleAccountSigners->isFieldPresent (sfSignerListID)); assert (sleAccountSigners->getFieldU32 (sfSignerListID) == 0); auto accountSigners = SignerEntries::deserialize (*sleAccountSigners, ctx.j, "ledger"); if (accountSigners.second != tesSUCCESS) return accountSigners.second; // Get the array of transaction signers. STArray const& txSigners (ctx.tx.getFieldArray (sfSigners)); // Walk the accountSigners performing a variety of checks and see if // the quorum is met. // Both the multiSigners and accountSigners are sorted by account. So // matching multi-signers to account signers should be a simple // linear walk. *All* signers must be valid or the transaction fails. std::uint32_t weightSum = 0; auto iter = accountSigners.first.begin (); for (auto const& txSigner : txSigners) { AccountID const txSignerAcctID = txSigner.getAccountID (sfAccount); // Attempt to match the SignerEntry with a Signer; while (iter->account < txSignerAcctID) { if (++iter == accountSigners.first.end ()) { JLOG(ctx.j.trace()) << "applyTransaction: Invalid SigningAccount.Account."; return tefBAD_SIGNATURE; } } if (iter->account != txSignerAcctID) { // The SigningAccount is not in the SignerEntries. JLOG(ctx.j.trace()) << "applyTransaction: Invalid SigningAccount.Account."; return tefBAD_SIGNATURE; } // We found the SigningAccount in the list of valid signers. Now we // need to compute the accountID that is associated with the signer's // public key. auto const spk = txSigner.getFieldVL (sfSigningPubKey); if (!publicKeyType (makeSlice(spk))) { JLOG(ctx.j.trace()) << "checkMultiSign: signing public key type is unknown"; return tefBAD_SIGNATURE; } AccountID const signingAcctIDFromPubKey = calcAccountID(PublicKey (makeSlice(spk))); // Verify that the signingAcctID and the signingAcctIDFromPubKey // belong together. Here is are the rules: // // 1. "Phantom account": an account that is not in the ledger // A. If signingAcctID == signingAcctIDFromPubKey and the // signingAcctID is not in the ledger then we have a phantom // account. // B. Phantom accounts are always allowed as multi-signers. // // 2. "Master Key" // A. signingAcctID == signingAcctIDFromPubKey, and signingAcctID // is in the ledger. // B. If the signingAcctID in the ledger does not have the // asfDisableMaster flag set, then the signature is allowed. // // 3. "Regular Key" // A. signingAcctID != signingAcctIDFromPubKey, and signingAcctID // is in the ledger. // B. If signingAcctIDFromPubKey == signingAcctID.RegularKey (from // ledger) then the signature is allowed. // // No other signatures are allowed. (January 2015) // In any of these cases we need to know whether the account is in // the ledger. Determine that now. auto sleTxSignerRoot = ctx.view.read (keylet::account(txSignerAcctID)); if (signingAcctIDFromPubKey == txSignerAcctID) { // Either Phantom or Master. Phantoms automatically pass. if (sleTxSignerRoot) { // Master Key. Account may not have asfDisableMaster set. std::uint32_t const signerAccountFlags = sleTxSignerRoot->getFieldU32 (sfFlags); if (signerAccountFlags & lsfDisableMaster) { JLOG(ctx.j.trace()) << "applyTransaction: Signer:Account lsfDisableMaster."; return tefMASTER_DISABLED; } } } else { // May be a Regular Key. Let's find out. // Public key must hash to the account's regular key. if (!sleTxSignerRoot) { JLOG(ctx.j.trace()) << "applyTransaction: Non-phantom signer lacks account root."; return tefBAD_SIGNATURE; } if (!sleTxSignerRoot->isFieldPresent (sfRegularKey)) { JLOG(ctx.j.trace()) << "applyTransaction: Account lacks RegularKey."; return tefBAD_SIGNATURE; } if (signingAcctIDFromPubKey != sleTxSignerRoot->getAccountID (sfRegularKey)) { JLOG(ctx.j.trace()) << "applyTransaction: Account doesn't match RegularKey."; return tefBAD_SIGNATURE; } } // The signer is legitimate. Add their weight toward the quorum. weightSum += iter->weight; } // Cannot perform transaction if quorum is not met. if (weightSum < sleAccountSigners->getFieldU32 (sfSignerQuorum)) { JLOG(ctx.j.trace()) << "applyTransaction: Signers failed to meet quorum."; return tefBAD_QUORUM; } // Met the quorum. Continue. return tesSUCCESS; }
TER SetAccount::doApply () { std::uint32_t const uTxFlags = ctx_.tx.getFlags (); auto const sle = view().peek( keylet::account(account_)); std::uint32_t const uFlagsIn = sle->getFieldU32 (sfFlags); std::uint32_t uFlagsOut = uFlagsIn; std::uint32_t const uSetFlag = ctx_.tx.getFieldU32 (sfSetFlag); std::uint32_t const uClearFlag = ctx_.tx.getFieldU32 (sfClearFlag); // legacy AccountSet flags bool bSetRequireDest = (uTxFlags & TxFlag::requireDestTag) || (uSetFlag == asfRequireDest); bool bClearRequireDest = (uTxFlags & tfOptionalDestTag) || (uClearFlag == asfRequireDest); bool bSetRequireAuth = (uTxFlags & tfRequireAuth) || (uSetFlag == asfRequireAuth); bool bClearRequireAuth = (uTxFlags & tfOptionalAuth) || (uClearFlag == asfRequireAuth); bool bSetDisallowXRP = (uTxFlags & tfDisallowXRP) || (uSetFlag == asfDisallowXRP); bool bClearDisallowXRP = (uTxFlags & tfAllowXRP) || (uClearFlag == asfDisallowXRP); bool sigWithMaster = false; { auto const blob = ctx_.tx.getSigningPubKey(); if (!blob.empty ()) { auto const signingPubKey = RippleAddress::createAccountPublic(blob); if (calcAccountID(signingPubKey) == account_) sigWithMaster = true; } } // // RequireAuth // if (bSetRequireAuth && !(uFlagsIn & lsfRequireAuth)) { j_.trace << "Set RequireAuth."; uFlagsOut |= lsfRequireAuth; } if (bClearRequireAuth && (uFlagsIn & lsfRequireAuth)) { j_.trace << "Clear RequireAuth."; uFlagsOut &= ~lsfRequireAuth; } // // RequireDestTag // if (bSetRequireDest && !(uFlagsIn & lsfRequireDestTag)) { j_.trace << "Set lsfRequireDestTag."; uFlagsOut |= lsfRequireDestTag; } if (bClearRequireDest && (uFlagsIn & lsfRequireDestTag)) { j_.trace << "Clear lsfRequireDestTag."; uFlagsOut &= ~lsfRequireDestTag; } // // DisallowXRP // if (bSetDisallowXRP && !(uFlagsIn & lsfDisallowXRP)) { j_.trace << "Set lsfDisallowXRP."; uFlagsOut |= lsfDisallowXRP; } if (bClearDisallowXRP && (uFlagsIn & lsfDisallowXRP)) { j_.trace << "Clear lsfDisallowXRP."; uFlagsOut &= ~lsfDisallowXRP; } // // DisableMaster // if ((uSetFlag == asfDisableMaster) && !(uFlagsIn & lsfDisableMaster)) { if (!sigWithMaster) { j_.trace << "Must use master key to disable master key."; return tecNEED_MASTER_KEY; } if ((!sle->isFieldPresent (sfRegularKey)) && (!view().peek (keylet::signers (account_)))) { // Account has no regular key or multi-signer signer list. // Prevent transaction changes until we're ready. if (view().rules().enabled(featureMultiSign, ctx_.app.config().features)) return tecNO_ALTERNATIVE_KEY; return tecNO_REGULAR_KEY; } j_.trace << "Set lsfDisableMaster."; uFlagsOut |= lsfDisableMaster; } if ((uClearFlag == asfDisableMaster) && (uFlagsIn & lsfDisableMaster)) { j_.trace << "Clear lsfDisableMaster."; uFlagsOut &= ~lsfDisableMaster; } // // DefaultRipple // if (uSetFlag == asfDefaultRipple) { uFlagsOut |= lsfDefaultRipple; } else if (uClearFlag == asfDefaultRipple) { uFlagsOut &= ~lsfDefaultRipple; } // // NoFreeze // if (uSetFlag == asfNoFreeze) { if (!sigWithMaster && !(uFlagsIn & lsfDisableMaster)) { j_.trace << "Can't use regular key to set NoFreeze."; return tecNEED_MASTER_KEY; } j_.trace << "Set NoFreeze flag"; uFlagsOut |= lsfNoFreeze; } // Anyone may set global freeze if (uSetFlag == asfGlobalFreeze) { j_.trace << "Set GlobalFreeze flag"; uFlagsOut |= lsfGlobalFreeze; } // If you have set NoFreeze, you may not clear GlobalFreeze // This prevents those who have set NoFreeze from using // GlobalFreeze strategically. if ((uSetFlag != asfGlobalFreeze) && (uClearFlag == asfGlobalFreeze) && ((uFlagsOut & lsfNoFreeze) == 0)) { j_.trace << "Clear GlobalFreeze flag"; uFlagsOut &= ~lsfGlobalFreeze; } // // Track transaction IDs signed by this account in its root // if ((uSetFlag == asfAccountTxnID) && !sle->isFieldPresent (sfAccountTxnID)) { j_.trace << "Set AccountTxnID"; sle->makeFieldPresent (sfAccountTxnID); } if ((uClearFlag == asfAccountTxnID) && sle->isFieldPresent (sfAccountTxnID)) { j_.trace << "Clear AccountTxnID"; sle->makeFieldAbsent (sfAccountTxnID); } // // EmailHash // if (ctx_.tx.isFieldPresent (sfEmailHash)) { uint128 const uHash = ctx_.tx.getFieldH128 (sfEmailHash); if (!uHash) { j_.trace << "unset email hash"; sle->makeFieldAbsent (sfEmailHash); } else { j_.trace << "set email hash"; sle->setFieldH128 (sfEmailHash, uHash); } } // // WalletLocator // if (ctx_.tx.isFieldPresent (sfWalletLocator)) { uint256 const uHash = ctx_.tx.getFieldH256 (sfWalletLocator); if (!uHash) { j_.trace << "unset wallet locator"; sle->makeFieldAbsent (sfWalletLocator); } else { j_.trace << "set wallet locator"; sle->setFieldH256 (sfWalletLocator, uHash); } } // // MessageKey // if (ctx_.tx.isFieldPresent (sfMessageKey)) { Blob const messageKey = ctx_.tx.getFieldVL (sfMessageKey); if (messageKey.empty ()) { j_.debug << "set message key"; sle->makeFieldAbsent (sfMessageKey); } else { j_.debug << "set message key"; sle->setFieldVL (sfMessageKey, messageKey); } } // // Domain // if (ctx_.tx.isFieldPresent (sfDomain)) { Blob const domain = ctx_.tx.getFieldVL (sfDomain); if (domain.empty ()) { j_.trace << "unset domain"; sle->makeFieldAbsent (sfDomain); } else { j_.trace << "set domain"; sle->setFieldVL (sfDomain, domain); } } // // TransferRate // if (ctx_.tx.isFieldPresent (sfTransferRate)) { std::uint32_t uRate = ctx_.tx.getFieldU32 (sfTransferRate); if (uRate == 0 || uRate == QUALITY_ONE) { j_.trace << "unset transfer rate"; sle->makeFieldAbsent (sfTransferRate); } else if (uRate > QUALITY_ONE) { j_.trace << "set transfer rate"; sle->setFieldU32 (sfTransferRate, uRate); } } if (uFlagsIn != uFlagsOut) sle->setFieldU32 (sfFlags, uFlagsOut); return tesSUCCESS; }