Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alpha 0.10 multi asset #94

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ae812cf
0.10-only: Assets: base_uint<>::IsNull() and base_uint<>::SetNull()
jtimon May 14, 2015
4e411b4
Chainparams: Don't check the genesis block, but store its transaction…
jtimon Mar 11, 2016
5d6d4be
Assets: Introduce bool CTransaction::IsAssetDefinition()
jtimon Dec 2, 2014
ddc38c2
Assets: Adapt input loops to asset definition transactions
greenaddress Mar 14, 2016
23186f2
Assets: Introduce CAssetID, CAmountMap and +=, -=, < operators
jtimon Jan 14, 2015
635b629
Assets: Introduce -feeasset policy arg
jtimon May 17, 2015
3543834
HF Assets: Introduce assetID field in CTxOut
jtimon Nov 30, 2014
f406c03
HF Assets: primitives/transaction: post-CT: Explicit multi-asset fees
jtimon Mar 21, 2016
ba04aed
HF Assets: coins.cpp: Adapt CCoinsViewCache::VerifyAmounts and CCoins…
jtimon Jun 1, 2015
2611cec
HF Assets: Adapt Consensus functions (main.cpp):
jtimon Dec 10, 2014
6f0a16f
HF Assets: Adapt miner.cpp
jtimon Mar 21, 2016
e109041
HF Assets: Adapt bitcoin-tx.cpp
jtimon Aug 18, 2015
aee6723
HF Assets: Adapt rpcrawtransaction.cpp
jtimon Jul 17, 2015
6d72a1e
Assets?: Make sure there's a chainActive.Tip() before calling some fu…
jtimon Jun 1, 2015
b4f6f6f
UNDO: Assets: comment CTransaction::nTxFee in wallet
jtimon Nov 26, 2015
3a9809c
UNDO: Assets: comment CTransaction::nTxFee in qt
jtimon Nov 26, 2015
dad4286
UNDO: Assets: Test: Comments in blind_tests.cpp
jtimon Nov 26, 2015
4186ff2
Assets: Update genesis_tests.cpp
jtimon Nov 26, 2015
9f71ca4
Assets: Update script Automatically generated test cases
jtimon Mar 11, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ BITCOIN_CORE_H = \
netbase.h \
net.h \
noui.h \
policy/fees.h \
pow.h \
protocol.h \
pubkey.h \
Expand Down Expand Up @@ -176,6 +177,7 @@ libbitcoin_server_a_SOURCES = \
miner.cpp \
net.cpp \
noui.cpp \
policy/fees.cpp \
rest.cpp \
rpcblockchain.cpp \
rpcmining.cpp \
Expand Down
80 changes: 63 additions & 17 deletions src/bitcoin-tx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,18 @@ 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:VALUE:SEQ " + _("Add input to TX") + "\n";
strUsage += " blind=B1::B3:B4:... " + _("Blind transaction outputs") + "\n";
strUsage += " in=TXID:VOUT:VALUE:SEQ[:ASSETID] " + _("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";
strUsage += " outscript=VALUE:SCRIPT " + _("Add raw script output to TX") + "\n";
strUsage += " outaddr=VALUE:ADDRESS[:ASSETID] " + _("Add address-based output to TX") + "\n";
strUsage += " outscript=VALUE:SCRIPT[:ASSETID]" + _("Add raw script output to TX") + "\n";
strUsage += " sign=SIGHASH-FLAGS " + _("Add zero or more signatures to transaction") + "\n";
strUsage += " This command requires JSON registers:\n";
strUsage += " prevtxs=JSON object\n";
strUsage += " privatekeys=JSON object\n";
strUsage += " See signrawtransaction docs for format of sighash flags, JSON objects.\n";
strUsage += " newasset=1 " + _("Make it a transaction definition transaction. An error will be raised if used after the transaction already has inputs.") + "\n";
strUsage += "\n";
fprintf(stdout, "%s", strUsage.c_str());

Expand Down Expand Up @@ -209,26 +210,34 @@ static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)
throw runtime_error("invalid TX input vout");

// Remove vout
pos = strVout.find(':');
pos = strVout.find(':', pos + 1);
strVout = strVout.substr(pos + 1, string::npos);

// extract and validate VALUE
pos = strVout.find(':');
pos = strVout.find(':', pos + 1);
if (pos == string::npos)
throw runtime_error("TX input missing separator");
string strValue = strVout.substr(0, pos);
string strValue = strVout.substr(pos + 1, strVout.find(':', pos + 1) - pos - 1);
CAmount value;
if (!ParseMoney(strValue, value))
throw runtime_error("invalid TX output value");

CAssetID assetID = CAssetID(Params().HashGenesisBlock());
// 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");

string strSeq = strVout.substr(pos + 1, string::npos);
size_t posAsset = strVout.find(':', pos + 1);
if (posAsset != string::npos && posAsset != 0 && posAsset != (strVout.size() - 1)) {
strSeq = strVout.substr(pos + 1, posAsset);
string strAsset = strVout.substr(posAsset + 1, string::npos);
assetID = CAssetID(uint256(strAsset));
}

strVout.resize(pos);
int64_t nSeq = atoi64(strSeq);

Expand All @@ -251,7 +260,9 @@ 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;
CAmountMap mTxReward = CTransaction(tx).GetTxRewardMap();
mTxReward[assetID] += value;
tx.SetFeesFromTxRewardMap(mTxReward);
}

static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
Expand All @@ -271,6 +282,14 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)

// extract and validate ADDRESS
string strAddr = strInput.substr(pos + 1, string::npos);
CAssetID assetID = CAssetID(Params().HashGenesisBlock());
size_t posAsset = strInput.find(':', pos + 1);
if (posAsset != string::npos && posAsset != 0 && posAsset != (strInput.size() - 1)) {
strAddr = strInput.substr(pos + 1, posAsset - pos - 1);
string strAsset = strInput.substr(posAsset + 1, string::npos);
assetID = CAssetID(uint256(strAsset));
}

CBitcoinAddress addr(strAddr);
if (!addr.IsValid())
throw runtime_error("invalid TX output address");
Expand All @@ -279,13 +298,15 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
CScript scriptPubKey = GetScriptForDestination(addr.Get());

// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
CTxOut txout(value, scriptPubKey, assetID);
if (addr.IsBlinded()) {
CPubKey pubkey = addr.GetBlindingKey();
txout.nValue.vchNonceCommitment = std::vector<unsigned char>(pubkey.begin(), pubkey.end());
}
tx.nTxFee -= value;
tx.vout.push_back(txout);
CAmountMap mTxReward = CTransaction(tx).GetTxRewardMap();
mTxReward[assetID] -= value;
tx.SetFeesFromTxRewardMap(mTxReward);
}

static void MutateTxBlind(CMutableTransaction& tx, const string& strInput)
Expand Down Expand Up @@ -349,40 +370,53 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput

// extract and validate script
string strScript = strInput.substr(pos + 1, string::npos);
CAssetID assetID = CAssetID(Params().HashGenesisBlock());
size_t posAsset = strInput.find(':', pos + 1);
if (posAsset != string::npos && posAsset != 0 && posAsset != (strInput.size() - 1)) {
strScript = strInput.substr(pos, posAsset);
string strAsset = strInput.substr(posAsset + 1, string::npos);
assetID = CAssetID(uint256(strAsset));
}
CScript scriptPubKey = ParseScript(strScript); // throws on err

CAmountMap mTxReward = CTransaction(tx).GetTxRewardMap();
mTxReward[assetID] -= value;
tx.SetFeesFromTxRewardMap(mTxReward);
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
CTxOut txout(value, scriptPubKey, assetID);
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()) {
string strErr = "Invalid TX input index '" + strInIdx + "'";
throw runtime_error(strErr.c_str());
}
// TODO: reduce fees (SetFeesFromTxRewardMap)

// delete input from transaction
tx.vin.erase(tx.vin.begin() + inIdx);
}

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()) {
string strErr = "Invalid TX output index '" + strOutIdx + "'";
throw runtime_error(strErr.c_str());
}

if (!tx.vout[outIdx].nValue.IsAmount())
throw runtime_error("Cannot delete confidential outputs");

CAmountMap mTxReward = CTransaction(tx).GetTxRewardMap();
CTxOut txOut = tx.vout[outIdx];
mTxReward[txOut.assetID] -= txOut.nValue.GetAmount();
tx.SetFeesFromTxRewardMap(mTxReward);
// delete output from transaction
tx.vout.erase(tx.vout.begin() + outIdx);
}
Expand Down Expand Up @@ -624,6 +658,16 @@ static void MutateTxWithdrawSign(CMutableTransaction& tx, const string& flagStr)
}
}

static void MutateTxAssetDefinition(CMutableTransaction& tx)
{
if (tx.vin.size() > 0)
throw runtime_error("Asset definition transaction already has inputs");

tx.vin.resize(1);
tx.vin[0].prevout.SetNull();
tx.vin[0].scriptSig = CScript();
}

class Secp256k1Init
{
public:
Expand Down Expand Up @@ -661,6 +705,8 @@ static void MutateTx(CMutableTransaction& tx, const string& command,
MutateTxBlind(tx, commandVal);
} else if (command == "withdrawsign")
MutateTxWithdrawSign(tx, commandVal);
else if (command == "newasset")
MutateTxAssetDefinition(tx);

else if (command == "load")
RegisterLoad(commandVal);
Expand Down
2 changes: 1 addition & 1 deletion src/bloom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
if (fFound)
return true;

BOOST_FOREACH(const CTxIn& txin, tx.vin)
FOREACH_TXIN(txin, tx)
{
// Match if the filter contains an outpoint tx spends
if (contains(txin.prevout))
Expand Down
80 changes: 59 additions & 21 deletions src/coins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "coins.h"

#include "chainparams.h"
#include "random.h"

#include <assert.h>
Expand Down Expand Up @@ -255,60 +256,94 @@ const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) const

extern secp256k1_context* secp256k1_bitcoin_verify_context;

bool CCoinsViewCache::VerifyAmounts(const CTransaction& tx, const CAmount& excess) const
bool CCoinsViewCache::VerifyAmounts(const CTransaction& tx, const CAmountMap& mTxReward) const
{
CAmount nPlainAmount = excess;
CAmountMap nPlainAmounts = mTxReward;
std::vector<unsigned char> vchData;
std::vector<unsigned char *> vpchCommitsIn, vpchCommitsOut;
std::map<CAssetID, std::vector<unsigned char *> > vpchCommitsInMap, vpchCommitsOutMap;
bool fNullRangeproof = false;
vchData.resize(CTxOutValue::nCommitmentSize * (tx.vin.size() + tx.vout.size()));
unsigned char *p = vchData.data();
if (!tx.IsCoinBase())
{
for (size_t i = 0; i < tx.vin.size(); ++i)
// The first input is null for asset definition transactions
FOREACH_TXIN(txin, tx)
{
const CTxOutValue& val = GetOutputFor(tx.vin[i]).nValue;
const CTxOut& txOut = GetOutputFor(txin);
const CTxOutValue& val = txOut.nValue;
CAssetID assetID;
if (txOut.assetID.IsNull()) {
bool isPrevGenesis = false;
for (unsigned int j = 0; j < Params().GenesisBlock().vtx.size(); ++j) {
if (Params().GenesisBlock().vtx[j].GetHash() == txin.prevout.hash)
isPrevGenesis = true;
}
assetID = isPrevGenesis ? Params().HashGenesisBlock() : CAssetID(txin.prevout.hash);
}
else
assetID = txOut.assetID;
if (val.IsAmount())
nPlainAmount -= val.GetAmount();
nPlainAmounts[assetID] -= val.GetAmount();
else
{
// Touch the asset ID in the map to later iterate
nPlainAmounts[assetID] += 0;
assert(val.vchCommitment.size() == CTxOutValue::nCommitmentSize);
memcpy(p, &val.vchCommitment[0], CTxOutValue::nCommitmentSize);
vpchCommitsIn.push_back(p);
vpchCommitsInMap[assetID].push_back(p);
p += CTxOutValue::nCommitmentSize;
}
}
}
for (size_t i = 0; i < tx.vout.size(); ++i)
{
const CTxOutValue& val = tx.vout[i].nValue;
const CTxOut& txOut = tx.vout[i];
const CTxOutValue& val = txOut.nValue;
assert(val.vchCommitment.size() == CTxOutValue::nCommitmentSize);
if (val.vchNonceCommitment.size() > CTxOutValue::nCommitmentSize || val.vchRangeproof.size() > 5000)
return false;
if (val.IsAmount())
nPlainAmount += val.GetAmount();
nPlainAmounts[txOut.assetID] += val.GetAmount();
else
{
// Touch the asset ID in the map to later iterate
nPlainAmounts[txOut.assetID] += 0;
memcpy(p, &val.vchCommitment[0], CTxOutValue::nCommitmentSize);
vpchCommitsOut.push_back(p);
vpchCommitsOutMap[txOut.assetID].push_back(p);
p += CTxOutValue::nCommitmentSize;

if (val.vchRangeproof.empty())
fNullRangeproof = true;
}
}

for(std::map<CAssetID, CAmount>::const_iterator it = nPlainAmounts.begin(); it != nPlainAmounts.end(); ++it) {
const CAssetID& assetID = it->first;
const CAmount& nPlainAmount = nPlainAmounts[assetID];
std::vector<unsigned char *>& vpchCommitsIn = vpchCommitsInMap[assetID];
std::vector<unsigned char *>& vpchCommitsOut = vpchCommitsOutMap[assetID];
// If there are no encrypted input or output values, we can do simple math
if (vpchCommitsIn.size() + vpchCommitsOut.size() == 0)
return (nPlainAmount == 0);
if (vpchCommitsIn.size() + vpchCommitsOut.size() == 0) {
// Within an asset definition transaction, the asset being defined is identified with a 0
if (assetID.IsNull()) {
// Only asset definitions can have null asset IDs in outputs
if (!tx.IsAssetDefinition())
return false;
// Cannot issue negative amounts
if (nPlainAmount < 0)
return false;
} else if (nPlainAmount != 0)
return false;
} else {
// Newly issued assets cannot be confidential
if (assetID.IsNull())
return false;

if (!secp256k1_pedersen_verify_tally(secp256k1_bitcoin_verify_context, vpchCommitsIn.data(), vpchCommitsIn.size(), vpchCommitsOut.data(), vpchCommitsOut.size(), nPlainAmount))
return false;

// Rangeproof is optional in this case
if ((!vpchCommitsIn.empty()) && vpchCommitsOut.size() == 1 && nPlainAmount <= 0 && fNullRangeproof)
return true;

// Rangeproof is optional in case none of the conditions are satisfied
if (!fNullRangeproof || vpchCommitsIn.empty() || vpchCommitsOut.size() != 1 || nPlainAmount > 0) {
uint64_t min_value, max_value;
for (size_t i = 0; i < tx.vout.size(); ++i)
{
Expand All @@ -318,21 +353,24 @@ bool CCoinsViewCache::VerifyAmounts(const CTransaction& tx, const CAmount& exces
if (!secp256k1_rangeproof_verify(secp256k1_bitcoin_verify_context, &min_value, &max_value, &val.vchCommitment[0], val.vchRangeproof.data(), val.vchRangeproof.size()))
return false;
}
}
}
}

return true;
}

bool CCoinsViewCache::VerifyAmounts(const CTransaction& tx) const
{
const CAmount& excess = tx.nTxFee;
return VerifyAmounts(tx, excess);
return VerifyAmounts(tx, tx.GetTxRewardMap());
}

bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
{
if (!tx.IsCoinBase()) {
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
// Don't check the null input in asset defintion transactions
FOREACH_TXIN(txin, tx) {
const COutPoint &prevout = txin.prevout;
const CCoins* coins = AccessCoins(prevout.hash);
if (!coins || !coins->IsAvailable(prevout.n)) {
return false;
Expand All @@ -347,7 +385,7 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const
if (tx.IsCoinBase())
return 0.0;
double dResult = 0.0;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
FOREACH_TXIN(txin, tx)
{
const CCoins* coins = AccessCoins(txin.prevout.hash);
assert(coins);
Expand Down
Loading