mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-19 13:27:35 +02:00
Wallet, RPC, bitcoin-tx, etc updates for confidential transactions
This commit is contained in:
parent
74987d30b1
commit
292f923d03
29 changed files with 968 additions and 175 deletions
|
|
@ -75,6 +75,7 @@ BITCOIN_CORE_H = \
|
|||
allocators.h \
|
||||
amount.h \
|
||||
base58.h \
|
||||
blind.h \
|
||||
bloom.h \
|
||||
callrpc.h \
|
||||
chain.h \
|
||||
|
|
@ -232,6 +233,7 @@ libbitcoin_common_a_SOURCES = \
|
|||
allocators.cpp \
|
||||
amount.cpp \
|
||||
base58.cpp \
|
||||
blind.cpp \
|
||||
bloom.cpp \
|
||||
chainparams.cpp \
|
||||
coins.cpp \
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ BITCOIN_TESTS =\
|
|||
test/base32_tests.cpp \
|
||||
test/base58_tests.cpp \
|
||||
test/base64_tests.cpp \
|
||||
test/blind_tests.cpp \
|
||||
test/bloom_tests.cpp \
|
||||
test/checkblock_tests.cpp \
|
||||
test/Checkpoints_tests.cpp \
|
||||
|
|
|
|||
|
|
@ -235,21 +235,58 @@ bool CBitcoinAddress::Set(const CTxDestination& dest)
|
|||
return boost::apply_visitor(CBitcoinAddressVisitor(this), dest);
|
||||
}
|
||||
|
||||
CBitcoinAddress& CBitcoinAddress::AddBlindingKey(const CPubKey& pubkey)
|
||||
{
|
||||
assert(pubkey.size() == 33);
|
||||
assert(!IsBlinded());
|
||||
std::vector<unsigned char> data = vchVersion;
|
||||
data.insert(data.end(), pubkey.begin(), pubkey.end());
|
||||
data.insert(data.end(), vchData.begin(), vchData.end());
|
||||
SetData(Params().Base58Prefix(CChainParams::BLINDED_ADDRESS), &data[0], data.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
CPubKey CBitcoinAddress::GetBlindingKey() const
|
||||
{
|
||||
assert(IsBlinded());
|
||||
CPubKey pubkey(&vchData[1], &vchData[34]);
|
||||
return pubkey;
|
||||
}
|
||||
|
||||
bool CBitcoinAddress::IsBlinded(const CChainParams& params) const
|
||||
{
|
||||
return (vchVersion == params.Base58Prefix(CChainParams::BLINDED_ADDRESS) && vchData.size() > 34);
|
||||
}
|
||||
|
||||
bool CBitcoinAddress::IsValid() const
|
||||
{
|
||||
return IsValid(Params());
|
||||
}
|
||||
|
||||
CBitcoinAddress CBitcoinAddress::GetUnblinded() const
|
||||
{
|
||||
CBitcoinAddress subaddr;
|
||||
subaddr.SetData(std::vector<unsigned char>(&vchData[0], &vchData[1]), &vchData[34], vchData.size() - 34);
|
||||
return subaddr;
|
||||
}
|
||||
|
||||
bool CBitcoinAddress::IsValid(const CChainParams& params) const
|
||||
{
|
||||
if (IsBlinded(params)) {
|
||||
return GetUnblinded().IsValid(params);
|
||||
}
|
||||
bool fCorrectSize = vchData.size() == 20;
|
||||
bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) ||
|
||||
vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
|
||||
return fCorrectSize && fKnownVersion;
|
||||
}
|
||||
|
||||
|
||||
CTxDestination CBitcoinAddress::Get() const
|
||||
{
|
||||
if (IsBlinded()) {
|
||||
return GetUnblinded().Get();
|
||||
}
|
||||
if (!IsValid())
|
||||
return CNoDestination();
|
||||
uint160 id;
|
||||
|
|
@ -264,6 +301,9 @@ CTxDestination CBitcoinAddress::Get() const
|
|||
|
||||
bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const
|
||||
{
|
||||
if (IsBlinded()) {
|
||||
return GetUnblinded().GetKeyID(keyID);
|
||||
}
|
||||
if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
|
||||
return false;
|
||||
uint160 id;
|
||||
|
|
@ -274,6 +314,9 @@ bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const
|
|||
|
||||
bool CBitcoinAddress::IsScript() const
|
||||
{
|
||||
if (IsBlinded()) {
|
||||
return GetUnblinded().IsScript();
|
||||
}
|
||||
return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@ public:
|
|||
* The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
|
||||
*/
|
||||
class CBitcoinAddress : public CBase58Data {
|
||||
private:
|
||||
|
||||
public:
|
||||
bool Set(const CKeyID &id);
|
||||
bool Set(const CScriptID &id);
|
||||
|
|
@ -116,6 +118,11 @@ public:
|
|||
CTxDestination Get() const;
|
||||
bool GetKeyID(CKeyID &keyID) const;
|
||||
bool IsScript() const;
|
||||
|
||||
CBitcoinAddress& AddBlindingKey(const CPubKey &pubkey);
|
||||
CPubKey GetBlindingKey() const;
|
||||
CBitcoinAddress GetUnblinded() const;
|
||||
bool IsBlinded(const CChainParams& params = Params()) const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "base58.h"
|
||||
#include "blind.h"
|
||||
#include "clientversion.h"
|
||||
#include "primitives/block.h" // for MAX_BLOCK_SIZE
|
||||
#include "primitives/transaction.h"
|
||||
|
|
@ -72,7 +73,8 @@ static bool AppInitRawTx(int argc, char* argv[])
|
|||
strUsage = _("Commands:") + "\n";
|
||||
strUsage += " delin=N " + _("Delete input N from TX") + "\n";
|
||||
strUsage += " delout=N " + _("Delete output N from TX") + "\n";
|
||||
strUsage += " in=TXID:VOUT[:SEQ] " + _("Add input to TX") + "\n";
|
||||
strUsage += " in=TXID:VOUT:VALUE:SEQ " + _("Add input to TX") + "\n";
|
||||
strUsage += " blind=B1::B3:B4:... " + _("Blind transaction outputs") + "\n";
|
||||
strUsage += " locktime=N " + _("Set TX lock time to N") + "\n";
|
||||
strUsage += " nversion=N " + _("Set TX version to N") + "\n";
|
||||
strUsage += " outaddr=VALUE:ADDRESS " + _("Add address-based output to TX") + "\n";
|
||||
|
|
@ -201,9 +203,18 @@ static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)
|
|||
// Remove txid
|
||||
string strVout = strInput.substr(pos + 1, string::npos);
|
||||
|
||||
// extract and validate VALUE
|
||||
pos = strVout.find(':');
|
||||
if (pos == string::npos)
|
||||
throw runtime_error("TX input missing separator");
|
||||
string strValue = strVout.substr(0, pos);
|
||||
CAmount value;
|
||||
if (!ParseMoney(strValue, value))
|
||||
throw runtime_error("invalid TX output value");
|
||||
|
||||
// extract and validate sequence number
|
||||
uint32_t nSequence = ~(uint32_t)0;
|
||||
pos = strVout.find(':');
|
||||
pos = strVout.find(':', pos + 1);
|
||||
if (pos != string::npos) {
|
||||
if ((pos == 0) || (pos == (strVout.size() - 1)))
|
||||
throw runtime_error("empty TX input field");
|
||||
|
|
@ -224,6 +235,8 @@ static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)
|
|||
}
|
||||
|
||||
nSequence = (uint32_t)nSeq;
|
||||
} else {
|
||||
throw runtime_error("invalid TX input: sequence missing");
|
||||
}
|
||||
|
||||
// extract and validate vout
|
||||
|
|
@ -234,6 +247,7 @@ static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)
|
|||
// append to transaction input list
|
||||
CTxIn txin(txid, vout, CScript(), nSequence);
|
||||
tx.vin.push_back(txin);
|
||||
tx.nTxFee += value;
|
||||
}
|
||||
|
||||
static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
|
||||
|
|
@ -262,9 +276,57 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
|
|||
|
||||
// construct TxOut, append to transaction output list
|
||||
CTxOut txout(value, scriptPubKey);
|
||||
if (addr.IsBlinded()) {
|
||||
CPubKey pubkey = addr.GetBlindingKey();
|
||||
txout.nValue.vchNonceCommitment = std::vector<unsigned char>(pubkey.begin(), pubkey.end());
|
||||
}
|
||||
tx.vout.push_back(txout);
|
||||
}
|
||||
|
||||
static void MutateTxBlind(CMutableTransaction& tx, const string& strInput)
|
||||
{
|
||||
std::vector<std::string> input_blinding_factors;
|
||||
boost::split(input_blinding_factors, strInput, boost::is_any_of(":"));
|
||||
|
||||
if (input_blinding_factors.size() != tx.vin.size())
|
||||
throw runtime_error("One input blinding factor required per transaction input");
|
||||
|
||||
bool fBlindedIns = false;
|
||||
bool fBlindedOuts = false;
|
||||
std::vector<std::vector<unsigned char> > input_blinds;
|
||||
std::vector<std::vector<unsigned char> > output_blinds;
|
||||
std::vector<CPubKey> output_pubkeys;
|
||||
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
|
||||
std::vector<unsigned char> blind = ParseHex(input_blinding_factors[nIn]);
|
||||
if (blind.size() == 0) {
|
||||
input_blinds.push_back(blind);
|
||||
} else if (blind.size() == 32) {
|
||||
input_blinds.push_back(blind);
|
||||
fBlindedIns = true;
|
||||
}
|
||||
}
|
||||
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
|
||||
if (!tx.vout[nOut].nValue.IsAmount())
|
||||
throw runtime_error("Invalid parameter: transaction outputs must be unblinded");
|
||||
if (tx.vout[nOut].nValue.vchNonceCommitment.size() == 0) {
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
} else {
|
||||
CPubKey pubkey(tx.vout[nOut].nValue.vchNonceCommitment);
|
||||
if (!pubkey.IsValid()) {
|
||||
throw runtime_error("Invalid parameter: invalid confidentiality public key given");
|
||||
}
|
||||
output_pubkeys.push_back(pubkey);
|
||||
fBlindedOuts = true;
|
||||
}
|
||||
output_blinds.push_back(std::vector<unsigned char>(0, 0));
|
||||
}
|
||||
|
||||
if (fBlindedIns && !fBlindedOuts) {
|
||||
throw runtime_error("Confidential inputs without confidential outputs");
|
||||
}
|
||||
BlindOutputs(input_blinds, output_blinds, output_pubkeys, tx);
|
||||
}
|
||||
|
||||
static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput)
|
||||
{
|
||||
// separate VALUE:SCRIPT in string
|
||||
|
|
@ -286,10 +348,13 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput
|
|||
// construct TxOut, append to transaction output list
|
||||
CTxOut txout(value, scriptPubKey);
|
||||
tx.vout.push_back(txout);
|
||||
tx.nTxFee -= value;
|
||||
}
|
||||
|
||||
static void MutateTxDelInput(CMutableTransaction& tx, const string& strInIdx)
|
||||
{
|
||||
// TODO: reduce nTxFee
|
||||
|
||||
// parse requested deletion index
|
||||
int inIdx = atoi(strInIdx);
|
||||
if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
|
||||
|
|
@ -303,6 +368,8 @@ static void MutateTxDelInput(CMutableTransaction& tx, const string& strInIdx)
|
|||
|
||||
static void MutateTxDelOutput(CMutableTransaction& tx, const string& strOutIdx)
|
||||
{
|
||||
// TODO: increase nTxFee
|
||||
|
||||
// parse requested deletion index
|
||||
int outIdx = atoi(strOutIdx);
|
||||
if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
|
||||
|
|
@ -552,8 +619,8 @@ static void MutateTxWithdrawSign(CMutableTransaction& tx, const string& flagStr)
|
|||
class Secp256k1Init
|
||||
{
|
||||
public:
|
||||
Secp256k1Init() { ECC_Start(); }
|
||||
~Secp256k1Init() { ECC_Stop(); }
|
||||
Secp256k1Init() { ECC_Start(); ECC_Blinding_Start(); }
|
||||
~Secp256k1Init() { ECC_Stop(); ECC_Blinding_Stop(); }
|
||||
};
|
||||
|
||||
static void MutateTx(CMutableTransaction& tx, const string& command,
|
||||
|
|
@ -581,6 +648,9 @@ static void MutateTx(CMutableTransaction& tx, const string& command,
|
|||
else if (command == "sign") {
|
||||
if (!ecc) { ecc.reset(new Secp256k1Init()); }
|
||||
MutateTxSign(tx, commandVal);
|
||||
} else if (command == "blind") {
|
||||
if (!ecc) { ecc.reset(new Secp256k1Init()); }
|
||||
MutateTxBlind(tx, commandVal);
|
||||
} else if (command == "withdrawsign")
|
||||
MutateTxWithdrawSign(tx, commandVal);
|
||||
|
||||
|
|
|
|||
137
src/blind.cpp
Normal file
137
src/blind.cpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#include "blind.h"
|
||||
|
||||
#include "hash.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "random.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <secp256k1.h>
|
||||
|
||||
static secp256k1_context_t* secp256k1_context = NULL;
|
||||
|
||||
void ECC_Blinding_Start() {
|
||||
assert(secp256k1_context == NULL);
|
||||
|
||||
secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_COMMIT | SECP256K1_CONTEXT_RANGEPROOF);
|
||||
assert(ctx != NULL);
|
||||
|
||||
secp256k1_context = ctx;
|
||||
}
|
||||
|
||||
void ECC_Blinding_Stop() {
|
||||
secp256k1_context_t *ctx = secp256k1_context;
|
||||
secp256k1_context = NULL;
|
||||
|
||||
if (ctx) {
|
||||
secp256k1_context_destroy(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
const secp256k1_context_t* ECC_Blinding_Context() {
|
||||
return secp256k1_context;
|
||||
}
|
||||
|
||||
int UnblindOutput(const CKey &key, const CTxOut& txout, CAmount& amount_out, std::vector<unsigned char>& blinding_factor_out)
|
||||
{
|
||||
if (txout.nValue.IsAmount()) {
|
||||
amount_out = txout.nValue.GetAmount();
|
||||
blinding_factor_out.resize(0);
|
||||
return -1;
|
||||
}
|
||||
CPubKey ephemeral_key(txout.nValue.vchNonceCommitment);
|
||||
if (!ephemeral_key.IsValid()) {
|
||||
return 0;
|
||||
}
|
||||
CPubKey nonce_key = key.ECDH(ephemeral_key);
|
||||
unsigned char nonce[32];
|
||||
CHash256().Write(nonce_key.begin(), nonce_key.size()).Finalize(nonce);
|
||||
unsigned char msg[4096];
|
||||
int msg_size;
|
||||
uint64_t min_value, max_value, amount;
|
||||
blinding_factor_out.resize(32);
|
||||
int res = secp256k1_rangeproof_rewind(secp256k1_context, &blinding_factor_out[0], &amount, msg, &msg_size, nonce, &min_value, &max_value, &txout.nValue.vchCommitment[0], &txout.nValue.vchRangeproof[0], txout.nValue.vchRangeproof.size());
|
||||
if (!res || amount > (uint64_t)MAX_MONEY || !MoneyRange((CAmount)amount)) {
|
||||
amount_out = 0;
|
||||
blinding_factor_out.resize(0);
|
||||
} else
|
||||
amount_out = (CAmount)amount;
|
||||
return res ? 1 : 0;
|
||||
}
|
||||
|
||||
void BlindOutputs(const std::vector<std::vector<unsigned char> >& input_blinding_factors, const std::vector<std::vector<unsigned char> >& output_blinding_factors, const std::vector<CPubKey>& output_pubkeys, CMutableTransaction& tx)
|
||||
{
|
||||
assert(tx.vout.size() == output_blinding_factors.size());
|
||||
assert(tx.vout.size() == output_pubkeys.size());
|
||||
assert(tx.vin.size() == input_blinding_factors.size());
|
||||
|
||||
std::vector<const unsigned char*> blindptrs;
|
||||
blindptrs.reserve(tx.vout.size() + tx.vin.size());
|
||||
|
||||
int nBlindsIn = 0;
|
||||
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
|
||||
if (input_blinding_factors[nIn].size() != 0) {
|
||||
assert(input_blinding_factors[nIn].size() == 32);
|
||||
blindptrs.push_back(&input_blinding_factors[nIn][0]);
|
||||
nBlindsIn++;
|
||||
}
|
||||
}
|
||||
|
||||
int nBlindsOut = 0;
|
||||
int nToBlind = 0;
|
||||
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
|
||||
assert((output_blinding_factors[nOut].size() != 0) == !tx.vout[nOut].nValue.IsAmount());
|
||||
if (output_blinding_factors[nOut].size() != 0) {
|
||||
assert(output_blinding_factors[nOut].size() == 32);
|
||||
blindptrs.push_back(&output_blinding_factors[nOut][0]);
|
||||
nBlindsOut++;
|
||||
} else {
|
||||
if (output_pubkeys[nOut].IsValid()) {
|
||||
nToBlind++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nBlindsIn != 0) {
|
||||
assert((nBlindsOut + nToBlind) != 0);
|
||||
}
|
||||
|
||||
int nBlinded = 0;
|
||||
unsigned char blind[nToBlind][32];
|
||||
|
||||
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
|
||||
if (tx.vout[nOut].nValue.IsAmount() && output_pubkeys[nOut].IsValid()) {
|
||||
assert(output_pubkeys[nOut].IsValid());
|
||||
if (nBlinded + 1 == nToBlind) {
|
||||
// Last to-be-blinded value: compute from all other blinding factors.
|
||||
assert(secp256k1_pedersen_blind_sum(ECC_Blinding_Context(), &blind[nBlinded][0], &blindptrs[0], nBlindsOut + nBlindsIn, nBlindsIn));
|
||||
blindptrs.push_back(&blind[nBlinded++][0]);
|
||||
} else {
|
||||
GetRandBytes(&blind[nBlinded][0], 32);
|
||||
blindptrs.push_back(&blind[nBlinded++][0]);
|
||||
}
|
||||
nBlindsOut++;
|
||||
// Create blinded value
|
||||
CTxOutValue& value = tx.vout[nOut].nValue;
|
||||
CAmount amount = value.GetAmount();
|
||||
assert(secp256k1_pedersen_commit(ECC_Blinding_Context(), &value.vchCommitment[0], (unsigned char*)blindptrs.back(), amount));
|
||||
// Generate ephemeral key for ECDH nonce generation
|
||||
CKey ephemeral_key;
|
||||
ephemeral_key.MakeNewKey(true);
|
||||
CPubKey ephemeral_pubkey = ephemeral_key.GetPubKey();
|
||||
value.vchNonceCommitment.resize(33);
|
||||
memcpy(&value.vchNonceCommitment[0], &ephemeral_pubkey[0], 33);
|
||||
// Generate nonce
|
||||
CPubKey nonce_key = ephemeral_key.ECDH(output_pubkeys[nOut]);
|
||||
unsigned char nonce[32];
|
||||
CHash256().Write(nonce_key.begin(), nonce_key.size()).Finalize(nonce);
|
||||
// Create range proof
|
||||
int nRangeProofLen = 5134;
|
||||
// TODO: smarter min_value selection
|
||||
value.vchRangeproof.resize(nRangeProofLen);
|
||||
int res = secp256k1_rangeproof_sign(ECC_Blinding_Context(), &value.vchRangeproof[0], &nRangeProofLen, 0, &value.vchCommitment[0], blindptrs.back(), nonce, std::min(std::max((int)GetArg("-ct_exponent", 0), -1),18), std::min(std::max((int)GetArg("-ct_bits", 32), 1), 51), amount);
|
||||
value.vchRangeproof.resize(nRangeProofLen);
|
||||
// TODO: do something smarter here
|
||||
assert(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/blind.h
Normal file
14
src/blind.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef BITCOIN_BLIND_H_
|
||||
#define BITCOIN_BLIND_H_ 1
|
||||
|
||||
#include "key.h"
|
||||
#include "pubkey.h"
|
||||
#include "primitives/transaction.h"
|
||||
|
||||
void ECC_Blinding_Start();
|
||||
void ECC_Blinding_Stop();
|
||||
|
||||
int UnblindOutput(const CKey& blinding_key, const CTxOut& txout, CAmount& amount_out, std::vector<unsigned char>& blinding_factor_out);
|
||||
void BlindOutputs(const std::vector<std::vector<unsigned char> >& input_blinding_factors, const std::vector<std::vector<unsigned char> >& output_blinding_factors, const std::vector<CPubKey>& output_pubkeys, CMutableTransaction& tx);
|
||||
|
||||
#endif
|
||||
|
|
@ -140,6 +140,7 @@ public:
|
|||
|
||||
base58Prefixes[PUBKEY_ADDRESS] = list_of(0);
|
||||
base58Prefixes[SCRIPT_ADDRESS] = list_of(5);
|
||||
base58Prefixes[BLINDED_ADDRESS]= list_of(10);
|
||||
base58Prefixes[SECRET_KEY] = list_of(128);
|
||||
base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x88)(0xB2)(0x1E);
|
||||
base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x88)(0xAD)(0xE4);
|
||||
|
|
@ -200,6 +201,7 @@ public:
|
|||
|
||||
base58Prefixes[PUBKEY_ADDRESS] = list_of(111);
|
||||
base58Prefixes[SCRIPT_ADDRESS] = list_of(196);
|
||||
base58Prefixes[BLINDED_ADDRESS]= list_of(25);
|
||||
base58Prefixes[SECRET_KEY] = list_of(239);
|
||||
base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x35)(0x87)(0xCF);
|
||||
base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x35)(0x83)(0x94);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public:
|
|||
enum Base58Type {
|
||||
PUBKEY_ADDRESS,
|
||||
SCRIPT_ADDRESS,
|
||||
BLINDED_ADDRESS,
|
||||
SECRET_KEY,
|
||||
EXT_PUBLIC_KEY,
|
||||
EXT_SECRET_KEY,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "addrman.h"
|
||||
#include "amount.h"
|
||||
#include "blind.h"
|
||||
#include "checkpoints.h"
|
||||
#include "compat/sanity.h"
|
||||
#include "key.h"
|
||||
|
|
@ -196,6 +197,7 @@ void Shutdown()
|
|||
delete pwalletMain;
|
||||
pwalletMain = NULL;
|
||||
#endif
|
||||
ECC_Blinding_Stop();
|
||||
ECC_Stop();
|
||||
ECC_Verify_Stop();
|
||||
LogPrintf("%s: done\n", __func__);
|
||||
|
|
@ -772,6 +774,7 @@ bool AppInit2(boost::thread_group& threadGroup)
|
|||
// ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log
|
||||
|
||||
// Initialize elliptic curve code
|
||||
ECC_Blinding_Start();
|
||||
ECC_Verify_Start();
|
||||
ECC_Start();
|
||||
|
||||
|
|
|
|||
11
src/key.cpp
11
src/key.cpp
|
|
@ -58,6 +58,17 @@ CPubKey CKey::GetPubKey() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
CPubKey CKey::ECDH(const CPubKey& pubkey) const {
|
||||
assert(fValid);
|
||||
CPubKey result = pubkey;
|
||||
int clen = result.size();
|
||||
int ret = secp256k1_point_multiply((unsigned char*)result.begin(), &clen, begin());
|
||||
assert((int)result.size() == clen);
|
||||
assert(ret);
|
||||
assert(result.IsValid());
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, uint32_t test_case) const {
|
||||
if (!fValid)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -122,6 +122,11 @@ public:
|
|||
*/
|
||||
CPubKey GetPubKey() const;
|
||||
|
||||
/**
|
||||
* Compute the ECDH exchange result using this private key and another public key.
|
||||
*/
|
||||
CPubKey ECDH(const CPubKey& pubkey) const;
|
||||
|
||||
/**
|
||||
* Create a DER-serialized signature.
|
||||
* The test_case parameter tweaks the deterministic nonce.
|
||||
|
|
|
|||
|
|
@ -185,12 +185,12 @@ unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const
|
|||
std::string CTransaction::ToString() const
|
||||
{
|
||||
std::string str;
|
||||
str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n",
|
||||
str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u, fee=%u)\n",
|
||||
GetHash().ToString().substr(0,10),
|
||||
nVersion,
|
||||
vin.size(),
|
||||
vout.size(),
|
||||
nLockTime);
|
||||
nLockTime, nTxFee);
|
||||
for (unsigned int i = 0; i < vin.size(); i++)
|
||||
str += " " + vin[i].ToString() + "\n";
|
||||
for (unsigned int i = 0; i < vout.size(); i++)
|
||||
|
|
|
|||
|
|
@ -506,10 +506,10 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
|
|||
nQuantity++;
|
||||
|
||||
// Amount
|
||||
nAmount += out.tx->vout[out.i].nValue;
|
||||
nAmount += out.tx->GetValueOut(out.i);
|
||||
|
||||
// Priority
|
||||
dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
|
||||
dPriorityInputs += (double)COIN * (out.nDepth+1);
|
||||
|
||||
// Bytes
|
||||
CTxDestination address;
|
||||
|
|
@ -708,7 +708,7 @@ void CoinControlDialog::updateView()
|
|||
BOOST_FOREACH(const COutput& out, coins.second)
|
||||
{
|
||||
int nInputSize = 0;
|
||||
nSum += out.tx->vout[out.i].nValue;
|
||||
nSum += out.tx->GetValueOut(out.i);
|
||||
nChildren++;
|
||||
|
||||
QTreeWidgetItem *itemOutput;
|
||||
|
|
@ -750,8 +750,8 @@ void CoinControlDialog::updateView()
|
|||
}
|
||||
|
||||
// amount
|
||||
itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue));
|
||||
itemOutput->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(out.tx->vout[out.i].nValue), 15, " ")); // padding so that sorting works correctly
|
||||
itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->GetValueOut(out.i)));
|
||||
itemOutput->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(out.tx->GetValueOut(out.i)), 15, " ")); // padding so that sorting works correctly
|
||||
|
||||
// date
|
||||
itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime()));
|
||||
|
|
@ -761,10 +761,10 @@ void CoinControlDialog::updateView()
|
|||
itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " "));
|
||||
|
||||
// priority
|
||||
double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10
|
||||
double dPriority = ((double)COIN / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10
|
||||
itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority));
|
||||
itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPriority), 20, " "));
|
||||
dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
|
||||
dPrioritySum += (double)COIN * (out.nDepth+1);
|
||||
nInputSum += nInputSize;
|
||||
|
||||
// transaction hash
|
||||
|
|
|
|||
|
|
@ -133,9 +133,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
|
|||
//
|
||||
// Coinbase
|
||||
//
|
||||
CAmount nUnmatured = 0;
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
nUnmatured += wallet->GetCredit(txout, ISMINE_ALL);
|
||||
CAmount nUnmatured = wallet->GetCredit(wtx, ISMINE_ALL);
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> ";
|
||||
if (wtx.IsInMainChain())
|
||||
strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")";
|
||||
|
|
@ -174,8 +172,9 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
|
|||
//
|
||||
// Debit
|
||||
//
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
for (unsigned int i = 0; i < wtx.vout.size(); i++)
|
||||
{
|
||||
const CTxOut& txout = wtx.vout[i];
|
||||
// Ignore change
|
||||
isminetype toSelf = wallet->IsMine(txout);
|
||||
if ((toSelf == ISMINE_SPENDABLE) && (fAllFromMe == ISMINE_SPENDABLE))
|
||||
|
|
@ -199,9 +198,9 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
|
|||
}
|
||||
}
|
||||
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -txout.nValue) + "<br>";
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wtx.GetValueOut(i)) + "<br>";
|
||||
if(toSelf)
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, txout.nValue) + "<br>";
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wtx.GetValueOut(i)) + "<br>";
|
||||
}
|
||||
|
||||
if (fAllToMe)
|
||||
|
|
@ -213,9 +212,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
|
|||
strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "<br>";
|
||||
}
|
||||
|
||||
CAmount nTxFee = nDebit - wtx.GetValueOut();
|
||||
if (nTxFee > 0)
|
||||
strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nTxFee) + "<br>";
|
||||
if (wtx.nTxFee > 0)
|
||||
strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wtx.nTxFee) + "<br>";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -225,9 +223,9 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
|
|||
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
|
||||
if (wallet->IsMine(txin))
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
if (wallet->IsMine(txout))
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
|
||||
for (unsigned int i = 0; i < wtx.vout.size(); i++)
|
||||
if (wallet->IsMine(wtx.vout[i]) & ISMINE_ALL)
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wtx.GetValueOut(i)) + "<br>";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -278,9 +276,9 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
|
|||
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
|
||||
if(wallet->IsMine(txin))
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
if(wallet->IsMine(txout))
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
|
||||
for (unsigned int i = 0; i < wtx.vout.size(); i++)
|
||||
if(wallet->IsMine(wtx.vout[i]) & ISMINE_ALL)
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wtx.GetCredit(i)) + "<br>";
|
||||
|
||||
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
|
||||
strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);
|
||||
|
|
@ -306,7 +304,6 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
|
|||
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
|
||||
strHTML += QString::fromStdString(CBitcoinAddress(address).ToString());
|
||||
}
|
||||
strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue);
|
||||
strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";
|
||||
strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,15 +43,16 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
|
|||
//
|
||||
// Credit
|
||||
//
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
for (unsigned int i = 0; i < wtx.vout.size(); i++)
|
||||
{
|
||||
const CTxOut& txout = wtx.vout[i];
|
||||
isminetype mine = wallet->IsMine(txout);
|
||||
if(mine)
|
||||
{
|
||||
TransactionRecord sub(hash, nTime);
|
||||
CTxDestination address;
|
||||
sub.idx = parts.size(); // sequence number
|
||||
sub.credit = txout.nValue;
|
||||
sub.credit = wtx.GetValueOut(i);
|
||||
sub.involvesWatchAddress = mine == ISMINE_WATCH_ONLY;
|
||||
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address))
|
||||
{
|
||||
|
|
@ -108,7 +109,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
|
|||
//
|
||||
// Debit
|
||||
//
|
||||
CAmount nTxFee = nDebit - wtx.GetValueOut();
|
||||
CAmount nTxFee = wtx.nTxFee;
|
||||
|
||||
for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
|
||||
{
|
||||
|
|
@ -138,7 +139,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
|
|||
sub.address = mapValue["to"];
|
||||
}
|
||||
|
||||
CAmount nValue = txout.nValue;
|
||||
CAmount nValue = wtx.GetValueOut(nOut);
|
||||
/* Add fee to first output */
|
||||
if (nTxFee > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ CAmount WalletModel::getBalance(const CCoinControl *coinControl) const
|
|||
wallet->AvailableCoins(vCoins, true, coinControl);
|
||||
BOOST_FOREACH(const COutput& out, vCoins)
|
||||
if(out.fSpendable)
|
||||
nBalance += out.tx->vout[out.i].nValue;
|
||||
nBalance += out.tx->GetValueOut(out.i);
|
||||
|
||||
return nBalance;
|
||||
}
|
||||
|
|
@ -192,7 +192,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
|
|||
{
|
||||
CAmount total = 0;
|
||||
QList<SendCoinsRecipient> recipients = transaction.getRecipients();
|
||||
std::vector<std::pair<CScript, CAmount> > vecSend;
|
||||
std::vector<CSend> vecSend;
|
||||
|
||||
if(recipients.empty())
|
||||
{
|
||||
|
|
@ -216,7 +216,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
|
|||
subtotal += out.amount();
|
||||
const unsigned char* scriptStr = (const unsigned char*)out.script().data();
|
||||
CScript scriptPubKey(scriptStr, scriptStr+out.script().size());
|
||||
vecSend.push_back(std::pair<CScript, CAmount>(scriptPubKey, out.amount()));
|
||||
vecSend.push_back(CSend(scriptPubKey, out.amount()));
|
||||
}
|
||||
if (subtotal <= 0)
|
||||
{
|
||||
|
|
@ -237,8 +237,13 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
|
|||
setAddress.insert(rcp.address);
|
||||
++nAddresses;
|
||||
|
||||
CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get());
|
||||
vecSend.push_back(std::pair<CScript, CAmount>(scriptPubKey, rcp.amount));
|
||||
CBitcoinAddress addr(rcp.address.toStdString());
|
||||
CScript scriptPubKey = GetScriptForDestination(addr.Get());
|
||||
CPubKey confidentiality_pubkey;
|
||||
if (addr.IsBlinded()) {
|
||||
confidentiality_pubkey = addr.GetBlindingKey();
|
||||
}
|
||||
vecSend.push_back(CSend(scriptPubKey, rcp.amount, confidentiality_pubkey));
|
||||
|
||||
total += rcp.amount;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,6 +167,9 @@ Value validateaddress(const Array& params, bool fHelp)
|
|||
" \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n"
|
||||
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
|
||||
" \"account\" : \"account\" (string) The account associated with the address, \"\" is the default account\n"
|
||||
" \"confidential_key\" : \"pubkey\" (string) The confidentiality key associated with the address, or \"\" if none\n"
|
||||
" \"unconfidential\" : \"address\" (string) The address without confidentiality key\n"
|
||||
" \"confidential\" : \"address\" (string) Confidential version of the address, only if it is yours and unconfidential\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
|
||||
|
|
@ -183,9 +186,23 @@ Value validateaddress(const Array& params, bool fHelp)
|
|||
CTxDestination dest = address.Get();
|
||||
string currentAddress = address.ToString();
|
||||
ret.push_back(Pair("address", currentAddress));
|
||||
if (address.IsBlinded()) {
|
||||
CPubKey key = address.GetBlindingKey();
|
||||
ret.push_back(Pair("confidential_key", HexStr(key.begin(), key.end())));
|
||||
ret.push_back(Pair("unconfidential", address.GetUnblinded().ToString()));
|
||||
} else {
|
||||
ret.push_back(Pair("confidential_key", ""));
|
||||
ret.push_back(Pair("unconfidential", currentAddress));
|
||||
}
|
||||
#ifdef ENABLE_WALLET
|
||||
isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO;
|
||||
if (address.IsBlinded() && address.GetBlindingKey() != pwalletMain->blinding_pubkey) {
|
||||
mine = ISMINE_NO;
|
||||
}
|
||||
ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false));
|
||||
if (!address.IsBlinded() && mine != ISMINE_NO) {
|
||||
ret.push_back(Pair("confidential", address.AddBlindingKey(pwalletMain->blinding_pubkey).ToString()));
|
||||
}
|
||||
if (mine != ISMINE_NO) {
|
||||
ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false));
|
||||
Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "base58.h"
|
||||
#include "blind.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "core_io.h"
|
||||
#include "init.h"
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
#include <boost/assign/list_of.hpp>
|
||||
#include "json/json_spirit_utils.h"
|
||||
#include "json/json_spirit_value.h"
|
||||
#include <secp256k1.h>
|
||||
|
||||
using namespace boost;
|
||||
using namespace boost::assign;
|
||||
|
|
@ -60,6 +62,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
|
|||
entry.push_back(Pair("txid", tx.GetHash().GetHex()));
|
||||
entry.push_back(Pair("version", tx.nVersion));
|
||||
entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
|
||||
entry.push_back(Pair("fee", ValueFromAmount(tx.nTxFee)));
|
||||
Array vin;
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
|
||||
Object in;
|
||||
|
|
@ -81,9 +84,24 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
|
|||
for (unsigned int i = 0; i < tx.vout.size(); i++) {
|
||||
const CTxOut& txout = tx.vout[i];
|
||||
Object out;
|
||||
if (txout.nValue.IsAmount())
|
||||
if (txout.nValue.IsAmount()) {
|
||||
out.push_back(Pair("value", ValueFromAmount(txout.nValue.GetAmount())));
|
||||
// TODO: Non-Amount values
|
||||
} else {
|
||||
int exp;
|
||||
int mantissa;
|
||||
uint64_t minv;
|
||||
uint64_t maxv;
|
||||
if (secp256k1_rangeproof_info(NULL, &exp, &mantissa, &minv, &maxv, &txout.nValue.vchRangeproof[0], txout.nValue.vchRangeproof.size())) {
|
||||
if (exp == -1) {
|
||||
out.push_back(Pair("value", ValueFromAmount((CAmount)minv)));
|
||||
} else {
|
||||
out.push_back(Pair("value-minimum", ValueFromAmount((CAmount)minv)));
|
||||
out.push_back(Pair("value-maximum", ValueFromAmount((CAmount)maxv)));
|
||||
}
|
||||
out.push_back(Pair("ct-exponent", exp));
|
||||
out.push_back(Pair("ct-bits", mantissa));
|
||||
}
|
||||
}
|
||||
out.push_back(Pair("n", (int64_t)i));
|
||||
Object o;
|
||||
ScriptPubKeyToJSON(txout.scriptPubKey, o, true);
|
||||
|
|
@ -333,7 +351,9 @@ Value listunspent(const Array& params, bool fHelp)
|
|||
" \"account\" : \"account\", (string) The associated account, or \"\" for the default account\n"
|
||||
" \"scriptPubKey\" : \"key\", (string) the script key\n"
|
||||
" \"amount\" : x.xxx, (numeric) the transaction amount in btc\n"
|
||||
" \"confirmations\" : n (numeric) The number of confirmations\n"
|
||||
" \"serValue\" : \"hex\", (string) the output's value commitment\n"
|
||||
" \"confirmations\" : n, (numeric) The number of confirmations\n"
|
||||
" \"blinder\" : \"blind\" (string) The blinding factor used for a confidential output (or \"\")\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
"]\n"
|
||||
|
|
@ -384,14 +404,18 @@ Value listunspent(const Array& params, bool fHelp)
|
|||
continue;
|
||||
}
|
||||
|
||||
CAmount nValue = out.tx->vout[out.i].nValue;
|
||||
CAmount nValue = out.tx->GetValueOut(out.i);
|
||||
const CScript& pk = out.tx->vout[out.i].scriptPubKey;
|
||||
Object entry;
|
||||
entry.push_back(Pair("txid", out.tx->GetHash().GetHex()));
|
||||
entry.push_back(Pair("vout", out.i));
|
||||
CTxDestination address;
|
||||
if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) {
|
||||
entry.push_back(Pair("address", CBitcoinAddress(address).ToString()));
|
||||
CBitcoinAddress addr(address);
|
||||
if (out.tx->GetBlindingFactor(out.i).size() > 0) {
|
||||
addr.AddBlindingKey(pwalletMain->blinding_pubkey);
|
||||
}
|
||||
entry.push_back(Pair("address", addr.ToString()));
|
||||
if (pwalletMain->mapAddressBook.count(address))
|
||||
entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name));
|
||||
}
|
||||
|
|
@ -405,7 +429,11 @@ Value listunspent(const Array& params, bool fHelp)
|
|||
entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())));
|
||||
}
|
||||
}
|
||||
CDataStream ssValue(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssValue << nValue;
|
||||
entry.push_back(Pair("serValue", HexStr(ssValue.begin(), ssValue.end())));
|
||||
entry.push_back(Pair("amount",ValueFromAmount(nValue)));
|
||||
entry.push_back(Pair("blinder",HexStr(out.tx->GetBlindingFactor(out.i))));
|
||||
entry.push_back(Pair("confirmations",out.nDepth));
|
||||
entry.push_back(Pair("spendable", out.fSpendable));
|
||||
results.push_back(entry);
|
||||
|
|
@ -431,6 +459,7 @@ Value createrawtransaction(const Array& params, bool fHelp)
|
|||
" {\n"
|
||||
" \"txid\":\"id\", (string, required) The transaction id\n"
|
||||
" \"vout\":n (numeric, required) The output number\n"
|
||||
" \"nValue\":x.xxx, (numeric, required) The amount being spent\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
|
|
@ -455,6 +484,8 @@ Value createrawtransaction(const Array& params, bool fHelp)
|
|||
|
||||
CMutableTransaction rawTx;
|
||||
|
||||
CAmount inputValue = 0;
|
||||
|
||||
BOOST_FOREACH(const Value& input, inputs) {
|
||||
const Object& o = input.get_obj();
|
||||
|
||||
|
|
@ -467,10 +498,15 @@ Value createrawtransaction(const Array& params, bool fHelp)
|
|||
if (nOutput < 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
|
||||
|
||||
const Value& vout_value = find_value(o, "nValue");
|
||||
inputValue += AmountFromValue(vout_value);
|
||||
|
||||
CTxIn in(COutPoint(txid, nOutput));
|
||||
rawTx.vin.push_back(in);
|
||||
}
|
||||
|
||||
CAmount outputValue = 0;
|
||||
|
||||
set<CBitcoinAddress> setAddress;
|
||||
BOOST_FOREACH(const Pair& s, sendTo) {
|
||||
CBitcoinAddress address(s.name_);
|
||||
|
|
@ -483,14 +519,171 @@ Value createrawtransaction(const Array& params, bool fHelp)
|
|||
|
||||
CScript scriptPubKey = GetScriptForDestination(address.Get());
|
||||
CAmount nAmount = AmountFromValue(s.value_);
|
||||
outputValue += nAmount;
|
||||
|
||||
CTxOut out(nAmount, scriptPubKey);
|
||||
if (address.IsBlinded()) {
|
||||
CPubKey confidentiality_pubkey = address.GetBlindingKey();
|
||||
if (!confidentiality_pubkey.IsValid())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: invalid confidentiality public key given"));
|
||||
out.nValue.vchNonceCommitment = std::vector<unsigned char>(confidentiality_pubkey.begin(), confidentiality_pubkey.end());
|
||||
}
|
||||
rawTx.vout.push_back(out);
|
||||
}
|
||||
|
||||
rawTx.nTxFee = inputValue - outputValue;
|
||||
|
||||
return EncodeHexTx(rawTx);
|
||||
}
|
||||
|
||||
Value rawblindrawtransaction(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || (params.size() != 2 && params.size() != 3))
|
||||
throw runtime_error(
|
||||
"rawblindrawtransaction \"hexstring\" [\"inputblinder\",...] [\"totalblinder\"]\n"
|
||||
"\nConvert one or more outputs of a raw transaction into confidential ones.\n"
|
||||
"Returns the hex-encoded raw transaction.\n"
|
||||
"If at least one of the inputs is confidential, at least one of the outputs must be.\n"
|
||||
"The input raw transaction cannot have already-blinded outputs.\n"
|
||||
"The output keys used can be specified by using a confidential address in createrawtransaction.\n"
|
||||
|
||||
"\nArguments:\n"
|
||||
"1. \"hexstring\", (string, required) A hex-encoded raw transaction.\n"
|
||||
"2. [ (array, required) An array with one entry per transaction input.\n"
|
||||
" \"inputblinder\" (string, required) A hex-encoded blinding factor, one for each input.\n"
|
||||
" Blinding factors can be found in the \"blinder\" output of listunspent.\n"
|
||||
" ],\n"
|
||||
"3. \"totalblinder\" (string, optional) Ignored for now.\n"
|
||||
|
||||
"\nResult:\n"
|
||||
"\"transaction\" (string) hex string of the transaction\n"
|
||||
);
|
||||
|
||||
if (params.size() == 2) {
|
||||
RPCTypeCheck(params, list_of(str_type)(array_type));
|
||||
} else {
|
||||
RPCTypeCheck(params, list_of(str_type)(array_type)(str_type));
|
||||
}
|
||||
|
||||
CMutableTransaction rawTx;
|
||||
|
||||
vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
|
||||
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
||||
CMutableTransaction tx;
|
||||
try {
|
||||
ssData >> tx;
|
||||
} catch (const std::exception &) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
}
|
||||
|
||||
Array inputBlinds = params[1].get_array();
|
||||
|
||||
if (inputBlinds.size() != tx.vin.size()) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: one (potentially empty) input blind for each input must be provided"));
|
||||
|
||||
std::vector<std::vector<unsigned char> > input_blinds;
|
||||
std::vector<std::vector<unsigned char> > output_blinds;
|
||||
std::vector<CPubKey> output_pubkeys;
|
||||
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
|
||||
if (!tx.vout[nOut].nValue.IsAmount())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: transaction outputs must be unblinded"));
|
||||
if (tx.vout[nOut].nValue.vchNonceCommitment.size() == 0) {
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
} else {
|
||||
CPubKey pubkey(tx.vout[nOut].nValue.vchNonceCommitment);
|
||||
if (!pubkey.IsValid()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: invalid confidentiality public key given"));
|
||||
}
|
||||
output_pubkeys.push_back(pubkey);
|
||||
}
|
||||
output_blinds.push_back(std::vector<unsigned char>(0, 0));
|
||||
}
|
||||
|
||||
BlindOutputs(input_blinds, output_blinds, output_pubkeys, tx);
|
||||
|
||||
return EncodeHexTx(tx);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
Value blindrawtransaction(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || (params.size() != 1 && params.size() != 2))
|
||||
throw runtime_error(
|
||||
"blindrawtransaction \"hexstring\" [\"totalblinder\"]\n"
|
||||
"\nConvert one or more outputs of a raw transaction into confidential ones using only wallet inputs.\n"
|
||||
"Returns the hex-encoded raw transaction.\n"
|
||||
"If at least one of the inputs is confidential, at least one of the outputs must be.\n"
|
||||
"The output keys used can be specified by using a confidential address in createrawtransaction.\n"
|
||||
|
||||
"\nArguments:\n"
|
||||
"1. \"hexstring\", (string, required) A hex-encoded raw transaction.\n"
|
||||
"2. \"totalblinder\" (string, optional) Ignored for now.\n"
|
||||
|
||||
"\nResult:\n"
|
||||
"\"transaction\" (string) hex string of the transaction\n"
|
||||
);
|
||||
|
||||
if (params.size() == 1) {
|
||||
RPCTypeCheck(params, list_of(str_type));
|
||||
} else {
|
||||
RPCTypeCheck(params, list_of(str_type)(str_type));
|
||||
}
|
||||
|
||||
CMutableTransaction rawTx;
|
||||
|
||||
vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
|
||||
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
||||
CMutableTransaction tx;
|
||||
try {
|
||||
ssData >> tx;
|
||||
} catch (const std::exception &) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
}
|
||||
|
||||
LOCK(pwalletMain->cs_wallet);
|
||||
|
||||
std::vector<std::vector<unsigned char> > input_blinds;
|
||||
std::vector<std::vector<unsigned char> > output_blinds;
|
||||
std::vector<CPubKey> output_pubkeys;
|
||||
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
|
||||
std::map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.find(tx.vin[nIn].prevout.hash);
|
||||
if (it == pwalletMain->mapWallet.end()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: transaction spends from non-wallet output"));
|
||||
}
|
||||
if (tx.vin[nIn].prevout.n >= it->second.vout.size()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: transaction spends non-existing output"));
|
||||
}
|
||||
input_blinds.push_back(it->second.GetBlindingFactor(tx.vin[nIn].prevout.n));
|
||||
}
|
||||
|
||||
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
|
||||
if (!tx.vout[nOut].nValue.IsAmount()) {
|
||||
std::vector<unsigned char> blinding_factor;
|
||||
CAmount amount;
|
||||
if (UnblindOutput(pwalletMain->blinding_key, tx.vout[nOut], amount, blinding_factor) != 0) {
|
||||
output_blinds.push_back(blinding_factor);
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: transaction outputs must be unblinded or to wallet"));
|
||||
}
|
||||
} else if (tx.vout[nOut].nValue.vchNonceCommitment.size() == 0) {
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
output_blinds.push_back(std::vector<unsigned char>(0, 0));
|
||||
} else {
|
||||
CPubKey pubkey(tx.vout[nOut].nValue.vchNonceCommitment);
|
||||
if (!pubkey.IsValid()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: invalid confidentiality public key given"));
|
||||
}
|
||||
output_pubkeys.push_back(pubkey);
|
||||
output_blinds.push_back(std::vector<unsigned char>(0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
BlindOutputs(input_blinds, output_blinds, output_pubkeys, tx);
|
||||
|
||||
return EncodeHexTx(tx);
|
||||
}
|
||||
#endif
|
||||
|
||||
Value decoderawtransaction(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
|
|
|
|||
|
|
@ -297,7 +297,9 @@ static const CRPCCommand vRPCCommands[] =
|
|||
{ "rawtransactions", "getrawtransaction", &getrawtransaction, true, false, false },
|
||||
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, false, false },
|
||||
{ "rawtransactions", "signrawtransaction", &signrawtransaction, false, false, false }, /* uses wallet if enabled */
|
||||
{ "rawtransactions", "rawblindrawtransaction", &rawblindrawtransaction, false, false, false },
|
||||
#ifdef ENABLE_WALLET
|
||||
{ "rawtransactions", "blindrawtransaction", &blindrawtransaction, true, false, false },
|
||||
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, false, false, true },
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -214,6 +214,8 @@ extern json_spirit::Value signrawtransaction(const json_spirit::Array& params, b
|
|||
extern json_spirit::Value sendrawtransaction(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value gettxoutproof(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value verifytxoutproof(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value blindrawtransaction(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value rawblindrawtransaction(const json_spirit::Array& params, bool fHelp);
|
||||
|
||||
extern json_spirit::Value getblockcount(const json_spirit::Array& params, bool fHelp); // in rpcblockchain.cpp
|
||||
extern json_spirit::Value getbestblockhash(const json_spirit::Array& params, bool fHelp);
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ Value getnewaddress(const Array& params, bool fHelp)
|
|||
|
||||
pwalletMain->SetAddressBook(keyID, strAccount, "receive");
|
||||
|
||||
return CBitcoinAddress(keyID).ToString();
|
||||
return CBitcoinAddress(keyID).AddBlindingKey(pwalletMain->blinding_pubkey).ToString();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -148,7 +148,7 @@ CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
|
|||
walletdb.WriteAccount(strAccount, account);
|
||||
}
|
||||
|
||||
return CBitcoinAddress(account.vchPubKey.GetID());
|
||||
return CBitcoinAddress(account.vchPubKey.GetID()).AddBlindingKey(pwalletMain->blinding_pubkey);
|
||||
}
|
||||
|
||||
Value getaccountaddress(const Array& params, bool fHelp)
|
||||
|
|
@ -205,7 +205,7 @@ Value getrawchangeaddress(const Array& params, bool fHelp)
|
|||
|
||||
CKeyID keyID = vchPubKey.GetID();
|
||||
|
||||
return CBitcoinAddress(keyID).ToString();
|
||||
return CBitcoinAddress(keyID).AddBlindingKey(pwalletMain->blinding_pubkey).ToString();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -310,7 +310,7 @@ Value getaddressesbyaccount(const Array& params, bool fHelp)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void SendMoney(const CTxDestination &address, CAmount nValue, CWalletTx& wtxNew)
|
||||
void SendMoney(const CTxDestination &address, CAmount nValue, const CPubKey &confidentiality_key, CWalletTx& wtxNew)
|
||||
{
|
||||
// Check amount
|
||||
if (nValue <= 0)
|
||||
|
|
@ -333,7 +333,7 @@ void SendMoney(const CTxDestination &address, CAmount nValue, CWalletTx& wtxNew)
|
|||
// Create and send the transaction
|
||||
CReserveKey reservekey(pwalletMain);
|
||||
CAmount nFeeRequired;
|
||||
if (!pwalletMain->CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired, strError))
|
||||
if (!pwalletMain->CreateTransaction(scriptPubKey, nValue, confidentiality_key, wtxNew, reservekey, nFeeRequired, strError))
|
||||
{
|
||||
if (nValue + nFeeRequired > pwalletMain->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));
|
||||
|
|
@ -373,6 +373,10 @@ Value sendtoaddress(const Array& params, bool fHelp)
|
|||
|
||||
// Amount
|
||||
CAmount nAmount = AmountFromValue(params[1]);
|
||||
CPubKey confidentiality_pubkey;
|
||||
if (address.IsBlinded()) {
|
||||
confidentiality_pubkey = address.GetBlindingKey();
|
||||
}
|
||||
|
||||
// Wallet comments
|
||||
CWalletTx wtx;
|
||||
|
|
@ -383,7 +387,7 @@ Value sendtoaddress(const Array& params, bool fHelp)
|
|||
|
||||
EnsureWalletIsUnlocked();
|
||||
|
||||
SendMoney(address.Get(), nAmount, wtx);
|
||||
SendMoney(address.Get(), nAmount, confidentiality_pubkey, wtx);
|
||||
|
||||
return wtx.GetHash().GetHex();
|
||||
}
|
||||
|
|
@ -421,12 +425,14 @@ Value listaddressgroupings(const Array& params, bool fHelp)
|
|||
BOOST_FOREACH(CTxDestination address, grouping)
|
||||
{
|
||||
Array addressInfo;
|
||||
addressInfo.push_back(CBitcoinAddress(address).ToString());
|
||||
CBitcoinAddress addr(address);
|
||||
addr.AddBlindingKey(pwalletMain->blinding_pubkey);
|
||||
addressInfo.push_back(addr.ToString());
|
||||
addressInfo.push_back(ValueFromAmount(balances[address]));
|
||||
{
|
||||
LOCK(pwalletMain->cs_wallet);
|
||||
if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
|
||||
addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name);
|
||||
if (pwalletMain->mapAddressBook.find(addr.Get()) != pwalletMain->mapAddressBook.end())
|
||||
addressInfo.push_back(pwalletMain->mapAddressBook.find(addr.Get())->second.name);
|
||||
}
|
||||
jsonGrouping.push_back(addressInfo);
|
||||
}
|
||||
|
|
@ -515,6 +521,8 @@ Value getreceivedbyaddress(const Array& params, bool fHelp)
|
|||
CScript scriptPubKey = GetScriptForDestination(address.Get());
|
||||
if (!IsMine(*pwalletMain,scriptPubKey))
|
||||
return (double)0.0;
|
||||
if (address.IsBlinded() && address.GetBlindingKey() != pwalletMain->blinding_pubkey)
|
||||
return (double)0.0;
|
||||
|
||||
// Minimum confirmations
|
||||
int nMinDepth = 1;
|
||||
|
|
@ -529,10 +537,10 @@ Value getreceivedbyaddress(const Array& params, bool fHelp)
|
|||
if (wtx.IsCoinBase() || CheckLockTime(wtx))
|
||||
continue;
|
||||
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
if (txout.scriptPubKey == scriptPubKey)
|
||||
for (unsigned int i = 0; i < wtx.vout.size(); i++)
|
||||
if (wtx.vout[i].scriptPubKey == scriptPubKey)
|
||||
if (wtx.GetDepthInMainChain() >= nMinDepth)
|
||||
nAmount += txout.nValue;
|
||||
nAmount += wtx.GetValueOut(i);
|
||||
}
|
||||
|
||||
return ValueFromAmount(nAmount);
|
||||
|
|
@ -578,12 +586,12 @@ Value getreceivedbyaccount(const Array& params, bool fHelp)
|
|||
if (wtx.IsCoinBase() || CheckLockTime(wtx))
|
||||
continue;
|
||||
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
for (unsigned int i = 0; i < wtx.vout.size(); i++)
|
||||
{
|
||||
CTxDestination address;
|
||||
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
|
||||
if (ExtractDestination(wtx.vout[i].scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
|
||||
if (wtx.GetDepthInMainChain() >= nMinDepth)
|
||||
nAmount += txout.nValue;
|
||||
nAmount += wtx.GetValueOut(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -809,6 +817,10 @@ Value sendfrom(const Array& params, bool fHelp)
|
|||
int nMinDepth = 1;
|
||||
if (params.size() > 3)
|
||||
nMinDepth = params[3].get_int();
|
||||
CPubKey confidentiality_pubkey;
|
||||
if (address.IsBlinded()) {
|
||||
confidentiality_pubkey = address.GetBlindingKey();
|
||||
}
|
||||
|
||||
CWalletTx wtx;
|
||||
wtx.strFromAccount = strAccount;
|
||||
|
|
@ -824,7 +836,7 @@ Value sendfrom(const Array& params, bool fHelp)
|
|||
if (nAmount > nBalance)
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
|
||||
|
||||
SendMoney(address.Get(), nAmount, wtx);
|
||||
SendMoney(address.Get(), nAmount, confidentiality_pubkey, wtx);
|
||||
|
||||
return wtx.GetHash().GetHex();
|
||||
}
|
||||
|
|
@ -870,7 +882,7 @@ Value sendmany(const Array& params, bool fHelp)
|
|||
wtx.mapValue["comment"] = params[3].get_str();
|
||||
|
||||
set<CBitcoinAddress> setAddress;
|
||||
vector<pair<CScript, CAmount> > vecSend;
|
||||
vector<CSend> vecSend;
|
||||
|
||||
CAmount totalAmount = 0;
|
||||
BOOST_FOREACH(const Pair& s, sendTo)
|
||||
|
|
@ -886,8 +898,12 @@ Value sendmany(const Array& params, bool fHelp)
|
|||
CScript scriptPubKey = GetScriptForDestination(address.Get());
|
||||
CAmount nAmount = AmountFromValue(s.value_);
|
||||
totalAmount += nAmount;
|
||||
CPubKey confidentiality_pubkey;
|
||||
if (address.IsBlinded()) {
|
||||
confidentiality_pubkey = address.GetBlindingKey();
|
||||
}
|
||||
|
||||
vecSend.push_back(make_pair(scriptPubKey, nAmount));
|
||||
vecSend.push_back(CSend(scriptPubKey, nAmount, confidentiality_pubkey));
|
||||
}
|
||||
|
||||
EnsureWalletIsUnlocked();
|
||||
|
|
@ -1002,10 +1018,10 @@ Value ListReceived(const Array& params, bool fByAccounts)
|
|||
if (nDepth < nMinDepth)
|
||||
continue;
|
||||
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
for (unsigned int i = 0; i < wtx.vout.size(); i++)
|
||||
{
|
||||
CTxDestination address;
|
||||
if (!ExtractDestination(txout.scriptPubKey, address))
|
||||
if (!ExtractDestination(wtx.vout[i].scriptPubKey, address))
|
||||
continue;
|
||||
|
||||
isminefilter mine = IsMine(*pwalletMain, address);
|
||||
|
|
@ -1013,7 +1029,7 @@ Value ListReceived(const Array& params, bool fByAccounts)
|
|||
continue;
|
||||
|
||||
tallyitem& item = mapTally[address];
|
||||
item.nAmount += txout.nValue;
|
||||
item.nAmount += wtx.GetValueOut(i);
|
||||
item.nConf = min(item.nConf, nDepth);
|
||||
item.txids.push_back(wtx.GetHash());
|
||||
if (mine & ISMINE_WATCH_ONLY)
|
||||
|
|
@ -1153,11 +1169,15 @@ Value listreceivedbyaccount(const Array& params, bool fHelp)
|
|||
return ListReceived(params, true);
|
||||
}
|
||||
|
||||
static void MaybePushAddress(Object & entry, const CTxDestination &dest)
|
||||
static void MaybePushAddress(Object & entry, const CTxDestination &dest, const CPubKey& pubkey)
|
||||
{
|
||||
CBitcoinAddress addr;
|
||||
if (addr.Set(dest))
|
||||
if (addr.Set(dest)) {
|
||||
if (pubkey.size() == 33) {
|
||||
addr.AddBlindingKey(pubkey);
|
||||
}
|
||||
entry.push_back(Pair("address", addr.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
|
||||
|
|
@ -1181,7 +1201,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
|
|||
if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY))
|
||||
entry.push_back(Pair("involvesWatchonly", true));
|
||||
entry.push_back(Pair("account", strSentAccount));
|
||||
MaybePushAddress(entry, s.destination);
|
||||
MaybePushAddress(entry, s.destination, s.confidentiality_pubkey);
|
||||
entry.push_back(Pair("category", "send"));
|
||||
entry.push_back(Pair("amount", ValueFromAmount(-s.amount)));
|
||||
entry.push_back(Pair("vout", s.vout));
|
||||
|
|
@ -1206,7 +1226,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
|
|||
if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY))
|
||||
entry.push_back(Pair("involvesWatchonly", true));
|
||||
entry.push_back(Pair("account", account));
|
||||
MaybePushAddress(entry, r.destination);
|
||||
MaybePushAddress(entry, r.destination, r.confidentiality_pubkey);
|
||||
if (wtx.IsCoinBase())
|
||||
{
|
||||
if (wtx.GetDepthInMainChain() < 1)
|
||||
|
|
@ -1574,7 +1594,7 @@ Value gettransaction(const Array& params, bool fHelp)
|
|||
CAmount nCredit = wtx.GetCredit(filter);
|
||||
CAmount nDebit = wtx.GetDebit(filter);
|
||||
CAmount nNet = nCredit - nDebit;
|
||||
CAmount nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
|
||||
CAmount nFee = (wtx.IsFromMe(filter) ? wtx.nTxFee : 0);
|
||||
|
||||
entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
|
||||
if (wtx.IsFromMe(filter))
|
||||
|
|
|
|||
129
src/test/blind_tests.cpp
Normal file
129
src/test/blind_tests.cpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
// Copyright (c) 2011-2014 The Bitcoin Core developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "blind.h"
|
||||
#include "coins.h"
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(bind_tests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(naive_blinding_test)
|
||||
{
|
||||
CCoinsView viewBase;
|
||||
CCoinsViewCache cache(&viewBase);
|
||||
|
||||
CKey key1;
|
||||
CKey key2;
|
||||
|
||||
unsigned char k1[32] = {1,2,3};
|
||||
unsigned char k2[32] = {22,33,44};
|
||||
key1.Set(&k1[0], &k1[32], true);
|
||||
key2.Set(&k2[0], &k2[32], true);
|
||||
CPubKey pubkey1 = key1.GetPubKey();
|
||||
CPubKey pubkey2 = key2.GetPubKey();
|
||||
|
||||
std::vector<unsigned char> blind3, blind4;
|
||||
|
||||
{
|
||||
CCoinsModifier tx1 = cache.ModifyCoins(uint256(1));
|
||||
tx1->vout.resize(1);
|
||||
tx1->vout[0].nValue = 11;
|
||||
}
|
||||
|
||||
{
|
||||
CCoinsModifier tx2 = cache.ModifyCoins(uint256(2));
|
||||
tx2->vout.resize(2);
|
||||
tx2->vout[0].nValue = 111;
|
||||
}
|
||||
|
||||
{
|
||||
// Build a transaction that spends 2 unblinded coins (11, 111), and produces a single blinded one (100) and fee (22).
|
||||
CMutableTransaction tx3;
|
||||
tx3.vin.resize(2);
|
||||
tx3.vin[0].prevout.hash = uint256(1);
|
||||
|
||||
tx3.vin[0].prevout.n = 0;
|
||||
tx3.vin[1].prevout.hash = uint256(2);
|
||||
tx3.vin[1].prevout.n = 0;
|
||||
tx3.vout.resize(1);
|
||||
tx3.vout[0].nValue = 100;
|
||||
tx3.nTxFee = 22;
|
||||
BOOST_CHECK(cache.VerifyAmounts(tx3));
|
||||
|
||||
std::vector<std::vector<unsigned char> > input_blinds;
|
||||
std::vector<std::vector<unsigned char> > output_blinds;
|
||||
std::vector<CPubKey> output_pubkeys;
|
||||
input_blinds.push_back(std::vector<unsigned char>(0, 0));
|
||||
input_blinds.push_back(std::vector<unsigned char>(0, 0));
|
||||
output_blinds.push_back(std::vector<unsigned char>(0, 0));
|
||||
output_pubkeys.push_back(pubkey1);
|
||||
BlindOutputs(input_blinds, output_blinds, output_pubkeys, tx3);
|
||||
BOOST_CHECK(!tx3.vout[0].nValue.IsAmount());
|
||||
BOOST_CHECK(cache.VerifyAmounts(tx3));
|
||||
|
||||
CAmount unblinded_amount;
|
||||
BOOST_CHECK(UnblindOutput(key2, tx3.vout[0], unblinded_amount, blind3) == 0);
|
||||
BOOST_CHECK(UnblindOutput(key1, tx3.vout[0], unblinded_amount, blind3) == 1);
|
||||
BOOST_CHECK(unblinded_amount == 100);
|
||||
|
||||
CCoinsModifier in3 = cache.ModifyCoins(uint256(3));
|
||||
in3->vout.resize(1);
|
||||
in3->vout[0] = tx3.vout[0];
|
||||
|
||||
tx3.nTxFee--;
|
||||
BOOST_CHECK(!cache.VerifyAmounts(tx3));
|
||||
}
|
||||
|
||||
{
|
||||
// Build a transactions that spends an unblinded (111) and blinded (100) coin, and produces a blinded (30), unblinded (40), and blinded (50) coin and fee (91)
|
||||
CMutableTransaction tx4;
|
||||
tx4.vin.resize(2);
|
||||
tx4.vin[0].prevout.hash = uint256(2);
|
||||
tx4.vin[0].prevout.n = 0;
|
||||
tx4.vin[1].prevout.hash = uint256(3);
|
||||
tx4.vin[1].prevout.n = 0;
|
||||
tx4.vout.resize(3);
|
||||
tx4.vout[0].nValue = 30;
|
||||
tx4.vout[1].nValue = 40;
|
||||
tx4.vout[2].nValue = 50;
|
||||
tx4.nTxFee = 100 + 111 - 30 - 40 - 50;
|
||||
BOOST_CHECK(cache.VerifyAmounts(tx4));
|
||||
|
||||
std::vector<std::vector<unsigned char> > input_blinds;
|
||||
std::vector<std::vector<unsigned char> > output_blinds;
|
||||
std::vector<CPubKey> output_pubkeys;
|
||||
input_blinds.push_back(std::vector<unsigned char>(0, 0));
|
||||
input_blinds.push_back(blind3);
|
||||
output_blinds.push_back(std::vector<unsigned char>(0, 0));
|
||||
output_blinds.push_back(std::vector<unsigned char>(0, 0));
|
||||
output_blinds.push_back(std::vector<unsigned char>(0, 0));
|
||||
output_pubkeys.push_back(pubkey2);
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
output_pubkeys.push_back(pubkey2);
|
||||
BlindOutputs(input_blinds, output_blinds, output_pubkeys, tx4);
|
||||
BOOST_CHECK(!tx4.vout[0].nValue.IsAmount());
|
||||
BOOST_CHECK(tx4.vout[1].nValue.IsAmount());
|
||||
BOOST_CHECK(!tx4.vout[2].nValue.IsAmount());
|
||||
BOOST_CHECK(cache.VerifyAmounts(tx4));
|
||||
|
||||
CAmount unblinded_amount;
|
||||
BOOST_CHECK(UnblindOutput(key1, tx4.vout[0], unblinded_amount, blind4) == 0);
|
||||
BOOST_CHECK(UnblindOutput(key2, tx4.vout[0], unblinded_amount, blind4) == 1);
|
||||
BOOST_CHECK(unblinded_amount == 30);
|
||||
BOOST_CHECK(UnblindOutput(key2, tx4.vout[2], unblinded_amount, blind4) == 1);
|
||||
BOOST_CHECK(unblinded_amount == 50);
|
||||
|
||||
CCoinsModifier in4 = cache.ModifyCoins(uint256(4));
|
||||
in4->vout.resize(3);
|
||||
in4->vout[0] = tx4.vout[0];
|
||||
in4->vout[1] = tx4.vout[1];
|
||||
in4->vout[2] = tx4.vout[2];
|
||||
|
||||
tx4.nTxFee--;
|
||||
BOOST_CHECK(!cache.VerifyAmounts(tx4));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#define BOOST_TEST_MODULE Bitcoin Test Suite
|
||||
|
||||
#include "blind.h"
|
||||
#include "key.h"
|
||||
#include "main.h"
|
||||
#include "pubkey.h"
|
||||
|
|
@ -33,6 +34,7 @@ struct TestingSetup {
|
|||
|
||||
TestingSetup() {
|
||||
ECC_Verify_Start();
|
||||
ECC_Blinding_Start();
|
||||
ECC_Start();
|
||||
SetupEnvironment();
|
||||
fPrintToDebugLog = false; // don't want to write to debug.log file
|
||||
|
|
@ -77,6 +79,7 @@ struct TestingSetup {
|
|||
#endif
|
||||
boost::filesystem::remove_all(pathTemp);
|
||||
ECC_Stop();
|
||||
ECC_Blinding_Stop();
|
||||
ECC_Verify_Stop();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -25,10 +25,9 @@ typedef set<pair<const CWalletTx*,unsigned int> > CoinSet;
|
|||
|
||||
BOOST_AUTO_TEST_SUITE(wallet_tests)
|
||||
|
||||
static CWallet wallet;
|
||||
static vector<COutput> vCoins;
|
||||
|
||||
static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0)
|
||||
static void add_coin(CWallet &wallet, const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0)
|
||||
{
|
||||
static int nextLockTime = 0;
|
||||
CMutableTransaction tx;
|
||||
|
|
@ -68,6 +67,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
|||
CoinSet setCoinsRet, setCoinsRet2;
|
||||
CAmount nValueRet;
|
||||
|
||||
CWallet wallet;
|
||||
LOCK(wallet.cs_wallet);
|
||||
|
||||
// test multiple times to allow for differences in the shuffle order
|
||||
|
|
@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
|||
// with an empty wallet we can't even pay one cent
|
||||
BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
|
||||
|
||||
add_coin(1*CENT, 4); // add a new 1 cent coin
|
||||
add_coin(wallet, 1*CENT, 4); // add a new 1 cent coin
|
||||
|
||||
// with a new 1 cent coin, we still can't find a mature 1 cent
|
||||
BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
|
||||
|
|
@ -87,7 +87,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
|||
BOOST_CHECK( wallet.SelectCoinsMinConf( 1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
|
||||
|
||||
add_coin(2*CENT); // add a mature 2 cent coin
|
||||
add_coin(wallet, 2*CENT); // add a mature 2 cent coin
|
||||
|
||||
// we can't make 3 cents of mature coins
|
||||
BOOST_CHECK(!wallet.SelectCoinsMinConf( 3 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
|
||||
|
|
@ -96,9 +96,9 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
|||
BOOST_CHECK( wallet.SelectCoinsMinConf( 3 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 3 * CENT);
|
||||
|
||||
add_coin(5*CENT); // add a mature 5 cent coin,
|
||||
add_coin(10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses
|
||||
add_coin(20*CENT); // and a mature 20 cent coin
|
||||
add_coin(wallet, 5*CENT); // add a mature 5 cent coin,
|
||||
add_coin(wallet, 10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses
|
||||
add_coin(wallet, 20*CENT); // and a mature 20 cent coin
|
||||
|
||||
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
|
||||
|
||||
|
|
@ -136,11 +136,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
|||
// now clear out the wallet and start again to test choosing between subsets of smaller coins and the next biggest coin
|
||||
empty_wallet();
|
||||
|
||||
add_coin( 6*CENT);
|
||||
add_coin( 7*CENT);
|
||||
add_coin( 8*CENT);
|
||||
add_coin(20*CENT);
|
||||
add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total
|
||||
add_coin(wallet, 6*CENT);
|
||||
add_coin(wallet, 7*CENT);
|
||||
add_coin(wallet, 8*CENT);
|
||||
add_coin(wallet, 20*CENT);
|
||||
add_coin(wallet, 30*CENT); // now we have 6+7+8+20+30 = 71 cents total
|
||||
|
||||
// check that we have 71 and not 72
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(71 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
|
|
@ -151,14 +151,14 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
|||
BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
|
||||
|
||||
add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
|
||||
add_coin(wallet, 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
|
||||
|
||||
// now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
|
||||
|
||||
add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30
|
||||
add_coin(wallet, 18*CENT); // now we have 5+6+7+8+18+20+30
|
||||
|
||||
// and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
|
|
@ -171,10 +171,10 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
|||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
|
||||
|
||||
// check that the smallest bigger coin is used
|
||||
add_coin( 1*COIN);
|
||||
add_coin( 2*COIN);
|
||||
add_coin( 3*COIN);
|
||||
add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
|
||||
add_coin(wallet, 1*COIN);
|
||||
add_coin(wallet, 2*COIN);
|
||||
add_coin(wallet, 3*COIN);
|
||||
add_coin(wallet, 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(95 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
|
||||
|
|
@ -185,11 +185,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
|||
|
||||
// empty the wallet and start again, now with fractions of a cent, to test sub-cent change avoidance
|
||||
empty_wallet();
|
||||
add_coin(0.1*CENT);
|
||||
add_coin(0.2*CENT);
|
||||
add_coin(0.3*CENT);
|
||||
add_coin(0.4*CENT);
|
||||
add_coin(0.5*CENT);
|
||||
add_coin(wallet, 0.1*CENT);
|
||||
add_coin(wallet, 0.2*CENT);
|
||||
add_coin(wallet, 0.3*CENT);
|
||||
add_coin(wallet, 0.4*CENT);
|
||||
add_coin(wallet, 0.5*CENT);
|
||||
|
||||
// try making 1 cent from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 = 1.5 cents
|
||||
// we'll get sub-cent change whatever happens, so can expect 1.0 exactly
|
||||
|
|
@ -197,15 +197,15 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
|||
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
|
||||
|
||||
// but if we add a bigger coin, making it possible to avoid sub-cent change, things change:
|
||||
add_coin(1111*CENT);
|
||||
add_coin(wallet, 1111*CENT);
|
||||
|
||||
// try making 1 cent from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5 cents
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we should get the exact amount
|
||||
|
||||
// if we add more sub-cent coins:
|
||||
add_coin(0.6*CENT);
|
||||
add_coin(0.7*CENT);
|
||||
add_coin(wallet, 0.6*CENT);
|
||||
add_coin(wallet, 0.7*CENT);
|
||||
|
||||
// and try again to make 1.0 cents, we can still make 1.0 cents
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
|
|
@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
|||
// they tried to consolidate 10 50k coins into one 500k coin, and ended up with 50k in change
|
||||
empty_wallet();
|
||||
for (int i = 0; i < 20; i++)
|
||||
add_coin(50000 * COIN);
|
||||
add_coin(wallet, 50000 * COIN);
|
||||
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(500000 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount
|
||||
|
|
@ -226,29 +226,29 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
|||
|
||||
// sometimes it will fail, and so we use the next biggest coin:
|
||||
empty_wallet();
|
||||
add_coin(0.5 * CENT);
|
||||
add_coin(0.6 * CENT);
|
||||
add_coin(0.7 * CENT);
|
||||
add_coin(1111 * CENT);
|
||||
add_coin(wallet, 0.5 * CENT);
|
||||
add_coin(wallet, 0.6 * CENT);
|
||||
add_coin(wallet, 0.7 * CENT);
|
||||
add_coin(wallet, 1111 * CENT);
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 1111 * CENT); // we get the bigger coin
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
|
||||
|
||||
// but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0)
|
||||
empty_wallet();
|
||||
add_coin(0.4 * CENT);
|
||||
add_coin(0.6 * CENT);
|
||||
add_coin(0.8 * CENT);
|
||||
add_coin(1111 * CENT);
|
||||
add_coin(wallet, 0.4 * CENT);
|
||||
add_coin(wallet, 0.6 * CENT);
|
||||
add_coin(wallet, 0.8 * CENT);
|
||||
add_coin(wallet, 1111 * CENT);
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we should get the exact amount
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6
|
||||
|
||||
// test avoiding sub-cent change
|
||||
empty_wallet();
|
||||
add_coin(0.0005 * COIN);
|
||||
add_coin(0.01 * COIN);
|
||||
add_coin(1 * COIN);
|
||||
add_coin(wallet, 0.0005 * COIN);
|
||||
add_coin(wallet, 0.01 * COIN);
|
||||
add_coin(wallet, 1 * COIN);
|
||||
|
||||
// trying to make 1.0001 from these three coins
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(1.0001 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
|
|
@ -264,7 +264,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
|||
{
|
||||
empty_wallet();
|
||||
for (int i2 = 0; i2 < 100; i2++)
|
||||
add_coin(COIN);
|
||||
add_coin(wallet, COIN);
|
||||
|
||||
// picking 50 from 100 coins doesn't depend on the shuffle,
|
||||
// but does depend on randomness in the stochastic approximation code
|
||||
|
|
@ -287,7 +287,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
|||
// add 75 cents in small change. not enough to make 90 cents,
|
||||
// then try making 90 cents. there are multiple competing "smallest bigger" coins,
|
||||
// one of which should be picked at random
|
||||
add_coin( 5*CENT); add_coin(10*CENT); add_coin(15*CENT); add_coin(20*CENT); add_coin(25*CENT);
|
||||
add_coin(wallet, 5*CENT); add_coin(wallet, 10*CENT); add_coin(wallet, 15*CENT); add_coin(wallet, 20*CENT); add_coin(wallet, 25*CENT);
|
||||
|
||||
fails = 0;
|
||||
for (int i = 0; i < RANDOM_REPEATS; i++)
|
||||
|
|
|
|||
117
src/wallet.cpp
117
src/wallet.cpp
|
|
@ -20,8 +20,11 @@
|
|||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include <secp256k1.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
/**
|
||||
* Settings
|
||||
*/
|
||||
|
|
@ -54,7 +57,7 @@ struct CompareValueOnly
|
|||
|
||||
std::string COutput::ToString() const
|
||||
{
|
||||
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue));
|
||||
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->GetValueOut(i)));
|
||||
}
|
||||
|
||||
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
|
||||
|
|
@ -753,12 +756,36 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
|
|||
const CWalletTx& prev = (*mi).second;
|
||||
if (txin.prevout.n < prev.vout.size())
|
||||
if (IsMine(prev.vout[txin.prevout.n]) & filter)
|
||||
return prev.vout[txin.prevout.n].nValue;
|
||||
return prev.GetValueOut(txin.prevout.n);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
CAmount CWallet::GetCredit(const CWalletTx& tx, const isminefilter& filter) const
|
||||
{
|
||||
CAmount nCredit = 0;
|
||||
for (unsigned int i = 0; i < tx.vout.size(); i++)
|
||||
{
|
||||
nCredit += tx.GetCredit(i, filter);
|
||||
if (!MoneyRange(nCredit))
|
||||
throw std::runtime_error("CWallet::GetCredit() : value out of range");
|
||||
}
|
||||
return nCredit;
|
||||
}
|
||||
|
||||
CAmount CWallet::GetChange(const CWalletTx& tx) const
|
||||
{
|
||||
CAmount nChange = 0;
|
||||
for (unsigned int i = 0; i < tx.vout.size(); i++)
|
||||
{
|
||||
nChange += tx.GetChange(i);
|
||||
if (!MoneyRange(nChange))
|
||||
throw std::runtime_error("CWallet::GetChange() : value out of range");
|
||||
}
|
||||
return nChange;
|
||||
}
|
||||
|
||||
bool CWallet::IsChange(const CTxOut& txout) const
|
||||
{
|
||||
// TODO: fix handling of 'change' outputs. The assumption is that any
|
||||
|
|
@ -837,10 +864,7 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
|
|||
// Compute fee:
|
||||
CAmount nDebit = GetDebit(filter);
|
||||
if (nDebit > 0) // debit>0 means we signed/sent this transaction
|
||||
{
|
||||
CAmount nValueOut = GetValueOut();
|
||||
nFee = nDebit - nValueOut;
|
||||
}
|
||||
nFee = nTxFee;
|
||||
|
||||
// Sent/received.
|
||||
for (unsigned int i = 0; i < vout.size(); ++i)
|
||||
|
|
@ -868,7 +892,11 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
|
|||
address = CNoDestination();
|
||||
}
|
||||
|
||||
COutputEntry output = {address, txout.nValue, (int)i};
|
||||
COutputEntry output = {address, GetValueOut(i), (int)i, CPubKey()};
|
||||
|
||||
if (!txout.nValue.IsAmount() && GetValueOut(i) > 0) {
|
||||
output.confidentiality_pubkey = pwallet->blinding_pubkey;
|
||||
}
|
||||
|
||||
// If we are debited by the transaction, add the output as a "sent" entry
|
||||
if (nDebit > 0)
|
||||
|
|
@ -1179,7 +1207,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
|
|||
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
|
||||
isminetype mine = IsMine(pcoin->vout[i]);
|
||||
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
|
||||
!IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 &&
|
||||
!IsLockedCoin((*it).first, i) && pcoin->GetValueOut(i) > 0 &&
|
||||
(!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
|
||||
vCoins.push_back(COutput(pcoin, i, nDepth, ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (includeWatching && ((mine & ISMINE_WATCH_ONLY) != ISMINE_NO))));
|
||||
}
|
||||
|
|
@ -1259,7 +1287,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
|
|||
continue;
|
||||
|
||||
int i = output.i;
|
||||
CAmount n = pcoin->vout[i].nValue;
|
||||
CAmount n = pcoin->GetValueOut(i);
|
||||
|
||||
pair<CAmount,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i));
|
||||
|
||||
|
|
@ -1349,8 +1377,8 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
|
|||
BOOST_FOREACH(const COutput& out, vCoins)
|
||||
{
|
||||
if (!out.fSpendable)
|
||||
continue;
|
||||
nValueRet += out.tx->vout[out.i].nValue;
|
||||
continue;
|
||||
nValueRet += out.tx->GetValueOut(out.i);
|
||||
setCoinsRet.insert(make_pair(out.tx, out.i));
|
||||
}
|
||||
return (nValueRet >= nTargetValue);
|
||||
|
|
@ -1369,7 +1397,7 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
|
|||
if (!out.fSpendable)
|
||||
continue;
|
||||
|
||||
nValueTroughVINs += out.tx->vout[out.i].nValue;
|
||||
nValueTroughVINs += out.tx->GetValueOut(out.i);
|
||||
|
||||
// temporarily keep the coin to add them later after SelectCoinsMinConf has added some
|
||||
setTempCoins.insert(make_pair(out.tx, out.i));
|
||||
|
|
@ -1407,12 +1435,16 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
|
|||
bool CWallet::FundTransaction(const CTransaction& txToFund, CMutableTransaction& txNew, CAmount &nFeeRet, std::string& strFailReason, bool includeWatching)
|
||||
{
|
||||
|
||||
vector<pair<CScript, CAmount> > vecSend;
|
||||
vector<CSend> vecSend;
|
||||
vector<CTxIn> vin;
|
||||
|
||||
BOOST_FOREACH (const CTxOut& txOut, txToFund.vout)
|
||||
{
|
||||
vecSend.push_back(make_pair(txOut.scriptPubKey, txOut.nValue));
|
||||
if (!txOut.nValue.IsAmount()) {
|
||||
strFailReason = _("Pre-funded amounts must be non-blinded");
|
||||
return false;
|
||||
}
|
||||
vecSend.push_back(CSend(txOut.scriptPubKey, txOut.nValue.GetAmount()));
|
||||
}
|
||||
|
||||
BOOST_FOREACH (const CTxIn& txIn, txToFund.vin)
|
||||
|
|
@ -1434,20 +1466,20 @@ bool CWallet::FundTransaction(const CTransaction& txToFund, CMutableTransaction&
|
|||
return result;
|
||||
}
|
||||
|
||||
bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
||||
bool CWallet::CreateTransaction(const vector<CSend>& vecSend,
|
||||
CWalletTx& wtxNew, CMutableTransaction& txNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign, bool includeWatching)
|
||||
{
|
||||
vector<CTxIn> vINs;
|
||||
return CreateTransaction(vecSend, vINs, wtxNew, txNew, reservekey, nFeeRet, strFailReason, coinControl, sign, includeWatching);
|
||||
}
|
||||
|
||||
bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend, const vector<CTxIn> vINs,
|
||||
bool CWallet::CreateTransaction(const vector<CSend>& vecSend, const vector<CTxIn> vINs,
|
||||
CWalletTx& wtxNew, CMutableTransaction& txNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign, bool includeWatching)
|
||||
{
|
||||
bool cannotFundFee = false; // used with includeWatching
|
||||
|
||||
CAmount nValue = 0;
|
||||
BOOST_FOREACH (const PAIRTYPE(CScript, CAmount)& s, vecSend)
|
||||
BOOST_FOREACH (const CSend& s, vecSend)
|
||||
{
|
||||
if (nValue < 0)
|
||||
{
|
||||
|
|
@ -1471,6 +1503,10 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
|||
nFeeRet = 0;
|
||||
while (true)
|
||||
{
|
||||
std::vector<CPubKey> output_pubkeys;
|
||||
bool fBlindedOuts = false;
|
||||
CAmount nValueOut = 0;
|
||||
|
||||
txNew.vin.clear();
|
||||
txNew.vout.clear();
|
||||
wtxNew.fFromMe = true;
|
||||
|
|
@ -1478,7 +1514,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
|||
CAmount nTotalValue = nValue + nFeeRet;
|
||||
double dPriority = 0;
|
||||
// vouts to the payees
|
||||
BOOST_FOREACH (const PAIRTYPE(CScript, CAmount)& s, vecSend)
|
||||
BOOST_FOREACH (const CSend& s, vecSend)
|
||||
{
|
||||
CTxOut txout(s.second, s.first);
|
||||
if (txout.IsDust(::minRelayTxFee))
|
||||
|
|
@ -1487,6 +1523,11 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
|||
return false;
|
||||
}
|
||||
txNew.vout.push_back(txout);
|
||||
nValueOut += s.second;
|
||||
output_pubkeys.push_back(s.confidentiality_key);
|
||||
if (s.confidentiality_key.size() != 0) {
|
||||
fBlindedOuts = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Choose coins to use
|
||||
|
|
@ -1511,7 +1552,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
|||
}
|
||||
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
|
||||
{
|
||||
CAmount nCredit = pcoin.first->vout[pcoin.second].nValue;
|
||||
CAmount nCredit = pcoin.first->GetValueOut(pcoin.second);
|
||||
//The coin age after the next block (depth+1) is used instead of the current,
|
||||
//reflecting an assumption the user would accept a bit more delay for
|
||||
//a chance at a free transaction.
|
||||
|
|
@ -1566,8 +1607,11 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
|||
else
|
||||
{
|
||||
// Insert change txn at random position:
|
||||
vector<CTxOut>::iterator position = txNew.vout.begin()+GetRandInt(txNew.vout.size()+1);
|
||||
int pos = GetRandInt(txNew.vout.size()+1);
|
||||
vector<CTxOut>::iterator position = txNew.vout.begin()+pos;
|
||||
txNew.vout.insert(position, newTxOut);
|
||||
output_pubkeys.insert(output_pubkeys.begin() + pos, blinding_pubkey);
|
||||
nValueOut += nChange;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -1577,7 +1621,30 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
|||
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
|
||||
txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second));
|
||||
|
||||
// Sign (also calculate fee)
|
||||
txNew.nTxFee = nValueIn - nValueOut;
|
||||
LogPrintf("Created transaction (before blinding): %s", CTransaction(txNew).ToString());
|
||||
|
||||
// Create blinded outputs
|
||||
bool fBlindedIns = false;
|
||||
std::vector<std::vector<unsigned char> > input_blinds;
|
||||
std::vector<std::vector<unsigned char> > output_blinds;
|
||||
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) {
|
||||
std::vector<unsigned char> blind =coin.first->GetBlindingFactor(coin.second);
|
||||
if (!blind.empty()) {
|
||||
fBlindedIns = true;
|
||||
}
|
||||
input_blinds.push_back(blind);
|
||||
}
|
||||
for (size_t nOut = 0; nOut < txNew.vout.size(); nOut++) {
|
||||
output_blinds.push_back(std::vector<unsigned char>(0, 0));
|
||||
}
|
||||
if (fBlindedIns && !fBlindedOuts) {
|
||||
strFailReason = _("Confidential inputs without confidential outputs");
|
||||
return false;
|
||||
}
|
||||
BlindOutputs(input_blinds, output_blinds, output_pubkeys, txNew);
|
||||
|
||||
// Sign
|
||||
int nIn = 0;
|
||||
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
|
||||
// when unsignable and watchonly enabled, this may be a watchonly so don't error
|
||||
|
|
@ -1648,11 +1715,11 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CWallet::CreateTransaction(CScript scriptPubKey, const CAmount& nValue,
|
||||
bool CWallet::CreateTransaction(CScript scriptPubKey, const CAmount& nValue, const CPubKey& confidentiality_key,
|
||||
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign, bool includeWatching)
|
||||
{
|
||||
vector< pair<CScript, CAmount> > vecSend;
|
||||
vecSend.push_back(make_pair(scriptPubKey, nValue));
|
||||
vector<CSend> vecSend;
|
||||
vecSend.push_back(CSend(scriptPubKey, nValue, confidentiality_key));
|
||||
CMutableTransaction txNew;
|
||||
vector<CTxIn> vINs;
|
||||
return CreateTransaction(vecSend, vINs, wtxNew, txNew, reservekey, nFeeRet, strFailReason, coinControl, sign, includeWatching);
|
||||
|
|
@ -2006,7 +2073,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
|
|||
if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr))
|
||||
continue;
|
||||
|
||||
CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->vout[i].nValue;
|
||||
CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->GetValueOut(i);
|
||||
|
||||
if (!balances.count(addr))
|
||||
balances[addr] = 0;
|
||||
|
|
|
|||
120
src/wallet.h
120
src/wallet.h
|
|
@ -7,6 +7,7 @@
|
|||
#define BITCOIN_WALLET_H
|
||||
|
||||
#include "amount.h"
|
||||
#include "blind.h"
|
||||
#include "primitives/block.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "crypter.h"
|
||||
|
|
@ -103,6 +104,16 @@ public:
|
|||
StringMap destdata;
|
||||
};
|
||||
|
||||
struct CSend
|
||||
{
|
||||
CScript first;
|
||||
CAmount second;
|
||||
CPubKey confidentiality_key;
|
||||
|
||||
CSend(const CScript& key_in, const CAmount& amount_in) : first(key_in), second(amount_in) {}
|
||||
CSend(const CScript& key_in, const CAmount& amount_in, const CPubKey& pubkey_in) : first(key_in), second(amount_in), confidentiality_key(pubkey_in) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
|
@ -185,6 +196,9 @@ public:
|
|||
nNextResend = 0;
|
||||
nLastResend = 0;
|
||||
nTimeFirstKey = 0;
|
||||
unsigned char static_blinding_key[32] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32};
|
||||
blinding_key.Set(&static_blinding_key[0], &static_blinding_key[32], true);
|
||||
blinding_pubkey = blinding_key.GetPubKey();
|
||||
}
|
||||
|
||||
std::map<uint256, CWalletTx> mapWallet;
|
||||
|
|
@ -200,6 +214,9 @@ public:
|
|||
|
||||
int64_t nTimeFirstKey;
|
||||
|
||||
CKey blinding_key;
|
||||
CPubKey blinding_pubkey;
|
||||
|
||||
const CWalletTx* GetWalletTx(const uint256& hash) const;
|
||||
|
||||
//! check whether we are allowed to upgrade (or already support) to the named feature
|
||||
|
|
@ -289,11 +306,11 @@ public:
|
|||
CAmount GetUnconfirmedWatchOnlyBalance() const;
|
||||
CAmount GetImmatureWatchOnlyBalance() const;
|
||||
bool FundTransaction(const CTransaction& txToFund, CMutableTransaction& txNew, CAmount& nFeeRet, std::string& strFailReason, bool includeWatching = false);
|
||||
bool CreateTransaction(const std::vector<std::pair<CScript, CAmount> >& vecSend, const std::vector<CTxIn> vins,
|
||||
bool CreateTransaction(const std::vector<CSend>& vecSend, const std::vector<CTxIn> vins,
|
||||
CWalletTx& wtxNew, CMutableTransaction& txNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true, bool includeWatching = false);
|
||||
bool CreateTransaction(const std::vector<std::pair<CScript, CAmount> >& vecSend,
|
||||
bool CreateTransaction(const std::vector<CSend>& vecSend,
|
||||
CWalletTx& wtxNew, CMutableTransaction& txNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true, bool includeWatching = false);
|
||||
bool CreateTransaction(CScript scriptPubKey, const CAmount& nValue,
|
||||
bool CreateTransaction(CScript scriptPubKey, const CAmount& nValue, const CPubKey& confidentiality_key,
|
||||
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true, bool includeWatching = false);
|
||||
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
|
||||
|
||||
|
|
@ -320,19 +337,9 @@ public:
|
|||
{
|
||||
return ::IsMine(*this, txout.scriptPubKey);
|
||||
}
|
||||
CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const
|
||||
{
|
||||
if (!MoneyRange(txout.nValue))
|
||||
throw std::runtime_error("CWallet::GetCredit() : value out of range");
|
||||
return ((IsMine(txout) & filter) ? txout.nValue : 0);
|
||||
}
|
||||
|
||||
bool IsChange(const CTxOut& txout) const;
|
||||
CAmount GetChange(const CTxOut& txout) const
|
||||
{
|
||||
if (!MoneyRange(txout.nValue))
|
||||
throw std::runtime_error("CWallet::GetChange() : value out of range");
|
||||
return (IsChange(txout) ? txout.nValue : 0);
|
||||
}
|
||||
|
||||
bool IsMine(const CTransaction& tx) const
|
||||
{
|
||||
BOOST_FOREACH(const CTxOut& txout, tx.vout)
|
||||
|
|
@ -356,28 +363,8 @@ public:
|
|||
}
|
||||
return nDebit;
|
||||
}
|
||||
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const
|
||||
{
|
||||
CAmount nCredit = 0;
|
||||
BOOST_FOREACH(const CTxOut& txout, tx.vout)
|
||||
{
|
||||
nCredit += GetCredit(txout, filter);
|
||||
if (!MoneyRange(nCredit))
|
||||
throw std::runtime_error("CWallet::GetCredit() : value out of range");
|
||||
}
|
||||
return nCredit;
|
||||
}
|
||||
CAmount GetChange(const CTransaction& tx) const
|
||||
{
|
||||
CAmount nChange = 0;
|
||||
BOOST_FOREACH(const CTxOut& txout, tx.vout)
|
||||
{
|
||||
nChange += GetChange(txout);
|
||||
if (!MoneyRange(nChange))
|
||||
throw std::runtime_error("CWallet::GetChange() : value out of range");
|
||||
}
|
||||
return nChange;
|
||||
}
|
||||
CAmount GetCredit(const CWalletTx& tx, const isminefilter& filter) const;
|
||||
CAmount GetChange(const CWalletTx& tx) const;
|
||||
void SetBestChain(const CBlockLocator& loc);
|
||||
|
||||
DBErrors LoadWallet(bool& fFirstRunRet);
|
||||
|
|
@ -493,6 +480,7 @@ struct COutputEntry
|
|||
CTxDestination destination;
|
||||
CAmount amount;
|
||||
int vout;
|
||||
CPubKey confidentiality_pubkey;
|
||||
};
|
||||
|
||||
/** A transaction with a merkle branch linking it to the block chain. */
|
||||
|
|
@ -573,6 +561,9 @@ public:
|
|||
std::string strFromAccount;
|
||||
int64_t nOrderPos; //! position in ordered transaction list
|
||||
|
||||
mutable std::vector<std::vector<unsigned char> > vBlindingFactors;
|
||||
mutable std::vector<CAmount> vAmountsOut;
|
||||
|
||||
// memory only
|
||||
mutable bool fDebitCached;
|
||||
mutable bool fCreditCached;
|
||||
|
|
@ -671,6 +662,8 @@ public:
|
|||
READWRITE(nTimeReceived);
|
||||
READWRITE(fFromMe);
|
||||
READWRITE(fSpent);
|
||||
READWRITE(vBlindingFactors);
|
||||
READWRITE(vAmountsOut);
|
||||
|
||||
if (ser_action.ForRead())
|
||||
{
|
||||
|
|
@ -739,6 +732,16 @@ public:
|
|||
return debit;
|
||||
}
|
||||
|
||||
CAmount GetCredit(unsigned int nTxOut, const isminefilter& filter) const
|
||||
{
|
||||
CAmount amount = 0;
|
||||
if (pwallet->IsMine(vout[nTxOut]) & filter)
|
||||
amount = GetValueOut(nTxOut);
|
||||
if (!MoneyRange(amount))
|
||||
throw std::runtime_error("CWallet::GetCredit() : value out of range");
|
||||
return amount;
|
||||
}
|
||||
|
||||
CAmount GetCredit(const isminefilter& filter) const
|
||||
{
|
||||
// Must wait until coinbase is safely deep enough in the chain before valuing it
|
||||
|
|
@ -804,8 +807,7 @@ public:
|
|||
{
|
||||
if (!pwallet->IsSpent(hashTx, i))
|
||||
{
|
||||
const CTxOut &txout = vout[i];
|
||||
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
|
||||
nCredit += GetCredit(i, ISMINE_SPENDABLE);
|
||||
if (!MoneyRange(nCredit))
|
||||
throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
|
||||
}
|
||||
|
|
@ -847,8 +849,7 @@ public:
|
|||
{
|
||||
if (!pwallet->IsSpent(GetHash(), i))
|
||||
{
|
||||
const CTxOut &txout = vout[i];
|
||||
nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
|
||||
nCredit += GetCredit(i, ISMINE_WATCH_ONLY);
|
||||
if (!MoneyRange(nCredit))
|
||||
throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
|
||||
}
|
||||
|
|
@ -859,6 +860,16 @@ public:
|
|||
return nCredit;
|
||||
}
|
||||
|
||||
CAmount GetChange(unsigned int nTxOut) const
|
||||
{
|
||||
CAmount amount = 0;
|
||||
if (pwallet->IsChange(vout[nTxOut]))
|
||||
amount = GetValueOut(nTxOut);
|
||||
if (!MoneyRange(amount))
|
||||
throw std::runtime_error("CWallet::GetCredit() : value out of range");
|
||||
return amount;
|
||||
}
|
||||
|
||||
CAmount GetChange() const
|
||||
{
|
||||
if (fChangeCached)
|
||||
|
|
@ -914,6 +925,33 @@ public:
|
|||
void RelayWalletTransaction();
|
||||
|
||||
std::set<uint256> GetConflicts() const;
|
||||
|
||||
private:
|
||||
void FillValuesAndBlindingFactors() const {
|
||||
if (!vAmountsOut.size()) {
|
||||
vAmountsOut.resize(vout.size());
|
||||
vBlindingFactors.resize(vout.size());
|
||||
for (unsigned int i = 0; i < vout.size(); i++) {
|
||||
std::vector<unsigned char> nonce(32, 0);
|
||||
int res = UnblindOutput(pwallet->blinding_key, vout[i], vAmountsOut[i], vBlindingFactors[i]);
|
||||
if (!res)
|
||||
vAmountsOut[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
//! Returns either the value out (if it is to us) or 0
|
||||
CAmount GetValueOut(unsigned int nOut) const {
|
||||
FillValuesAndBlindingFactors();
|
||||
return vAmountsOut[nOut];
|
||||
}
|
||||
|
||||
//! Returns either the blinding factor (if it is to us) or 0
|
||||
std::vector<unsigned char> GetBlindingFactor(unsigned int nOut) const {
|
||||
FillValuesAndBlindingFactors();
|
||||
return vBlindingFactors[nOut];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -193,6 +193,11 @@ bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
|
|||
return WriteAccountingEntry(++nAccountingEntryNumber, acentry);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteBlindingKey(const CKey& privKey)
|
||||
{
|
||||
return Write(std::string("blindingkey"), std::vector<unsigned char>(privKey.begin(), privKey.end()));
|
||||
}
|
||||
|
||||
CAmount CWalletDB::GetAccountCreditDebit(const string& strAccount)
|
||||
{
|
||||
list<CAccountingEntry> entries;
|
||||
|
|
@ -590,6 +595,16 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
|||
return false;
|
||||
}
|
||||
}
|
||||
else if (strType == "blindingkey")
|
||||
{
|
||||
assert(!pwallet->blinding_key.IsValid());
|
||||
std::vector<unsigned char> vchBlindingKey;
|
||||
ssValue >> vchBlindingKey;
|
||||
pwallet->blinding_key.Set(vchBlindingKey.begin(), vchBlindingKey.end(), true);
|
||||
if (pwallet->blinding_key.IsValid()) {
|
||||
pwallet->blinding_pubkey = pwallet->blinding_key.GetPubKey();
|
||||
}
|
||||
}
|
||||
} catch (...)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -701,6 +716,12 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
|
|||
if (wss.fAnyUnordered)
|
||||
result = ReorderTransactions(pwallet);
|
||||
|
||||
if (!pwallet->blinding_key.IsValid()) {
|
||||
pwallet->blinding_key.MakeNewKey(true);
|
||||
pwallet->blinding_pubkey = pwallet->blinding_key.GetPubKey();
|
||||
WriteBlindingKey(pwallet->blinding_key);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@ public:
|
|||
CAmount GetAccountCreditDebit(const std::string& strAccount);
|
||||
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& acentries);
|
||||
|
||||
bool WriteBlindingKey(const CKey& privKey);
|
||||
|
||||
DBErrors ReorderTransactions(CWallet* pwallet);
|
||||
DBErrors LoadWallet(CWallet* pwallet);
|
||||
DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue