Browse Source

Merge pull request #339 from VerusCoin/dev

Dev
pull/433/head v0.7.3-9
Asher Dawes 3 years ago
committed by GitHub
parent
commit
f86f251fd6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .gitlab-ci.yml
  2. 2
      README.md
  3. 2
      doc/man/verus-cli/linux/README.txt
  4. 2
      doc/man/verus-cli/mac/README.txt
  5. 2
      doc/man/verus-cli/windows/README.txt
  6. 2
      src/deprecation.h
  7. 28
      src/komodo_bitcoind.h
  8. 51
      src/main.cpp
  9. 11
      src/net.cpp
  10. 3
      src/pbaas/pbaas.cpp
  11. 2
      src/pbaas/reserves.cpp
  12. 4
      src/pbaas/vdxf.cpp
  13. 31
      src/pbaas/vdxf.h
  14. 21
      src/primitives/transaction.h
  15. 51
      src/rpc/pbaasrpc.cpp
  16. 6
      src/rpc/rawtransaction.cpp
  17. 11
      src/script/interpreter.cpp
  18. 2
      src/script/script.cpp
  19. 47
      src/script/sign.cpp
  20. 46
      src/transaction_builder.cpp
  21. 3
      src/transaction_builder.h
  22. 2
      src/version.h
  23. 98
      src/wallet/asyncrpcoperation_sendmany.cpp

4
.gitlab-ci.yml

@ -7,7 +7,7 @@ stages:
########################################################################################################################
variables:
VERSION: 0.7.3-8
VERSION: 0.7.3-9
VERUS_CLI_ARM64_LINUX: Verus-CLI-Linux-v${VERSION}-arm64.tar.gz
VERUS_CLI_LINUX_X86_64: Verus-CLI-Linux-v${VERSION}-x86_64.tar.gz
@ -397,4 +397,4 @@ deploy:
expire_in: 1 week
########################################################################################################################
####END#### Deploy ####END####
########################################################################################################################
########################################################################################################################

2
README.md

@ -1,5 +1,5 @@
## VerusCoin version 0.7.3-8
## VerusCoin version 0.7.3-9
Arguably the world's most advanced technology, zero knowledge privacy-centric blockchain, Verus Coin brings Sapling performance and zero knowledge features to an intelligent system with interchain smart contracts and a completely original, combined proof of stake/proof of work consensus algorithm that solves the nothing at stake problem. With this and its approach towards CPU mining and ASICs, Verus Coin strives to be one of the most naturally decentralizing and attack resistant blockchains in existence.

2
doc/man/verus-cli/linux/README.txt

@ -1,5 +1,5 @@
VerusCoin Command Line Tools v0.7.3-8
VerusCoin Command Line Tools v0.7.3-9
Contents:
verusd - VerusCoin daemon

2
doc/man/verus-cli/mac/README.txt

@ -1,5 +1,5 @@
VerusCoin Command Line Tools v0.7.3-8
VerusCoin Command Line Tools v0.7.3-9
Contents:
verusd - VerusCoin daemon.

2
doc/man/verus-cli/windows/README.txt

@ -1,5 +1,5 @@
VerusCoin Command Line Tools v0.7.3-8
VerusCoin Command Line Tools v0.7.3-9
Contents:
verusd.exe - VerusCoin daemon

2
src/deprecation.h

@ -9,7 +9,7 @@
// * Shut down 20 weeks' worth of blocks after the estimated release block height.
// * A warning is shown during the 2 weeks' worth of blocks prior to shut down.
static const int APPROX_RELEASE_HEIGHT = 1572000;
static const int APPROX_RELEASE_HEIGHT = 1589000;
static const int WEEKS_UNTIL_DEPRECATION = 20;
static const int DEPRECATION_HEIGHT = APPROX_RELEASE_HEIGHT + (WEEKS_UNTIL_DEPRECATION * 7 * 60 * 24);

28
src/komodo_bitcoind.h

@ -1588,23 +1588,17 @@ bool verusCheckPOSBlock(int32_t slowflag, const CBlock *pblock, int32_t height)
else
{
uint256 pastHash = chainActive.GetVerusEntropyHash(height);
// if height is over when Nonce is required to be the new format, we check that the new format is correct
// if we are on a version requiring the new nonce format, we check that the new format is correct
// if over when we have the new POS hash function, we validate that as well
// they are 100 blocks apart
CPOSNonce nonce = pblock->nNonce;
//printf("before nNonce: %s, height: %d\n", pblock->nNonce.GetHex().c_str(), height);
validHash = pblock->GetRawVerusPOSHash(rawHash, height);
hash = UintToArith256(rawHash) / tx.vout[voutNum].nValue;
//printf("Raw POShash: %s\n", hash.GetHex().c_str());
hash = UintToArith256(tx.GetVerusPOSHash(&nonce, voutNum, height, pastHash));
//printf("after nNonce: %s, height: %d\n", nonce.GetHex().c_str(), height);
//printf("POShash: %s\n\n", hash.GetHex().c_str());
//printf("blkHash: %s\n\n", blkHash.GetHex().c_str());
//printf("posHash: %s\n\n", posHash.GetHex().c_str());
if ((!newPOSEnforcement || posHash == hash) && hash <= target)
{
BlockMap::const_iterator it = mapBlockIndex.find(blkHash);
@ -1834,8 +1828,22 @@ bool verusCheckPOSBlock(int32_t slowflag, const CBlock *pblock, int32_t height)
}
else
{
printf("ERROR: malformed nonce value for PoS block\nnNonce: %s\nrawHash: %s\nposHash: %s\nvalue: %lu\n",
pblock->nNonce.GetHex().c_str(), rawHash.GetHex().c_str(), posHash.GetHex().c_str(), value);
// improved logging
if ((newPOSEnforcement && posHash != hash))
{
LogPrint("pos", "%s: conflicting hash values between GetRawVerusPOSHash (%s/%s) and GetVerusPOSHash (%s)\n",
__func__,
rawHash.GetHex().c_str(),
ArithToUint256(posHash).GetHex().c_str(),
ArithToUint256(hash).GetHex().c_str());
}
LogPrint("pos", "%s: malformed nonce value for PoS block\nnNonce: %s\nrawHash: %s\nposHash: %s\nvalue: %lu\n",
__func__,
pblock->nNonce.GetHex().c_str(),
rawHash.GetHex().c_str(),
posHash.GetHex().c_str(),
value);
}
}
}

51
src/main.cpp

@ -6004,8 +6004,8 @@ static bool AcceptBlockHeader(int32_t *futureblockp,const CBlockHeader& block, C
*ppindex = pindex;
if ( pindex != 0 && pindex->nStatus & BLOCK_FAILED_MASK )
{
LogPrintf("block height: %u\n", pindex->GetHeight());
return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate");
LogPrint("net", "block height: %u\n", pindex->GetHeight());
return state.DoS(100, error("%s: block is marked invalid", __func__), REJECT_INVALID, "banned-for-invalid-block");
}
/*if ( pindex != 0 && hash == komodo_requestedhash )
{
@ -6080,7 +6080,9 @@ static bool AcceptBlock(int32_t *futureblockp, const CBlock& block, CValidationS
CBlockIndex *&pindex = *ppindex;
if (!AcceptBlockHeader(futureblockp, block, state, chainparams, &pindex))
{
if ( *futureblockp == 0 )
int nDoS = 0;
if ( *futureblockp == 0 || (state.IsInvalid(nDoS) && nDoS >= 100) )
{
LogPrintf("AcceptBlock AcceptBlockHeader error\n");
return false;
@ -6121,14 +6123,6 @@ static bool AcceptBlock(int32_t *futureblockp, const CBlock& block, CValidationS
{
if ( *futureblockp == 0 )
{
// DEBUG ONLY
if (state.IsInvalid() &&
!state.CorruptionPossible() &&
block.IsVerusPOSBlock())
{
verusCheckPOSBlock(false, &block, pindex->GetHeight());
} //*/
if (state.IsInvalid() && !state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
@ -7563,19 +7557,29 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
vector<CInv> vNotFound;
LOCK(cs_main);
LogPrint("getdata", "%s\n", __func__);
while (it != pfrom->vRecvGetData.end()) {
// Don't bother if send buffer is too full to respond anyway
if (pfrom->nSendSize >= SendBufferSize())
{
LogPrint("net", "%s: send buffer too full\n", __func__);
break;
}
const CInv &inv = *it;
{
LogPrint("getdata", "%s: one inventory item %s\n", __func__, inv.ToString().c_str());
boost::this_thread::interruption_point();
it++;
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK)
{
LogPrint("getdata", "%s: inv %s\n", __func__, inv.type == MSG_BLOCK ? "MSG_BLOCK" : "MSG_FILTERED_BLOCK");
bool send = false;
BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
if (mi != mapBlockIndex.end())
@ -7599,6 +7603,8 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// it's available before trying to send.
if (send && (mi->second->nStatus & BLOCK_HAVE_DATA))
{
LogPrint("getdata", "%s: is send\n", __func__);
// Send block from disk
CBlock block;
if (!ReadBlockFromDisk(block, (*mi).second, consensusParams, 1))
@ -7653,6 +7659,8 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
}
else if (inv.IsKnownType())
{
LogPrint("getdata", "%s: inv 3 %d\n", __func__, inv.type);
// Check the mempool to see if a transaction is expiring soon. If so, do not send to peer.
// Note that a transaction enters the mempool first, before the serialized form is cached
// in mapRelay after a successful relay.
@ -7826,7 +7834,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->ssSend.SetVersion(min(pfrom->nVersion, CConstVerusSolutionVector::activationHeight.ActiveVersion(nHeight) >= CConstVerusSolutionVector::activationHeight.ACTIVATE_PBAAS ?
MIN_PBAAS_VERSION :
MIN_PEER_PROTO_VERSION));
if (!pfrom->fInbound)
{
// Advertise our address
@ -8084,6 +8092,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
else if (strCommand == "getdata")
{
LogPrint("getdata", "received getdata peer=%d\n", pfrom->id);
vector<CInv> vInv;
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
@ -8099,6 +8108,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id);
pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
LogPrint("getdata", "calling ProcessGetData\n");
ProcessGetData(pfrom, chainparams.GetConsensus());
}
@ -8419,15 +8429,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
int32_t futureblock;
if (!AcceptBlockHeader(&futureblock, header, state, chainparams, &pindexLast)) {
int nDoS;
if (state.IsInvalid(nDoS) && futureblock == 0)
if (state.IsInvalid(nDoS) && (futureblock == 0 || nDoS >= 100))
{
if (nDoS > 0 && futureblock == 0)
Misbehaving(pfrom->GetId(), nDoS/nDoS);
Misbehaving(pfrom->GetId(), nDoS);
return error("invalid header received");
}
}
}
if (pindexLast)
UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
@ -8473,7 +8482,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
}
}
// This asymmetric behavior for inbound and outbound connections was introduced
// to prevent a fingerprinting attack: an attacker can send specific fake addresses
@ -8765,6 +8774,7 @@ bool ProcessMessages(CNode* pfrom)
// Scan for message start
if (memcmp(msg.hdr.pchMessageStart, chainparams.MessageStart(), MESSAGE_START_SIZE) != 0) {
LogPrintf("PROCESSMESSAGE: MESSAGESTART DOES NOT MATCH NETWORK %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id);
Misbehaving(pfrom->GetId(), 100);
fOk = false;
break;
}
@ -8774,6 +8784,7 @@ bool ProcessMessages(CNode* pfrom)
if (!hdr.IsValid(chainparams.MessageStart()))
{
LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id);
Misbehaving(pfrom->GetId(), 20);
continue;
}
string strCommand = hdr.GetCommand();

11
src/net.cpp

@ -1582,6 +1582,11 @@ void ThreadMessageHandler()
BOOST_FOREACH(CNode* pnode, vNodesCopy)
{
if (!pnode)
{
LogPrintf("%s: unexpected NULL node\n", __func__);
}
if (pnode->fDisconnect)
continue;
@ -1616,7 +1621,13 @@ void ThreadMessageHandler()
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodesCopy)
{
if (!pnode)
{
LogPrintf("%s: unexpected NULL node 2\n", __func__);
}
pnode->Release();
}
}
if (fSleep)

3
src/pbaas/pbaas.cpp

@ -1825,8 +1825,7 @@ bool CConnectedChains::CreateLatestImports(const CCurrencyDefinition &sourceSyst
return false;
}
if (oneIT.first.second.GetBlockHeight() &&
proofNotarization.proofRoots[sourceSystemID].stateRoot != oneIT.first.second.CheckPartialTransaction(exportTx))
if (proofNotarization.proofRoots[sourceSystemID].stateRoot != oneIT.first.second.CheckPartialTransaction(exportTx))
{
LogPrintf("%s: export tx %s fails verification\n", __func__, oneIT.first.first.txIn.prevout.hash.GetHex().c_str());
return false;

2
src/pbaas/reserves.cpp

@ -3433,7 +3433,9 @@ bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const CCurre
spentCurrencyOut.valueMap[importCurrencyID] += netPrimaryOut;
}
//newCurrencyState.primaryCurrencyOut = netPrimaryOut - (burnedChangePrice + burnedChangeWeight);
newCurrencyState.primaryCurrencyOut = netPrimaryOut;
if (importCurrencyDef.IsPBaaSChain() && importCurrencyState.IsLaunchConfirmed() && !importCurrencyState.IsLaunchClear())
{
// pre-conversions should already be on this chain as gateway deposits on behalf of the

4
src/pbaas/vdxf.cpp

@ -12,7 +12,9 @@
#include "crosschainrpc.h"
std::string CVDXF::DATA_KEY_SEPARATOR = "::";
std::map<uint160,std::pair<std::pair<uint32_t, uint32_t>,std::pair<uint32_t, uint32_t>>> CVDXF::VDXF_TYPES;
// TODO: HARDENING - ensure discussion on question of data limits
uint160 CVDXF::STRUCTURED_DATA_KEY = CVDXF_StructuredData::StructuredDataKey();
uint160 CVDXF::ZMEMO_MESSAGE_KEY = CVDXF_Data::ZMemoMessageKey();
uint160 CVDXF::ZMEMO_SIGNATURE_KEY = CVDXF_Data::ZMemoSignatureKey();

31
src/pbaas/vdxf.h

@ -191,9 +191,6 @@ void FromVector(const std::vector<unsigned char> &vch, SERIALIZABLE &obj, bool *
class CVDXF
{
protected:
static std::map<uint160,std::pair<std::pair<uint32_t, uint32_t>,std::pair<uint32_t, uint32_t>>> VDXF_TYPES;
public:
static uint160 STRUCTURED_DATA_KEY;
static uint160 ZMEMO_MESSAGE_KEY;
@ -229,15 +226,9 @@ public:
static uint160 GetID(const std::string &Name);
static uint160 GetID(const std::string &Name, uint160 &parent);
static uint160 GetDataKey(const std::string &keyName, uint160 &nameSpaceID);
static void RegisterVDXFType(const uint160 &typeID, uint32_t minVer, uint32_t maxVer, uint32_t minVecSize, uint32_t maxVecSize)
{
assert(typeID != STRUCTURED_DATA_KEY);
VDXF_TYPES.insert(std::make_pair(typeID, std::make_pair(std::make_pair(minVer, maxVer), std::make_pair(minVecSize, maxVecSize))));
}
bool IsValid(bool registered=false)
bool IsValid()
{
return (((key == STRUCTURED_DATA_KEY || !registered ) && version >= FIRST_VERSION && version <= LAST_VERSION) ||
(VDXF_TYPES.count(key) && version >= VDXF_TYPES[key].first.first && version <= VDXF_TYPES[key].first.second));
return !key.IsNull() && version >= FIRST_VERSION && version <= LAST_VERSION;
}
};
@ -254,16 +245,9 @@ public:
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(*(CVDXF *)this);
if (version != VERSION_INVALID)
if (IsValid())
{
if (VDXF_TYPES.count(key))
{
READWRITE(data);
if (!(VDXF_TYPES[key].second.first <= data.size() && VDXF_TYPES[key].second.second >= data.size()))
{
version = VERSION_INVALID;
}
}
READWRITE(data);
}
}
static std::string ZMemoMessageKeyName()
@ -296,12 +280,9 @@ public:
static uint160 currencyStartNotarization = GetDataKey(CurrencyStartNotarizationKeyName(), nameSpace);
return currencyStartNotarization;
}
bool IsValid(bool registered=false)
bool IsValid()
{
return CVDXF::IsValid(registered) &&
(!registered ||
(data.size() <= VDXF_TYPES[key].second.first &&
data.size() >= VDXF_TYPES[key].second.second));
return CVDXF::IsValid();
}
};

21
src/primitives/transaction.h

@ -1233,6 +1233,7 @@ public:
TYPE_FULLTX = 1,
TYPE_PBAAS = 2,
TYPE_ETH = 3,
TYPE_LAST = 3
};
int8_t version; // to enable versioning of this type of proof
int8_t type; // this may represent transactions from different systems
@ -1315,20 +1316,22 @@ public:
// chain proofs have a tx proof in block, a merkle proof bridge, and a chain proof
bool IsChainProof() const
{
return TYPE_PBAAS == type &&
txProof.proofSequence.size() >= 3 &&
txProof.proofSequence[1]->branchType == CMerkleBranchBase::BRANCH_MMRBLAKE_NODE &&
txProof.proofSequence[2]->branchType == CMerkleBranchBase::BRANCH_MMRBLAKE_POWERNODE;
return IsValid() &&
((type == TYPE_ETH) ||
(TYPE_PBAAS == type &&
txProof.proofSequence.size() >= 3 &&
txProof.proofSequence[1]->branchType == CMerkleBranchBase::BRANCH_MMRBLAKE_NODE &&
txProof.proofSequence[2]->branchType == CMerkleBranchBase::BRANCH_MMRBLAKE_POWERNODE));
}
bool IsValid() const
{
return version >= VERSION_FIRST && version <= VERSION_LAST;
return version >= VERSION_FIRST && version <= VERSION_LAST && type != TYPE_INVALID && type <= TYPE_LAST;
}
uint256 GetBlockHash() const
{
if (IsChainProof())
if ((type == TYPE_PBAAS || type == TYPE_FULLTX) && IsChainProof())
{
std::vector<uint256> &branch = ((CMMRNodeBranch *)(txProof.proofSequence[1]))->branch;
if (branch.size() == 1)
@ -1341,7 +1344,7 @@ public:
uint256 GetBlockPower() const
{
if (IsChainProof())
if (type == TYPE_PBAAS && IsChainProof())
{
std::vector<uint256> &branch = ((CMMRPowerNodeBranch *)(txProof.proofSequence[2]))->branch;
if (branch.size() >= 1)
@ -1354,7 +1357,7 @@ public:
uint32_t GetBlockHeight() const
{
if (IsChainProof())
if ((type == TYPE_PBAAS || type == TYPE_FULLTX) && IsChainProof())
{
return ((CMMRPowerNodeBranch *)(txProof.proofSequence[2]))->nIndex;
}
@ -1363,7 +1366,7 @@ public:
uint32_t GetProofHeight() const
{
if (IsChainProof())
if ((type == TYPE_PBAAS || type == TYPE_FULLTX) && IsChainProof())
{
return ((CMMRPowerNodeBranch *)(txProof.proofSequence[2]))->nSize - 1;
}

51
src/rpc/pbaasrpc.cpp

@ -3403,7 +3403,7 @@ UniValue sendcurrency(const UniValue& params, bool fHelp)
" \"burn\":\"false\", (bool, optional) destroy the currency and subtract it from the supply. Currency must be a token.\n"
" \"mintnew\":\"false\", (bool, optional) if the transaction is sent from the currency ID of a centralized currency, this creates new currency to send\n"
" }, ... ]\n"
"3. \"feeamount\" (bool, optional) specific fee amount requested instead of default miner's fee\n"
"3. \"feeamount\" (number, optional) specific fee amount requested instead of default miner's fee\n"
"\nResult:\n"
" \"txid\" : \"transactionid\" (string) The transaction id if (returntx) is false\n"
@ -4814,24 +4814,6 @@ CCurrencyDefinition ValidateNewUnivalueCurrencyDefinition(const UniValue &uniObj
{
// it is a PBaaS chain, and it is its own system, responsible for its own communication and currency control
newCurrency.systemID = newCurrency.GetID();
std::vector<CNodeData> vNodes;
UniValue nodeArr = find_value(uniObj, "nodes");
if (nodeArr.isArray() && nodeArr.size())
{
for (int i = 0; i < nodeArr.size(); i++)
{
CNodeData nd(nodeArr[i]);
if (nd.IsValid())
{
vNodes.push_back(nd);
if (vNodes.size() == 2)
{
break;
}
}
}
}
}
if (currentChainDefinition)
@ -5707,7 +5689,7 @@ UniValue registeridentity(const UniValue& params, bool fHelp)
if (fHelp || params.size() < 1 || params.size() > 2)
{
throw runtime_error(
"registeridentity \"jsonidregistration\" feeoffer\n"
"registeridentity \"jsonidregistration\" (returntx) feeoffer\n"
"\n\n"
"\nArguments\n"
@ -5725,6 +5707,7 @@ UniValue registeridentity(const UniValue& params, bool fHelp)
" ...\n"
" }\n"
"}\n"
"returntx (bool, optional) default=false if true, return a transaction for additional signatures rather than committing it\n"
"feeoffer (amount, optional) amount to offer miner/staker for the registration fee, if missing, uses standard price\n\n"
"\nResult:\n"
@ -5793,14 +5776,16 @@ UniValue registeridentity(const UniValue& params, bool fHelp)
newID.recoveryAuthority = newID.GetID();
}
bool returnTx = params.size() > 1 ? uni_get_bool(params[1]) : false;
CAmount feeOffer;
CAmount minFeeOffer = reservation.referral.IsNull() ?
ConnectedChains.ThisChain().IDFullRegistrationAmount() :
ConnectedChains.ThisChain().IDReferredRegistrationAmount();
if (params.size() > 1)
if (params.size() > 2)
{
feeOffer = AmountFromValue(params[1]);
feeOffer = AmountFromValue(params[2]);
}
else
{
@ -5993,18 +5978,22 @@ UniValue registeridentity(const UniValue& params, bool fHelp)
signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &wtx, i, value, coins.vout[wtx.vin[i].prevout.n].scriptPubKey), coins.vout[wtx.vin[i].prevout.n].scriptPubKey, sigdata, CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()));
if (!signSuccess)
if (!signSuccess && !returnTx)
{
LogPrintf("%s: failure to sign identity registration tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
printf("%s: failure to sign identity registration tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
} else {
} else if (sigdata.scriptSig.size()) {
UpdateTransaction(mtx, i, sigdata);
}
}
*static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
if (!pwalletMain->CommitTransaction(wtx, reserveKey))
if (returnTx)
{
return EncodeHexTx(wtx);
}
else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
{
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
}
@ -6193,12 +6182,12 @@ UniValue updateidentity(const UniValue& params, bool fHelp)
signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &wtx, i, value, coins.vout[wtx.vin[i].prevout.n].scriptPubKey), coins.vout[wtx.vin[i].prevout.n].scriptPubKey, sigdata, CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()));
if (!signSuccess)
if (!signSuccess && !returnTx)
{
LogPrintf("%s: failure to sign identity recovery tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
printf("%s: failure to sign identity recovery tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
} else {
} else if (sigdata.scriptSig.size()) {
UpdateTransaction(mtx, i, sigdata);
}
}
@ -6304,12 +6293,12 @@ UniValue revokeidentity(const UniValue& params, bool fHelp)
signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &wtx, i, value, coins.vout[wtx.vin[i].prevout.n].scriptPubKey), coins.vout[wtx.vin[i].prevout.n].scriptPubKey, sigdata, CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()));
if (!signSuccess)
if (!signSuccess && !returnTx)
{
LogPrintf("%s: failure to sign identity revocation tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
printf("%s: failure to sign identity revocation tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
} else {
} else if (sigdata.scriptSig.size()) {
UpdateTransaction(mtx, i, sigdata);
}
}
@ -6422,12 +6411,12 @@ UniValue recoveridentity(const UniValue& params, bool fHelp)
signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &wtx, i, value, coins.vout[wtx.vin[i].prevout.n].scriptPubKey), coins.vout[wtx.vin[i].prevout.n].scriptPubKey, sigdata, CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()));
if (!signSuccess)
if (!signSuccess && !returnTx)
{
LogPrintf("%s: failure to sign identity recovery tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
printf("%s: failure to sign identity recovery tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
} else {
} else if (sigdata.scriptSig.size()) {
UpdateTransaction(mtx, i, sigdata);
}
}

6
src/rpc/rawtransaction.cpp

@ -1189,15 +1189,17 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
if (!fHashSingle || (i < mergedTx.vout.size()))
ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, prevPubKey, INT32_MAX, nHashType), prevPubKey, sigdata, consensusBranchId);
TransactionSignatureChecker checker(&txConst, i, amount);
// ... and merge in other signatures:
BOOST_FOREACH(const CMutableTransaction& txv, txVariants) {
sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i), consensusBranchId);
sigdata = CombineSignatures(prevPubKey, checker, sigdata, DataFromTransaction(txv, i), consensusBranchId);
}
UpdateTransaction(mergedTx, i, sigdata);
ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), consensusBranchId, &serror)) {
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, checker, consensusBranchId, &serror)) {
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
}
}

11
src/script/interpreter.cpp

@ -1394,7 +1394,7 @@ std::map<uint160, pair<int, std::vector<std::vector<unsigned char>>>> BaseSignat
return idAddresses;
}
TransactionSignatureChecker::TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const CScript *pScriptPubKeyIn, const CKeyStore *pKeyStore, uint32_t spendHeight) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL)
TransactionSignatureChecker::TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const CScript *pScriptPubKeyIn, const CKeyStore *pKeyStore, uint32_t spendHeight) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL), idMapSet(false)
{
if (pScriptPubKeyIn && pKeyStore)
{
@ -1402,7 +1402,7 @@ TransactionSignatureChecker::TransactionSignatureChecker(const CTransaction* txT
}
}
TransactionSignatureChecker::TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn, const CScript *pScriptPubKeyIn, const CKeyStore *pKeyStore, uint32_t spendHeight) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL)
TransactionSignatureChecker::TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn, const CScript *pScriptPubKeyIn, const CKeyStore *pKeyStore, uint32_t spendHeight) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL), idMapSet(false)
{
if (pScriptPubKeyIn && pKeyStore)
{
@ -1466,9 +1466,8 @@ int TransactionSignatureChecker::CheckCryptoCondition(
if (ffillBin.empty())
return false;
int expectedEvals = 1;
CC *outputCC = nullptr;
int nHashType;
int nHashType = SIGHASH_ALL;
if (p.IsValid() && p.version >= p.VERSION_V3 && p.vData.size())
{
@ -1646,6 +1645,10 @@ int TransactionSignatureChecker::CheckCryptoCondition(
return true;
}
}
else // early out
{
return false;
}
}
else
{

2
src/script/script.cpp

@ -525,6 +525,8 @@ bool CScript::IsInstantSpend() const
{
COptCCParams p;
bool isInstantSpend = false;
// TODO: HARDENING - this must run on the Verus chain, but should have a version check and parameter
if (!_IsVerusActive() && IsPayToCryptoCondition(p) && p.IsValid())
{
// instant spends must be to expected instant spend crypto conditions and to the right address as well

47
src/script/sign.cpp

@ -1001,19 +1001,54 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature
}
}
std::vector<unsigned char> _GetFulfillmentVector(CScript const& scriptSig)
{
auto pc = scriptSig.begin();
opcodetype opcode;
std::vector<unsigned char> ffbin;
if (scriptSig.GetOp(pc, opcode, ffbin))
return ffbin;
return std::vector<unsigned char>();
}
SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
const SignatureData& scriptSig1, const SignatureData& scriptSig2,
uint32_t consensusBranchId)
{
txnouttype txType;
vector<vector<unsigned char> > vSolutions;
Solver(scriptPubKey, txType, vSolutions);
return CombineSignatures(
scriptPubKey, checker, txType, vSolutions,
Stacks(scriptSig1, consensusBranchId),
Stacks(scriptSig2, consensusBranchId),
consensusBranchId).Output();
COptCCParams p;
if (scriptPubKey.IsPayToCryptoCondition(p) &&
p.IsValid() &&
p.version >= COptCCParams::VERSION_V3)
{
CSmartTransactionSignatures smartSigs1, smartSigs2;
std::vector<unsigned char> ffVec1 = _GetFulfillmentVector(scriptSig1.scriptSig);
smartSigs1 = CSmartTransactionSignatures(std::vector<unsigned char>(ffVec1.begin(), ffVec1.end()));
std::vector<unsigned char> ffVec2 = _GetFulfillmentVector(scriptSig2.scriptSig);
smartSigs2 = CSmartTransactionSignatures(std::vector<unsigned char>(ffVec2.begin(), ffVec2.end()));
if (smartSigs1.sigHashType == smartSigs2.sigHashType && smartSigs1.version == smartSigs2.version)
{
for (auto oneSig : smartSigs2.signatures)
{
smartSigs1.AddSignature(oneSig.second);
}
}
SignatureData sigRet;
sigRet.scriptSig = PushAll(std::vector<valtype>({smartSigs1.AsVector()}));
return sigRet;
}
else
{
Solver(scriptPubKey, txType, vSolutions);
return CombineSignatures(
scriptPubKey, checker, txType, vSolutions,
Stacks(scriptSig1, consensusBranchId),
Stacks(scriptSig2, consensusBranchId),
consensusBranchId).Output();
}
}
namespace {

46
src/transaction_builder.cpp

@ -32,11 +32,29 @@ bool TransactionBuilderResult::IsTx() { return maybeTx != boost::none; }
bool TransactionBuilderResult::IsError() { return maybeError != boost::none; }
bool TransactionBuilderResult::IsHexTx()
{
CTransaction tx;
if (maybeError != boost::none &&
IsHex(maybeError.get()))
{
return DecodeHexTx(tx, maybeError.get());
}
return false;
}
CTransaction TransactionBuilderResult::GetTxOrThrow() {
if (maybeTx) {
return maybeTx.get();
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to build transaction: " + GetError());
if (IsHex(GetError()))
{
throw JSONRPCError(RPC_WALLET_ERROR, GetError());
}
else
{
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to build transaction: " + GetError());
}
}
}
@ -251,7 +269,7 @@ void TransactionBuilder::SendChangeTo(const CTxDestination &changeAddr)
sproutChangeAddr = boost::none;
}
TransactionBuilderResult TransactionBuilder::Build()
TransactionBuilderResult TransactionBuilder::Build(bool throwTxWithPartialSig)
{
//
// Consistency checks
@ -271,6 +289,12 @@ TransactionBuilderResult TransactionBuilder::Build()
view.SetBackend(viewMemPool);
CReserveTransactionDescriptor rtxd(mtx, view, chainActive.Height() + 1);
if (!rtxd.IsValid())
{
CReserveTransactionDescriptor checkRtxd(mtx, view, chainActive.Height() + 1);
LogPrint("txbuilder", "Invalid reserve transaction descriptor\n", __func__);
return TransactionBuilderResult("Invalid reserve transaction descriptor");
}
reserveChange = (rtxd.ReserveInputMap() - rtxd.ReserveOutputMap()) - reserveFee;
//printf("\n%s: reserve input:\n%s\noutput:\n%s\nchange:\n%s\n\n", __func__, rtxd.ReserveInputMap().ToUniValue().write(1,2).c_str(), rtxd.ReserveOutputMap().ToUniValue().write(1,2).c_str(), reserveChange.ToUniValue().write(1,2).c_str());
@ -301,7 +325,7 @@ TransactionBuilderResult TransactionBuilder::Build()
//TxToUniv(mtx, uint256(), jsonTx);
//printf("%s: mtx: %s\n", __func__, jsonTx.write(1,2).c_str());
printf("%s: Change cannot be negative, %s\n", __func__, ("native: " + std::to_string(change) + "\nreserves: " + reserveChange.ToUniValue().write()).c_str());
return TransactionBuilderResult("Change cannot be negative, native: " + std::to_string(change) + "\nreserves: " + reserveChange.ToUniValue().write() + "\n");
return TransactionBuilderResult("Change cannot be negative, native: " + std::to_string(change) + "\nreserves: " + reserveChange.ToUniValue().write());
}
if ((rtxd.NativeFees() - this->fee) != change)
@ -532,6 +556,7 @@ TransactionBuilderResult TransactionBuilder::Build()
}
// Transparent signatures
bool throwPartialSig = false;
CTransaction txNewConst(mtx);
for (int nIn = 0; nIn < mtx.vin.size(); nIn++) {
auto tIn = tIns[nIn];
@ -544,12 +569,25 @@ TransactionBuilderResult TransactionBuilder::Build()
//extern void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry);
//TxToUniv(txNewConst, uint256(), jsonTx);
//printf("Failed to sign for script:\n%s\n", jsonTx.write(1,2).c_str());
return TransactionBuilderResult("Failed to sign transaction");
if (sigdata.scriptSig.size() && throwTxWithPartialSig)
{
UpdateTransaction(mtx, nIn, sigdata);
throwPartialSig = true;
}
else
{
return TransactionBuilderResult("Failed to sign transaction");
}
} else {
UpdateTransaction(mtx, nIn, sigdata);
}
}
if (throwPartialSig)
{
return TransactionBuilderResult(EncodeHexTx(mtx));
}
return TransactionBuilderResult(CTransaction(mtx));
}

3
src/transaction_builder.h

@ -64,6 +64,7 @@ public:
TransactionBuilderResult(const std::string& error);
bool IsTx();
bool IsError();
bool IsHexTx();
CTransaction GetTxOrThrow();
std::string GetError();
};
@ -164,7 +165,7 @@ public:
void SendChangeTo(const CTxDestination &changeAddr);
TransactionBuilderResult Build();
TransactionBuilderResult Build(bool throwTxWithPartialSig=false);
private:
void CreateJSDescriptions();

2
src/version.h

@ -35,6 +35,6 @@ static const int MEMPOOL_GD_VERSION = 60002;
static const int NO_BLOOM_VERSION = 170004;
#define KOMODO_VERSION "0.2.1"
#define VERUS_VERSION "0.7.3-8"
#define VERUS_VERSION "0.7.3-9"
#endif // BITCOIN_VERSION_H

98
src/wallet/asyncrpcoperation_sendmany.cpp

@ -253,6 +253,8 @@ bool AsyncRPCOperation_sendmany::main_impl() {
bool isPureTaddrOnlyTx = (isfromtaddr_ && z_outputs_.size() == 0);
CAmount minersFee = fee_;
bool isFromSpecificID = false;
uint32_t solutionVersion = CConstVerusSolutionVector::GetVersionByHeight(chainActive.Height() + 1);
// figure out just how much we need before getting inputs
@ -285,6 +287,11 @@ bool AsyncRPCOperation_sendmany::main_impl() {
// When spending coinbase utxos, you can only specify a single zaddr as the change must go somewhere
// and if there are multiple zaddrs, we don't know where to send it.
if (isfromtaddr_) {
if (fromtaddr_.which() == COptCCParams::ADDRTYPE_ID && !GetDestinationID(fromtaddr_).IsNull())
{
isFromSpecificID = true;
}
// if we don't need to protect coinbases, they can be included in inputs
if (isSingleZaddrOutput || !Params().GetConsensus().fCoinbaseMustBeProtected) {
bool b = find_utxos(true);
@ -295,9 +302,9 @@ bool AsyncRPCOperation_sendmany::main_impl() {
bool b = find_utxos(false);
if (!b) {
if (isMultipleZaddrOutput) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any coinbase UTXOs without shielding requirements to spend. Protected coinbase UTXOs can only be sent to a single zaddr recipient.");
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find enough UTXOs without shielding requirements to spend. Protected coinbase UTXOs can only be sent to a single zaddr recipient.");
} else {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any coinbase UTXOs without shielding requirements to spend.");
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find enough UTXOs without shielding requirements to spend.");
}
}
}
@ -637,7 +644,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
}
// Build the transaction
tx_ = builder_.Build().GetTxOrThrow();
tx_ = builder_.Build(isFromSpecificID).GetTxOrThrow();
UniValue sendResult = SendTransaction(tx_, keyChange, testmode);
set_result(sendResult);
@ -1076,7 +1083,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
return true;
}
bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptProtectedCoinbase=false)
bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptProtectedCoinbase)
{
assert(isfromtaddr_);
@ -1084,6 +1091,7 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptProtectedCoinbase=false)
bool wildCardPKH = false;
bool wildCardID = false;
bool isFromSpecificID = fromtaddr_.which() == COptCCParams::ADDRTYPE_ID && !GetDestinationID(fromtaddr_).IsNull();
// if no specific address type, wildcard outputs to all transparent addresses and IDs are valid to consider
if (fromtaddr_.which() == COptCCParams::ADDRTYPE_INVALID)
@ -1126,10 +1134,11 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptProtectedCoinbase=false)
pwalletMain->AvailableCoins(vecOutputs, false, NULL, false, true, fAcceptProtectedCoinbase);
}
BOOST_FOREACH(const COutput& out, vecOutputs) {
for (COutput& out : vecOutputs)
{
CTxDestination dest;
if (!out.fSpendable) {
if (!isFromSpecificID && !out.fSpendable) {
continue;
}
@ -1137,32 +1146,87 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptProtectedCoinbase=false)
continue;
}
std::vector<CTxDestination> addresses;
int nRequired;
bool canSign, canSpend;
CTxDestination address;
if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) {
txnouttype txType;
if (!ExtractDestinations(out.tx->vout[out.i].scriptPubKey, txType, addresses, nRequired, pwalletMain, &canSign, &canSpend))
{
continue;
}
if (wildCardID || wildCardPKH)
if (isFromSpecificID)
{
bool keep = false;
if (wildCardPKH)
// if we have more address destinations than just this address and have specified from a single ID only,
// the condition must be such that the ID itself can spend, even if this wallet cannot due to a multisig
// ID. if the ID cannot spend, even given a valid multisig ID, then to select this as a source without
// an explicit, multisig match would cause potentially unwanted sourcing of funds. a spend just to this ID
// is fine.
COptCCParams p, m;
// if we can't spend and can only sign,
// ensure that this output is spendable by just this ID as a 1 of n and 1 of n at the master
// smart transaction level as well
if (!canSpend &&
(!canSign ||
!(out.tx->vout[out.i].scriptPubKey.IsPayToCryptoCondition(p) &&
p.IsValid() &&
(p.version < COptCCParams::VERSION_V3 ||
(p.vData.size() &&
(m = COptCCParams(p.vData.back())).IsValid() &&
(m.m == 1 || m.m == 0))) &&
p.m == 1)))
{
keep = address.which() == COptCCParams::ADDRTYPE_PKH || address.which() == COptCCParams::ADDRTYPE_PK;
continue;
}
if (!keep && wildCardID)
else
{
keep = address.which() == COptCCParams::ADDRTYPE_ID;
out.fSpendable = true; // this may not really be spendable, but set it if its the correct ID source and can sign
}
if (!keep)
}
else
{
if (!out.fSpendable)
{
continue;
}
}
else
bool keep = false;
for (auto &address : addresses)
{
if (!destinations.count(address)) {
continue;
if (isFromSpecificID)
{
if (address == fromtaddr_)
{
keep = true;
}
}
else if (wildCardID || wildCardPKH)
{
if (wildCardPKH)
{
keep = address.which() == COptCCParams::ADDRTYPE_PKH || address.which() == COptCCParams::ADDRTYPE_PK;
}
if (!keep && wildCardID)
{
keep = address.which() == COptCCParams::ADDRTYPE_ID;
}
}
else
{
keep = destinations.count(address);
}
if (keep)
{
break;
}
}
if (!keep)
{
continue;
}
t_inputs_.push_back(out);

Loading…
Cancel
Save