Merge pull request #13 from myriadcoin/sadev

Sadev
This commit is contained in:
myriadcoin
2014-11-02 10:40:15 +02:00
34 changed files with 2621 additions and 114 deletions
+3 -1
View File
@@ -84,7 +84,8 @@ BITCOIN_CORE_H = \
sph_shavite.h \
hashgroestl.h \
hashskein.h \
hashqubit.h
hashqubit.h \
stealthaddress.h
JSON_H = \
json/json_spirit.h \
@@ -134,6 +135,7 @@ libbitcoin_wallet_a_SOURCES = \
rpcwallet.cpp \
wallet.cpp \
walletdb.cpp \
stealthaddress.cpp \
$(BITCOIN_CORE_H)
libbitcoin_common_a_SOURCES = \
+3
View File
@@ -7,6 +7,8 @@
#include "hash.h"
#include "uint256.h"
#include "stealthaddress.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
@@ -196,6 +198,7 @@ namespace {
bool operator()(const CKeyID &id) const { return addr->Set(id); }
bool operator()(const CScriptID &id) const { return addr->Set(id); }
bool operator()(const CNoDestination &no) const { return false; }
bool operator()(const CStealthAddress &stxAddr) const { return false; }
};
};
+16 -8
View File
@@ -16,14 +16,14 @@
#include <stdint.h>
enum {
ALGO_SHA256D = 0,
ALGO_SCRYPT = 1,
enum {
ALGO_SHA256D = 0,
ALGO_SCRYPT = 1,
ALGO_GROESTL = 2,
ALGO_SKEIN = 3,
ALGO_QUBIT = 4,
NUM_ALGOS };
enum
{
// primary version
@@ -70,7 +70,7 @@ inline std::string GetAlgoName(int Algo)
case ALGO_QUBIT:
return std::string("qubit");
}
return std::string("unknown");
return std::string("unknown");
}
class CTransaction;
@@ -217,7 +217,7 @@ public:
// to spend something, then we consider it dust.
// A typical txout is 34 bytes big, and will
// need a CTxIn of at least 148 bytes to spend,
// so dust is a txout less than 546 satoshis
// so dust is a txout less than 546 satoshis
// with default nMinRelayTxFee.
return ((nValue*1000)/(3*((int)GetSerializeSize(SER_DISK,0)+148)) < nMinRelayTxFee);
}
@@ -233,6 +233,14 @@ public:
return !(a == b);
}
bool IsScriptOpReturn() const
{
opcodetype opCode;
CScript::const_iterator itTxA = scriptPubKey.begin();
return (scriptPubKey.GetOp(itTxA, opCode)
&& opCode == OP_RETURN);
}
std::string ToString() const;
void print() const;
};
@@ -420,7 +428,7 @@ public:
}
int GetAlgo() const { return ::GetAlgo(nVersion); }
IMPLEMENT_SERIALIZE
(
READWRITE(this->nVersion);
@@ -473,7 +481,7 @@ public:
}
return GetHash();
}
int64_t GetBlockTime() const
{
return (int64_t)nTime;
+3 -4
View File
@@ -113,15 +113,14 @@ bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned
class CCryptoKeyStore : public CBasicKeyStore
{
private:
CryptedKeyMap mapCryptedKeys;
CKeyingMaterial vMasterKey;
// if fUseCrypto is true, mapKeys must be empty
// if fUseCrypto is false, vMasterKey must be empty
bool fUseCrypto;
protected:
CryptedKeyMap mapCryptedKeys;
CKeyingMaterial vMasterKey;
bool SetCrypted();
// will encrypt previously unencrypted keys
+1 -1
View File
@@ -211,6 +211,7 @@ protected:
return (ret == 0);
}
public:
Dbc* GetCursor()
{
if (!pdb)
@@ -261,7 +262,6 @@ protected:
return 0;
}
public:
bool TxnBegin()
{
if (!pdb || activeTxn)
+5 -5
View File
@@ -173,7 +173,7 @@ public:
if (d2i_ECPrivateKey(&pkey, &pbegin, privkey.size())) {
if(fSkipCheck)
return true;
// d2i_ECPrivateKey returns true if parsing succeeds.
// This doesn't necessarily mean the key is valid.
if (EC_KEY_check_key(pkey))
@@ -420,17 +420,17 @@ bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) {
CECKey key;
if (!key.SetPrivKey(privkey, fSkipCheck))
return false;
key.GetSecretBytes(vch);
fCompressed = vchPubKey.IsCompressed();
fValid = true;
if (fSkipCheck)
return true;
if (GetPubKey() != vchPubKey)
return false;
return true;
}
+4
View File
@@ -92,6 +92,8 @@ public:
const unsigned char *end() const { return vch+size(); }
const unsigned char &operator[](unsigned int pos) const { return vch[pos]; }
std::vector<uint8_t> Raw() { return std::vector<uint8_t>(begin(), end()); }
// Comparator implementation.
friend bool operator==(const CPubKey &a, const CPubKey &b) {
return a.vch[0] == b.vch[0] &&
@@ -174,6 +176,8 @@ public:
// secure_allocator is defined in allocators.h
// CPrivKey is a serialized private key, with all parameters included (279 bytes)
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
// CSecret is a serialization of just the secret parameter (32 bytes)
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CSecret;
/** An encapsulated private key. */
class CKey {
+65 -18
View File
@@ -536,24 +536,46 @@ bool IsStandardTx(const CTransaction& tx, string& reason)
}
unsigned int nDataOut = 0;
unsigned int nTxnOut = 0;
txnouttype whichType;
BOOST_FOREACH(const CTxOut& txout, tx.vout) {
if (!::IsStandard(txout.scriptPubKey, whichType)) {
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
if (!::IsStandard(txout.scriptPubKey, whichType))
{
LogPrintf("tx-nonstd: %s %s\n", txout.ToString(), whichType);
reason = "scriptpubkey";
return false;
}
if (whichType == TX_NULL_DATA)
nDataOut++;
else if (txout.IsDust(CTransaction::nMinRelayTxFee)) {
reason = "dust";
return false;
else
{
if (txout.IsDust(CTransaction::nMinRelayTxFee))
{
reason = "dust";
return false;
}
nTxnOut++;
}
}
// only one OP_RETURN txout is permitted
if (nDataOut > 1) {
reason = "multi-op-return";
return false;
if (!TestNet() && (chainActive.Height() < BLOCK_STEALTH_START))
{
// allow one OP_RETURN per transaction
if (nDataOut > 1)
{
reason = "multi-op-return";
return false;
}
}
else
{
// allow one OP_RETURN per txout
if (nDataOut > nTxnOut)
{
reason = "multi-op-return";
return false;
}
}
return true;
@@ -1211,9 +1233,9 @@ int64_t GetBlockValue(int nHeight, int64_t nFees)
// Mining phase: Subsidy is cut in half every SubsidyHalvingInterval
nSubsidy >>= (nHeight / Params().SubsidyHalvingInterval());
// Inflation phase: Subsidy reaches minimum subsidy
// Network is rewarded for transaction processing with transaction fees and
// Network is rewarded for transaction processing with transaction fees and
// the inflationary subsidy
if (nSubsidy < nMinSubsidy)
{
@@ -1267,7 +1289,7 @@ unsigned int ComputeMinWork(unsigned int nBase, int64_t nTime)
static const int64_t nMinActualTimespan = nAveragingTargetTimespan * (100 - nMaxAdjustUp) / 100;
static const int64_t nMaxActualTimespan = nAveragingTargetTimespan * (100 + nMaxAdjustDown) / 100;
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, int algo)
{
unsigned int nProofOfWorkLimit = Params().ProofOfWorkLimit(algo).GetCompact();
@@ -1295,7 +1317,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
// find previous block with same algo
const CBlockIndex* pindexPrev = GetLastBlockIndexForAlgo(pindexLast, algo);
// find first block in averaging interval
// Go back by what we want to be nAveragingInterval blocks
const CBlockIndex* pindexFirst = pindexPrev;
@@ -1309,7 +1331,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
// Limit adjustment step
int64_t nActualTimespan = pindexPrev->GetBlockTime() - pindexFirst->GetBlockTime();
LogPrintf(" nActualTimespan = %d before bounds\n", nActualTimespan);
LogPrintf(" nActualTimespan = %d before bounds %d %d\n", nActualTimespan, pindexPrev->GetBlockTime(), pindexFirst->GetBlockTime());
if (nActualTimespan < nMinActualTimespan)
nActualTimespan = nMinActualTimespan;
if (nActualTimespan > nMaxActualTimespan)
@@ -2019,7 +2041,7 @@ bool static DisconnectTip(CValidationState &state) {
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
// ignore validation errors in resurrected transactions
list<CTransaction> removed;
CValidationState stateDummy;
CValidationState stateDummy;
if (!tx.IsCoinBase())
if (!AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL))
mempool.remove(tx, removed, true);
@@ -2415,13 +2437,36 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp)
// Get prev block index
CBlockIndex* pindexPrev = NULL;
int nHeight = 0;
if (hash != Params().HashGenesisBlock()) {
if (hash != Params().HashGenesisBlock())
{
// Check previous block
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
return state.DoS(10, error("AcceptBlock() : prev block not found"), 0, "bad-prevblk");
pindexPrev = (*mi).second;
nHeight = pindexPrev->nHeight+1;
// Check count of sequence of same algo
if ( (TestNet() && (nHeight > 200))
|| (nHeight > nBlockSequentialAlgoRuleStart) )
{
int nAlgo = block.GetAlgo();
int nAlgoCount = 1;
CBlockIndex* piPrev = pindexPrev;
while (piPrev && (nAlgoCount <= nBlockSequentialAlgoMaxCount))
{
if (piPrev->GetAlgo() != nAlgo)
break;
nAlgoCount++;
piPrev = piPrev->pprev;
}
if (nAlgoCount > nBlockSequentialAlgoMaxCount)
{
return state.DoS(100, error("AcceptBlock() : too many blocks from same algo"),
REJECT_INVALID, "algo-toomany");
}
}
// Check proof of work
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, block.GetAlgo()))
return state.DoS(100, error("AcceptBlock() : incorrect proof of work"),
@@ -2459,6 +2504,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp)
}
}
// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
/*
if (block.nVersion >= 2)
{
// if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
@@ -2472,6 +2518,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp)
REJECT_INVALID, "bad-cb-height");
}
}
*/
}
// Write block to history file
@@ -4394,8 +4441,8 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
// in flight for over two minutes, since we first had a chance to
// process an incoming block.
int64_t nNow = GetTimeMicros();
if (!pto->fDisconnect && state.nBlocksInFlight &&
state.nLastBlockReceive < state.nLastBlockProcess - BLOCK_DOWNLOAD_TIMEOUT*1000000 &&
if (!pto->fDisconnect && state.nBlocksInFlight &&
state.nLastBlockReceive < state.nLastBlockProcess - BLOCK_DOWNLOAD_TIMEOUT*1000000 &&
state.vBlocksInFlight.front().nTime < state.nLastBlockProcess - 2*BLOCK_DOWNLOAD_TIMEOUT*1000000) {
LogPrintf("Peer %s is stalling block download, disconnecting\n", state.name.c_str());
pto->fDisconnect = true;
+45 -5
View File
@@ -67,6 +67,9 @@ static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 128;
/** Timeout in seconds before considering a block download peer unresponsive. */
static const unsigned int BLOCK_DOWNLOAD_TIMEOUT = 60;
/** Stealth addresses */
static const int64_t BLOCK_STEALTH_START = 740000;
#ifdef USE_UPNP
static const int fHaveUPnP = true;
#else
@@ -685,6 +688,9 @@ enum BlockStatus {
};
const int64_t nBlockAlgoWorkWeightStart = 142000; // block where algo work weighting starts
const int64_t nBlockAlgoNormalisedWorkStart = 740000; // block where algo combined weight starts
const int64_t nBlockSequentialAlgoRuleStart = 740000; // block where sequential algo rule starts
const int nBlockSequentialAlgoMaxCount = 6; // maximum sequential blocks of same algo
/** The block chain is a tree shaped structure starting with the
* genesis block at the root, with each block potentially having multiple
@@ -778,7 +784,7 @@ public:
}
int GetAlgo() const { return ::GetAlgo(nVersion); }
CDiskBlockPos GetBlockPos() const {
CDiskBlockPos ret;
if (nStatus & BLOCK_HAVE_DATA) {
@@ -820,6 +826,21 @@ public:
return (int64_t)nTime;
}
CBigNum GetPrevWorkForAlgo(int algo) const
{
CBigNum nWork;
CBlockIndex* pindex = this->pprev;
while (pindex)
{
if (pindex->GetAlgo() == algo)
{
return pindex->GetBlockWork();
}
pindex = pindex->pprev;
}
return Params().ProofOfWorkLimit(algo);
}
CBigNum GetBlockWork() const
{
CBigNum bnTarget;
@@ -829,7 +850,7 @@ public:
return (CBigNum(1)<<256) / (bnTarget+1);
}
int GetAlgoWorkFactor() const
int GetAlgoWorkFactor() const
{
if (!TestNet() && (nHeight < nBlockAlgoWorkWeightStart))
{
@@ -842,7 +863,7 @@ public:
switch (GetAlgo())
{
case ALGO_SHA256D:
return 1;
return 1;
// work factor = absolute work ratio * optimisation factor
case ALGO_SCRYPT:
return 1024 * 4;
@@ -860,10 +881,29 @@ public:
CBigNum GetBlockWorkAdjusted() const
{
CBigNum bnRes;
bnRes = GetBlockWork() * GetAlgoWorkFactor();
if ((TestNet() && (nHeight > 500)) ||
(nHeight >= nBlockAlgoNormalisedWorkStart))
{
// Adjusted block work is the sum of work of this block and the
// most recent work of one block of each algo
CBigNum nBlockWork = GetBlockWork();
int nAlgo = GetAlgo();
for (int algo = 0; algo < NUM_ALGOS; algo++)
{
if (algo != nAlgo)
{
nBlockWork += GetPrevWorkForAlgo(algo);
}
}
bnRes = nBlockWork / NUM_ALGOS;
}
else
{
bnRes = GetBlockWork() * GetAlgoWorkFactor();
}
return bnRes;
}
bool CheckIndex() const
{
int algo = GetAlgo();
+109 -31
View File
@@ -27,10 +27,11 @@ struct AddressTableEntry
Type type;
QString label;
QString address;
bool stealth;
AddressTableEntry() {}
AddressTableEntry(Type type, const QString &label, const QString &address):
type(type), label(label), address(address) {}
AddressTableEntry(Type type, const QString &label, const QString &address, const bool &stealth = false):
type(type), label(label), address(address), stealth(stealth) {}
};
struct AddressTableEntryLessThan
@@ -90,6 +91,17 @@ public:
QString::fromStdString(strName),
QString::fromStdString(address.ToString())));
}
std::set<CStealthAddress>::iterator it;
for (it = wallet->stealthAddresses.begin(); it != wallet->stealthAddresses.end(); ++it)
{
bool fMine = !(it->scan_secret.size() < 1);
cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending,
QString::fromStdString(it->label),
QString::fromStdString(it->Encoded()),
true));
};
}
// qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
// Even though the map is already sorted this re-sorting step is needed because the originating map
@@ -242,6 +254,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
std::string strPurpose = (rec->type == AddressTableEntry::Sending ? "send" : "receive");
editStatus = OK;
std::string strTemp, strValue;
if(role == Qt::EditRole)
{
LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */
@@ -254,7 +267,15 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
editStatus = NO_CHANGES;
return false;
}
wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose);
strTemp = rec->address.toStdString();
if (IsStealthAddress(strTemp))
{
strValue = value.toString().toStdString();
wallet->UpdateStealthAddress(strTemp, strValue, false);
} else
{
wallet->SetAddressBook(CBitcoinAddress(strTemp).Get(), value.toString().toStdString(), strPurpose);
}
} else if(index.column() == Address) {
CTxDestination newAddress = CBitcoinAddress(value.toString().toStdString()).Get();
// Refuse to set invalid address, set error status and return false
@@ -340,7 +361,7 @@ void AddressTableModel::updateEntry(const QString &address,
priv->updateEntry(address, label, isMine, purpose, status);
}
QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address, int addressType)
{
std::string strLabel = label.toStdString();
std::string strAddress = address.toStdString();
@@ -349,53 +370,93 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
if(type == Send)
{
if(!walletModel->validateAddress(address))
if (strAddress.length() > 75)
{
editStatus = INVALID_ADDRESS;
return QString();
}
// Check for duplicate addresses
{
LOCK(wallet->cs_wallet);
if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get()))
CStealthAddress sxAddr;
if (!sxAddr.SetEncoded(strAddress))
{
editStatus = DUPLICATE_ADDRESS;
editStatus = INVALID_ADDRESS;
return QString();
}
// -- Check for duplicate addresses
{
LOCK(wallet->cs_wallet);
if (wallet->stealthAddresses.count(sxAddr))
{
editStatus = DUPLICATE_ADDRESS;
return QString();
};
sxAddr.label = strLabel;
wallet->AddStealthAddress(sxAddr);
}
}
else
{
if(!walletModel->validateAddress(address))
{
editStatus = INVALID_ADDRESS;
return QString();
}
// Check for duplicate addresses
{
LOCK(wallet->cs_wallet);
if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get()))
{
editStatus = DUPLICATE_ADDRESS;
return QString();
}
wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel,
(type == Send ? "send" : "receive"));
}
}
}
else if(type == Receive)
{
// Generate a new address to associate with given label
CPubKey newKey;
if(!wallet->GetKeyFromPool(newKey))
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
if(!ctx.isValid())
{
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
if(!ctx.isValid())
// Unlock wallet failed or was cancelled
editStatus = WALLET_UNLOCK_FAILURE;
return QString();
}
// Generate a new address to associate with given label
if (addressType == AT_Stealth)
{
CStealthAddress newStealthAddr;
std::string sError;
if (!wallet->NewStealthAddress(sError, strLabel, newStealthAddr)
|| !wallet->AddStealthAddress(newStealthAddr))
{
// Unlock wallet failed or was cancelled
editStatus = WALLET_UNLOCK_FAILURE;
editStatus = KEY_GENERATION_FAILURE;
return QString();
}
strAddress = newStealthAddr.Encoded();
} else
{
CPubKey newKey;
if(!wallet->GetKeyFromPool(newKey))
{
editStatus = KEY_GENERATION_FAILURE;
return QString();
}
strAddress = CBitcoinAddress(newKey.GetID()).ToString();
{
LOCK(wallet->cs_wallet);
wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel,
(type == Send ? "send" : "receive"));
}
}
strAddress = CBitcoinAddress(newKey.GetID()).ToString();
}
else
{
return QString();
}
// Add entry
{
LOCK(wallet->cs_wallet);
wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel,
(type == Send ? "send" : "receive"));
}
return QString::fromStdString(strAddress);
}
@@ -422,11 +483,28 @@ QString AddressTableModel::labelForAddress(const QString &address) const
{
{
LOCK(wallet->cs_wallet);
CBitcoinAddress address_parsed(address.toStdString());
std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get());
if (mi != wallet->mapAddressBook.end())
std::string sAddr = address.toStdString();
if (sAddr.length() > 75)
{
return QString::fromStdString(mi->second.name);
CStealthAddress sxAddr;
if (!sxAddr.SetEncoded(sAddr))
return QString();
std::set<CStealthAddress>:: iterator it;
it = wallet->stealthAddresses.find(sxAddr);
if (it == wallet->stealthAddresses.end())
return QString();
return QString::fromStdString(it->label);
}
else
{
CBitcoinAddress address_parsed(sAddr);
std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get());
if (mi != wallet->mapAddressBook.end())
{
return QString::fromStdString(mi->second.name);
}
}
}
return QString();
+12 -3
View File
@@ -24,9 +24,18 @@ public:
explicit AddressTableModel(CWallet *wallet, WalletModel *parent = 0);
~AddressTableModel();
enum ColumnIndex {
enum AddressType
{
AT_Unknown = 0,
AT_Normal = 1,
AT_Stealth = 2
};
enum ColumnIndex
{
Label = 0, /**< User specified label */
Address = 1 /**< Bitcoin address */
Address = 1, /**< Bitcoin address */
Type = 2 /**< Address type */
};
enum RoleIndex {
@@ -61,7 +70,7 @@ public:
/* Add an address to the model.
Returns the added address on success, and an empty string otherwise.
*/
QString addRow(const QString &type, const QString &label, const QString &address);
QString addRow(const QString &type, const QString &label, const QString &address, int addressType);
/* Look up label for address in address book, if not found return empty string.
*/
+8 -1
View File
@@ -88,8 +88,15 @@ BitcoinAddressCheckValidator::BitcoinAddressCheckValidator(QObject *parent) :
QValidator::State BitcoinAddressCheckValidator::validate(QString &input, int &pos) const
{
Q_UNUSED(pos);
string saddr = input.toStdString();
// Validate stealth address
if (saddr.length() > 75)
{
if (IsStealthAddress(saddr))
return QValidator::Acceptable;
};
// Validate the passed Bitcoin address
CBitcoinAddress addr(input.toStdString());
CBitcoinAddress addr(saddr);
if (addr.IsValid())
return QValidator::Acceptable;
+12 -1
View File
@@ -27,16 +27,23 @@ EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) :
case NewReceivingAddress:
setWindowTitle(tr("New receiving address"));
ui->addressEdit->setEnabled(false);
ui->stealthCB->setEnabled(true);
ui->stealthCB->setVisible(true);
break;
case NewSendingAddress:
setWindowTitle(tr("New sending address"));
ui->stealthCB->setVisible(false);
break;
case EditReceivingAddress:
setWindowTitle(tr("Edit receiving address"));
ui->addressEdit->setEnabled(false);
ui->addressEdit->setVisible(true);
ui->stealthCB->setEnabled(false);
ui->addressEdit->setVisible(true);
break;
case EditSendingAddress:
setWindowTitle(tr("Edit sending address"));
ui->stealthCB->setVisible(false);
break;
}
@@ -58,6 +65,7 @@ void EditAddressDialog::setModel(AddressTableModel *model)
mapper->setModel(model);
mapper->addMapping(ui->labelEdit, AddressTableModel::Label);
mapper->addMapping(ui->addressEdit, AddressTableModel::Address);
mapper->addMapping(ui->stealthCB, AddressTableModel::Type);
}
void EditAddressDialog::loadRow(int row)
@@ -70,14 +78,17 @@ bool EditAddressDialog::saveCurrentRow()
if(!model)
return false;
int typeInd;
switch(mode)
{
case NewReceivingAddress:
case NewSendingAddress:
typeInd = ui->stealthCB->isChecked() ? AddressTableModel::AT_Stealth : AddressTableModel::AT_Normal;
address = model->addRow(
mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive,
ui->labelEdit->text(),
ui->addressEdit->text());
ui->addressEdit->text(),
typeInd);
break;
case EditReceivingAddress:
case EditSendingAddress:
+1 -1
View File
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>760</width>
<width>960</width>
<height>380</height>
</rect>
</property>
+8 -1
View File
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>457</width>
<height>126</height>
<height>130</height>
</rect>
</property>
<property name="windowTitle">
@@ -53,6 +53,13 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="stealthCB">
<property name="text">
<string>Stealth Address</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
+1 -1
View File
@@ -140,7 +140,7 @@ void ReceiveCoinsDialog::on_receiveButton_clicked()
}
} else {
/* Generate new receiving address */
address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, "");
address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, "", AddressTableModel::AT_Normal);
}
SendCoinsRecipient info(address, label,
ui->reqAmount->value(), ui->reqMessage->text());
+10
View File
@@ -11,6 +11,8 @@
#include "optionsmodel.h"
#include "walletmodel.h"
#include "stealthaddress.h"
#include <QApplication>
#include <QClipboard>
@@ -147,6 +149,14 @@ SendCoinsRecipient SendCoinsEntry::getValue()
// Normal payment
recipient.address = ui->payTo->text();
// address type
if (recipient.address.length() > 75
&& IsStealthAddress(recipient.address.toStdString()))
recipient.typeInd = AddressTableModel::AT_Stealth;
else
recipient.typeInd = AddressTableModel::AT_Normal;
recipient.label = ui->addAsLabel->text();
recipient.amount = ui->payAmount->value();
recipient.message = ui->messageTextLabel->text();
+5
View File
@@ -109,6 +109,11 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
// from a transaction sent back to our own address.
continue;
}
if (txout.IsScriptOpReturn())
{
// OP_RETURN txout
continue;
}
CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address))
+143 -20
View File
@@ -18,6 +18,8 @@
#include "wallet.h"
#include "walletdb.h" // for BackupWallet
#include "stealthaddress.h"
#include <stdint.h>
#include <QDebug>
@@ -159,7 +161,15 @@ void WalletModel::updateAddressBook(const QString &address, const QString &label
bool WalletModel::validateAddress(const QString &address)
{
CBitcoinAddress addressParsed(address.toStdString());
std::string sAddr = address.toStdString();
if (sAddr.length() > 75)
{
if (IsStealthAddress(sAddr))
return true;
};
CBitcoinAddress addressParsed(sAddr);
return addressParsed.IsValid();
}
@@ -212,8 +222,79 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
setAddress.insert(rcp.address);
++nAddresses;
// stealth
std::string sAddr = rcp.address.toStdString();
if (rcp.typeInd == AddressTableModel::AT_Stealth)
{
if ( (!TestNet() && (chainActive.Height() < BLOCK_STEALTH_START))
|| (TestNet() && (chainActive.Height() < 200)) )
{
emit message(tr("Send Coins"), tr("Stealth addresses not yet supported"),
CClientUIInterface::MSG_ERROR);
return InvalidAddress;
}
CStealthAddress sxAddr;
if (sxAddr.SetEncoded(sAddr))
{
ec_secret ephem_secret;
ec_secret secretShared;
ec_point pkSendTo;
ec_point ephem_pubkey;
if (GenerateRandomSecret(ephem_secret) != 0)
{
LogPrintf("GenerateRandomSecret failed.\n");
return InvalidAddress;
};
if (StealthSecret(ephem_secret, sxAddr.scan_pubkey, sxAddr.spend_pubkey, secretShared, pkSendTo) != 0)
{
LogPrintf("Could not generate receiving public key.\n");
return InvalidAddress;
};
CPubKey cpkTo(pkSendTo);
if (!cpkTo.IsValid())
{
LogPrintf("Invalid public key generated.\n");
return InvalidAddress;
};
CKeyID ckidTo = cpkTo.GetID();
CBitcoinAddress addrTo(ckidTo);
if (SecretToPublicKey(ephem_secret, ephem_pubkey) != 0)
{
LogPrintf("Could not generate ephem public key.\n");
return InvalidAddress;
};
if (fDebug)
{
LogPrintf("Stealth send to generated pubkey %ui: %s\n", pkSendTo.size(), HexStr(pkSendTo).c_str());
LogPrintf("hash %s\n", addrTo.ToString().c_str());
LogPrintf("ephem_pubkey %ui: %s\n", ephem_pubkey.size(), HexStr(ephem_pubkey).c_str());
};
CScript scriptPubKey;
scriptPubKey.SetDestination(addrTo.Get());
vecSend.push_back(make_pair(scriptPubKey, rcp.amount));
CScript scriptP = CScript() << OP_RETURN << ephem_pubkey;
vecSend.push_back(make_pair(scriptP, 0));
continue;
}; // else drop through to normal
}
CScript scriptPubKey;
scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get());
scriptPubKey.SetDestination(CBitcoinAddress(sAddr).Get());
vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, rcp.amount));
total += rcp.amount;
@@ -246,7 +327,28 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
CWalletTx *newTx = transaction.getTransaction();
CReserveKey *keyChange = transaction.getPossibleKeyChange();
bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, strFailReason, coinControl);
// int nChangePos = -1;
bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, /*nChangePos,*/ strFailReason, coinControl);
/*
std::map<int, std::string>::iterator it;
for (it = mapStealthNarr.begin(); it != mapStealthNarr.end(); ++it)
{
int pos = it->first;
if (nChangePos > -1 && it->first >= nChangePos)
pos++;
char key[64];
if (snprintf(key, sizeof(key), "n_%u", pos) < 1)
{
printf("CreateStealthTransaction(): Error creating narration key.");
continue;
};
wtx.mapValue[key] = it->second;
};
*/
transaction.setTransactionFee(nFeeRequired);
if(!fCreated)
@@ -309,17 +411,24 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
{
LOCK(wallet->cs_wallet);
std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest);
// stealth
if (rcp.typeInd == AddressTableModel::AT_Stealth)
{
wallet->UpdateStealthAddress(strAddress, strLabel, true);
} else
{
std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest);
// Check if we have a new address or an updated label
if (mi == wallet->mapAddressBook.end())
{
wallet->SetAddressBook(dest, strLabel, "send");
}
else if (mi->second.name != strLabel)
{
wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose
}
// Check if we have a new address or an updated label
if (mi == wallet->mapAddressBook.end())
{
wallet->SetAddressBook(dest, strLabel, "send");
}
else if (mi->second.name != strLabel)
{
wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose
}
};
}
}
emit coinsSent(wallet, rcp, transaction_array);
@@ -423,13 +532,27 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
QString strLabel = QString::fromStdString(label);
QString strPurpose = QString::fromStdString(purpose);
qDebug() << "NotifyAddressBookChanged : " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status);
QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
Q_ARG(QString, strAddress),
Q_ARG(QString, strLabel),
Q_ARG(bool, isMine),
Q_ARG(QString, strPurpose),
Q_ARG(int, status));
if (address.type() == typeid(CStealthAddress))
{
CStealthAddress sxAddr = boost::get<CStealthAddress>(address);
std::string enc = sxAddr.Encoded();
qDebug() << "NotifyAddressBookChanged : " + QString::fromStdString(enc) + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status);
QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(enc)),
Q_ARG(QString, strLabel),
Q_ARG(bool, isMine),
Q_ARG(QString, strPurpose),
Q_ARG(int, status));
} else
{
qDebug() << "NotifyAddressBookChanged : " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status);
QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
Q_ARG(QString, strAddress),
Q_ARG(QString, strLabel),
Q_ARG(bool, isMine),
Q_ARG(QString, strPurpose),
Q_ARG(int, status));
}
}
// queue notifications to show a non freezing progress dialog e.g. for rescan
+1
View File
@@ -47,6 +47,7 @@ public:
// Todo: This is a hack, should be replaced with a cleaner solution!
QString address;
QString label;
int typeInd;
qint64 amount;
// If from a payment request, this is used for storing the memo
QString message;
+6 -5
View File
@@ -20,16 +20,17 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeH
double GetDifficulty(const CBlockIndex* blockindex, int algo)
{
unsigned int nBits;
// Floating point number that is a multiple of the minimum difficulty,
// minimum difficulty = 1.0.
if (blockindex == NULL)
{
if (chainActive.Tip() == NULL)
CBlockIndex* tip = chainActive.Tip();
if (tip == NULL)
nBits = Params().ProofOfWorkLimit(ALGO_SHA256D).GetCompact();
else
{
blockindex = GetLastBlockIndexForAlgo(chainActive.Tip(), algo);
blockindex = GetLastBlockIndexForAlgo(tip, algo);
if (blockindex == NULL)
nBits = Params().ProofOfWorkLimit(algo).GetCompact();
else
@@ -38,11 +39,11 @@ double GetDifficulty(const CBlockIndex* blockindex, int algo)
}
else
nBits = blockindex->nBits;
int nShift = (nBits >> 24) & 0xff;
double dDiff =
(double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff);
(double)0x0000ffff / (double)(nBits & 0x00ffffff);
while (nShift < 29)
{
+1
View File
@@ -176,6 +176,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
if (strMethod == "verifychain" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "keypoolrefill" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "getrawmempool" && n > 0) ConvertTo<bool>(params[0]);
if (strMethod == "sendtostealthaddress" && n > 1) ConvertTo<double>(params[1]);
return params;
}
+10
View File
@@ -81,6 +81,7 @@ Value getinfo(const Array& params, bool fHelp)
obj.push_back(Pair("difficulty_skein", (double)GetDifficulty(NULL, ALGO_SKEIN)));
obj.push_back(Pair("difficulty_qubit", (double)GetDifficulty(NULL, ALGO_QUBIT)));
obj.push_back(Pair("testnet", TestNet()));
#ifdef ENABLE_WALLET
if (pwalletMain) {
obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime()));
@@ -92,6 +93,7 @@ Value getinfo(const Array& params, bool fHelp)
#endif
obj.push_back(Pair("relayfee", ValueFromAmount(CTransaction::nMinRelayTxFee)));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
return obj;
}
@@ -130,6 +132,14 @@ public:
obj.push_back(Pair("sigsrequired", nRequired));
return obj;
}
Object operator()(const CStealthAddress &stxAddr) const {
Object obj;
obj.push_back(Pair("isstealth", true));
obj.push_back(Pair("label", stxAddr.label));
obj.push_back(Pair("address", stxAddr.Encoded()));
return obj;
}
};
#endif
+10
View File
@@ -315,6 +315,16 @@ static const CRPCCommand vRPCCommands[] =
{ "gethashespersec", &gethashespersec, true, false, false },
{ "getwork", &getwork, true, false, true },
{ "setgenerate", &setgenerate, true, true, false },
/* Stealth */
{ "getnewstealthaddress", &getnewstealthaddress, false, false, true},
{ "liststealthaddresses", &liststealthaddresses, false, false, true},
{ "importstealthaddress", &importstealthaddress, false, false, true},
{ "sendtostealthaddress", &sendtostealthaddress, false, false, true},
{ "clearwallettransactions",&clearwallettransactions,false, false, true},
{ "scanforalltxns", &scanforalltxns, false, false, true},
{ "scanforstealthtxns", &scanforstealthtxns, false, false, true},
#endif // ENABLE_WALLET
};
+7
View File
@@ -190,5 +190,12 @@ extern json_spirit::Value verifychain(const json_spirit::Array& params, bool fHe
extern json_spirit::Value makekeypair(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getnewstealthaddress(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value liststealthaddresses(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value importstealthaddress(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value sendtostealthaddress(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value clearwallettransactions(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value scanforalltxns(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value scanforstealthtxns(const json_spirit::Array& params, bool fHelp);
#endif
+459
View File
@@ -1917,3 +1917,462 @@ Value getwalletinfo(const Array& params, bool fHelp)
obj.push_back(Pair("unlocked_until", nWalletUnlockTime));
return obj;
}
Value getnewstealthaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"getnewstealthaddress [label]\n"
"Returns a new stealth address for receiving payments anonymously.");
if (pwalletMain->IsLocked())
throw runtime_error("Failed: Wallet must be unlocked.");
std::string sLabel;
if (params.size() > 0)
sLabel = params[0].get_str();
CStealthAddress sxAddr;
std::string sError;
if (!pwalletMain->NewStealthAddress(sError, sLabel, sxAddr))
throw runtime_error(std::string("Could not get new stealth address: ") + sError);
if (!pwalletMain->AddStealthAddress(sxAddr))
throw runtime_error("Could not save to wallet.");
return sxAddr.Encoded();
}
Value liststealthaddresses(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"liststealthaddresses [show_secrets=0]\n"
"List owned stealth addresses.");
bool fShowSecrets = false;
if (params.size() > 0)
{
std::string str = params[0].get_str();
if (str == "0" || str == "n" || str == "no" || str == "-" || str == "false")
fShowSecrets = false;
else
fShowSecrets = true;
};
if (fShowSecrets)
{
if (pwalletMain->IsLocked())
throw runtime_error("Failed: Wallet must be unlocked.");
};
Object result;
std::set<CStealthAddress>::iterator it;
for (it = pwalletMain->stealthAddresses.begin(); it != pwalletMain->stealthAddresses.end(); ++it)
{
if (it->scan_secret.size() < 1)
continue; // stealth address is not owned
if (fShowSecrets)
{
Object objA;
objA.push_back(Pair("label", it->label));
objA.push_back(Pair("address", it->Encoded()));
objA.push_back(Pair("scan_secret", HexStr(it->scan_secret.begin(), it->scan_secret.end())));
objA.push_back(Pair("spend_secret", HexStr(it->spend_secret.begin(), it->spend_secret.end())));
result.push_back(Pair("stealth_address", objA));
} else
{
result.push_back(Pair("stealth_address", it->Encoded() + " - " + it->label));
};
};
return result;
}
Value importstealthaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 2)
throw runtime_error(
"importstealthaddress <scan_secret> <spend_secret> [label]\n"
"Import an owned stealth addresses.");
std::string sScanSecret = params[0].get_str();
std::string sSpendSecret = params[1].get_str();
std::string sLabel;
if (params.size() > 2)
{
sLabel = params[2].get_str();
};
std::vector<uint8_t> vchScanSecret;
std::vector<uint8_t> vchSpendSecret;
if (IsHex(sScanSecret))
{
vchScanSecret = ParseHex(sScanSecret);
} else
{
if (!DecodeBase58(sScanSecret, vchScanSecret))
throw runtime_error("Could not decode scan secret as hex or base58.");
};
if (IsHex(sSpendSecret))
{
vchSpendSecret = ParseHex(sSpendSecret);
} else
{
if (!DecodeBase58(sSpendSecret, vchSpendSecret))
throw runtime_error("Could not decode spend secret as hex or base58.");
};
if (vchScanSecret.size() != 32)
throw runtime_error("Scan secret is not 32 bytes.");
if (vchSpendSecret.size() != 32)
throw runtime_error("Spend secret is not 32 bytes.");
ec_secret scan_secret;
ec_secret spend_secret;
memcpy(&scan_secret.e[0], &vchScanSecret[0], 32);
memcpy(&spend_secret.e[0], &vchSpendSecret[0], 32);
ec_point scan_pubkey, spend_pubkey;
if (SecretToPublicKey(scan_secret, scan_pubkey) != 0)
throw runtime_error("Could not get scan public key.");
if (SecretToPublicKey(spend_secret, spend_pubkey) != 0)
throw runtime_error("Could not get spend public key.");
CStealthAddress sxAddr;
sxAddr.label = sLabel;
sxAddr.scan_pubkey = scan_pubkey;
sxAddr.spend_pubkey = spend_pubkey;
sxAddr.scan_secret = vchScanSecret;
sxAddr.spend_secret = vchSpendSecret;
Object result;
bool fFound = false;
// -- find if address already exists
std::set<CStealthAddress>::iterator it;
for (it = pwalletMain->stealthAddresses.begin(); it != pwalletMain->stealthAddresses.end(); ++it)
{
CStealthAddress &sxAddrIt = const_cast<CStealthAddress&>(*it);
if (sxAddrIt.scan_pubkey == sxAddr.scan_pubkey
&& sxAddrIt.spend_pubkey == sxAddr.spend_pubkey)
{
if (sxAddrIt.scan_secret.size() < 1)
{
sxAddrIt.scan_secret = sxAddr.scan_secret;
sxAddrIt.spend_secret = sxAddr.spend_secret;
fFound = true; // update stealth address with secrets
break;
};
result.push_back(Pair("result", "Import failed - stealth address exists."));
return result;
};
};
if (fFound)
{
result.push_back(Pair("result", "Success, updated " + sxAddr.Encoded()));
} else
{
pwalletMain->stealthAddresses.insert(sxAddr);
result.push_back(Pair("result", "Success, imported " + sxAddr.Encoded()));
};
if (!pwalletMain->AddStealthAddress(sxAddr))
throw runtime_error("Could not save to wallet.");
return result;
}
Value sendtostealthaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 2 || params.size() > 5)
throw runtime_error(
"sendtostealthaddress <stealth_address> <amount> [narration] [comment] [comment-to]\n"
"<amount> is a real and is rounded to the nearest 0.000001"
+ HelpRequiringPassphrase());
if (pwalletMain->IsLocked())
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
std::string sEncoded = params[0].get_str();
int64_t nAmount = AmountFromValue(params[1]);
std::string sNarr;
if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty())
sNarr = params[2].get_str();
if (sNarr.length() > 24)
throw runtime_error("Narration must be 24 characters or less.");
CStealthAddress sxAddr;
Object result;
if (!sxAddr.SetEncoded(sEncoded))
{
result.push_back(Pair("result", "Invalid stealth address."));
return result;
};
CWalletTx wtx;
if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty())
wtx.mapValue["comment"] = params[3].get_str();
if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
wtx.mapValue["to"] = params[4].get_str();
std::string sError;
if (!pwalletMain->SendStealthMoneyToDestination(sxAddr, nAmount, sNarr, wtx, sError))
throw JSONRPCError(RPC_WALLET_ERROR, sError);
return wtx.GetHash().GetHex();
}
Value clearwallettransactions(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 0)
throw runtime_error(
"clearwallettransactions \n"
"delete all transactions from wallet - reload with scanforalltxns\n"
"Warning: Backup your wallet first!");
Object result;
uint32_t nTransactions = 0;
char cbuf[256];
{
LOCK2(cs_main, pwalletMain->cs_wallet);
CWalletDB walletdb(pwalletMain->strWalletFile);
walletdb.TxnBegin();
Dbc* pcursor = walletdb.GetCursor();
if (!pcursor)
throw runtime_error("Cannot get wallet DB cursor");
Dbt datKey;
Dbt datValue;
datKey.set_flags(DB_DBT_USERMEM);
datValue.set_flags(DB_DBT_USERMEM);
std::vector<unsigned char> vchKey;
std::vector<unsigned char> vchType;
std::vector<unsigned char> vchKeyData;
std::vector<unsigned char> vchValueData;
vchKeyData.resize(100);
vchValueData.resize(100);
datKey.set_ulen(vchKeyData.size());
datKey.set_data(&vchKeyData[0]);
datValue.set_ulen(vchValueData.size());
datValue.set_data(&vchValueData[0]);
unsigned int fFlags = DB_NEXT; // same as using DB_FIRST for new cursor
while (true)
{
int ret = pcursor->get(&datKey, &datValue, fFlags);
if (ret == ENOMEM
|| ret == DB_BUFFER_SMALL)
{
if (datKey.get_size() > datKey.get_ulen())
{
vchKeyData.resize(datKey.get_size());
datKey.set_ulen(vchKeyData.size());
datKey.set_data(&vchKeyData[0]);
};
if (datValue.get_size() > datValue.get_ulen())
{
vchValueData.resize(datValue.get_size());
datValue.set_ulen(vchValueData.size());
datValue.set_data(&vchValueData[0]);
};
// -- try once more, when DB_BUFFER_SMALL cursor is not expected to move
ret = pcursor->get(&datKey, &datValue, fFlags);
};
if (ret == DB_NOTFOUND)
break;
else
if (datKey.get_data() == NULL || datValue.get_data() == NULL
|| ret != 0)
{
snprintf(cbuf, sizeof(cbuf), "wallet DB error %d, %s", ret, db_strerror(ret));
throw runtime_error(cbuf);
};
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
ssValue.SetType(SER_DISK);
ssValue.clear();
ssValue.write((char*)datKey.get_data(), datKey.get_size());
ssValue >> vchType;
std::string strType(vchType.begin(), vchType.end());
//LogPrintf("strType %s\n", strType.c_str());
if (strType == "tx")
{
uint256 hash;
ssValue >> hash;
if ((ret = pcursor->del(0)) != 0)
{
LogPrintf("Delete transaction failed %d, %s\n", ret, db_strerror(ret));
continue;
};
pwalletMain->mapWallet.erase(hash);
pwalletMain->NotifyTransactionChanged(pwalletMain, hash, CT_DELETED);
nTransactions++;
};
};
pcursor->close();
walletdb.TxnCommit();
//pwalletMain->mapWallet.clear();
}
snprintf(cbuf, sizeof(cbuf), "Removed %u transactions.", nTransactions);
result.push_back(Pair("complete", std::string(cbuf)));
result.push_back(Pair("", "Reload with scanforstealthtxns or re-download blockchain."));
return result;
}
Value scanforalltxns(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"scanforalltxns [fromHeight]\n"
"Scan blockchain for owned transactions.");
Object result;
int32_t nFromHeight = 0;
CBlockIndex *pindex = chainActive.Genesis();
if (params.size() > 0)
nFromHeight = params[0].get_int();
if (nFromHeight > 0)
{
// pindex = mapBlockIndex[hashBestChain];
pindex = mapBlockIndex[chainActive.Tip()->GetBlockHash()];
while (pindex->nHeight > nFromHeight
&& pindex->pprev)
pindex = pindex->pprev;
};
if (pindex == NULL)
throw runtime_error("Genesis block is not set.");
{
LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->MarkDirty();
pwalletMain->ScanForWalletTransactions(pindex, true);
pwalletMain->ReacceptWalletTransactions();
}
result.push_back(Pair("result", "Scan complete."));
return result;
}
Value scanforstealthtxns(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"scanforstealthtxns [fromHeight]\n"
"Scan blockchain for owned stealth transactions.");
Object result;
uint32_t nBlocks = 0;
uint32_t nTransactions = 0;
int32_t nFromHeight = 0;
CBlockIndex *pindex = chainActive.Genesis();
if (params.size() > 0)
nFromHeight = params[0].get_int();
if (nFromHeight > 0)
{
// pindex = mapBlockIndex[hashBestChain];
pindex = mapBlockIndex[chainActive.Tip()->GetBlockHash()];
while (pindex->nHeight > nFromHeight
&& pindex->pprev)
pindex = pindex->pprev;
};
if (pindex == NULL)
throw runtime_error("Genesis block is not set.");
// -- locks in AddToWalletIfInvolvingMe
bool fUpdate = true; // todo: option?
pwalletMain->nStealth = 0;
pwalletMain->nFoundStealth = 0;
while (pindex)
{
nBlocks++;
CBlock block;
ReadBlockFromDisk(block, pindex);
BOOST_FOREACH(CTransaction& tx, block.vtx)
{
string reason;
if (!IsStandardTx(tx, reason))
continue; // leave out coinbase and others
nTransactions++;
pwalletMain->AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate);
};
pindex = chainActive.Next(pindex);
};
LogPrintf("Scanned %u blocks, %u transactions\n", nBlocks, nTransactions);
LogPrintf("Found %u stealth transactions in blockchain.\n", pwalletMain->nStealth);
LogPrintf("Found %u new owned stealth transactions.\n", pwalletMain->nFoundStealth);
char cbuf[256];
snprintf(cbuf, sizeof(cbuf), "%u new stealth transactions.", pwalletMain->nFoundStealth);
result.push_back(Pair("result", "Scan complete."));
result.push_back(Pair("found", std::string(cbuf)));
return result;
}
+13
View File
@@ -1454,6 +1454,7 @@ public:
bool operator()(const CNoDestination &dest) const { return false; }
bool operator()(const CKeyID &keyID) const { return keystore->HaveKey(keyID); }
bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); }
bool operator()(const CStealthAddress &stxAddr) const { return stxAddr.scan_secret.size() == ec_secret_size; }
};
bool IsMine(const CKeyStore &keystore, const CTxDestination &dest)
@@ -1589,6 +1590,10 @@ public:
Process(script);
}
void operator()(const CStealthAddress &stxAddr) {
CScript script;
}
void operator()(const CNoDestination &none) {}
};
@@ -1931,6 +1936,14 @@ public:
*script << OP_HASH160 << scriptID << OP_EQUAL;
return true;
}
bool operator()(const CStealthAddress &stxAddr) const {
script->clear();
//*script << OP_HASH160 << scriptID << OP_EQUAL;
LogPrintf("TODO\n");
return false;
}
};
void CScript::SetDestination(const CTxDestination& dest)
+3 -1
View File
@@ -10,6 +10,8 @@
#include "key.h"
#include "util.h"
#include "stealthaddress.h"
#include <stdexcept>
#include <stdint.h>
#include <string>
@@ -67,7 +69,7 @@ public:
* * CScriptID: TX_SCRIPTHASH destination
* A CTxDestination is the internal data type encoded in a CBitcoinAddress
*/
typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
typedef boost::variant<CNoDestination, CKeyID, CScriptID, CStealthAddress> CTxDestination;
const char* GetTxnOutputType(txnouttype t);
+683
View File
@@ -0,0 +1,683 @@
// Copyright (c) 2014 The ShadowCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#include "stealthaddress.h"
#include "base58.h"
#include <openssl/rand.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/obj_mac.h>
//const uint8_t stealth_version_byte = 0x2a;
const uint8_t stealth_version_byte = 0x28;
bool CStealthAddress::SetEncoded(const std::string& encodedAddress)
{
data_chunk raw;
if (!DecodeBase58(encodedAddress, raw))
{
if (fDebug)
LogPrintf("CStealthAddress::SetEncoded DecodeBase58 falied.\n");
return false;
};
if (!VerifyChecksum(raw))
{
if (fDebug)
LogPrintf("CStealthAddress::SetEncoded verify_checksum falied.\n");
return false;
};
if (raw.size() < 1 + 1 + 33 + 1 + 33 + 1 + 1 + 4)
{
if (fDebug)
LogPrintf("CStealthAddress::SetEncoded() too few bytes provided.\n");
return false;
};
uint8_t* p = &raw[0];
uint8_t version = *p++;
if (version != stealth_version_byte)
{
LogPrintf("CStealthAddress::SetEncoded version mismatch 0x%x != 0x%x.\n", version, stealth_version_byte);
return false;
};
options = *p++;
scan_pubkey.resize(33);
memcpy(&scan_pubkey[0], p, 33);
p += 33;
//uint8_t spend_pubkeys = *p++;
p++;
spend_pubkey.resize(33);
memcpy(&spend_pubkey[0], p, 33);
return true;
};
std::string CStealthAddress::Encoded() const
{
// https://wiki.unsystem.net/index.php/DarkWallet/Stealth#Address_format
// [version] [options] [scan_key] [N] ... [Nsigs] [prefix_length] ...
data_chunk raw;
raw.push_back(stealth_version_byte);
raw.push_back(options);
raw.insert(raw.end(), scan_pubkey.begin(), scan_pubkey.end());
raw.push_back(1); // number of spend pubkeys
raw.insert(raw.end(), spend_pubkey.begin(), spend_pubkey.end());
raw.push_back(0); // number of signatures
raw.push_back(0); // ?
AppendChecksum(raw);
return EncodeBase58(raw);
};
uint32_t BitcoinChecksum(uint8_t* p, uint32_t nBytes)
{
if (!p || nBytes == 0)
return 0;
uint8_t hash1[32];
SHA256(p, nBytes, (uint8_t*)hash1);
uint8_t hash2[32];
SHA256((uint8_t*)hash1, sizeof(hash1), (uint8_t*)hash2);
// -- checksum is the 1st 4 bytes of the hash
uint32_t checksum = from_little_endian<uint32_t>(&hash2[0]);
return checksum;
};
void AppendChecksum(data_chunk& data)
{
uint32_t checksum = BitcoinChecksum(&data[0], data.size());
// -- to_little_endian
std::vector<uint8_t> tmp(4);
//memcpy(&tmp[0], &checksum, 4);
for (int i = 0; i < 4; ++i)
{
tmp[i] = checksum & 0xFF;
checksum >>= 8;
};
data.insert(data.end(), tmp.begin(), tmp.end());
};
bool VerifyChecksum(const data_chunk& data)
{
if (data.size() < 4)
return false;
uint32_t checksum = from_little_endian<uint32_t>(data.end() - 4);
return BitcoinChecksum((uint8_t*)&data[0], data.size()-4) == checksum;
};
int GenerateRandomSecret(ec_secret& out)
{
RandAddSeedPerfmon();
static uint256 max("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140");
static uint256 min(16000); // increase? min valid key is 1
uint256 test;
int i;
// -- check max, try max 32 times
for (i = 0; i < 32; ++i)
{
RAND_bytes((unsigned char*) test.begin(), 32);
if (test > min && test < max)
{
memcpy(&out.e[0], test.begin(), 32);
break;
};
};
if (i > 31)
{
LogPrintf("Error: GenerateRandomSecret failed to generate a valid key.\n");
return 1;
};
return 0;
};
int SecretToPublicKey(const ec_secret& secret, ec_point& out)
{
// -- public key = private * G
int rv = 0;
EC_GROUP *ecgrp = EC_GROUP_new_by_curve_name(NID_secp256k1);
if (!ecgrp)
{
LogPrintf("SecretToPublicKey(): EC_GROUP_new_by_curve_name failed.\n");
return 1;
};
BIGNUM* bnIn = BN_bin2bn(&secret.e[0], ec_secret_size, BN_new());
if (!bnIn)
{
EC_GROUP_free(ecgrp);
LogPrintf("SecretToPublicKey(): BN_bin2bn failed\n");
return 1;
};
EC_POINT* pub = EC_POINT_new(ecgrp);
EC_POINT_mul(ecgrp, pub, bnIn, NULL, NULL, NULL);
BIGNUM* bnOut = EC_POINT_point2bn(ecgrp, pub, POINT_CONVERSION_COMPRESSED, BN_new(), NULL);
if (!bnOut)
{
LogPrintf("SecretToPublicKey(): point2bn failed\n");
rv = 1;
} else
{
out.resize(ec_compressed_size);
if (BN_num_bytes(bnOut) != (int) ec_compressed_size
|| BN_bn2bin(bnOut, &out[0]) != (int) ec_compressed_size)
{
LogPrintf("SecretToPublicKey(): bnOut incorrect length.\n");
rv = 1;
};
BN_free(bnOut);
};
EC_GROUP_free(ecgrp);
BN_free(bnIn);
EC_POINT_free(pub);
return rv;
};
int StealthSecret(ec_secret& secret, ec_point& pubkey, const ec_point& pkSpend, ec_secret& sharedSOut, ec_point& pkOut)
{
/*
send:
secret = ephem_secret, pubkey = scan_pubkey
receive:
secret = scan_secret, pubkey = ephem_pubkey
c = H(dP)
Q = public scan key (EC point, 33 bytes)
d = private scan key (integer, 32 bytes)
R = public spend key
f = private spend key
Q = dG
R = fG
Sender (has Q and R, not d or f):
P = eG
c = H(eQ) = H(dP)
R' = R + cG
Recipient gets R' and P
test 0 and infinity?
*/
int rv = 0;
std::vector<uint8_t> vchOutQ;
BN_CTX* bnCtx = NULL;
BIGNUM* bnEphem = NULL;
BIGNUM* bnQ = NULL;
EC_POINT* Q = NULL;
BIGNUM* bnOutQ = NULL;
BIGNUM* bnc = NULL;
EC_POINT* C = NULL;
BIGNUM* bnR = NULL;
EC_POINT* R = NULL;
EC_POINT* Rout = NULL;
BIGNUM* bnOutR = NULL;
EC_GROUP* ecgrp = EC_GROUP_new_by_curve_name(NID_secp256k1);
if (!ecgrp)
{
LogPrintf("StealthSecret(): EC_GROUP_new_by_curve_name failed.\n");
return 1;
};
if (!(bnCtx = BN_CTX_new()))
{
LogPrintf("StealthSecret(): BN_CTX_new failed.\n");
rv = 1;
goto End;
};
if (!(bnEphem = BN_bin2bn(&secret.e[0], ec_secret_size, BN_new())))
{
LogPrintf("StealthSecret(): bnEphem BN_bin2bn failed.\n");
rv = 1;
goto End;
};
if (!(bnQ = BN_bin2bn(&pubkey[0], pubkey.size(), BN_new())))
{
LogPrintf("StealthSecret(): bnQ BN_bin2bn failed\n");
rv = 1;
goto End;
};
if (!(Q = EC_POINT_bn2point(ecgrp, bnQ, NULL, bnCtx)))
{
LogPrintf("StealthSecret(): Q EC_POINT_bn2point failed\n");
rv = 1;
goto End;
};
// -- eQ
// EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *n, const EC_POINT *q, const BIGNUM *m, BN_CTX *ctx);
// EC_POINT_mul calculates the value generator * n + q * m and stores the result in r. The value n may be NULL in which case the result is just q * m.
if (!EC_POINT_mul(ecgrp, Q, NULL, Q, bnEphem, bnCtx))
{
LogPrintf("StealthSecret(): eQ EC_POINT_mul failed\n");
rv = 1;
goto End;
};
if (!(bnOutQ = EC_POINT_point2bn(ecgrp, Q, POINT_CONVERSION_COMPRESSED, BN_new(), bnCtx)))
{
LogPrintf("StealthSecret(): Q EC_POINT_bn2point failed\n");
rv = 1;
goto End;
};
vchOutQ.resize(ec_compressed_size);
if (BN_num_bytes(bnOutQ) != (int) ec_compressed_size
|| BN_bn2bin(bnOutQ, &vchOutQ[0]) != (int) ec_compressed_size)
{
LogPrintf("StealthSecret(): bnOutQ incorrect length.\n");
rv = 1;
goto End;
};
SHA256(&vchOutQ[0], vchOutQ.size(), &sharedSOut.e[0]);
if (!(bnc = BN_bin2bn(&sharedSOut.e[0], ec_secret_size, BN_new())))
{
LogPrintf("StealthSecret(): BN_bin2bn failed\n");
rv = 1;
goto End;
};
// -- cG
if (!(C = EC_POINT_new(ecgrp)))
{
LogPrintf("StealthSecret(): C EC_POINT_new failed\n");
rv = 1;
goto End;
};
if (!EC_POINT_mul(ecgrp, C, bnc, NULL, NULL, bnCtx))
{
LogPrintf("StealthSecret(): C EC_POINT_mul failed\n");
rv = 1;
goto End;
};
if (!(bnR = BN_bin2bn(&pkSpend[0], pkSpend.size(), BN_new())))
{
LogPrintf("StealthSecret(): bnR BN_bin2bn failed\n");
rv = 1;
goto End;
};
if (!(R = EC_POINT_bn2point(ecgrp, bnR, NULL, bnCtx)))
{
LogPrintf("StealthSecret(): R EC_POINT_bn2point failed\n");
rv = 1;
goto End;
};
if (!EC_POINT_mul(ecgrp, C, bnc, NULL, NULL, bnCtx))
{
LogPrintf("StealthSecret(): C EC_POINT_mul failed\n");
rv = 1;
goto End;
};
if (!(Rout = EC_POINT_new(ecgrp)))
{
LogPrintf("StealthSecret(): Rout EC_POINT_new failed\n");
rv = 1;
goto End;
};
if (!EC_POINT_add(ecgrp, Rout, R, C, bnCtx))
{
LogPrintf("StealthSecret(): Rout EC_POINT_add failed\n");
rv = 1;
goto End;
};
if (!(bnOutR = EC_POINT_point2bn(ecgrp, Rout, POINT_CONVERSION_COMPRESSED, BN_new(), bnCtx)))
{
LogPrintf("StealthSecret(): Rout EC_POINT_bn2point failed\n");
rv = 1;
goto End;
};
pkOut.resize(ec_compressed_size);
if (BN_num_bytes(bnOutR) != (int) ec_compressed_size
|| BN_bn2bin(bnOutR, &pkOut[0]) != (int) ec_compressed_size)
{
LogPrintf("StealthSecret(): pkOut incorrect length.\n");
rv = 1;
goto End;
};
End:
if (bnOutR) BN_free(bnOutR);
if (Rout) EC_POINT_free(Rout);
if (R) EC_POINT_free(R);
if (bnR) BN_free(bnR);
if (C) EC_POINT_free(C);
if (bnc) BN_free(bnc);
if (bnOutQ) BN_free(bnOutQ);
if (Q) EC_POINT_free(Q);
if (bnQ) BN_free(bnQ);
if (bnEphem) BN_free(bnEphem);
if (bnCtx) BN_CTX_free(bnCtx);
EC_GROUP_free(ecgrp);
return rv;
};
int StealthSecretSpend(ec_secret& scanSecret, ec_point& ephemPubkey, ec_secret& spendSecret, ec_secret& secretOut)
{
/*
c = H(dP)
R' = R + cG [without decrypting wallet]
= (f + c)G [after decryption of wallet]
Remember: mod curve.order, pad with 0x00s where necessary?
*/
int rv = 0;
std::vector<uint8_t> vchOutP;
BN_CTX* bnCtx = NULL;
BIGNUM* bnScanSecret = NULL;
BIGNUM* bnP = NULL;
EC_POINT* P = NULL;
BIGNUM* bnOutP = NULL;
BIGNUM* bnc = NULL;
BIGNUM* bnOrder = NULL;
BIGNUM* bnSpend = NULL;
EC_GROUP* ecgrp = EC_GROUP_new_by_curve_name(NID_secp256k1);
if (!ecgrp)
{
LogPrintf("StealthSecretSpend(): EC_GROUP_new_by_curve_name failed.\n");
return 1;
};
if (!(bnCtx = BN_CTX_new()))
{
LogPrintf("StealthSecretSpend(): BN_CTX_new failed.\n");
rv = 1;
goto End;
};
if (!(bnScanSecret = BN_bin2bn(&scanSecret.e[0], ec_secret_size, BN_new())))
{
LogPrintf("StealthSecretSpend(): bnScanSecret BN_bin2bn failed.\n");
rv = 1;
goto End;
};
if (!(bnP = BN_bin2bn(&ephemPubkey[0], ephemPubkey.size(), BN_new())))
{
LogPrintf("StealthSecretSpend(): bnP BN_bin2bn failed\n");
rv = 1;
goto End;
};
if (!(P = EC_POINT_bn2point(ecgrp, bnP, NULL, bnCtx)))
{
LogPrintf("StealthSecretSpend(): P EC_POINT_bn2point failed\n");
rv = 1;
goto End;
};
// -- dP
if (!EC_POINT_mul(ecgrp, P, NULL, P, bnScanSecret, bnCtx))
{
LogPrintf("StealthSecretSpend(): dP EC_POINT_mul failed\n");
rv = 1;
goto End;
};
if (!(bnOutP = EC_POINT_point2bn(ecgrp, P, POINT_CONVERSION_COMPRESSED, BN_new(), bnCtx)))
{
LogPrintf("StealthSecretSpend(): P EC_POINT_bn2point failed\n");
rv = 1;
goto End;
};
vchOutP.resize(ec_compressed_size);
if (BN_num_bytes(bnOutP) != (int) ec_compressed_size
|| BN_bn2bin(bnOutP, &vchOutP[0]) != (int) ec_compressed_size)
{
LogPrintf("StealthSecretSpend(): bnOutP incorrect length.\n");
rv = 1;
goto End;
};
uint8_t hash1[32];
SHA256(&vchOutP[0], vchOutP.size(), (uint8_t*)hash1);
if (!(bnc = BN_bin2bn(&hash1[0], 32, BN_new())))
{
LogPrintf("StealthSecretSpend(): BN_bin2bn failed\n");
rv = 1;
goto End;
};
if (!(bnOrder = BN_new())
|| !EC_GROUP_get_order(ecgrp, bnOrder, bnCtx))
{
LogPrintf("StealthSecretSpend(): EC_GROUP_get_order failed\n");
rv = 1;
goto End;
};
if (!(bnSpend = BN_bin2bn(&spendSecret.e[0], ec_secret_size, BN_new())))
{
LogPrintf("StealthSecretSpend(): bnSpend BN_bin2bn failed.\n");
rv = 1;
goto End;
};
//if (!BN_add(r, a, b)) return 0;
//return BN_nnmod(r, r, m, ctx);
if (!BN_mod_add(bnSpend, bnSpend, bnc, bnOrder, bnCtx))
{
LogPrintf("StealthSecretSpend(): bnSpend BN_mod_add failed.\n");
rv = 1;
goto End;
};
if (BN_is_zero(bnSpend)) // possible?
{
LogPrintf("StealthSecretSpend(): bnSpend is zero.\n");
rv = 1;
goto End;
};
if (BN_num_bytes(bnSpend) != (int) ec_secret_size
|| BN_bn2bin(bnSpend, &secretOut.e[0]) != (int) ec_secret_size)
{
LogPrintf("StealthSecretSpend(): bnSpend incorrect length.\n");
rv = 1;
goto End;
};
End:
if (bnSpend) BN_free(bnSpend);
if (bnOrder) BN_free(bnOrder);
if (bnc) BN_free(bnc);
if (bnOutP) BN_free(bnOutP);
if (P) EC_POINT_free(P);
if (bnP) BN_free(bnP);
if (bnScanSecret) BN_free(bnScanSecret);
if (bnCtx) BN_CTX_free(bnCtx);
EC_GROUP_free(ecgrp);
return rv;
};
int StealthSharedToSecretSpend(ec_secret& sharedS, ec_secret& spendSecret, ec_secret& secretOut)
{
int rv = 0;
std::vector<uint8_t> vchOutP;
BN_CTX* bnCtx = NULL;
BIGNUM* bnc = NULL;
BIGNUM* bnOrder = NULL;
BIGNUM* bnSpend = NULL;
EC_GROUP* ecgrp = EC_GROUP_new_by_curve_name(NID_secp256k1);
if (!ecgrp)
{
LogPrintf("StealthSecretSpend(): EC_GROUP_new_by_curve_name failed.\n");
return 1;
};
if (!(bnCtx = BN_CTX_new()))
{
LogPrintf("StealthSecretSpend(): BN_CTX_new failed.\n");
rv = 1;
goto End;
};
if (!(bnc = BN_bin2bn(&sharedS.e[0], ec_secret_size, BN_new())))
{
LogPrintf("StealthSecretSpend(): BN_bin2bn failed\n");
rv = 1;
goto End;
};
if (!(bnOrder = BN_new())
|| !EC_GROUP_get_order(ecgrp, bnOrder, bnCtx))
{
LogPrintf("StealthSecretSpend(): EC_GROUP_get_order failed\n");
rv = 1;
goto End;
};
if (!(bnSpend = BN_bin2bn(&spendSecret.e[0], ec_secret_size, BN_new())))
{
LogPrintf("StealthSecretSpend(): bnSpend BN_bin2bn failed.\n");
rv = 1;
goto End;
};
//if (!BN_add(r, a, b)) return 0;
//return BN_nnmod(r, r, m, ctx);
if (!BN_mod_add(bnSpend, bnSpend, bnc, bnOrder, bnCtx))
{
LogPrintf("StealthSecretSpend(): bnSpend BN_mod_add failed.\n");
rv = 1;
goto End;
};
if (BN_is_zero(bnSpend)) // possible?
{
LogPrintf("StealthSecretSpend(): bnSpend is zero.\n");
rv = 1;
goto End;
};
if (BN_num_bytes(bnSpend) != (int) ec_secret_size
|| BN_bn2bin(bnSpend, &secretOut.e[0]) != (int) ec_secret_size)
{
LogPrintf("StealthSecretSpend(): bnSpend incorrect length.\n");
rv = 1;
goto End;
};
End:
if (bnSpend) BN_free(bnSpend);
if (bnOrder) BN_free(bnOrder);
if (bnc) BN_free(bnc);
if (bnCtx) BN_CTX_free(bnCtx);
EC_GROUP_free(ecgrp);
return rv;
};
bool IsStealthAddress(const std::string& encodedAddress)
{
data_chunk raw;
if (!DecodeBase58(encodedAddress, raw))
{
LogPrintf("IsStealthAddress DecodeBase58 falied.\n");
return false;
};
if (!VerifyChecksum(raw))
{
LogPrintf("IsStealthAddress verify_checksum falied.\n");
return false;
};
if (raw.size() < 1 + 1 + 33 + 1 + 33 + 1 + 1 + 4)
{
LogPrintf("IsStealthAddress too few bytes provided.\n");
return false;
};
uint8_t* p = &raw[0];
uint8_t version = *p++;
if (version != stealth_version_byte)
{
LogPrintf("IsStealthAddress version mismatch 0x%x != 0x%x.\n", version, stealth_version_byte);
return false;
};
return true;
};
+119
View File
@@ -0,0 +1,119 @@
// Copyright (c) 2014 The ShadowCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_STEALTHADDRESS_H
#define BITCOIN_STEALTHADDRESS_H
#include "util.h"
#include "serialize.h"
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <inttypes.h>
typedef std::vector<uint8_t> data_chunk;
const size_t ec_secret_size = 32;
const size_t ec_compressed_size = 33;
const size_t ec_uncompressed_size = 65;
typedef struct ec_secret { uint8_t e[ec_secret_size]; } ec_secret;
typedef data_chunk ec_point;
typedef uint32_t stealth_bitfield;
struct stealth_prefix
{
uint8_t number_bits;
stealth_bitfield bitfield;
};
template <typename T, typename Iterator>
T from_big_endian(Iterator in)
{
//VERIFY_UNSIGNED(T);
T out = 0;
size_t i = sizeof(T);
while (0 < i)
out |= static_cast<T>(*in++) << (8 * --i);
return out;
}
template <typename T, typename Iterator>
T from_little_endian(Iterator in)
{
//VERIFY_UNSIGNED(T);
T out = 0;
size_t i = 0;
while (i < sizeof(T))
out |= static_cast<T>(*in++) << (8 * i++);
return out;
}
class CStealthAddress
{
public:
CStealthAddress()
{
options = 0;
}
uint8_t options;
ec_point scan_pubkey;
ec_point spend_pubkey;
//std::vector<ec_point> spend_pubkeys;
size_t number_signatures;
stealth_prefix prefix;
mutable std::string label;
data_chunk scan_secret;
data_chunk spend_secret;
bool SetEncoded(const std::string& encodedAddress);
std::string Encoded() const;
bool operator <(const CStealthAddress& y) const
{
return memcmp(&scan_pubkey[0], &y.scan_pubkey[0], ec_compressed_size) < 0;
}
bool operator ==(const CStealthAddress& y) const
{
return memcmp(&scan_pubkey[0], &y.scan_pubkey[0], ec_compressed_size) == 0;
}
IMPLEMENT_SERIALIZE
(
READWRITE(this->options);
READWRITE(this->scan_pubkey);
READWRITE(this->spend_pubkey);
READWRITE(this->label);
READWRITE(this->scan_secret);
READWRITE(this->spend_secret);
);
};
void AppendChecksum(data_chunk& data);
bool VerifyChecksum(const data_chunk& data);
int GenerateRandomSecret(ec_secret& out);
int SecretToPublicKey(const ec_secret& secret, ec_point& out);
int StealthSecret(ec_secret& secret, ec_point& pubkey, const ec_point& pkSpend, ec_secret& sharedSOut, ec_point& pkOut);
int StealthSecretSpend(ec_secret& scanSecret, ec_point& ephemPubkey, ec_secret& spendSecret, ec_secret& secretOut);
int StealthSharedToSecretSpend(ec_secret& sharedS, ec_secret& spendSecret, ec_secret& secretOut);
bool IsStealthAddress(const std::string& encodedAddress);
#endif // BITCOIN_STEALTHADDRESS_H
+759 -3
View File
@@ -128,6 +128,35 @@ bool CWallet::AddCScript(const CScript& redeemScript)
return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
}
bool CWallet::Lock()
{
if (IsLocked())
return true;
{
LOCK(cs_wallet);
CWalletDB wdb(strWalletFile);
CStealthAddress sxAddrTemp;
std::set<CStealthAddress>::iterator it;
for (it = stealthAddresses.begin(); it != stealthAddresses.end(); ++it)
{
if (it->scan_secret.size() < 32)
continue;
CStealthAddress &sxAddr = const_cast<CStealthAddress&>(*it);
if (fDebug)
LogPrintf("Recrypting stealth key %s\n", sxAddr.Encoded().c_str());
sxAddrTemp.scan_pubkey = sxAddr.scan_pubkey;
if (!wdb.ReadStealthAddress(sxAddrTemp))
{
LogPrintf("Error: Failed to read stealth key from db %s\n", sxAddr.Encoded().c_str());
continue;
}
sxAddr.spend_secret = sxAddrTemp.spend_secret;
};
}
// return LockKeyStore();
return true;
}
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{
CCrypter crypter;
@@ -144,6 +173,7 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase)
if (CCryptoKeyStore::Unlock(vMasterKey))
return true;
}
// UnlockStealthAddresses(vMasterKey);
}
return false;
}
@@ -593,6 +623,10 @@ bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction&
AssertLockHeld(cs_wallet);
bool fExisted = mapWallet.count(hash);
if (fExisted && !fUpdate) return false;
mapValue_t mapNarr;
FindStealthTransactions(tx, mapNarr);
if (fExisted || IsMine(tx) || IsFromMe(tx))
{
CWalletTx wtx(this,tx);
@@ -1242,14 +1276,20 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
int64_t nTotalValue = nValue + nFeeRet;
double dPriority = 0;
// vouts to the payees
BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend)
{
CTxOut txout(s.second, s.first);
if (txout.IsDust(CTransaction::nMinRelayTxFee))
CScript::const_iterator itTxA = txout.scriptPubKey.begin();
if (!txout.IsScriptOpReturn())
{
strFailReason = _("Transaction amount too small");
return false;
if (txout.IsDust(CTransaction::nMinRelayTxFee))
{
LogPrintf("%s\n", txout.ToString());
strFailReason = _("Transaction amount too small");
return false;
}
}
wtxNew.vout.push_back(txout);
}
@@ -1428,8 +1468,724 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
return true;
}
bool CWallet::NewStealthAddress(std::string& sError, std::string& sLabel, CStealthAddress& sxAddr)
{
ec_secret scan_secret;
ec_secret spend_secret;
if (GenerateRandomSecret(scan_secret) != 0
|| GenerateRandomSecret(spend_secret) != 0)
{
sError = "GenerateRandomSecret failed.";
LogPrintf("Error CWallet::NewStealthAddress - %s\n", sError.c_str());
return false;
};
ec_point scan_pubkey, spend_pubkey;
if (SecretToPublicKey(scan_secret, scan_pubkey) != 0)
{
sError = "Could not get scan public key.";
LogPrintf("Error CWallet::NewStealthAddress - %s\n", sError.c_str());
return false;
};
if (SecretToPublicKey(spend_secret, spend_pubkey) != 0)
{
sError = "Could not get spend public key.";
LogPrintf("Error CWallet::NewStealthAddress - %s\n", sError.c_str());
return false;
};
if (fDebug)
{
LogPrintf("getnewstealthaddress: ");
LogPrintf("scan_pubkey ");
for (uint32_t i = 0; i < scan_pubkey.size(); ++i)
LogPrintf("%02x", scan_pubkey[i]);
LogPrintf("\n");
LogPrintf("spend_pubkey ");
for (uint32_t i = 0; i < spend_pubkey.size(); ++i)
LogPrintf("%02x", spend_pubkey[i]);
LogPrintf("\n");
};
sxAddr.label = sLabel;
sxAddr.scan_pubkey = scan_pubkey;
sxAddr.spend_pubkey = spend_pubkey;
sxAddr.scan_secret.resize(32);
memcpy(&sxAddr.scan_secret[0], &scan_secret.e[0], 32);
sxAddr.spend_secret.resize(32);
memcpy(&sxAddr.spend_secret[0], &spend_secret.e[0], 32);
return true;
}
bool CWallet::AddStealthAddress(CStealthAddress& sxAddr)
{
LOCK(cs_wallet);
// must add before changing spend_secret
stealthAddresses.insert(sxAddr);
bool fOwned = sxAddr.scan_secret.size() == ec_secret_size;
if (fOwned)
{
// -- owned addresses can only be added when wallet is unlocked
if (IsLocked())
{
LogPrintf("Error: CWallet::AddStealthAddress wallet must be unlocked.\n");
stealthAddresses.erase(sxAddr);
return false;
};
if (IsCrypted())
{
std::vector<unsigned char> vchCryptedSecret;
CSecret vchSecret;
vchSecret.resize(32);
memcpy(&vchSecret[0], &sxAddr.spend_secret[0], 32);
uint256 iv = Hash(sxAddr.spend_pubkey.begin(), sxAddr.spend_pubkey.end());
if (!EncryptSecret(vMasterKey, vchSecret, iv, vchCryptedSecret))
{
LogPrintf("Error: Failed encrypting stealth key %s\n", sxAddr.Encoded().c_str());
stealthAddresses.erase(sxAddr);
return false;
};
sxAddr.spend_secret = vchCryptedSecret;
};
};
bool rv = CWalletDB(strWalletFile).WriteStealthAddress(sxAddr);
if (rv)
NotifyAddressBookChanged(this, sxAddr, sxAddr.label, fOwned, "", CT_NEW);
return rv;
}
bool CWallet::UnlockStealthAddresses(const CKeyingMaterial& vMasterKeyIn)
{
// -- decrypt spend_secret of Stealth Addresses
std::set<CStealthAddress>::iterator it;
for (it = stealthAddresses.begin(); it != stealthAddresses.end(); ++it)
{
if (it->scan_secret.size() < 32)
continue; // Stealth Address is not owned
// -- CStealthAddress are only sorted on spend_pubkey
CStealthAddress &sxAddr = const_cast<CStealthAddress&>(*it);
if (fDebug)
LogPrintf("Decrypting stealth key %s\n", sxAddr.Encoded().c_str());
CSecret vchSecret;
uint256 iv = Hash(sxAddr.spend_pubkey.begin(), sxAddr.spend_pubkey.end());
if(!DecryptSecret(vMasterKeyIn, sxAddr.spend_secret, iv, vchSecret)
|| vchSecret.size() != 32)
{
LogPrintf("Error: Failed decrypting stealth key %s\n", sxAddr.Encoded().c_str());
continue;
};
ec_secret testSecret;
memcpy(&testSecret.e[0], &vchSecret[0], 32);
ec_point pkSpendTest;
if (SecretToPublicKey(testSecret, pkSpendTest) != 0
|| pkSpendTest != sxAddr.spend_pubkey)
{
LogPrintf("Error: Failed decrypting stealth key, public key mismatch %s\n", sxAddr.Encoded().c_str());
continue;
};
sxAddr.spend_secret.resize(32);
memcpy(&sxAddr.spend_secret[0], &vchSecret[0], 32);
};
CryptedKeyMap::iterator mi = mapCryptedKeys.begin();
for (; mi != mapCryptedKeys.end(); ++mi)
{
CPubKey &pubKey = (*mi).second.first;
std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
if (vchCryptedSecret.size() != 0)
continue;
CKeyID ckid = pubKey.GetID();
CBitcoinAddress addr(ckid);
StealthKeyMetaMap::iterator mi = mapStealthKeyMeta.find(ckid);
if (mi == mapStealthKeyMeta.end())
{
LogPrintf("Error: No metadata found to add secret for %s\n", addr.ToString().c_str());
continue;
};
CStealthKeyMetadata& sxKeyMeta = mi->second;
CStealthAddress sxFind;
sxFind.scan_pubkey = sxKeyMeta.pkScan.Raw();
std::set<CStealthAddress>::iterator si = stealthAddresses.find(sxFind);
if (si == stealthAddresses.end())
{
LogPrintf("No stealth key found to add secret for %s\n", addr.ToString().c_str());
continue;
};
if (fDebug)
LogPrintf("Expanding secret for %s\n", addr.ToString().c_str());
ec_secret sSpendR;
ec_secret sSpend;
ec_secret sScan;
if (si->spend_secret.size() != ec_secret_size
|| si->scan_secret.size() != ec_secret_size)
{
LogPrintf("Stealth Address has no secret key for %s\n", addr.ToString().c_str());
continue;
}
memcpy(&sScan.e[0], &si->scan_secret[0], ec_secret_size);
memcpy(&sSpend.e[0], &si->spend_secret[0], ec_secret_size);
ec_point pkEphem = sxKeyMeta.pkEphem.Raw();
if (StealthSecretSpend(sScan, pkEphem, sSpend, sSpendR) != 0)
{
LogPrintf("StealthSecretSpend() failed.\n");
continue;
};
ec_point pkTestSpendR;
if (SecretToPublicKey(sSpendR, pkTestSpendR) != 0)
{
LogPrintf("SecretToPublicKey() failed.\n");
continue;
};
CSecret vchSecret;
vchSecret.resize(ec_secret_size);
memcpy(&vchSecret[0], &sSpendR.e[0], ec_secret_size);
CKey ckey;
try {
ckey.Set(vchSecret.begin(), vchSecret.end(), true);
} catch (std::exception& e) {
LogPrintf("ckey.SetSecret() threw: %s.\n", e.what());
continue;
};
CPubKey cpkT = ckey.GetPubKey();
if (!cpkT.IsValid())
{
LogPrintf("cpkT is invalid.\n");
continue;
};
if (cpkT != pubKey)
{
LogPrintf("Error: Generated secret does not match.\n");
if (fDebug)
{
LogPrintf("cpkT %s\n", HexStr(cpkT.Raw()).c_str());
// LogPrintf("pubKey %s\n", HexStr(pubKey.Raw()).c_str());
};
continue;
};
if (!ckey.IsValid())
{
LogPrintf("Reconstructed key is invalid.\n");
continue;
};
if (fDebug)
{
CKeyID keyID = cpkT.GetID();
CBitcoinAddress coinAddress(keyID);
LogPrintf("Adding secret to key %s.\n", coinAddress.ToString().c_str());
};
if (!AddKey(ckey))
{
LogPrintf("AddKey failed.\n");
continue;
};
if (!CWalletDB(strWalletFile).EraseStealthKeyMeta(ckid))
LogPrintf("EraseStealthKeyMeta failed for %s\n", addr.ToString().c_str());
};
return true;
}
bool CWallet::UpdateStealthAddress(std::string &addr, std::string &label, bool addIfNotExist)
{
if (fDebug)
LogPrintf("UpdateStealthAddress %s\n", addr.c_str());
CStealthAddress sxAddr;
if (!sxAddr.SetEncoded(addr))
return false;
std::set<CStealthAddress>::iterator it;
it = stealthAddresses.find(sxAddr);
ChangeType nMode = CT_UPDATED;
CStealthAddress sxFound;
if (it == stealthAddresses.end())
{
if (addIfNotExist)
{
sxFound = sxAddr;
sxFound.label = label;
stealthAddresses.insert(sxFound);
nMode = CT_NEW;
} else
{
LogPrintf("UpdateStealthAddress %s, not in set\n", addr.c_str());
return false;
};
} else
{
sxFound = const_cast<CStealthAddress&>(*it);
if (sxFound.label == label)
{
// no change
return true;
};
it->label = label; // update in .stealthAddresses
if (sxFound.scan_secret.size() == ec_secret_size)
{
LogPrintf("UpdateStealthAddress: todo - update owned Stealth Address.\n");
return false;
};
};
sxFound.label = label;
if (!CWalletDB(strWalletFile).WriteStealthAddress(sxFound))
{
LogPrintf("UpdateStealthAddress(%s) Write to db failed.\n", addr.c_str());
return false;
};
bool fOwned = sxFound.scan_secret.size() == ec_secret_size;
NotifyAddressBookChanged(this, sxFound, sxFound.label, fOwned, "", nMode);
return true;
}
bool CWallet::CreateStealthTransaction(CScript scriptPubKey, int64_t nValue, std::vector<uint8_t>& P, std::vector<uint8_t>& narr, std::string& sNarr, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl)
{
vector< pair<CScript, int64_t> > vecSend;
vecSend.push_back(make_pair(scriptPubKey, nValue));
CScript scriptP = CScript() << OP_RETURN << P;
if (narr.size() > 0)
scriptP = scriptP << OP_RETURN << narr;
vecSend.push_back(make_pair(scriptP, 0));
// -- shuffle inputs, change output won't mix enough as it must be not fully random for plantext narrations
std::random_shuffle(vecSend.begin(), vecSend.end());
string strError;
// int nChangePos;
bool rv = CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, /*nChangePos,*/ strError, coinControl);
/*
// -- the change txn is inserted in a random pos, check here to match narr to output
if (rv && narr.size() > 0)
{
for (unsigned int k = 0; k < wtxNew.vout.size(); ++k)
{
if (wtxNew.vout[k].scriptPubKey != scriptPubKey
|| wtxNew.vout[k].nValue != nValue)
continue;
char key[64];
if (snprintf(key, sizeof(key), "n_%u", k) < 1)
{
LogPrintf("CreateStealthTransaction(): Error creating narration key.");
break;
};
wtxNew.mapValue[key] = sNarr;
break;
};
};
*/
return rv;
}
string CWallet::SendStealthMoney(CScript scriptPubKey, int64_t nValue, std::vector<uint8_t>& P, std::vector<uint8_t>& narr, std::string& sNarr, CWalletTx& wtxNew, bool fAskFee)
{
CReserveKey reservekey(this);
int64_t nFeeRequired;
if ( (!TestNet() && (chainActive.Height() < BLOCK_STEALTH_START))
|| (TestNet() && (chainActive.Height() < 200))
)
{
string strError = _("Error: Stealth addresses not yet supported");
LogPrintf("SendStealthMoney() : %s", strError.c_str());
return strError;
}
if (IsLocked())
{
string strError = _("Error: Wallet locked, unable to create transaction ");
LogPrintf("SendStealthMoney() : %s", strError.c_str());
return strError;
}
if (!CreateStealthTransaction(scriptPubKey, nValue, P, narr, sNarr, wtxNew, reservekey, nFeeRequired))
{
string strError;
if (nValue + nFeeRequired > GetBalance())
strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds "), FormatMoney(nFeeRequired).c_str());
else
strError = _("Error: Transaction creation failed ");
LogPrintf("SendStealthMoney() : %s", strError.c_str());
return strError;
}
/* Stealth TODO
if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired, _("Sending...")))
return "ABORTED";
*/
if (!CommitTransaction(wtxNew, reservekey))
return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
return "";
}
bool CWallet::SendStealthMoneyToDestination(CStealthAddress& sxAddress, int64_t nValue, std::string& sNarr, CWalletTx& wtxNew, std::string& sError, bool fAskFee)
{
// -- Check amount
if (nValue <= 0)
{
sError = "Invalid amount";
return false;
};
if (nValue + nTransactionFee > GetBalance())
{
sError = "Insufficient funds";
return false;
};
ec_secret ephem_secret;
ec_secret secretShared;
ec_point pkSendTo;
ec_point ephem_pubkey;
if (GenerateRandomSecret(ephem_secret) != 0)
{
sError = "GenerateRandomSecret failed.";
return false;
};
if (StealthSecret(ephem_secret, sxAddress.scan_pubkey, sxAddress.spend_pubkey, secretShared, pkSendTo) != 0)
{
sError = "Could not generate receiving public key.";
return false;
};
CPubKey cpkTo(pkSendTo);
if (!cpkTo.IsValid())
{
sError = "Invalid public key generated.";
return false;
};
CKeyID ckidTo = cpkTo.GetID();
CBitcoinAddress addrTo(ckidTo);
if (SecretToPublicKey(ephem_secret, ephem_pubkey) != 0)
{
sError = "Could not generate ephem public key.";
return false;
};
if (fDebug)
{
LogPrintf("Stealth send to generated pubkey %ui: %s\n", pkSendTo.size(), HexStr(pkSendTo).c_str());
LogPrintf("hash %s\n", addrTo.ToString().c_str());
LogPrintf("ephem_pubkey %ui: %s\n", ephem_pubkey.size(), HexStr(ephem_pubkey).c_str());
};
std::vector<unsigned char> vchNarr;
if (sNarr.length() > 0)
{
//SecMsgCrypter crypter;
//crypter.SetKey(&secretShared.e[0], &ephem_pubkey[0]);
//if (!crypter.Encrypt((uint8_t*)&sNarr[0], sNarr.length(), vchNarr))
//{
// sError = "Narration encryption failed.";
// return false;
//};
if (vchNarr.size() > 48)
{
sError = "Encrypted narration is too long.";
return false;
};
};
// -- Parse Bitcoin address
CScript scriptPubKey;
scriptPubKey.SetDestination(addrTo.Get());
if ((sError = SendStealthMoney(scriptPubKey, nValue, ephem_pubkey, vchNarr, sNarr, wtxNew, fAskFee)) != "")
return false;
return true;
}
bool CWallet::FindStealthTransactions(const CTransaction& tx, mapValue_t& mapNarr)
{
if (fDebug)
LogPrintf("FindStealthTransactions() tx: %s\n", tx.GetHash().GetHex().c_str());
// mapNarr.clear();
LOCK(cs_wallet);
ec_secret sSpendR;
ec_secret sSpend;
ec_secret sScan;
ec_secret sShared;
ec_point pkExtracted;
std::vector<uint8_t> vchEphemPK;
std::vector<uint8_t> vchDataB;
std::vector<uint8_t> vchENarr;
opcodetype opCode;
char cbuf[256];
int32_t nOutputIdOuter = -1;
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
nOutputIdOuter++;
// -- for each OP_RETURN need to check all other valid outputs
//LogPrintf("txout scriptPubKey %s\n", txout.scriptPubKey.ToString().c_str());
CScript::const_iterator itTxA = txout.scriptPubKey.begin();
if (!txout.scriptPubKey.GetOp(itTxA, opCode, vchEphemPK)
|| opCode != OP_RETURN)
continue;
else
if (!txout.scriptPubKey.GetOp(itTxA, opCode, vchEphemPK)
|| vchEphemPK.size() != 33)
{
// -- look for plaintext narrations
if (vchEphemPK.size() > 1
&& vchEphemPK[0] == 'n'
&& vchEphemPK[1] == 'p')
{
if (txout.scriptPubKey.GetOp(itTxA, opCode, vchENarr)
&& opCode == OP_RETURN
&& txout.scriptPubKey.GetOp(itTxA, opCode, vchENarr)
&& vchENarr.size() > 0)
{
std::string sNarr = std::string(vchENarr.begin(), vchENarr.end());
snprintf(cbuf, sizeof(cbuf), "n_%d", nOutputIdOuter-1); // plaintext narration always matches preceding value output
mapNarr[cbuf] = sNarr;
} else
{
LogPrintf("Warning: FindStealthTransactions() tx: %s, Could not extract plaintext narration.\n", tx.GetHash().GetHex().c_str());
};
}
continue;
}
int32_t nOutputId = -1;
nStealth++;
BOOST_FOREACH(const CTxOut& txoutB, tx.vout)
{
nOutputId++;
if (&txoutB == &txout)
continue;
bool txnMatch = false; // only 1 txn will match an ephem pk
//LogPrintf("txoutB scriptPubKey %s\n", txoutB.scriptPubKey.ToString().c_str());
CTxDestination address;
if (!ExtractDestination(txoutB.scriptPubKey, address))
continue;
if (address.type() != typeid(CKeyID))
continue;
CKeyID ckidMatch = boost::get<CKeyID>(address);
if (HaveKey(ckidMatch)) // no point checking if already have key
continue;
std::set<CStealthAddress>::iterator it;
for (it = stealthAddresses.begin(); it != stealthAddresses.end(); ++it)
{
if (it->scan_secret.size() != ec_secret_size)
continue; // Stealth Address is not owned
//LogPrintf("it->Encodeded() %s\n", it->Encoded().c_str());
memcpy(&sScan.e[0], &it->scan_secret[0], ec_secret_size);
if (StealthSecret(sScan, vchEphemPK, it->spend_pubkey, sShared, pkExtracted) != 0)
{
LogPrintf("StealthSecret failed.\n");
continue;
};
//LogPrintf("pkExtracted %"PRIszu": %s\n", pkExtracted.size(), HexStr(pkExtracted).c_str());
CPubKey cpkE(pkExtracted);
if (!cpkE.IsValid())
continue;
CKeyID ckidE = cpkE.GetID();
if (ckidMatch != ckidE)
continue;
if (fDebug)
LogPrintf("Found stealth txn to address %s\n", it->Encoded().c_str());
if (IsLocked())
{
if (fDebug)
LogPrintf("Wallet is locked, adding key without secret.\n");
// -- add key without secret
std::vector<uint8_t> vchEmpty;
AddCryptedKey(cpkE, vchEmpty);
CKeyID keyId = cpkE.GetID();
CBitcoinAddress coinAddress(keyId);
std::string sLabel = it->Encoded();
std::string sPurpose;
SetAddressBook(keyId, sLabel, sPurpose);
CPubKey cpkEphem(vchEphemPK);
CPubKey cpkScan(it->scan_pubkey);
CStealthKeyMetadata lockedSkMeta(cpkEphem, cpkScan);
if (!CWalletDB(strWalletFile).WriteStealthKeyMeta(keyId, lockedSkMeta))
LogPrintf("WriteStealthKeyMeta failed for %s\n", coinAddress.ToString().c_str());
mapStealthKeyMeta[keyId] = lockedSkMeta;
nFoundStealth++;
} else
{
if (it->spend_secret.size() != ec_secret_size)
continue;
memcpy(&sSpend.e[0], &it->spend_secret[0], ec_secret_size);
if (StealthSharedToSecretSpend(sShared, sSpend, sSpendR) != 0)
{
LogPrintf("StealthSharedToSecretSpend() failed.\n");
continue;
};
ec_point pkTestSpendR;
if (SecretToPublicKey(sSpendR, pkTestSpendR) != 0)
{
LogPrintf("SecretToPublicKey() failed.\n");
continue;
};
CSecret vchSecret;
vchSecret.resize(ec_secret_size);
memcpy(&vchSecret[0], &sSpendR.e[0], ec_secret_size);
CKey ckey;
try {
ckey.Set(vchSecret.begin(), vchSecret.end(), true);
} catch (std::exception& e) {
LogPrintf("ckey.SetSecret() threw: %s.\n", e.what());
continue;
};
CPubKey cpkT = ckey.GetPubKey();
if (!cpkT.IsValid())
{
LogPrintf("cpkT is invalid.\n");
continue;
};
if (!ckey.IsValid())
{
LogPrintf("Reconstructed key is invalid.\n");
continue;
};
CKeyID keyID = cpkT.GetID();
if (fDebug)
{
CBitcoinAddress coinAddress(keyID);
LogPrintf("Adding key %s.\n", coinAddress.ToString().c_str());
};
if (!AddKey(ckey))
{
LogPrintf("AddKey failed.\n");
continue;
};
std::string sLabel = it->Encoded();
std::string sPurpose;
SetAddressBook(keyID, sLabel, sPurpose);
nFoundStealth++;
};
if (txout.scriptPubKey.GetOp(itTxA, opCode, vchENarr)
&& opCode == OP_RETURN
&& txout.scriptPubKey.GetOp(itTxA, opCode, vchENarr)
&& vchENarr.size() > 0)
{
//SecMsgCrypter crypter;
//crypter.SetKey(&sShared.e[0], &vchEphemPK[0]);
//std::vector<uint8_t> vchNarr;
//if (!crypter.Decrypt(&vchENarr[0], vchENarr.size(), vchNarr))
//{
// LogPrintf("Decrypt narration failed.\n");
// continue;
//};
//std::string sNarr = std::string(vchNarr.begin(), vchNarr.end());
//snprintf(cbuf, sizeof(cbuf), "n_%d", nOutputId);
//mapNarr[cbuf] = sNarr;
};
txnMatch = true;
break;
};
if (txnMatch)
break;
};
};
return true;
};
string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew)
{
+20 -4
View File
@@ -13,6 +13,7 @@
#include "ui_interface.h"
#include "util.h"
#include "walletdb.h"
#include "stealthaddress.h"
#include <algorithm>
#include <map>
@@ -39,6 +40,8 @@ class CReserveKey;
class CScript;
class CWalletTx;
typedef std::map<CKeyID, CStealthKeyMetadata> StealthKeyMetaMap;
/** (client) version numbers for particular wallet features */
enum WalletFeature
{
@@ -94,6 +97,8 @@ public:
StringMap destdata;
};
typedef std::map<std::string, std::string> mapValue_t;
/** A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
* and provides the ability to create new transactions.
*/
@@ -141,6 +146,10 @@ public:
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID;
std::set<CStealthAddress> stealthAddresses;
StealthKeyMetaMap mapStealthKeyMeta;
uint32_t nStealth, nFoundStealth;
CWallet()
{
SetNull();
@@ -222,6 +231,7 @@ public:
/// Look up a destination data tuple in the store, return true if found false otherwise
bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
bool Lock();
bool Unlock(const SecureString& strWalletPassphrase);
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase);
@@ -258,6 +268,16 @@ public:
bool CreateTransaction(CScript scriptPubKey, int64_t nValue,
CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL);
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
bool NewStealthAddress(std::string& sError, std::string& sLabel, CStealthAddress& sxAddr);
bool AddStealthAddress(CStealthAddress& sxAddr);
bool UnlockStealthAddresses(const CKeyingMaterial& vMasterKeyIn);
bool UpdateStealthAddress(std::string &addr, std::string &label, bool addIfNotExist);
bool CreateStealthTransaction(CScript scriptPubKey, int64_t nValue, std::vector<uint8_t>& P, std::vector<uint8_t>& narr, std::string& sNarr, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl = NULL);
string SendStealthMoney(CScript scriptPubKey, int64_t nValue, std::vector<uint8_t>& P, std::vector<uint8_t>& narr, std::string& sNarr, CWalletTx& wtxNew, bool fAskFee);
bool SendStealthMoneyToDestination(CStealthAddress& sxAddress, int64_t nValue, std::string& sNarr, CWalletTx& wtxNew, std::string& sError, bool fAskFee = false);
bool FindStealthTransactions(const CTransaction& tx, mapValue_t& mapNarr);
std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew);
std::string SendMoneyToDestination(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew);
@@ -422,10 +442,6 @@ public:
void KeepKey();
};
typedef std::map<std::string, std::string> mapValue_t;
static void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
{
if (!mapValue.count("n"))
+47
View File
@@ -112,6 +112,31 @@ bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false);
}
bool CWalletDB::WriteStealthKeyMeta(const CKeyID& keyId, const CStealthKeyMetadata& sxKeyMeta)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("sxKeyMeta"), keyId), sxKeyMeta, true);
}
bool CWalletDB::EraseStealthKeyMeta(const CKeyID& keyId)
{
nWalletDBUpdated++;
return Erase(std::make_pair(std::string("sxKeyMeta"), keyId));
}
bool CWalletDB::WriteStealthAddress(const CStealthAddress& sxAddr)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("sxAddr"), sxAddr.scan_pubkey), sxAddr, true);
}
bool CWalletDB::ReadStealthAddress(CStealthAddress& sxAddr)
{
// -- set scan_pubkey before reading
return Read(std::make_pair(std::string("sxAddr"), sxAddr.scan_pubkey), sxAddr);
}
bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
{
nWalletDBUpdated++;
@@ -566,6 +591,28 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
return false;
}
}
else if (strType == "sxAddr")
{
if (fDebug)
LogPrintf("WalletDB ReadKeyValue sxAddr\n");
CStealthAddress sxAddr;
ssValue >> sxAddr;
pwallet->stealthAddresses.insert(sxAddr);
}
else if (strType == "sxKeyMeta")
{
if (fDebug)
LogPrintf("WalletDB ReadKeyValue sxKeyMeta\n");
CKeyID keyId;
ssKey >> keyId;
CStealthKeyMetadata sxKeyMeta;
ssValue >> sxKeyMeta;
pwallet->mapStealthKeyMeta[keyId] = sxKeyMeta;
}
} catch (...)
{
return false;
+29
View File
@@ -7,6 +7,7 @@
#include "db.h"
#include "key.h"
#include "stealthaddress.h"
#include <list>
#include <stdint.h>
@@ -67,6 +68,29 @@ public:
}
};
class CStealthKeyMetadata
{
// -- used to get secret for keys created by stealth transaction with wallet locked
public:
CStealthKeyMetadata() {};
CStealthKeyMetadata(CPubKey pkEphem_, CPubKey pkScan_)
{
pkEphem = pkEphem_;
pkScan = pkScan_;
};
CPubKey pkEphem;
CPubKey pkScan;
IMPLEMENT_SERIALIZE
(
READWRITE(pkEphem);
READWRITE(pkScan);
)
};
/** Access to the wallet database (wallet.dat) */
class CWalletDB : public CDB
{
@@ -93,6 +117,11 @@ public:
bool WriteCScript(const uint160& hash, const CScript& redeemScript);
bool WriteStealthKeyMeta(const CKeyID& keyId, const CStealthKeyMetadata& sxKeyMeta);
bool EraseStealthKeyMeta(const CKeyID& keyId);
bool WriteStealthAddress(const CStealthAddress& sxAddr);
bool ReadStealthAddress(CStealthAddress& sxAddr);
bool WriteBestBlock(const CBlockLocator& locator);
bool ReadBestBlock(CBlockLocator& locator);