Account::Account(std::string name, std::pair<PublicKey, SecretKey> const& keys) : name_(std::move(name)) , pk_ (keys.first) , sk_ (keys.second) , id_ (calcAccountID(pk_)) , human_ (toBase58(id_)) { }
Account::Account (std::string name, KeyType type) : name_(std::move(name)) { auto const keys = generateKeyPair( type, generateSeed(name_)); pk_ = keys.first; sk_ = keys.second; id_ = calcAccountID(pk_); human_ = toBase58(id_); }
void testSeedParsing () { testcase ("Parsing"); // account IDs and node and account public and private // keys should not be parseable as seeds. auto const node1 = randomKeyPair(KeyType::secp256k1); BEAST_EXPECT(!parseGenericSeed ( toBase58 (TokenType::TOKEN_NODE_PUBLIC, node1.first))); BEAST_EXPECT(!parseGenericSeed ( toBase58 (TokenType::TOKEN_NODE_PRIVATE, node1.second))); auto const node2 = randomKeyPair(KeyType::ed25519); BEAST_EXPECT(!parseGenericSeed ( toBase58 (TokenType::TOKEN_NODE_PUBLIC, node2.first))); BEAST_EXPECT(!parseGenericSeed ( toBase58 (TokenType::TOKEN_NODE_PRIVATE, node2.second))); auto const account1 = generateKeyPair( KeyType::secp256k1, randomSeed ()); BEAST_EXPECT(!parseGenericSeed ( toBase58(calcAccountID(account1.first)))); BEAST_EXPECT(!parseGenericSeed ( toBase58(TokenType::TOKEN_ACCOUNT_PUBLIC, account1.first))); BEAST_EXPECT(!parseGenericSeed ( toBase58(TokenType::TOKEN_ACCOUNT_SECRET, account1.second))); auto const account2 = generateKeyPair( KeyType::ed25519, randomSeed ()); BEAST_EXPECT(!parseGenericSeed ( toBase58(calcAccountID(account2.first)))); BEAST_EXPECT(!parseGenericSeed ( toBase58(TokenType::TOKEN_ACCOUNT_PUBLIC, account2.first))); BEAST_EXPECT(!parseGenericSeed ( toBase58(TokenType::TOKEN_ACCOUNT_SECRET, account2.second))); }
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::uint64_t SetRegularKey::calculateBaseFee ( PreclaimContext const& ctx) { auto const id = ctx.tx.getAccountID(sfAccount); auto const spk = ctx.tx.getSigningPubKey(); if (publicKeyType (makeSlice (spk))) { if (calcAccountID(PublicKey (makeSlice(spk))) == id) { auto const sle = ctx.view.read(keylet::account(id)); if (sle && (! (sle->getFlags () & lsfPasswordSpent))) { // flag is armed and they signed with the right account return 0; } } } return Transactor::calculateBaseFee (ctx); }
Json::Value accounts ( Ledger::ref lrLedger, RippleAddress const& naMasterGenerator, NetworkOPs& netOps) { Json::Value jsonAccounts (Json::arrayValue); // YYY Don't want to leak to thin server that these accounts are related. // YYY Would be best to alternate requests to servers and to cache results. unsigned int uIndex = 0; do { // VFALCO Should be PublicKey and Generator RippleAddress pk; pk.setAccountPublic (naMasterGenerator, uIndex++); auto const sle = cachedRead(*lrLedger, keylet::account(calcAccountID(pk)).key, ltACCOUNT_ROOT); if (sle) { Json::Value jsonAccount (Json::objectValue); injectSLE(jsonAccount, *sle); jsonAccounts.append (jsonAccount); } else { uIndex = 0; } } while (uIndex); return jsonAccounts; }
void testKeypairGenerationAndSigning () { std::string const message1 = "http://www.ripple.com"; std::string const message2 = "https://www.ripple.com"; { testcase ("Node keypair generation & signing (secp256k1)"); auto const secretKey = generateSecretKey ( KeyType::secp256k1, generateSeed ("masterpassphrase")); auto const publicKey = derivePublicKey ( KeyType::secp256k1, secretKey); BEAST_EXPECT(toBase58(TokenType::TOKEN_NODE_PUBLIC, publicKey) == "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9"); BEAST_EXPECT(toBase58(TokenType::TOKEN_NODE_PRIVATE, secretKey) == "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe"); BEAST_EXPECT(to_string(calcNodeID(publicKey)) == "7E59C17D50F5959C7B158FEC95C8F815BF653DC8"); auto sig = sign (publicKey, secretKey, makeSlice(message1)); BEAST_EXPECT(sig.size() != 0); BEAST_EXPECT(verify (publicKey, makeSlice(message1), sig)); // Correct public key but wrong message BEAST_EXPECT(!verify (publicKey, makeSlice(message2), sig)); // Verify with incorrect public key { auto const otherPublicKey = derivePublicKey ( KeyType::secp256k1, generateSecretKey ( KeyType::secp256k1, generateSeed ("otherpassphrase"))); BEAST_EXPECT(!verify (otherPublicKey, makeSlice(message1), sig)); } // Correct public key but wrong signature { // Slightly change the signature: if (auto ptr = sig.data()) ptr[sig.size() / 2]++; BEAST_EXPECT(!verify (publicKey, makeSlice(message1), sig)); } } { testcase ("Node keypair generation & signing (ed25519)"); auto const secretKey = generateSecretKey ( KeyType::ed25519, generateSeed ("masterpassphrase")); auto const publicKey = derivePublicKey ( KeyType::ed25519, secretKey); BEAST_EXPECT(toBase58(TokenType::TOKEN_NODE_PUBLIC, publicKey) == "nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf"); BEAST_EXPECT(toBase58(TokenType::TOKEN_NODE_PRIVATE, secretKey) == "paKv46LztLqK3GaKz1rG2nQGN6M4JLyRtxFBYFTw4wAVHtGys36"); BEAST_EXPECT(to_string(calcNodeID(publicKey)) == "AA066C988C712815CC37AF71472B7CBBBD4E2A0A"); auto sig = sign (publicKey, secretKey, makeSlice(message1)); BEAST_EXPECT(sig.size() != 0); BEAST_EXPECT(verify (publicKey, makeSlice(message1), sig)); // Correct public key but wrong message BEAST_EXPECT(!verify (publicKey, makeSlice(message2), sig)); // Verify with incorrect public key { auto const otherPublicKey = derivePublicKey ( KeyType::ed25519, generateSecretKey ( KeyType::ed25519, generateSeed ("otherpassphrase"))); BEAST_EXPECT(!verify (otherPublicKey, makeSlice(message1), sig)); } // Correct public key but wrong signature { // Slightly change the signature: if (auto ptr = sig.data()) ptr[sig.size() / 2]++; BEAST_EXPECT(!verify (publicKey, makeSlice(message1), sig)); } } { testcase ("Account keypair generation & signing (secp256k1)"); auto const keyPair = generateKeyPair ( KeyType::secp256k1, generateSeed ("masterpassphrase")); BEAST_EXPECT(toBase58(calcAccountID(keyPair.first)) == "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"); BEAST_EXPECT(toBase58(TokenType::TOKEN_ACCOUNT_PUBLIC, keyPair.first) == "aBQG8RQAzjs1eTKFEAQXr2gS4utcDiEC9wmi7pfUPTi27VCahwgw"); BEAST_EXPECT(toBase58(TokenType::TOKEN_ACCOUNT_SECRET, keyPair.second) == "p9JfM6HHi64m6mvB6v5k7G2b1cXzGmYiCNJf6GHPKvFTWdeRVjh"); auto sig = sign (keyPair.first, keyPair.second, makeSlice(message1)); BEAST_EXPECT(sig.size() != 0); BEAST_EXPECT(verify (keyPair.first, makeSlice(message1), sig)); // Correct public key but wrong message BEAST_EXPECT(!verify (keyPair.first, makeSlice(message2), sig)); // Verify with incorrect public key { auto const otherKeyPair = generateKeyPair ( KeyType::secp256k1, generateSeed ("otherpassphrase")); BEAST_EXPECT(!verify (otherKeyPair.first, makeSlice(message1), sig)); } // Correct public key but wrong signature { // Slightly change the signature: if (auto ptr = sig.data()) ptr[sig.size() / 2]++; BEAST_EXPECT(!verify (keyPair.first, makeSlice(message1), sig)); } } { testcase ("Account keypair generation & signing (ed25519)"); auto const keyPair = generateKeyPair ( KeyType::ed25519, generateSeed ("masterpassphrase")); BEAST_EXPECT(to_string(calcAccountID(keyPair.first)) == "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf"); BEAST_EXPECT(toBase58(TokenType::TOKEN_ACCOUNT_PUBLIC, keyPair.first) == "aKGheSBjmCsKJVuLNKRAKpZXT6wpk2FCuEZAXJupXgdAxX5THCqR"); BEAST_EXPECT(toBase58(TokenType::TOKEN_ACCOUNT_SECRET, keyPair.second) == "pwDQjwEhbUBmPuEjFpEG75bFhv2obkCB7NxQsfFxM7xGHBMVPu9"); auto sig = sign (keyPair.first, keyPair.second, makeSlice(message1)); BEAST_EXPECT(sig.size() != 0); BEAST_EXPECT(verify (keyPair.first, makeSlice(message1), sig)); // Correct public key but wrong message BEAST_EXPECT(!verify (keyPair.first, makeSlice(message2), sig)); // Verify with incorrect public key { auto const otherKeyPair = generateKeyPair ( KeyType::ed25519, generateSeed ("otherpassphrase")); BEAST_EXPECT(!verify (otherKeyPair.first, makeSlice(message1), sig)); } // Correct public key but wrong signature { // Slightly change the signature: if (auto ptr = sig.data()) ptr[sig.size() / 2]++; BEAST_EXPECT(!verify (keyPair.first, makeSlice(message1), sig)); } } }
Json::Value walletPropose (Json::Value const& params) { boost::optional<Seed> seed; KeyType keyType = KeyType::secp256k1; if (params.isMember (jss::key_type)) { if (! params[jss::key_type].isString()) { return RPC::expected_field_error ( jss::key_type, "string"); } keyType = keyTypeFromString ( params[jss::key_type].asString()); if (keyType == KeyType::invalid) return rpcError(rpcINVALID_PARAMS); } if (params.isMember (jss::passphrase) || params.isMember (jss::seed) || params.isMember (jss::seed_hex)) { Json::Value err; seed = RPC::getSeedFromRPC (params, err); if (!seed) return err; } else { seed = randomSeed (); } auto const publicKey = generateKeyPair (keyType, *seed).first; Json::Value obj (Json::objectValue); obj[jss::master_seed] = toBase58 (*seed); obj[jss::master_seed_hex] = strHex (seed->data(), seed->size()); obj[jss::master_key] = seedAs1751 (*seed); obj[jss::account_id] = toBase58(calcAccountID(publicKey)); obj[jss::public_key] = toBase58(TOKEN_ACCOUNT_PUBLIC, publicKey); obj[jss::key_type] = to_string (keyType); obj[jss::public_key_hex] = strHex (publicKey.data(), publicKey.size()); if (params.isMember (jss::passphrase)) { auto const entropy = estimate_entropy ( params[jss::passphrase].asString()); // 80 bits of entropy isn't bad, but it's better to // err on the side of caution and be conservative. if (entropy < 80.0) obj[jss::warning] = "This wallet was generated using a user-supplied " "passphrase that has low entropy and is vulnerable " "to brute-force attacks."; else obj[jss::warning] = "This wallet was generated using a user-supplied " "passphrase. It may be vulnerable to brute-force " "attacks."; } return obj; }
Json::Value doGatewayBalances (RPC::Context& context) { auto& params = context.params; // Get the current ledger std::shared_ptr<ReadView const> ledger; auto result = RPC::lookupLedger (ledger, context); if (!ledger) return result; if (!(params.isMember (jss::account) || params.isMember (jss::ident))) return RPC::missing_field_error (jss::account); std::string const strIdent (params.isMember (jss::account) ? params[jss::account].asString () : params[jss::ident].asString ()); bool const bStrict = params.isMember (jss::strict) && params[jss::strict].asBool (); // Get info on account. AccountID accountID; auto jvAccepted = RPC::accountFromString (accountID, strIdent, bStrict); if (jvAccepted) return jvAccepted; context.loadType = Resource::feeHighBurdenRPC; result[jss::account] = getApp().accountIDCache().toBase58 (accountID); // Parse the specified hotwallet(s), if any std::set <AccountID> hotWallets; if (params.isMember ("hotwallet")) { Json::Value const& hw = params["hotwallet"]; bool valid = true; auto addHotWallet = [&valid, &hotWallets](Json::Value const& j) { if (j.isString()) { RippleAddress ra; if (ra.setAccountPublic (j.asString ())) { hotWallets.insert(calcAccountID(ra)); } else { auto const a =parseBase58<AccountID>(j.asString()); if (! a) valid = false; else hotWallets.insert(*a); } } else { valid = false; } }; if (hw.isArray()) { for (unsigned i = 0; i < hw.size(); ++i) addHotWallet (hw[i]); } else if (hw.isString()) { addHotWallet (hw); } else { valid = false; } if (! valid) { result[jss::error] = "invalidHotWallet"; return result; } } std::map <Currency, STAmount> sums; std::map <AccountID, std::vector <STAmount>> hotBalances; std::map <AccountID, std::vector <STAmount>> assets; // Traverse the cold wallet's trust lines { forEachItem(*ledger, accountID, [&](std::shared_ptr<SLE const> const& sle) { auto rs = RippleState::makeItem (accountID, sle); if (!rs) return; int balSign = rs->getBalance().signum(); if (balSign == 0) return; auto const& peer = rs->getAccountIDPeer(); // Here, a negative balance means the cold wallet owes (normal) // A positive balance means the cold wallet has an asset (unusual) if (hotWallets.count (peer) > 0) { // This is a specified hot wallt hotBalances[peer].push_back (-rs->getBalance ()); } else if (balSign > 0) { // This is a gateway asset assets[peer].push_back (rs->getBalance ()); } else { // normal negative balance, obligation to customer auto& bal = sums[rs->getBalance().getCurrency()]; if (bal == zero) { // This is needed to set the currency code correctly bal = -rs->getBalance(); } else bal -= rs->getBalance(); } }); } if (! sums.empty()) { Json::Value& j = (result [jss::obligations] = Json::objectValue); for (auto const& e : sums) { j[to_string (e.first)] = e.second.getText (); } } if (! hotBalances.empty()) { Json::Value& j = (result [jss::balances] = Json::objectValue); for (auto const& account : hotBalances) { Json::Value& balanceArray = (j[to_string (account.first)] = Json::arrayValue); for (auto const& balance : account.second) { Json::Value& entry = balanceArray.append (Json::objectValue); entry[jss::currency] = to_string (balance.issue ().currency); entry[jss::value] = balance.getText(); } } } if (! assets.empty()) { Json::Value& j = (result [jss::assets] = Json::objectValue); for (auto const& account : assets) { Json::Value& balanceArray = (j[to_string (account.first)] = Json::arrayValue); for (auto const& balance : account.second) { Json::Value& entry = balanceArray.append (Json::objectValue); entry[jss::currency] = to_string (balance.issue ().currency); entry[jss::value] = balance.getText(); } } } return result; }
Json::Value walletPropose (Json::Value const& params) { boost::optional<KeyType> keyType; boost::optional<Seed> seed; bool rippleLibSeed = false; if (params.isMember (jss::key_type)) { if (! params[jss::key_type].isString()) { return RPC::expected_field_error ( jss::key_type, "string"); } keyType = keyTypeFromString ( params[jss::key_type].asString()); if (!keyType) return rpcError(rpcINVALID_PARAMS); } // ripple-lib encodes seed used to generate an Ed25519 wallet in a // non-standard way. While we never encode seeds that way, we try // to detect such keys to avoid user confusion. { if (params.isMember(jss::passphrase)) seed = RPC::parseRippleLibSeed(params[jss::passphrase]); else if (params.isMember(jss::seed)) seed = RPC::parseRippleLibSeed(params[jss::seed]); if(seed) { rippleLibSeed = true; // If the user *explicitly* requests a key type other than // Ed25519 we return an error. if (keyType.value_or(KeyType::ed25519) != KeyType::ed25519) return rpcError(rpcBAD_SEED); keyType = KeyType::ed25519; } } if (!seed) { if (params.isMember(jss::passphrase) || params.isMember(jss::seed) || params.isMember(jss::seed_hex)) { Json::Value err; seed = RPC::getSeedFromRPC(params, err); if (!seed) return err; } else { seed = randomSeed(); } } if (!keyType) keyType = KeyType::secp256k1; auto const publicKey = generateKeyPair (*keyType, *seed).first; Json::Value obj (Json::objectValue); auto const seed1751 = seedAs1751 (*seed); auto const seedHex = strHex (*seed); auto const seedBase58 = toBase58 (*seed); obj[jss::master_seed] = seedBase58; obj[jss::master_seed_hex] = seedHex; obj[jss::master_key] = seed1751; obj[jss::account_id] = toBase58(calcAccountID(publicKey)); obj[jss::public_key] = toBase58(TokenType::AccountPublic, publicKey); obj[jss::key_type] = to_string (*keyType); obj[jss::public_key_hex] = strHex (publicKey); // If a passphrase was specified, and it was hashed and used as a seed // run a quick entropy check and add an appropriate warning, because // "brain wallets" can be easily attacked. if (!rippleLibSeed && params.isMember (jss::passphrase)) { auto const passphrase = params[jss::passphrase].asString(); if (passphrase != seed1751 && passphrase != seedBase58 && passphrase != seedHex) { // 80 bits of entropy isn't bad, but it's better to // err on the side of caution and be conservative. if (estimate_entropy (passphrase) < 80.0) obj[jss::warning] = "This wallet was generated using a user-supplied " "passphrase that has low entropy and is vulnerable " "to brute-force attacks."; else obj[jss::warning] = "This wallet was generated using a user-supplied " "passphrase. It may be vulnerable to brute-force " "attacks."; } } return obj; }
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; }
Json::Value doGatewayBalances (RPC::Context& context) { auto& params = context.params; // Get the current ledger std::shared_ptr<ReadView const> ledger; auto result = RPC::lookupLedger (ledger, context); if (!ledger) return result; if (!(params.isMember (jss::account) || params.isMember (jss::ident))) return RPC::missing_field_error (jss::account); std::string const strIdent (params.isMember (jss::account) ? params[jss::account].asString () : params[jss::ident].asString ()); bool const bStrict = params.isMember (jss::strict) && params[jss::strict].asBool (); // Get info on account. AccountID accountID; auto jvAccepted = RPC::accountFromString (accountID, strIdent, bStrict); if (jvAccepted) return jvAccepted; context.loadType = Resource::feeHighBurdenRPC; result[jss::account] = context.app.accountIDCache().toBase58 (accountID); // Parse the specified hotwallet(s), if any std::set <AccountID> hotWallets; if (params.isMember (jss::hotwallet)) { auto addHotWallet = [&hotWallets](Json::Value const& j) { if (j.isString()) { auto const pk = parseBase58<PublicKey>( TokenType::TOKEN_ACCOUNT_PUBLIC, j.asString ()); if (pk) { hotWallets.insert(calcAccountID(*pk)); return true; } auto const id = parseBase58<AccountID>(j.asString()); if (id) { hotWallets.insert(*id); return true; } } return false; }; Json::Value const& hw = params[jss::hotwallet]; bool valid = true; if (hw.isArray()) { for (unsigned i = 0; i < hw.size(); ++i) valid &= addHotWallet (hw[i]); } else if (hw.isString()) { valid &= addHotWallet (hw); } else { valid = false; } if (! valid) { result[jss::error] = "invalidHotWallet"; return result; } } std::map <Currency, STAmount> sums; std::map <AccountID, std::vector <STAmount>> hotBalances; std::map <AccountID, std::vector <STAmount>> assets; std::map <AccountID, std::vector <STAmount>> frozenBalances; // Traverse the cold wallet's trust lines { forEachItem(*ledger, accountID, [&](std::shared_ptr<SLE const> const& sle) { auto rs = RippleState::makeItem (accountID, sle); if (!rs) return; int balSign = rs->getBalance().signum(); if (balSign == 0) return; auto const& peer = rs->getAccountIDPeer(); // Here, a negative balance means the cold wallet owes (normal) // A positive balance means the cold wallet has an asset (unusual) if (hotWallets.count (peer) > 0) { // This is a specified hot wallet hotBalances[peer].push_back (-rs->getBalance ()); } else if (balSign > 0) { // This is a gateway asset assets[peer].push_back (rs->getBalance ()); } else if (rs->getFreeze()) { // An obligation the gateway has frozen frozenBalances[peer].push_back (-rs->getBalance ()); } else { // normal negative balance, obligation to customer auto& bal = sums[rs->getBalance().getCurrency()]; if (bal == zero) { // This is needed to set the currency code correctly bal = -rs->getBalance(); } else bal -= rs->getBalance(); } }); } if (! sums.empty()) { Json::Value j; for (auto const& e : sums) { j[to_string (e.first)] = e.second.getText (); } result [jss::obligations] = std::move (j); } auto populate = []( std::map <AccountID, std::vector <STAmount>> const& array, Json::Value& result, Json::StaticString const& name) { if (!array.empty()) { Json::Value j; for (auto const& account : array) { Json::Value balanceArray; for (auto const& balance : account.second) { Json::Value entry; entry[jss::currency] = to_string (balance.issue ().currency); entry[jss::value] = balance.getText(); balanceArray.append (std::move (entry)); } j [to_string (account.first)] = std::move (balanceArray); } result [name] = std::move (j); } }; populate (hotBalances, result, jss::balances); populate (frozenBalances, result, jss::frozen_balances); populate (assets, result, jss::assets); return result; }
void run() { // Construct a seed. RippleAddress naSeed; expect (naSeed.setSeedGeneric ("masterpassphrase")); expect (naSeed.humanSeed () == "snoPBrXtMeMyMHUVTgbuqAfg1SUTb", naSeed.humanSeed ()); // Create node public/private key pair RippleAddress naNodePublic = RippleAddress::createNodePublic (naSeed); RippleAddress naNodePrivate = RippleAddress::createNodePrivate (naSeed); expect (naNodePublic.humanNodePublic () == "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9", naNodePublic.humanNodePublic ()); expect (naNodePrivate.humanNodePrivate () == "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe", naNodePrivate.humanNodePrivate ()); // Check node signing. Blob vucTextSrc = strCopy ("Hello, nurse!"); uint256 uHash = sha512Half(makeSlice(vucTextSrc)); Blob vucTextSig; naNodePrivate.signNodePrivate (uHash, vucTextSig); expect (naNodePublic.verifyNodePublic (uHash, vucTextSig, ECDSA::strict), "Verify failed."); // Construct a public generator from the seed. RippleAddress generator = RippleAddress::createGeneratorPublic (naSeed); expect (generator.humanGenerator () == "fhuJKrhSDzV2SkjLn9qbwm5AaRmrxDPfFsHDCP6yfDZWcxDFz4mt", generator.humanGenerator ()); // Create ed25519 account public/private key pair. KeyPair keys = generateKeysFromSeed (KeyType::ed25519, naSeed); expectEquals (keys.publicKey.humanAccountPublic(), "aKGheSBjmCsKJVuLNKRAKpZXT6wpk2FCuEZAXJupXgdAxX5THCqR"); // Check ed25519 account signing. vucTextSig = keys.secretKey.accountPrivateSign (vucTextSrc); expect (!vucTextSig.empty(), "ed25519 signing failed."); expect (keys.publicKey.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA()), "ed25519 verify failed."); // Create account #0 public/private key pair. RippleAddress naAccountPublic0 = RippleAddress::createAccountPublic (generator, 0); RippleAddress naAccountPrivate0 = RippleAddress::createAccountPrivate (generator, naSeed, 0); expect (toBase58(calcAccountID(naAccountPublic0)) == "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"); expect (naAccountPublic0.humanAccountPublic () == "aBQG8RQAzjs1eTKFEAQXr2gS4utcDiEC9wmi7pfUPTi27VCahwgw", naAccountPublic0.humanAccountPublic ()); // Create account #1 public/private key pair. RippleAddress naAccountPublic1 = RippleAddress::createAccountPublic (generator, 1); RippleAddress naAccountPrivate1 = RippleAddress::createAccountPrivate (generator, naSeed, 1); expect (toBase58(calcAccountID(naAccountPublic1)) == "r4bYF7SLUMD7QgSLLpgJx38WJSY12ViRjP"); expect (naAccountPublic1.humanAccountPublic () == "aBPXpTfuLy1Bhk3HnGTTAqnovpKWQ23NpFMNkAF6F1Atg5vDyPrw", naAccountPublic1.humanAccountPublic ()); // Check account signing. vucTextSig = naAccountPrivate0.accountPrivateSign (vucTextSrc); expect (!vucTextSig.empty(), "Signing failed."); expect (naAccountPublic0.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA::strict), "Verify failed."); expect (!naAccountPublic1.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA::not_strict), "Anti-verify failed."); expect (!naAccountPublic1.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA::strict), "Anti-verify failed."); vucTextSig = naAccountPrivate1.accountPrivateSign (vucTextSrc); expect (!vucTextSig.empty(), "Signing failed."); expect (naAccountPublic1.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA::strict), "Verify failed."); expect (!naAccountPublic0.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA::not_strict), "Anti-verify failed."); expect (!naAccountPublic0.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA::strict), "Anti-verify failed."); // Check account encryption. Blob vucTextCipher = naAccountPrivate0.accountPrivateEncrypt (naAccountPublic1, vucTextSrc); Blob vucTextRecovered = naAccountPrivate1.accountPrivateDecrypt (naAccountPublic0, vucTextCipher); expect (vucTextSrc == vucTextRecovered, "Encrypt-decrypt failed."); { RippleAddress nSeed; uint128 seed1, seed2; seed1.SetHex ("71ED064155FFADFA38782C5E0158CB26"); nSeed.setSeed (seed1); expect (nSeed.humanSeed() == "shHM53KPZ87Gwdqarm1bAmPeXg8Tn", "Incorrect human seed"); expect (nSeed.humanSeed1751() == "MAD BODY ACE MINT OKAY HUB WHAT DATA SACK FLAT DANA MATH", "Incorrect 1751 seed"); } }