Value importprivkey(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "importprivkey <emercoinprivkey> [label]\n" "Adds a private key (as returned by dumpprivkey) to your wallet."); string strSecret = params[0].get_str(); string strLabel = ""; if (params.size() > 1) strLabel = params[1].get_str(); CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(strSecret); if (!fGood) throw JSONRPCError(-5,"Invalid private key"); if (fWalletUnlockMintOnly) // ppcoin: no importprivkey in mint-only mode throw JSONRPCError(-102, "Wallet is unlocked for minting only."); CKey key; bool fCompressed; CSecret secret = vchSecret.GetSecret(fCompressed); key.SetSecret(secret, fCompressed); CKeyID vchAddress = key.GetPubKey().GetID(); { LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->MarkDirty(); pwalletMain->SetAddressBookName(vchAddress, strLabel); if (!pwalletMain->AddKey(key)) throw JSONRPCError(-4,"Error adding key to wallet"); pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true); pwalletMain->ReacceptWalletTransactions(); } MainFrameRepaint(); return Value::null; }
// create a compact signature (65 bytes), which allows reconstructing the used public key // The format is one header byte, followed by two times 32 bytes for the serialized r and s values. // The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, // 0x1D = second key with even y, 0x1E = second key with odd y bool CKey::SignCompact(uint256 hash, std::vector<unsigned char>& vchSig) { bool fOk = false; ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); if (sig==NULL) return false; vchSig.clear(); vchSig.resize(65,0); int nBitsR = BN_num_bits(sig->r); int nBitsS = BN_num_bits(sig->s); if (nBitsR <= 256 && nBitsS <= 256) { int nRecId = -1; for (int i=0; i<4; i++) { CKey keyRec; keyRec.fSet = true; if (fCompressedPubKey) keyRec.SetCompressedPubKey(); if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1) if (keyRec.GetPubKey() == this->GetPubKey()) { nRecId = i; break; } } if (nRecId == -1) { ECDSA_SIG_free(sig); throw key_error("CKey::SignCompact() : unable to construct recoverable key"); } vchSig[0] = nRecId+27+(fCompressedPubKey ? 4 : 0); BN_bn2bin(sig->r,&vchSig[33-(nBitsR+7)/8]); BN_bn2bin(sig->s,&vchSig[65-(nBitsS+7)/8]); fOk = true; } ECDSA_SIG_free(sig); return fOk; }
Value importprivkey(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "importprivkey <yacoinprivkey> [label]\n" "Adds a private key (as returned by dumpprivkey) to your wallet."); string strSecret = params[0].get_str(); string strLabel = ""; if (params.size() > 1) strLabel = params[1].get_str(); CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(strSecret); if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); if (fWalletUnlockMintOnly) // ppcoin: no importprivkey in mint-only mode throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Wallet is unlocked for minting only."); CKey key; bool fCompressed; CSecret secret = vchSecret.GetSecret(fCompressed); key.SetSecret(secret, fCompressed); CKeyID vchAddress = key.GetPubKey().GetID(); { LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->MarkDirty(); pwalletMain->SetAddressBookName(vchAddress, strLabel); if (!pwalletMain->AddKey(key)) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); // Groko - This is a pain in the butt. User can request -rescan after he is done importing // his private keys. //pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true); //pwalletMain->ReacceptWalletTransactions(); } return Value::null; }
bool TestChainForComputingMediansSetup::GenerateRandomTransaction(CTransaction& txNew) { CAmount amountToSend = 5000; std::vector<CTransaction> res; CKey key; key.MakeNewKey(true); CScript scriptPubKey = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; CBasicKeyStore keystore; keystore.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); CTransaction utxo = coinbaseTxns[0]; coinbaseTxns.erase(coinbaseTxns.begin()); txNew.nLockTime = chainActive.Height(); txNew.vin.clear(); txNew.vout.clear(); for (int j = 0; j < nOutputs; ++j) { CTxOut txout(amountToSend, scriptPubKey); txNew.vout.push_back(txout); } //vin CTxIn vin = CTxIn(utxo.GetHash(), 0, CScript(), std::numeric_limits<unsigned int>::max() - 1); txNew.vin.push_back(vin); //previous tx's script pub key that we need to sign CScript& scriptSigRes = txNew.vin[0].scriptSig; CTransaction txNewConst(txNew); ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0), utxo.vout[0].scriptPubKey, scriptSigRes); res.push_back(txNew); return true; }
bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) { { LOCK(cs_KeyStore); if (!mapCryptedKeys.empty() || IsCrypted()) return false; fUseCrypto = true; BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) { CKey key; if (!key.SetSecret(mKey.second.first, mKey.second.second)) return false; const CPubKey vchPubKey = key.GetPubKey(); std::vector<unsigned char> vchCryptedSecret; bool fCompressed; if (!EncryptSecret(vMasterKeyIn, key.GetSecret(fCompressed), vchPubKey.GetHash(), vchCryptedSecret)) return false; if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) return false; } mapKeys.clear(); }
Value importprivkey(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "importprivkey <GingerCoinprivkey> [label]\n" "Adds a private key (as returned by dumpprivkey) to your wallet."); string strSecret = params[0].get_str(); string strLabel = ""; if (params.size() > 1) strLabel = params[1].get_str(); CGingerCoinSecret vchSecret; bool fGood = vchSecret.SetString(strSecret); if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); CKey key; bool fCompressed; CSecret secret = vchSecret.GetSecret(fCompressed); key.SetSecret(secret, fCompressed); CKeyID vchAddress = key.GetPubKey().GetID(); { LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->MarkDirty(); pwalletMain->SetAddressBookName(vchAddress, strLabel); if (!pwalletMain->AddKey(key)) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true); pwalletMain->ReacceptWalletTransactions(); } return Value::null; }
void dumpKeyInfo(uint256 privkey) { CKey key; key.resize(32); memcpy(&secret[0], &privkey, 32); vector<unsigned char> sec; sec.resize(32); memcpy(&sec[0], &secret[0], 32); printf(" * secret (hex): %s\n", HexStr(sec).c_str()); for (int nCompressed=0; nCompressed<2; nCompressed++) { bool fCompressed = nCompressed == 1; printf(" * %s:\n", fCompressed ? "compressed" : "uncompressed"); CDgdollartest2Secret bsecret; bsecret.SetSecret(secret, fCompressed); printf(" * secret (base58): %s\n", bsecret.ToString().c_str()); CKey key; key.SetSecret(secret, fCompressed); vector<unsigned char> vchPubKey = key.GetPubKey(); printf(" * pubkey (hex): %s\n", HexStr(vchPubKey).c_str()); printf(" * address (base58): %s\n", CDgdollartest2Address(vchPubKey).ToString().c_str()); } }
void RippleAddress::setAccountPublic(const RippleAddress& generator, int seq) { CKey pubkey = CKey(generator, seq); setAccountPublic(pubkey.GetPubKey()); }
std::string RestoreGridcoinBackupWallet() { //AdvancedBackup //AdvancedSalvage //4-26-2014 boost::filesystem::path path = GetDataDir() / "walletbackups" / "backup.dat"; std::string errors = ""; std::string sWallet = getfilecontents(path.string().c_str()); if (sWallet == "-1") return "Unable to open backup file."; string strSecret = "from file"; string strLabel = "Restored"; std::vector<std::string> vWallet = split(sWallet.c_str(),"<KEY>"); if (vWallet.size() > 1) { for (unsigned int i = 0; i < vWallet.size(); i++) { std::string sKey = vWallet[i]; if (sKey.length() > 2) { printf("Restoring private key %s",sKey.substr(0,5).c_str()); //Key is delimited by <|> std::vector<std::string> vKey = split(sKey.c_str(),"<|>"); if (vKey.size() > 1) { std::string sSecret = vKey[0]; std::string sPublic = vKey[1]; CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(sSecret); if (!fGood) { errors = errors + "Invalid private key : " + sSecret + "\r\n"; } else { CKey key = vchSecret.GetKey(); CPubKey pubkey = key.GetPubKey(); CKeyID vchAddress = pubkey.GetID(); { LOCK2(cs_main, pwalletMain->cs_wallet); if (!pwalletMain->AddKeyPubKey(key, pubkey)) { errors = errors + "Error adding key to wallet: " + sKey + "\r\n"; } if (i==0) { pwalletMain->SetDefaultKey(pubkey); pwalletMain->SetAddressBookName(vchAddress, strLabel); } pwalletMain->MarkDirty(); } } } } } } //Rescan { LOCK2(cs_main, pwalletMain->cs_wallet); if (true) { pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true); pwalletMain->ReacceptWalletTransactions(); } } printf("Rebuilding wallet, results: %s",errors.c_str()); return errors; }
void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() { CBitcoinAddress addr(ui->addressIn_VM->text().toStdString()); if (!addr.IsValid()) { ui->addressIn_VM->setValid(false); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); return; } CKeyID keyID; if (!addr.GetKeyID(keyID)) { ui->addressIn_VM->setValid(false); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); return; } bool fInvalid = false; std::vector<unsigned char> vchSig = DecodeBase64(ui->signatureIn_VM->text().toStdString().c_str(), &fInvalid); if (fInvalid) { ui->signatureIn_VM->setValid(false); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(tr("The signature could not be decoded.") + QString(" ") + tr("Please check the signature and try again.")); return; } CDataStream ss(SER_GETHASH, 0); ss << strMessageMagic; ss << ui->messageIn_VM->document()->toPlainText().toStdString(); // get the public key from UI fInvalid = false; std::vector<unsigned char> vchPubKey = DecodeBase64(ui->pubkeyIn_VM->text().toStdString().c_str(), &fInvalid); if (fInvalid) { ui->pubkeyIn_VM->setValid(false); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(tr("The public key could not be decoded.") + QString(" ") + tr("Please check it and try again.")); return; } CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) { ui->pubkeyIn_VM->setValid(false); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(tr("The public key is not valid.") + QString(" ") + tr("Please check it and try again.")); return; } CKey key; if (!key.SetPubKey(pubkey)) { ui->pubkeyIn_VM->setValid(false); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(tr("The public key cannot be added.") + QString(" ") + tr("Please check it and try again.")); return; } if (!key.Verify(HashKeccak(ss.begin(), ss.end()), vchSig)) { ui->signatureIn_VM->setValid(false); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(tr("The signature did not match the message digest.") + QString(" ") + tr("Please check the signature and try again.")); return; } // TODO // add the public key //key.SetPubKey(); if (!(CBitcoinAddress(key.GetPubKey().GetID()) == addr)) { ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>")); return; } ui->statusLabel_VM->setStyleSheet("QLabel { color: green; }"); ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verified.") + QString("</nobr>")); }
void RunSerialiseTests() { int64_t nTest; int64_t nTest0 = 0l; int64_t nTest4 = 1432035740l; int64_t nTest4_1 = 2189410940l; // 2039 int64_t nTest5 = 4294967298l; // 2106 int64_t nTest8 = -3l; BOOST_CHECK(0 == GetNumBytesReqForInt(nTest0)); BOOST_CHECK(4 == GetNumBytesReqForInt(nTest4)); BOOST_CHECK(4 == GetNumBytesReqForInt(nTest4_1)); // expect 4, no sign bit BOOST_CHECK(5 == GetNumBytesReqForInt(nTest5)); BOOST_CHECK(8 == GetNumBytesReqForInt(nTest8)); //BOOST_TEST_MESSAGE(GetNumBytesReqForInt(nTest5)); std::vector<uint8_t> v; SetCompressedInt64(v, nTest0); GetCompressedInt64(v, (uint64_t&)nTest); BOOST_CHECK(nTest0 == nTest); SetCompressedInt64(v, nTest5); GetCompressedInt64(v, (uint64_t&)nTest); BOOST_CHECK(nTest5 == nTest); SetCompressedInt64(v, nTest8); GetCompressedInt64(v, (uint64_t&)nTest); BOOST_CHECK(nTest8 == nTest); CStoredExtKey sk, sk_; CStoredExtKey skInvalid, skInvalid_; CExtKey58 eKey58; BOOST_CHECK(0 == eKey58.Set58("moivYMcZoUdupxqBNASoNKWbyBzKFPzYA3ZauZhCyQGcUhdvxhgsYNdqBkCbspTmaXWtW68Ha7gjMBjb5gbudrictnzw9KAVKogAXC8FsqiSzRp")); sk.kp = eKey58.GetKey(); sk.sLabel = "sk label"; sk.nGenerated = 5; sk.nHGenerated = 6; sk.mapValue[EKVT_CREATED_AT] = SetCompressedInt64(v, nTest8); eKey58.SetKey(sk.kp, CChainParams::EXT_PUBLIC_KEY); BOOST_CHECK(eKey58.ToString() == "moipXY9njTPCnsVV8vPCLA1xKp2NXdRtPVyGABKcbshkKQUadnrk2XPccZcSDjefRX64mNjYpS33SAy97UHGWs9WoAufi9pdow9gsYMvVEcmgSk"); eKey58.SetKeyV(sk.kp); BOOST_CHECK(eKey58.ToString() == "moivYMcZoUdupxqBNASoNKWbyBzKFPzYA3ZauZhCyQGcUhdvxhgsYNdqBkCbspTmaXWtW68Ha7gjMBjb5gbudrictnzw9KAVKogAXC8FsqiSzRp"); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << sk << skInvalid; ss >> sk_; ss >> skInvalid_; BOOST_CHECK(sk.kp == sk_.kp); BOOST_CHECK(1 == sk_.kp.IsValidV()); BOOST_CHECK(1 == sk_.kp.IsValidP()); BOOST_CHECK(sk.sLabel == sk_.sLabel); BOOST_CHECK(sk.nGenerated == sk_.nGenerated); BOOST_CHECK(sk.nHGenerated == sk_.nHGenerated); BOOST_CHECK(nTest8 == GetCompressedInt64(sk_.mapValue[EKVT_CREATED_AT], (uint64_t&)nTest)); BOOST_CHECK(0 == skInvalid.kp.IsValidV()); BOOST_CHECK(0 == skInvalid.kp.IsValidP()); // path std::vector<uint8_t> vPath; PushUInt32(vPath, 1); PushUInt32(vPath, 3); PushUInt32(vPath, 2); PushUInt32(vPath, 4294967295); std::string sPath; BOOST_CHECK(0 == PathToString(vPath, sPath, 'h')); BOOST_CHECK(sPath == "m/1/3/2/2147483647h"); vPath.resize(0); PushUInt32(vPath, 1); PushUInt32(vPath, 4294967294); PushUInt32(vPath, 30); BOOST_CHECK(0 == PathToString(vPath, sPath)); BOOST_CHECK(sPath == "m/1/2147483646'/30"); // id CBitcoinAddress addr; CKeyID id = sk.GetID(); CKeyID idTest; BOOST_CHECK(true == addr.Set(id, CChainParams::EXT_KEY_HASH) && addr.IsValid(CChainParams::EXT_KEY_HASH) && addr.GetKeyID(idTest, CChainParams::EXT_KEY_HASH)); BOOST_CHECK(id == idTest); BOOST_CHECK_MESSAGE(addr.ToString() == "x9S4Xj1DZwFsdFno1uHknNNGqdMWgXdhX6", addr.ToString()); // - test DeriveNextKey CExtKey ev; CExtPubKey ep; uint32_t nChild=0; sk.nGenerated = 0; sk.nHGenerated = 0; BOOST_CHECK(0 == sk.DeriveNextKey(ev, nChild)); BOOST_CHECK_MESSAGE(1 == sk.nGenerated, "nGenerated " << sk.nGenerated); sk.nGenerated = 0; BOOST_CHECK(0 == sk.DeriveNextKey(ep, nChild)); BOOST_CHECK(ep.pubkey == ev.key.GetPubKey()); id = ev.key.GetPubKey().GetID(); addr.Set(id, CChainParams::EXT_KEY_HASH); BOOST_CHECK_MESSAGE(addr.ToString() == "xS8vxP6PVm3ycqm4NqvUkhiDWqeBhhekzn", addr.ToString()); sk.nGenerated = 1; BOOST_CHECK(0 == sk.DeriveNextKey(ev, nChild)); id = ev.key.GetPubKey().GetID(); addr.Set(id, CChainParams::EXT_KEY_HASH); BOOST_CHECK_MESSAGE(addr.ToString() == "xRfAtU1u43VJBTt4agxpnPvXjf28hSmwrL", addr.ToString()); sk.nHGenerated = 0; BOOST_CHECK(0 == sk.DeriveNextKey(ev, nChild, true)); id = ev.key.GetPubKey().GetID(); addr.Set(id, CChainParams::EXT_KEY_HASH); BOOST_CHECK_MESSAGE(addr.ToString() == "xEcqVH3fRnNZabMHVctAeScJY9ySkg6BSF", addr.ToString()); BOOST_CHECK_MESSAGE(1 == sk.nHGenerated, "nHGenerated " << sk.nHGenerated); sk.nHGenerated = 1; BOOST_CHECK(0 == sk.DeriveNextKey(ev, nChild, true)); id = ev.key.GetPubKey().GetID(); addr.Set(id, CChainParams::EXT_KEY_HASH); BOOST_CHECK_MESSAGE(addr.ToString() == "xRfBk2tuann5qTVKyW2HmA9CJZBAE1sRvJ", addr.ToString()); BOOST_CHECK_MESSAGE(2 == sk.nHGenerated, "nHGenerated " << sk.nHGenerated); sk.nHGenerated = 1; BOOST_CHECK(0 == sk.DeriveNextKey(ep, nChild, true)); id = ev.key.GetPubKey().GetID(); addr.Set(id, CChainParams::EXT_KEY_HASH); BOOST_CHECK_MESSAGE(addr.ToString() == "xRfBk2tuann5qTVKyW2HmA9CJZBAE1sRvJ", addr.ToString()); BOOST_CHECK(ep.pubkey == ev.key.GetPubKey()); CStoredExtKey skp = sk; skp.kp = skp.kp.Neutered(); CKey k; sk.nGenerated = 1; BOOST_CHECK(0 == sk.DeriveNextKey(k, nChild, false)); BOOST_CHECK_MESSAGE(nChild == 1, "nChild " << nChild); BOOST_CHECK_MESSAGE(HexStr(k.GetPubKey()) == "0245a12d2ce075d947b6232b3e424ffa5d2208b6ff69800a1f2501ac6392499bf8", "HexStr(k.GetPubKey()) " << HexStr(k.GetPubKey())); sk.nGenerated = 2; BOOST_CHECK(0 == sk.DeriveNextKey(k, nChild, false)); BOOST_CHECK_MESSAGE(nChild == 2, "nChild " << nChild); BOOST_CHECK_MESSAGE(HexStr(k.GetPubKey()) == "02f430d7efc4d1ecbac888fb49446ec0b13ec4196512be93054a9b5b30df238910", "HexStr(k.GetPubKey()) " << HexStr(k.GetPubKey())); sk.nHGenerated = 2; BOOST_CHECK(0 == sk.DeriveNextKey(k, nChild, true)); BOOST_CHECK_MESSAGE(nChild == 2147483650, "nChild " << nChild); BOOST_CHECK_MESSAGE(HexStr(k.GetPubKey()) == "0355825cbaf4365a2f7015d9c9bae4ecaf9b57a05e063237256f1565b20104c183", "HexStr(k.GetPubKey()) " << HexStr(k.GetPubKey())); // - can't derive keys from pubkeys skp.nGenerated = 1; BOOST_CHECK(1 == skp.DeriveNextKey(k, nChild, false)); skp.nHGenerated = 1; BOOST_CHECK(1 == skp.DeriveNextKey(k, nChild, true)); CPubKey pk; sk.nGenerated = 1; BOOST_CHECK(0 == sk.DeriveNextKey(pk, nChild, false)); BOOST_CHECK_MESSAGE(nChild == 1, "nChild " << nChild); BOOST_CHECK_MESSAGE(HexStr(pk) == "0245a12d2ce075d947b6232b3e424ffa5d2208b6ff69800a1f2501ac6392499bf8", "HexStr(pk) " << HexStr(pk)); sk.nHGenerated = 2; BOOST_CHECK(0 == sk.DeriveNextKey(pk, nChild, true)); BOOST_CHECK_MESSAGE(nChild == 2147483650, "nChild " << nChild); BOOST_CHECK_MESSAGE(HexStr(pk) == "0355825cbaf4365a2f7015d9c9bae4ecaf9b57a05e063237256f1565b20104c183", "HexStr(pk) " << HexStr(pk)); skp.nGenerated = 2; BOOST_CHECK(0 == skp.DeriveNextKey(pk, nChild, false)); BOOST_CHECK_MESSAGE(nChild == 2, "nChild " << nChild); BOOST_CHECK_MESSAGE(HexStr(pk) == "02f430d7efc4d1ecbac888fb49446ec0b13ec4196512be93054a9b5b30df238910", "HexStr(pk) " << HexStr(pk)); // - can't derive hardened pubkeys from pubkeys skp.nHGenerated = 1; BOOST_CHECK(1 == skp.DeriveNextKey(pk, nChild, true)); // - CBitcoinAddress tests // CBitcoinAddress always deals in public keys - should never expose a secret in an address CExtKeyPair kp, kpT; CTxDestination dest; BOOST_CHECK(0 == eKey58.Set58("moipXY9njTPCnsVV8vPCLA1xKp2NXdRtPVyGABKcbshkKQUadnrk2XPccZcSDjefRX64mNjYpS33SAy97UHGWs9WoAufi9pdow9gsYMvVEcmgSk")); kp = eKey58.GetKey(); CBitcoinAddress addrB(kp); BOOST_CHECK(addrB.IsValid() == true); BOOST_CHECK(addr.Set(kp) == true); BOOST_CHECK(addr.IsValid() == true); BOOST_CHECK(addr.IsValid(CChainParams::EXT_SECRET_KEY) == false); BOOST_CHECK(addr.IsValid(CChainParams::EXT_PUBLIC_KEY) == true); BOOST_CHECK(addr.ToString() == "moipXY9njTPCnsVV8vPCLA1xKp2NXdRtPVyGABKcbshkKQUadnrk2XPccZcSDjefRX64mNjYpS33SAy97UHGWs9WoAufi9pdow9gsYMvVEcmgSk"); dest = addr.Get(); BOOST_CHECK(dest.type() == typeid(CExtKeyPair)); kpT = boost::get<CExtKeyPair>(dest); BOOST_CHECK(kpT == kp); // - switch to testnet BOOST_TEST_MESSAGE("Entering Testnet"); fTestNet = true; SelectParams(CChainParams::TESTNET); id = sk.GetID(); BOOST_CHECK(true == addr.Set(id, CChainParams::EXT_KEY_HASH) && addr.IsValid(CChainParams::EXT_KEY_HASH) && addr.GetKeyID(idTest, CChainParams::EXT_KEY_HASH)); BOOST_CHECK(id == idTest); BOOST_CHECK_MESSAGE(addr.ToString() == "XCUfUzXMYkXYvP9RVtdzibVVpMP2bhfWRQ", addr.ToString()); BOOST_CHECK(0 == eKey58.Set58("toiprRP1Lw6KTeYbRxdfjunVry7emPouVaqrz8KX3uEKhKeZvsQaxqZEEPpB1uC2T8oMTnZTHj1BQHwUm8Tx634CXb4GrSmbwekU7LVQqt76VSJ")); kp = eKey58.GetKey(); CBitcoinAddress addrC("toivSKjjCEpyZjWAeFqJRCtPPXwTSBLDzX327GfZy2u8Gp1gNiTrE8HRuVWBhQFk3kXQw6woCxxYC4yC3MwzQ67RQ4bSgjWTQ1UPkQinHXv9sV9"); BOOST_CHECK(addrC.IsValid() == true); BOOST_CHECK(addrC.IsValid(CChainParams::EXT_PUBLIC_KEY) == true); BOOST_CHECK(addr.Set(kp) == true); BOOST_CHECK(addr.IsValid() == true); BOOST_CHECK(addr.IsValid(CChainParams::EXT_SECRET_KEY) == false); BOOST_CHECK(addr.IsValid(CChainParams::EXT_PUBLIC_KEY) == true); BOOST_CHECK(addr.ToString() == "toiprRP1Lw6KTeYbRxdfjunVry7emPouVaqrz8KX3uEKhKeZvsQaxqZEEPpB1uC2T8oMTnZTHj1BQHwUm8Tx634CXb4GrSmbwekU7LVQqt76VSJ"); dest = addr.Get(); BOOST_CHECK(dest.type() == typeid(CExtKeyPair)); kpT = boost::get<CExtKeyPair>(dest); BOOST_CHECK(kpT == kp); // -return to mainnet fTestNet = false; SelectParams(CChainParams::MAIN); }
const CPubKey PrivateCoin::getPubKey() const { CKey key; key.SetPrivKey(privkey, true); return key.GetPubKey(); }
BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { LOCK(cs_main); // Cap last block file size, and mine new block in a new block file. CBlockIndex* const nullBlock = nullptr; CBlockIndex* oldTip = chainActive.Tip(); GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE; CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CBlockIndex* newTip = chainActive.Tip(); // Verify ScanForWalletTransactions picks up transactions in both the old // and new block files. { CWallet wallet; LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } // Prune the older block file. PruneOneBlockFile(oldTip->GetBlockPos().nFile); UnlinkPrunedFiles({oldTip->GetBlockPos().nFile}); // Verify ScanForWalletTransactions only picks transactions in the new block // file. { CWallet wallet; LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } // Verify importmulti RPC returns failure for a key whose creation time is // before the missing block, and success for a key whose creation time is // after. { CWallet wallet; vpwallets.insert(vpwallets.begin(), &wallet); UniValue keys; keys.setArray(); UniValue key; key.setObject(); key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(coinbaseKey.GetPubKey()))); key.pushKV("timestamp", 0); key.pushKV("internal", UniValue(true)); keys.push_back(key); key.clear(); key.setObject(); CKey futureKey; futureKey.MakeNewKey(true); key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(futureKey.GetPubKey()))); key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1); key.pushKV("internal", UniValue(true)); keys.push_back(key); JSONRPCRequest request; request.params.setArray(); request.params.push_back(keys); UniValue response = importmulti(request); BOOST_CHECK_EQUAL(response.write(), strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Rescan failed for key with creation " "timestamp %d. There was an error reading a block from time %d, which is after or within %d " "seconds of key creation, and could contain transactions pertaining to the key. As a result, " "transactions and coins using this key may not appear in the wallet. This error could be caused " "by pruning or data corruption (see bitcoind log for details) and could be dealt with by " "downloading and rescanning the relevant blocks (see -reindex and -rescan " "options).\"}},{\"success\":true}]", 0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW)); vpwallets.erase(vpwallets.begin()); } }
static void AddKey(CWallet& wallet, const CKey& key) { LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(key, key.GetPubKey()); }
bool CKeyStore::AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey()); }
bool CBasicKeyStore::AddKey(const CKey& key) { CRITICAL_BLOCK(cs_KeyStore) mapKeys[CBitcoinAddress(key.GetPubKey())] = key.GetSecret(); return true; }
double benchmark_large_tx() { // Number of inputs in the spending transaction that we will simulate const size_t NUM_INPUTS = 555; // Create priv/pub key CKey priv; priv.MakeNewKey(false); auto pub = priv.GetPubKey(); CBasicKeyStore tempKeystore; tempKeystore.AddKey(priv); // The "original" transaction that the spending transaction will spend // from. CMutableTransaction m_orig_tx; m_orig_tx.vout.resize(1); m_orig_tx.vout[0].nValue = 1000000; CScript prevPubKey = GetScriptForDestination(pub.GetID()); m_orig_tx.vout[0].scriptPubKey = prevPubKey; auto orig_tx = CTransaction(m_orig_tx); CMutableTransaction spending_tx; auto input_hash = orig_tx.GetHash(); // Add NUM_INPUTS inputs for (size_t i = 0; i < NUM_INPUTS; i++) { spending_tx.vin.emplace_back(input_hash, 0); } // Sign for all the inputs for (size_t i = 0; i < NUM_INPUTS; i++) { SignSignature(tempKeystore, prevPubKey, spending_tx, i, SIGHASH_ALL); } // Serialize: { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << spending_tx; //std::cout << "SIZE OF SPENDING TX: " << ss.size() << std::endl; auto error = MAX_TX_SIZE / 20; // 5% error assert(ss.size() < MAX_TX_SIZE + error); assert(ss.size() > MAX_TX_SIZE - error); } // Spending tx has all its inputs signed and does not need to be mutated anymore CTransaction final_spending_tx(spending_tx); // Benchmark signature verification costs: struct timeval tv_start; timer_start(tv_start); for (size_t i = 0; i < NUM_INPUTS; i++) { ScriptError serror = SCRIPT_ERR_OK; assert(VerifyScript(final_spending_tx.vin[i].scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&final_spending_tx, i), &serror)); } return timer_stop(tv_start); }
BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { LOCK(cs_main); // Cap last block file size, and mine new block in a new block file. CBlockIndex* oldTip = chainActive.Tip(); GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE; CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CBlockIndex* newTip = chainActive.Tip(); // Verify ScanForWalletTransactions picks up transactions in both the old // and new block files. { CWallet wallet; LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } // Prune the older block file. PruneOneBlockFile(oldTip->GetBlockPos().nFile); UnlinkPrunedFiles({oldTip->GetBlockPos().nFile}); // Verify ScanForWalletTransactions only picks transactions in the new block // file. { CWallet wallet; LOCK(wallet.cs_wallet); wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); BOOST_CHECK_EQUAL(newTip, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } // Verify importmulti RPC returns failure for a key whose creation time is // before the missing block, and success for a key whose creation time is // after. { CWallet wallet; CWallet *backup = ::pwalletMain; ::pwalletMain = &wallet; UniValue keys; keys.setArray(); UniValue key; key.setObject(); key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(coinbaseKey.GetPubKey()))); key.pushKV("timestamp", 0); key.pushKV("internal", UniValue(true)); keys.push_back(key); key.clear(); key.setObject(); CKey futureKey; futureKey.MakeNewKey(true); key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(futureKey.GetPubKey()))); key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW); key.pushKV("internal", UniValue(true)); keys.push_back(key); JSONRPCRequest request; request.params.setArray(); request.params.push_back(keys); UniValue response = importmulti(request); BOOST_CHECK_EQUAL(response.write(), strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Failed to rescan before time %d, transactions may be missing.\"}},{\"success\":true}]", newTip->GetBlockTimeMax())); ::pwalletMain = backup; } // Verify ScanForWalletTransactions does not return null when the scan is // elided due to the nTimeFirstKey optimization. { CWallet wallet; { LOCK(wallet.cs_wallet); wallet.UpdateTimeFirstKey(newTip->GetBlockTime() + 7200 + 1); } BOOST_CHECK_EQUAL(newTip, wallet.ScanForWalletTransactions(newTip)); } }
int main(int argc, char* argv[]) { if (argc != 7){ printf("Hashchecker needs to know the cryptographic details of your wallet!\nUsage: hashchecker pw_to_permute iterations salt crypted_key public_key crypted_secret\n"); return 1; } CCrypter crypter; CKeyingMaterial vMasterKey; // Try any password as input SecureString attempt = argv[1]; const unsigned int nDeriveIterations = atoi(argv[2]);//29731; const vector<unsigned char> chSalt = Convert(argv[3]);//"b29a2e128e8e0a2f");//argv[1]; const vector<unsigned char> vchCryptedKey = Convert(argv[4]);//"982a07407ccb8d70514e7b7ccae4b53d68318ec41fd2bf99bf9dbcafd2f150a92c6eb8f9ea743b782fc5b85403421c1d");//argv[2]; const vector<unsigned char> vchPubKey = Convert(argv[5]);//"03fefd771544971f3ab95b041bbce02cc799a335d0d12c3bcd46c7c61a4e3ba897"); const vector<unsigned char> vchCryptedSecret = Convert(argv[6]);//"17169083a74b07ff3497027af7423b9aec1593c90f15a57f52c368593947c85e37b03430840ad48ef409e97ba5a4cdeb"); double count = Factorial(attempt.size()); bool found = false; for (int i = 0; i <= count; i++) { if (i > 0) {//test the word as typed in on first iteration permutation(i-1, attempt); } const SecureString strWalletPassphrase = attempt; cout << i << "-" << strWalletPassphrase <<"\n"; if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, chSalt, nDeriveIterations, 0)) { cout << i << " " << strWalletPassphrase <<"\n"; continue; } if (!crypter.Decrypt(vchCryptedKey, vMasterKey)) { cout << i << " " << strWalletPassphrase <<"\n"; continue; } CSecret vchSecret; if(!DecryptSecret(vMasterKey, vchCryptedSecret, Hash(vchPubKey.begin(), vchPubKey.end()), vchSecret)) { cout << "** didn't decrypt **" <<"\n"; continue; } if (vchSecret.size() != 32) { cout << "** wrong size secret **" <<"\n"; continue; } CKey key; key.SetPubKey(vchPubKey); key.SetSecret(vchSecret); if (key.GetPubKey() == vchPubKey) { cout<<"Found one: "<<strWalletPassphrase<<"\n"; found = true; break; } // else // cout << "** didn't get the pubkey back **\n"; } if (found) cout << "Found it! Congratulations\n"; return 0; }
void RunSerialiseTests() { int64_t nTest; int64_t nTest0 = 0l; int64_t nTest4 = 1432035740l; int64_t nTest4_1 = 2189410940l; // 2039 int64_t nTest5 = 4294967298l; // 2106 int64_t nTest8 = -3l; BOOST_CHECK(0 == GetNumBytesReqForInt(nTest0)); BOOST_CHECK(4 == GetNumBytesReqForInt(nTest4)); BOOST_CHECK(4 == GetNumBytesReqForInt(nTest4_1)); // expect 4, no sign bit BOOST_CHECK(5 == GetNumBytesReqForInt(nTest5)); BOOST_CHECK(8 == GetNumBytesReqForInt(nTest8)); //BOOST_MESSAGE(GetNumBytesReqForInt(nTest5)); std::vector<uint8_t> v; SetCompressedInt64(v, nTest0); GetCompressedInt64(v, (uint64_t&)nTest); BOOST_CHECK(nTest0 == nTest); SetCompressedInt64(v, nTest5); GetCompressedInt64(v, (uint64_t&)nTest); BOOST_CHECK(nTest5 == nTest); SetCompressedInt64(v, nTest8); GetCompressedInt64(v, (uint64_t&)nTest); BOOST_CHECK(nTest8 == nTest); CStoredExtKey sk, sk_; CStoredExtKey skInvalid, skInvalid_; CExtKey58 eKey58; BOOST_CHECK(0 == eKey58.Set58("sdcvmnKmFxG9k6UnN3wyLpTv83G1wgYEz1m21rZTUUimoDrYYMrZXUycudse21EZJTmkBBPN3k6Qhfzx5td8xzd9W893YhNozA3bZW3yVLVdrZU2")); sk.kp = eKey58.GetKey(); sk.sLabel = "sk label"; sk.nGenerated = 5; sk.nHGenerated = 6; sk.mapValue[EKVT_CREATED_AT] = SetCompressedInt64(v, nTest8); eKey58.SetKey(sk.kp, CChainParams::EXT_PUBLIC_KEY); BOOST_CHECK(eKey58.ToString() == "sdcpmphCJNSUos9rNqn6FNi3ztvMW1wft1PVbifvBrwhm6JnhD9yk8rSNFTGfozGbmBsr8vZv9mGYSTfmEMpbfTTMb8TQfj7JRABmvBFKgA2xG8J"); eKey58.SetKeyV(sk.kp); BOOST_CHECK(eKey58.ToString() == "sdcvmnKmFxG9k6UnN3wyLpTv83G1wgYEz1m21rZTUUimoDrYYMrZXUycudse21EZJTmkBBPN3k6Qhfzx5td8xzd9W893YhNozA3bZW3yVLVdrZU2"); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << sk << skInvalid; ss >> sk_; ss >> skInvalid_; BOOST_CHECK(sk.kp == sk_.kp); BOOST_CHECK(1 == sk_.kp.IsValidV()); BOOST_CHECK(1 == sk_.kp.IsValidP()); BOOST_CHECK(sk.sLabel == sk_.sLabel); BOOST_CHECK(sk.nGenerated == sk_.nGenerated); BOOST_CHECK(sk.nHGenerated == sk_.nHGenerated); BOOST_CHECK(nTest8 == GetCompressedInt64(sk_.mapValue[EKVT_CREATED_AT], (uint64_t&)nTest)); BOOST_CHECK(0 == skInvalid.kp.IsValidV()); BOOST_CHECK(0 == skInvalid.kp.IsValidP()); // path std::vector<uint8_t> vPath; PushUInt32(vPath, 1); PushUInt32(vPath, 3); PushUInt32(vPath, 2); PushUInt32(vPath, 4294967295); std::string sPath; BOOST_CHECK(0 == PathToString(vPath, sPath, 'h')); BOOST_CHECK(sPath == "m/1/3/2/2147483647h"); vPath.resize(0); PushUInt32(vPath, 1); PushUInt32(vPath, 4294967294); PushUInt32(vPath, 30); BOOST_CHECK(0 == PathToString(vPath, sPath)); BOOST_CHECK(sPath == "m/1/2147483646'/30"); // id CBitcoinAddress addr; CKeyID id = sk.GetID(); CKeyID idTest; BOOST_CHECK(true == addr.Set(id, CChainParams::EXT_KEY_HASH) && addr.IsValid(CChainParams::EXT_KEY_HASH) && addr.GetKeyID(idTest, CChainParams::EXT_KEY_HASH)); BOOST_CHECK(id == idTest); BOOST_CHECK_MESSAGE(addr.ToString() == "x9S4Xj1DZwFsdFno1uHknNNGqdMWgXdhX6", addr.ToString()); // - test DeriveNextKey CExtKey ev; CExtPubKey ep; uint32_t nChild=0; sk.nGenerated = 0; sk.nHGenerated = 0; BOOST_CHECK(0 == sk.DeriveNextKey(ev, nChild)); BOOST_CHECK_MESSAGE(1 == sk.nGenerated, "nGenerated " << sk.nGenerated); sk.nGenerated = 0; BOOST_CHECK(0 == sk.DeriveNextKey(ep, nChild)); BOOST_CHECK(ep.pubkey == ev.key.GetPubKey()); id = ev.key.GetPubKey().GetID(); addr.Set(id, CChainParams::EXT_KEY_HASH); BOOST_CHECK_MESSAGE(addr.ToString() == "xS8vxP6PVm3ycqm4NqvUkhiDWqeBhhekzn", addr.ToString()); sk.nGenerated = 1; BOOST_CHECK(0 == sk.DeriveNextKey(ev, nChild)); id = ev.key.GetPubKey().GetID(); addr.Set(id, CChainParams::EXT_KEY_HASH); BOOST_CHECK_MESSAGE(addr.ToString() == "xRfAtU1u43VJBTt4agxpnPvXjf28hSmwrL", addr.ToString()); sk.nHGenerated = 0; BOOST_CHECK(0 == sk.DeriveNextKey(ev, nChild, true)); id = ev.key.GetPubKey().GetID(); addr.Set(id, CChainParams::EXT_KEY_HASH); BOOST_CHECK_MESSAGE(addr.ToString() == "xEcqVH3fRnNZabMHVctAeScJY9ySkg6BSF", addr.ToString()); BOOST_CHECK_MESSAGE(1 == sk.nHGenerated, "nHGenerated " << sk.nHGenerated); sk.nHGenerated = 1; BOOST_CHECK(0 == sk.DeriveNextKey(ev, nChild, true)); id = ev.key.GetPubKey().GetID(); addr.Set(id, CChainParams::EXT_KEY_HASH); BOOST_CHECK_MESSAGE(addr.ToString() == "xRfBk2tuann5qTVKyW2HmA9CJZBAE1sRvJ", addr.ToString()); BOOST_CHECK_MESSAGE(2 == sk.nHGenerated, "nHGenerated " << sk.nHGenerated); sk.nHGenerated = 1; BOOST_CHECK(0 == sk.DeriveNextKey(ep, nChild, true)); id = ev.key.GetPubKey().GetID(); addr.Set(id, CChainParams::EXT_KEY_HASH); BOOST_CHECK_MESSAGE(addr.ToString() == "xRfBk2tuann5qTVKyW2HmA9CJZBAE1sRvJ", addr.ToString()); BOOST_CHECK(ep.pubkey == ev.key.GetPubKey()); CStoredExtKey skp = sk; skp.kp = skp.kp.Neutered(); CKey k; sk.nGenerated = 1; BOOST_CHECK(0 == sk.DeriveNextKey(k, nChild, false)); BOOST_CHECK_MESSAGE(nChild == 1, "nChild " << nChild); BOOST_CHECK_MESSAGE(HexStr(k.GetPubKey()) == "0245a12d2ce075d947b6232b3e424ffa5d2208b6ff69800a1f2501ac6392499bf8", "HexStr(k.GetPubKey()) " << HexStr(k.GetPubKey())); sk.nGenerated = 2; BOOST_CHECK(0 == sk.DeriveNextKey(k, nChild, false)); BOOST_CHECK_MESSAGE(nChild == 2, "nChild " << nChild); BOOST_CHECK_MESSAGE(HexStr(k.GetPubKey()) == "02f430d7efc4d1ecbac888fb49446ec0b13ec4196512be93054a9b5b30df238910", "HexStr(k.GetPubKey()) " << HexStr(k.GetPubKey())); sk.nHGenerated = 2; BOOST_CHECK(0 == sk.DeriveNextKey(k, nChild, true)); BOOST_CHECK_MESSAGE(nChild == 2147483650, "nChild " << nChild); BOOST_CHECK_MESSAGE(HexStr(k.GetPubKey()) == "0355825cbaf4365a2f7015d9c9bae4ecaf9b57a05e063237256f1565b20104c183", "HexStr(k.GetPubKey()) " << HexStr(k.GetPubKey())); // - can't derive keys from pubkeys skp.nGenerated = 1; BOOST_CHECK(1 == skp.DeriveNextKey(k, nChild, false)); skp.nHGenerated = 1; BOOST_CHECK(1 == skp.DeriveNextKey(k, nChild, true)); CPubKey pk; sk.nGenerated = 1; BOOST_CHECK(0 == sk.DeriveNextKey(pk, nChild, false)); BOOST_CHECK_MESSAGE(nChild == 1, "nChild " << nChild); BOOST_CHECK_MESSAGE(HexStr(pk) == "0245a12d2ce075d947b6232b3e424ffa5d2208b6ff69800a1f2501ac6392499bf8", "HexStr(pk) " << HexStr(pk)); sk.nHGenerated = 2; BOOST_CHECK(0 == sk.DeriveNextKey(pk, nChild, true)); BOOST_CHECK_MESSAGE(nChild == 2147483650, "nChild " << nChild); BOOST_CHECK_MESSAGE(HexStr(pk) == "0355825cbaf4365a2f7015d9c9bae4ecaf9b57a05e063237256f1565b20104c183", "HexStr(pk) " << HexStr(pk)); skp.nGenerated = 2; BOOST_CHECK(0 == skp.DeriveNextKey(pk, nChild, false)); BOOST_CHECK_MESSAGE(nChild == 2, "nChild " << nChild); BOOST_CHECK_MESSAGE(HexStr(pk) == "02f430d7efc4d1ecbac888fb49446ec0b13ec4196512be93054a9b5b30df238910", "HexStr(pk) " << HexStr(pk)); // - can't derive hardened pubkeys from pubkeys skp.nHGenerated = 1; BOOST_CHECK(1 == skp.DeriveNextKey(pk, nChild, true)); // - CBitcoinAddress tests // CBitcoinAddress always deals in public keys - should never expose a secret in an address CExtKeyPair kp, kpT; CTxDestination dest; BOOST_CHECK(0 == eKey58.Set58("sdcpmphCJNSUos9rNqn6FNi3ztvMW1wft1PVbifvBrwhm6JnhD9yk8rSNFTGfozGbmBsr8vZv9mGYSTfmEMpbfTTMb8TQfj7JRABmvBFKgA2xG8J")); kp = eKey58.GetKey(); CBitcoinAddress addrB(kp); BOOST_CHECK(addrB.IsValid() == true); BOOST_CHECK(addr.Set(kp) == true); BOOST_CHECK(addr.IsValid() == true); BOOST_CHECK(addr.IsValid(CChainParams::EXT_SECRET_KEY) == false); BOOST_CHECK(addr.IsValid(CChainParams::EXT_PUBLIC_KEY) == true); BOOST_CHECK(addr.ToString() == "sdcpmphCJNSUos9rNqn6FNi3ztvMW1wft1PVbifvBrwhm6JnhD9yk8rSNFTGfozGbmBsr8vZv9mGYSTfmEMpbfTTMb8TQfj7JRABmvBFKgA2xG8J"); dest = addr.Get(); BOOST_CHECK(dest.type() == typeid(CExtKeyPair)); kpT = boost::get<CExtKeyPair>(dest); BOOST_CHECK(kpT == kp); // - switch to testnet BOOST_MESSAGE("Entering Testnet"); fTestNet = true; SelectParams(CChainParams::TESTNET); id = sk.GetID(); BOOST_CHECK(true == addr.Set(id, CChainParams::EXT_KEY_HASH) && addr.IsValid(CChainParams::EXT_KEY_HASH) && addr.GetKeyID(idTest, CChainParams::EXT_KEY_HASH)); BOOST_CHECK(id == idTest); BOOST_CHECK_MESSAGE(addr.ToString() == "XCUfUzXMYkXYvP9RVtdzibVVpMP2bhfWRQ", addr.ToString()); BOOST_CHECK(0 == eKey58.Set58("SDCPTTad968GGU17vZThYUhb4WKgaLQ22ffBVjTnZGGCAcU1vfVFcJEroz4QeZjFZLs5a1dkpZxsKfB2Adnun1axAGuzrfBweXWSxuXu2Wj3AaGp")); kp = eKey58.GetKey(); CBitcoinAddress addrC("SDCVTZWtnQrSZ1LBHScXxi5amgw4Q1zGaRuAy5S12NAttbx3Bmsm1jDYh1B5P5qTPZaWpUZZ5mmubGTYjXPB1cQ9btJmhDoBLHZnwAGUBVH42gB3"); BOOST_CHECK(addrC.IsValid() == true); BOOST_CHECK(addrC.IsValid(CChainParams::EXT_PUBLIC_KEY) == true); BOOST_CHECK(addr.Set(kp) == true); BOOST_CHECK(addr.IsValid() == true); BOOST_CHECK(addr.IsValid(CChainParams::EXT_SECRET_KEY) == false); BOOST_CHECK(addr.IsValid(CChainParams::EXT_PUBLIC_KEY) == true); BOOST_CHECK(addr.ToString() == "SDCPTTad968GGU17vZThYUhb4WKgaLQ22ffBVjTnZGGCAcU1vfVFcJEroz4QeZjFZLs5a1dkpZxsKfB2Adnun1axAGuzrfBweXWSxuXu2Wj3AaGp"); dest = addr.Get(); BOOST_CHECK(dest.type() == typeid(CExtKeyPair)); kpT = boost::get<CExtKeyPair>(dest); BOOST_CHECK(kpT == kp); // -return to mainnet fTestNet = false; SelectParams(CChainParams::MAIN); }
bool HaveKey(const CKeyStore& store, const CKey& key) { CKey key2; key2.Set(key.begin(), key.end(), !key.IsCompressed()); return store.HaveKey(key.GetPubKey().GetID()) || store.HaveKey(key2.GetPubKey().GetID()); }
void PaperWalletDialog::on_getNewAddress_clicked() { // Create a new private key CKey privKey; privKey.MakeNewKey(true); // Derive the public key CPubKey pubkey = privKey.GetPubKey(); // Derive the public key hash CBitcoinAddress pubkeyhash; pubkeyhash.Set(pubkey.GetID()); // Create String versions of each string myPrivKey = CBitcoinSecret(privKey).ToString(); string myPubKey = HexStr(pubkey.begin(), pubkey.end()); string myAddress = pubkeyhash.ToString(); #ifdef USE_QRCODE // Generate the address QR code QRcode *code = QRcode_encodeString(myAddress.c_str(), 0, QR_ECLEVEL_M, QR_MODE_8, 1); if (!code) { ui->addressQRCode->setText(tr("Error encoding Address into QR Code.")); return; } QImage publicKeyImage = QImage(code->width, code->width, QImage::Format_ARGB32); publicKeyImage.fill(0x000000); unsigned char *p = code->data; for (int y = 0; y < code->width; y++) { for (int x = 0; x < code->width; x++) { publicKeyImage.setPixel(x, y, ((*p & 1) ? 0xff000000 : 0x0)); p++; } } QRcode_free(code); // Generate the private key QR code code = QRcode_encodeString(myPrivKey.c_str(), 0, QR_ECLEVEL_M, QR_MODE_8, 1); if (!code) { ui->privateKeyQRCode->setText(tr("Error encoding private key into QR Code.")); return; } QImage privateKeyImage = QImage(code->width, code->width, QImage::Format_ARGB32); privateKeyImage.fill(0x000000); p = code->data; for (int y = 0; y < code->width; y++) { for (int x = 0; x < code->width; x++) { privateKeyImage.setPixel(x, y, ((*p & 1) ? 0xff000000 : 0x0)); p++; } } QRcode_free(code); // Populate the QR Codes ui->addressQRCode->setPixmap(QPixmap::fromImage(publicKeyImage).scaled(ui->addressQRCode->width(), ui->addressQRCode->height())); ui->privateKeyQRCode->setPixmap(QPixmap::fromImage(privateKeyImage).scaled(ui->privateKeyQRCode->width(), ui->privateKeyQRCode->height())); #endif // Populate the Texts ui->addressText->setText(myAddress.c_str()); ui->privateKeyText->setText(tr(myPrivKey.c_str())); ui->publicKey->setHtml(myPubKey.c_str()); // Update the fonts to fit the height of the wallet. // This should only really trigger the first time since the font size persists. double paperHeight = (double) ui->paperTemplate->height(); double maxTextWidth = paperHeight * 0.99; double minTextWidth = paperHeight * 0.95; int pixelSizeStep = 1; int addressTextLength = ui->addressText->fontMetrics().boundingRect(ui->addressText->text()).width(); QFont font = ui->addressText->font(); for(int i = 0; i < PAPER_WALLET_READJUST_LIMIT; i++) { if ( addressTextLength < minTextWidth) { font.setPixelSize(font.pixelSize() + pixelSizeStep); ui->addressText->setFont(font); addressTextLength = ui->addressText->fontMetrics().boundingRect(ui->addressText->text()).width(); } else { break; } } if ( addressTextLength > maxTextWidth ) { font.setPixelSize(font.pixelSize() - pixelSizeStep); ui->addressText->setFont(font); addressTextLength = ui->addressText->fontMetrics().boundingRect(ui->addressText->text()).width(); } int privateKeyTextLength = ui->privateKeyText->fontMetrics().boundingRect(ui->privateKeyText->text()).width(); font = ui->privateKeyText->font(); for(int i = 0; i < PAPER_WALLET_READJUST_LIMIT; i++) { if ( privateKeyTextLength < minTextWidth) { font.setPixelSize(font.pixelSize() + pixelSizeStep); ui->privateKeyText->setFont(font); privateKeyTextLength = ui->privateKeyText->fontMetrics().boundingRect(ui->privateKeyText->text()).width(); } else { break; } } if ( privateKeyTextLength > maxTextWidth ) { font.setPixelSize(font.pixelSize() - pixelSizeStep); ui->privateKeyText->setFont(font); privateKeyTextLength = ui->privateKeyText->fontMetrics().boundingRect(ui->privateKeyText->text()).width(); } }
bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) { if (fDebug) LogPrintf("CCryptoKeyStore::Unlock()\n"); { LOCK(cs_KeyStore); if (!SetCrypted()) return false; int nUnlocked = 0; CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); for (; mi != mapCryptedKeys.end(); ++mi) { const CPubKey &vchPubKey = (*mi).second.first; const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; CSecret vchSecret; if (vchCryptedSecret.size() < 1) // key was recieved from stealth/anon txn with wallet locked, will be expanded after this { if (fDebug) LogPrintf("Skipping unexpanded key %s.\n", vchPubKey.GetHash().ToString().c_str()); continue; }; if (!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) { LogPrintf("DecryptSecret() failed.\n"); return false; }; if (vchSecret.size() != 32) return false; CKey key; key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); if (key.GetPubKey() != vchPubKey) { LogPrintf("Unlock failed: PubKey mismatch %s.\n", vchPubKey.GetHash().ToString().c_str()); return false; }; nUnlocked++; break; }; if (nUnlocked < 1) // at least 1 key must pass the test { if (mapCryptedKeys.size() > 0) { LogPrintf("Unlock failed: No keys unlocked.\n"); return false; }; }; vMasterKey = vMasterKeyIn; } NotifyStatusChanged(this); return true; }
int CWalletDB::LoadWallet(CWallet* pwallet) { pwallet->vchDefaultKey = CPubKey(); int nFileVersion = 0; vector<uint256> vWalletUpgrade; bool fIsEncrypted = false; //// todo: shouldn't we catch exceptions and try to recover and continue? { LOCK(pwallet->cs_wallet); int nMinVersion = 0; if (Read((string)"minversion", nMinVersion)) { if (nMinVersion > CLIENT_VERSION) return DB_TOO_NEW; pwallet->LoadMinVersion(nMinVersion); } // Get cursor Dbc* pcursor = GetCursor(); if (!pcursor) { printf("Error getting wallet database cursor\n"); return DB_CORRUPT; } loop { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue); if (ret == DB_NOTFOUND) break; else if (ret != 0) { printf("Error reading next record from wallet database\n"); return DB_CORRUPT; } // Unserialize // Taking advantage of the fact that pair serialization // is just the two items serialized one after the other string strType; ssKey >> strType; if (strType == "name") { string strAddress; ssKey >> strAddress; ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()]; } else if (strType == "key" || strType == "wkey") { vector<unsigned char> vchPubKey; ssKey >> vchPubKey; CKey key; if (strType == "key") { CPrivKey pkey; ssValue >> pkey; key.SetPubKey(vchPubKey); key.SetPrivKey(pkey); if (key.GetPubKey() != vchPubKey) { printf("Error reading wallet database: CPrivKey pubkey inconsistency\n"); return DB_CORRUPT; } if (!key.IsValid()) { printf("Error reading wallet database: invalid CPrivKey\n"); return DB_CORRUPT; } }
bool CreateCoinStake( CBlock &blocknew, CKey &key, vector<const CWalletTx*> &StakeInputs, uint64_t &CoinAge, CWallet &wallet, CBlockIndex* pindexPrev ) { int64_t CoinWeight; CBigNum StakeKernelHash; CTxDB txdb("r"); int64_t StakeWeightSum = 0; double StakeValueSum = 0; int64_t StakeWeightMin=MAX_MONEY; int64_t StakeWeightMax=0; uint64_t StakeCoinAgeSum=0; double StakeDiffSum = 0; double StakeDiffMax = 0; CTransaction &txnew = blocknew.vtx[1]; // second tx is coinstake //initialize the transaction txnew.nTime = blocknew.nTime & (~STAKE_TIMESTAMP_MASK); txnew.vin.clear(); txnew.vout.clear(); // Choose coins to use set <pair <const CWalletTx*,unsigned int> > CoinsToStake; int64_t BalanceToStake = wallet.GetBalance(); int64_t nValueIn = 0; //Request all the coins here, check reserve later if ( BalanceToStake<=0 || !wallet.SelectCoinsForStaking(BalanceToStake*2, txnew.nTime, CoinsToStake, nValueIn) ) { LOCK(MinerStatus.lock); MinerStatus.ReasonNotStaking+=_("No coins; "); if (fDebug) LogPrintf("CreateCoinStake: %s",MinerStatus.ReasonNotStaking); return false; } BalanceToStake -= nReserveBalance; if(fDebug2) LogPrintf("\nCreateCoinStake: Staking nTime/16= %d Bits= %u", txnew.nTime/16,blocknew.nBits); for(const auto& pcoin : CoinsToStake) { const CTransaction &CoinTx =*pcoin.first; //transaction that produced this coin unsigned int CoinTxN =pcoin.second; //index of this coin inside it CTxIndex txindex; { LOCK2(cs_main, wallet.cs_wallet); if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) continue; //error? } CBlock CoinBlock; //Block which contains CoinTx { LOCK2(cs_main, wallet.cs_wallet); if (!CoinBlock.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) continue; } // only count coins meeting min age requirement if (CoinBlock.GetBlockTime() + nStakeMinAge > txnew.nTime) continue; if (CoinTx.vout[CoinTxN].nValue > BalanceToStake) continue; { int64_t nStakeValue= CoinTx.vout[CoinTxN].nValue; StakeValueSum += nStakeValue /(double)COIN; //crazy formula... // todo: clean this // todo reuse calculated value for interst CBigNum bn = CBigNum(nStakeValue) * (blocknew.nTime-CoinTx.nTime) / CENT; bn = bn * CENT / COIN / (24 * 60 * 60); StakeCoinAgeSum += bn.getuint64(); } if(blocknew.nVersion==7) { NetworkTimer(); CoinWeight = CalculateStakeWeightV3(CoinTx,CoinTxN,GlobalCPUMiningCPID); StakeKernelHash= CalculateStakeHashV3(CoinBlock,CoinTx,CoinTxN,txnew.nTime,GlobalCPUMiningCPID,mdPORNonce); } else { uint64_t StakeModifier = 0; if(!FindStakeModifierRev(StakeModifier,pindexPrev)) continue; CoinWeight = CalculateStakeWeightV8(CoinTx,CoinTxN,GlobalCPUMiningCPID); StakeKernelHash= CalculateStakeHashV8(CoinBlock,CoinTx,CoinTxN,txnew.nTime,StakeModifier,GlobalCPUMiningCPID); } CBigNum StakeTarget; StakeTarget.SetCompact(blocknew.nBits); StakeTarget*=CoinWeight; StakeWeightSum += CoinWeight; StakeWeightMin=std::min(StakeWeightMin,CoinWeight); StakeWeightMax=std::max(StakeWeightMax,CoinWeight); double StakeKernelDiff = GetBlockDifficulty(StakeKernelHash.GetCompact())*CoinWeight; StakeDiffSum += StakeKernelDiff; StakeDiffMax = std::max(StakeDiffMax,StakeKernelDiff); if (fDebug2) { int64_t RSA_WEIGHT = GetRSAWeightByBlock(GlobalCPUMiningCPID); LogPrintf( "CreateCoinStake: V%d Time %.f, Por_Nonce %.f, Bits %jd, Weight %jd\n" " RSA_WEIGHT %.f\n" " Stk %72s\n" " Trg %72s\n" " Diff %0.7f of %0.7f\n", blocknew.nVersion, (double)txnew.nTime, mdPORNonce, (intmax_t)blocknew.nBits,(intmax_t)CoinWeight, (double)RSA_WEIGHT, StakeKernelHash.GetHex().c_str(), StakeTarget.GetHex().c_str(), StakeKernelDiff, GetBlockDifficulty(blocknew.nBits) ); } if( StakeKernelHash <= StakeTarget ) { // Found a kernel LogPrintf("\nCreateCoinStake: Found Kernel;\n"); blocknew.nNonce= mdPORNonce; vector<valtype> vSolutions; txnouttype whichType; CScript scriptPubKeyOut; CScript scriptPubKeyKernel; scriptPubKeyKernel = CoinTx.vout[CoinTxN].scriptPubKey; if (!Solver(scriptPubKeyKernel, whichType, vSolutions)) { LogPrintf("CreateCoinStake: failed to parse kernel\n"); break; } if (whichType == TX_PUBKEYHASH) // pay to address type { // convert to pay to public key type if (!wallet.GetKey(uint160(vSolutions[0]), key)) { LogPrintf("CreateCoinStake: failed to get key for kernel type=%d\n", whichType); break; // unable to find corresponding public key } scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG; } else if (whichType == TX_PUBKEY) // pay to public key type { valtype& vchPubKey = vSolutions[0]; if (!wallet.GetKey(Hash160(vchPubKey), key) || key.GetPubKey() != vchPubKey) { LogPrintf("CreateCoinStake: failed to get key for kernel type=%d\n", whichType); break; // unable to find corresponding public key } scriptPubKeyOut = scriptPubKeyKernel; } else { LogPrintf("CreateCoinStake: no support for kernel type=%d\n", whichType); break; // only support pay to public key and pay to address } txnew.vin.push_back(CTxIn(CoinTx.GetHash(), CoinTxN)); StakeInputs.push_back(pcoin.first); if (!txnew.GetCoinAge(txdb, CoinAge)) return error("CreateCoinStake: failed to calculate coin age"); int64_t nCredit = CoinTx.vout[CoinTxN].nValue; txnew.vout.push_back(CTxOut(0, CScript())); // First Must be empty txnew.vout.push_back(CTxOut(nCredit, scriptPubKeyOut)); //txnew.vout.push_back(CTxOut(0, scriptPubKeyOut)); LogPrintf("CreateCoinStake: added kernel type=%d credit=%f\n", whichType,CoinToDouble(nCredit)); LOCK(MinerStatus.lock); MinerStatus.KernelsFound++; MinerStatus.KernelDiffMax = 0; MinerStatus.KernelDiffSum = StakeDiffSum; return true; } } LOCK(MinerStatus.lock); MinerStatus.WeightSum = StakeWeightSum; MinerStatus.ValueSum = StakeValueSum; MinerStatus.WeightMin=StakeWeightMin; MinerStatus.WeightMax=StakeWeightMax; MinerStatus.CoinAgeSum=StakeCoinAgeSum; MinerStatus.KernelDiffMax = std::max(MinerStatus.KernelDiffMax,StakeDiffMax); MinerStatus.KernelDiffSum = StakeDiffSum; MinerStatus.nLastCoinStakeSearchInterval= txnew.nTime; return false; }
// Notes: // 1. #1159 Currently there is no limit set on the number of joinsplits, so size of tx could be invalid. // 2. #1360 Note selection is not optimal // 3. #1277 Spendable notes are not locked, so an operation running in parallel could also try to use them bool AsyncRPCOperation_sendmany::main_impl() { assert(isfromtaddr_ != isfromzaddr_); bool isSingleZaddrOutput = (t_outputs_.size()==0 && z_outputs_.size()==1); bool isMultipleZaddrOutput = (t_outputs_.size()==0 && z_outputs_.size()>=1); bool isPureTaddrOnlyTx = (isfromtaddr_ && z_outputs_.size() == 0); CAmount minersFee = fee_; // When spending coinbase utxos, you can only specify a single zaddr as the change must go somewhere // and if there are multiple zaddrs, we don't know where to send it. if (isfromtaddr_) { if (isSingleZaddrOutput) { bool b = find_utxos(true); if (!b) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds, no UTXOs found for taddr from address."); } } else { bool b = find_utxos(false); if (!b) { if (isMultipleZaddrOutput) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any non-coinbase UTXOs to spend. Coinbase UTXOs can only be sent to a single zaddr recipient."); } else { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any non-coinbase UTXOs to spend."); } } } } if (isfromzaddr_ && !find_unspent_notes()) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds, no unspent notes found for zaddr from address."); } CAmount t_inputs_total = 0; for (SendManyInputUTXO & t : t_inputs_) { t_inputs_total += std::get<2>(t); } CAmount z_inputs_total = 0; for (SendManyInputJSOP & t : z_inputs_) { z_inputs_total += std::get<2>(t); } CAmount t_outputs_total = 0; for (SendManyRecipient & t : t_outputs_) { t_outputs_total += std::get<1>(t); } CAmount z_outputs_total = 0; for (SendManyRecipient & t : z_outputs_) { z_outputs_total += std::get<1>(t); } CAmount sendAmount = z_outputs_total + t_outputs_total; CAmount targetAmount = sendAmount + minersFee; assert(!isfromtaddr_ || z_inputs_total == 0); assert(!isfromzaddr_ || t_inputs_total == 0); if (isfromtaddr_ && (t_inputs_total < targetAmount)) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strprintf("Insufficient transparent funds, have %s, need %s", FormatMoney(t_inputs_total), FormatMoney(targetAmount))); } if (isfromzaddr_ && (z_inputs_total < targetAmount)) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strprintf("Insufficient protected funds, have %s, need %s", FormatMoney(z_inputs_total), FormatMoney(targetAmount))); } // If from address is a taddr, select UTXOs to spend CAmount selectedUTXOAmount = 0; bool selectedUTXOCoinbase = false; if (isfromtaddr_) { // Get dust threshold CKey secret; secret.MakeNewKey(true); CScript scriptPubKey = GetScriptForDestination(secret.GetPubKey().GetID()); CTxOut out(CAmount(1), scriptPubKey); CAmount dustThreshold = out.GetDustThreshold(minRelayTxFee); CAmount dustChange = -1; std::vector<SendManyInputUTXO> selectedTInputs; for (SendManyInputUTXO & t : t_inputs_) { bool b = std::get<3>(t); if (b) { selectedUTXOCoinbase = true; } selectedUTXOAmount += std::get<2>(t); selectedTInputs.push_back(t); if (selectedUTXOAmount >= targetAmount) { // Select another utxo if there is change less than the dust threshold. dustChange = selectedUTXOAmount - targetAmount; if (dustChange == 0 || dustChange >= dustThreshold) { break; } } } // If there is transparent change, is it valid or is it dust? if (dustChange < dustThreshold && dustChange != 0) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strprintf("Insufficient transparent funds, have %s, need %s more to avoid creating invalid change output %s (dust threshold is %s)", FormatMoney(t_inputs_total), FormatMoney(dustThreshold - dustChange), FormatMoney(dustChange), FormatMoney(dustThreshold))); } t_inputs_ = selectedTInputs; t_inputs_total = selectedUTXOAmount; // Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects size_t limit = (size_t)GetArg("-mempooltxinputlimit", 0); { LOCK(cs_main); if (NetworkUpgradeActive(chainActive.Height() + 1, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) { limit = 0; } } if (limit > 0) { size_t n = t_inputs_.size(); if (n > limit) { throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Too many transparent inputs %zu > limit %zu", n, limit)); } } // update the transaction with these inputs CMutableTransaction rawTx(tx_); for (SendManyInputUTXO & t : t_inputs_) { uint256 txid = std::get<0>(t); int vout = std::get<1>(t); CAmount amount = std::get<2>(t); CTxIn in(COutPoint(txid, vout)); rawTx.vin.push_back(in); } tx_ = CTransaction(rawTx); } LogPrint((isfromtaddr_) ? "zrpc" : "zrpcunsafe", "%s: spending %s to send %s with fee %s\n", getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee)); LogPrint("zrpc", "%s: transparent input: %s (to choose from)\n", getId(), FormatMoney(t_inputs_total)); LogPrint("zrpcunsafe", "%s: private input: %s (to choose from)\n", getId(), FormatMoney(z_inputs_total)); LogPrint("zrpc", "%s: transparent output: %s\n", getId(), FormatMoney(t_outputs_total)); LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(z_outputs_total)); LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(minersFee)); // Grab the current consensus branch ID { LOCK(cs_main); consensusBranchId_ = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); } /** * SCENARIO #1 * * taddr -> taddrs * * There are no zaddrs or joinsplits involved. */ if (isPureTaddrOnlyTx) { add_taddr_outputs_to_tx(); CAmount funds = selectedUTXOAmount; CAmount fundsSpent = t_outputs_total + minersFee; CAmount change = funds - fundsSpent; if (change > 0) { add_taddr_change_output_to_tx(change); LogPrint("zrpc", "%s: transparent change in transaction output (amount=%s)\n", getId(), FormatMoney(change) ); } UniValue obj(UniValue::VOBJ); obj.push_back(Pair("rawtxn", EncodeHexTx(tx_))); sign_send_raw_transaction(obj); return true; } /** * END SCENARIO #1 */ // Prepare raw transaction to handle JoinSplits CMutableTransaction mtx(tx_); crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_); mtx.joinSplitPubKey = joinSplitPubKey_; tx_ = CTransaction(mtx); // Copy zinputs and zoutputs to more flexible containers std::deque<SendManyInputJSOP> zInputsDeque; // zInputsDeque stores minimum numbers of notes for target amount CAmount tmp = 0; for (auto o : z_inputs_) { zInputsDeque.push_back(o); tmp += std::get<2>(o); if (tmp >= targetAmount) { break; } } std::deque<SendManyRecipient> zOutputsDeque; for (auto o : z_outputs_) { // TODO: Add Sapling support. For now, ensure we can later convert freely. auto addr = DecodePaymentAddress(std::get<0>(o)); assert(boost::get<libzcash::SproutPaymentAddress>(&addr) != nullptr); zOutputsDeque.push_back(o); } // When spending notes, take a snapshot of note witnesses and anchors as the treestate will // change upon arrival of new blocks which contain joinsplit transactions. This is likely // to happen as creating a chained joinsplit transaction can take longer than the block interval. if (z_inputs_.size() > 0) { LOCK2(cs_main, pwalletMain->cs_wallet); for (auto t : z_inputs_) { JSOutPoint jso = std::get<0>(t); std::vector<JSOutPoint> vOutPoints = { jso }; uint256 inputAnchor; std::vector<boost::optional<ZCIncrementalWitness>> vInputWitnesses; pwalletMain->GetNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor); jsopWitnessAnchorMap[ jso.ToString() ] = WitnessAnchorData{ vInputWitnesses[0], inputAnchor }; } } /** * SCENARIO #2 * * taddr -> taddrs * -> zaddrs * * Note: Consensus rule states that coinbase utxos can only be sent to a zaddr. * Local wallet rule does not allow any change when sending coinbase utxos * since there is currently no way to specify a change address and we don't * want users accidentally sending excess funds to a recipient. */ if (isfromtaddr_) { add_taddr_outputs_to_tx(); CAmount funds = selectedUTXOAmount; CAmount fundsSpent = t_outputs_total + minersFee + z_outputs_total; CAmount change = funds - fundsSpent; if (change > 0) { if (selectedUTXOCoinbase) { assert(isSingleZaddrOutput); throw JSONRPCError(RPC_WALLET_ERROR, strprintf( "Change %s not allowed. When protecting coinbase funds, the wallet does not " "allow any change as there is currently no way to specify a change address " "in z_sendmany.", FormatMoney(change))); } else { add_taddr_change_output_to_tx(change); LogPrint("zrpc", "%s: transparent change in transaction output (amount=%s)\n", getId(), FormatMoney(change) ); } } // Create joinsplits, where each output represents a zaddr recipient. UniValue obj(UniValue::VOBJ); while (zOutputsDeque.size() > 0) { AsyncJoinSplitInfo info; info.vpub_old = 0; info.vpub_new = 0; int n = 0; while (n++<ZC_NUM_JS_OUTPUTS && zOutputsDeque.size() > 0) { SendManyRecipient smr = zOutputsDeque.front(); std::string address = std::get<0>(smr); CAmount value = std::get<1>(smr); std::string hexMemo = std::get<2>(smr); zOutputsDeque.pop_front(); PaymentAddress pa = DecodePaymentAddress(address); JSOutput jso = JSOutput(boost::get<libzcash::SproutPaymentAddress>(pa), value); if (hexMemo.size() > 0) { jso.memo = get_memo_from_hex_string(hexMemo); } info.vjsout.push_back(jso); // Funds are removed from the value pool and enter the private pool info.vpub_old += value; } obj = perform_joinsplit(info); } sign_send_raw_transaction(obj); return true; } /** * END SCENARIO #2 */ /** * SCENARIO #3 * * zaddr -> taddrs * -> zaddrs * * Send to zaddrs by chaining JoinSplits together and immediately consuming any change * Send to taddrs by creating dummy z outputs and accumulating value in a change note * which is used to set vpub_new in the last chained joinsplit. */ UniValue obj(UniValue::VOBJ); CAmount jsChange = 0; // this is updated after each joinsplit int changeOutputIndex = -1; // this is updated after each joinsplit if jsChange > 0 bool vpubNewProcessed = false; // updated when vpub_new for miner fee and taddr outputs is set in last joinsplit CAmount vpubNewTarget = minersFee; if (t_outputs_total > 0) { add_taddr_outputs_to_tx(); vpubNewTarget += t_outputs_total; } // Keep track of treestate within this transaction boost::unordered_map<uint256, ZCIncrementalMerkleTree, CCoinsKeyHasher> intermediates; std::vector<uint256> previousCommitments; while (!vpubNewProcessed) { AsyncJoinSplitInfo info; info.vpub_old = 0; info.vpub_new = 0; CAmount jsInputValue = 0; uint256 jsAnchor; std::vector<boost::optional<ZCIncrementalWitness>> witnesses; JSDescription prevJoinSplit; // Keep track of previous JoinSplit and its commitments if (tx_.vjoinsplit.size() > 0) { prevJoinSplit = tx_.vjoinsplit.back(); } // If there is no change, the chain has terminated so we can reset the tracked treestate. if (jsChange==0 && tx_.vjoinsplit.size() > 0) { intermediates.clear(); previousCommitments.clear(); } // // Consume change as the first input of the JoinSplit. // if (jsChange > 0) { LOCK2(cs_main, pwalletMain->cs_wallet); // Update tree state with previous joinsplit ZCIncrementalMerkleTree tree; auto it = intermediates.find(prevJoinSplit.anchor); if (it != intermediates.end()) { tree = it->second; } else if (!pcoinsTip->GetSproutAnchorAt(prevJoinSplit.anchor, tree)) { throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor"); } assert(changeOutputIndex != -1); boost::optional<ZCIncrementalWitness> changeWitness; int n = 0; for (const uint256& commitment : prevJoinSplit.commitments) { tree.append(commitment); previousCommitments.push_back(commitment); if (!changeWitness && changeOutputIndex == n++) { changeWitness = tree.witness(); } else if (changeWitness) { changeWitness.get().append(commitment); } } if (changeWitness) { witnesses.push_back(changeWitness); } jsAnchor = tree.root(); intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries) // Decrypt the change note's ciphertext to retrieve some data we need ZCNoteDecryption decryptor(boost::get<libzcash::SproutSpendingKey>(spendingkey_).receiving_key()); auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey); try { SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( decryptor, prevJoinSplit.ciphertexts[changeOutputIndex], prevJoinSplit.ephemeralKey, hSig, (unsigned char) changeOutputIndex); SproutNote note = plaintext.note(boost::get<libzcash::SproutPaymentAddress>(frompaymentaddress_)); info.notes.push_back(note); jsInputValue += plaintext.value(); LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n", getId(), FormatMoney(plaintext.value()) ); } catch (const std::exception& e) { throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what())); } } // // Consume spendable non-change notes // std::vector<SproutNote> vInputNotes; std::vector<JSOutPoint> vOutPoints; std::vector<boost::optional<ZCIncrementalWitness>> vInputWitnesses; uint256 inputAnchor; int numInputsNeeded = (jsChange>0) ? 1 : 0; while (numInputsNeeded++ < ZC_NUM_JS_INPUTS && zInputsDeque.size() > 0) { SendManyInputJSOP t = zInputsDeque.front(); JSOutPoint jso = std::get<0>(t); SproutNote note = std::get<1>(t); CAmount noteFunds = std::get<2>(t); zInputsDeque.pop_front(); WitnessAnchorData wad = jsopWitnessAnchorMap[ jso.ToString() ]; vInputWitnesses.push_back(wad.witness); if (inputAnchor.IsNull()) { inputAnchor = wad.anchor; } else if (inputAnchor != wad.anchor) { throw JSONRPCError(RPC_WALLET_ERROR, "Selected input notes do not share the same anchor"); } vOutPoints.push_back(jso); vInputNotes.push_back(note); jsInputValue += noteFunds; int wtxHeight = -1; int wtxDepth = -1; { LOCK2(cs_main, pwalletMain->cs_wallet); const CWalletTx& wtx = pwalletMain->mapWallet[jso.hash]; // Zero-confirmation notes belong to transactions which have not yet been mined if (mapBlockIndex.find(wtx.hashBlock) == mapBlockIndex.end()) { throw JSONRPCError(RPC_WALLET_ERROR, strprintf("mapBlockIndex does not contain block hash %s", wtx.hashBlock.ToString())); } wtxHeight = mapBlockIndex[wtx.hashBlock]->nHeight; wtxDepth = wtx.GetDepthInMainChain(); } LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n", getId(), jso.hash.ToString().substr(0, 10), jso.js, int(jso.n), // uint8_t FormatMoney(noteFunds), wtxHeight, wtxDepth ); } // Add history of previous commitments to witness if (vInputNotes.size() > 0) { if (vInputWitnesses.size()==0) { throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment"); } for (auto & optionalWitness : vInputWitnesses) { if (!optionalWitness) { throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null"); } ZCIncrementalWitness w = *optionalWitness; // could use .get(); if (jsChange > 0) { for (const uint256& commitment : previousCommitments) { w.append(commitment); } if (jsAnchor != w.root()) { throw JSONRPCError(RPC_WALLET_ERROR, "Witness for spendable note does not have same anchor as change input"); } } witnesses.push_back(w); } // The jsAnchor is null if this JoinSplit is at the start of a new chain if (jsAnchor.IsNull()) { jsAnchor = inputAnchor; } // Add spendable notes as inputs std::copy(vInputNotes.begin(), vInputNotes.end(), std::back_inserter(info.notes)); } // Find recipient to transfer funds to std::string address, hexMemo; CAmount value = 0; if (zOutputsDeque.size() > 0) { SendManyRecipient smr = zOutputsDeque.front(); address = std::get<0>(smr); value = std::get<1>(smr); hexMemo = std::get<2>(smr); zOutputsDeque.pop_front(); } // Reset change jsChange = 0; CAmount outAmount = value; // Set vpub_new in the last joinsplit (when there are no more notes to spend or zaddr outputs to satisfy) if (zOutputsDeque.size() == 0 && zInputsDeque.size() == 0) { assert(!vpubNewProcessed); if (jsInputValue < vpubNewTarget) { throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Insufficient funds for vpub_new %s (miners fee %s, taddr outputs %s)", FormatMoney(vpubNewTarget), FormatMoney(minersFee), FormatMoney(t_outputs_total))); } outAmount += vpubNewTarget; info.vpub_new += vpubNewTarget; // funds flowing back to public pool vpubNewProcessed = true; jsChange = jsInputValue - outAmount; assert(jsChange >= 0); } else { // This is not the last joinsplit, so compute change and any amount still due to the recipient if (jsInputValue > outAmount) { jsChange = jsInputValue - outAmount; } else if (outAmount > jsInputValue) { // Any amount due is owed to the recipient. Let the miners fee get paid first. CAmount due = outAmount - jsInputValue; SendManyRecipient r = SendManyRecipient(address, due, hexMemo); zOutputsDeque.push_front(r); // reduce the amount being sent right now to the value of all inputs value = jsInputValue; } } // create output for recipient if (address.empty()) { assert(value==0); info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new } else { PaymentAddress pa = DecodePaymentAddress(address); JSOutput jso = JSOutput(boost::get<libzcash::SproutPaymentAddress>(pa), value); if (hexMemo.size() > 0) { jso.memo = get_memo_from_hex_string(hexMemo); } info.vjsout.push_back(jso); } // create output for any change if (jsChange>0) { info.vjsout.push_back(JSOutput(boost::get<libzcash::SproutPaymentAddress>(frompaymentaddress_), jsChange)); LogPrint("zrpcunsafe", "%s: generating note for change (amount=%s)\n", getId(), FormatMoney(jsChange) ); } obj = perform_joinsplit(info, witnesses, jsAnchor); if (jsChange > 0) { changeOutputIndex = find_output(obj, 1); } } // Sanity check in case changes to code block above exits loop by invoking 'break' assert(zInputsDeque.size() == 0); assert(zOutputsDeque.size() == 0); assert(vpubNewProcessed); sign_send_raw_transaction(obj); return true; }