mirror of
https://github.com/jwinterm/myriadcoin.git
synced 2026-10-17 05:41:34 +02:00
+3
-1
@@ -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 = \
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>760</width>
|
||||
<width>960</width>
|
||||
<height>380</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
@@ -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
@@ -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"))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user