parent
8430716115
commit
f766ab3146
|
@ -170,7 +170,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
|
|||
txnouttype type;
|
||||
vector<vector<unsigned char> > vSolutions;
|
||||
if (Solver(txout.scriptPubKey, type, vSolutions) &&
|
||||
(type == TX_PUBKEY || type == TX_MULTISIG))
|
||||
(type == TX_PUBKEY || type == TX_PUBKEY_REPLAY || type == TX_MULTISIG || type == TX_MULTISIG_REPLAY))
|
||||
insert(COutPoint(hash, i));
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
#include "script/script.h"
|
||||
#include "uint256.h"
|
||||
|
||||
#include "main.h"
|
||||
#include <boost/algorithm/hex.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef vector<unsigned char> valtype;
|
||||
|
@ -188,6 +192,40 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Generic anti-replay protection using Script
|
||||
bool CheckBlockIndex(int &txBlockIndex, int blockIndex)
|
||||
{
|
||||
// checks for relative txBlockIndex
|
||||
if (txBlockIndex < 0)
|
||||
return false;
|
||||
// checks for absolute blockIndex
|
||||
else if (txBlockIndex >= 0) {
|
||||
ssize_t blockDelta = txBlockIndex - blockIndex;
|
||||
// blockDelta must be negative
|
||||
if (blockDelta > 0)
|
||||
return false;
|
||||
// blockDelta must be greater than 100 but lesser than 262144
|
||||
if ((blockDelta > -101) || (blockDelta < -262144))
|
||||
return false;
|
||||
// check if txBlockIndex refers to an actual block
|
||||
if (txBlockIndex > blockIndex)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generic anti-replay protection using Script
|
||||
bool CheckBlockHash(uint256 &txBlockHash, uint256 &blockHash)
|
||||
{
|
||||
// OP_CHECKBLOCKATHEIGHT matches active chain
|
||||
if (txBlockHash == blockHash)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) {
|
||||
// Empty signature. Not strictly DER encoded, but allowed to provide a
|
||||
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
|
||||
|
@ -379,7 +417,83 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
|
|||
break;
|
||||
}
|
||||
|
||||
case OP_NOP1: case OP_NOP3: case OP_NOP4: case OP_NOP5:
|
||||
#ifdef BITCOIN_TX // zen-tx can't process OP_CHECKBLOCKATHEIGHT because it requires an active chain
|
||||
case OP_CHECKBLOCKATHEIGHT:
|
||||
{
|
||||
return set_error(serror, SCRIPT_ERR_CHECKBLOCKATHEIGHT_UNVERIFIED);
|
||||
}
|
||||
break;
|
||||
#else
|
||||
// Generic anti-replay protection using Script
|
||||
// https://github.com/luke-jr/bips/blob/bip-noreplay/bip-noreplay.mediawiki
|
||||
// Author: Luke Dashjr <luke+bip@dashjr.org>
|
||||
// Implemented by: @movrcx; See: https://github.com/zencashio/zen/issues/12
|
||||
case OP_CHECKBLOCKATHEIGHT:
|
||||
{
|
||||
// we need two objects on the stack
|
||||
if (stack.size() < 2)
|
||||
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
|
||||
|
||||
CBlockIndex *currentBlock = chainActive.Tip();
|
||||
valtype vchBlockHash(stacktop(-2));
|
||||
valtype vchBlockIndex(stacktop(-1));
|
||||
int txBlockIndex;
|
||||
uint256 blockHash;
|
||||
uint256 txBlockHash;
|
||||
bool fSuccess = false;
|
||||
|
||||
// check for overflow before casting
|
||||
if ((vchBlockIndex.size() > 0x7ffffff) || (vchBlockHash.size() > 32))
|
||||
return set_error(serror, SCRIPT_ERR_CHECKBLOCKATHEIGHT);
|
||||
else
|
||||
{
|
||||
// get txBlockIndex from stack vch
|
||||
txBlockIndex = *reinterpret_cast<const uint16_t*>(&vchBlockIndex[0]);
|
||||
|
||||
// get txBlockHash from stack vch and convert to hex string
|
||||
std::string str;
|
||||
boost::algorithm::hex(vchBlockHash.begin(), vchBlockHash.end(), back_inserter(str));
|
||||
boost::algorithm::to_lower(str);
|
||||
txBlockHash.SetHex(str);
|
||||
}
|
||||
|
||||
if (currentBlock == NULL)
|
||||
return set_error(serror, SCRIPT_ERR_CHECKBLOCKATHEIGHT_UNVERIFIED);
|
||||
else
|
||||
{
|
||||
// relative blockIndex lookups are not permitted
|
||||
if (txBlockIndex < 0)
|
||||
return set_error(serror, SCRIPT_ERR_CHECKBLOCKATHEIGHT);
|
||||
else if (txBlockIndex >= 0)
|
||||
blockHash.SetHex(chainActive[txBlockIndex]->GetBlockHash().GetHex());
|
||||
|
||||
// ensure we are not doing any null comparisons
|
||||
if (blockHash.IsNull() || txBlockHash.IsNull())
|
||||
return set_error(serror, SCRIPT_ERR_CHECKBLOCKATHEIGHT_UNVERIFIED);
|
||||
|
||||
// check tx against blockchain
|
||||
fSuccess = ((CheckBlockIndex(txBlockIndex, currentBlock->nHeight)) && (CheckBlockHash(txBlockHash, blockHash)));
|
||||
}
|
||||
|
||||
// pop OP_CHECKBLOCKATHEIGHT related vars off the stack
|
||||
popstack(stack);
|
||||
popstack(stack);
|
||||
stack.push_back(fSuccess ? vchTrue : vchFalse);
|
||||
|
||||
if (opcode == OP_CHECKBLOCKATHEIGHT)
|
||||
{
|
||||
if (fSuccess)
|
||||
{
|
||||
popstack(stack);
|
||||
}
|
||||
else
|
||||
return set_error(serror, SCRIPT_ERR_CHECKBLOCKATHEIGHT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case OP_NOP1: case OP_NOP3: case OP_NOP4:
|
||||
case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
|
||||
{
|
||||
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
|
||||
|
|
|
@ -85,6 +85,7 @@ enum
|
|||
//
|
||||
// See BIP65 for details.
|
||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9),
|
||||
|
||||
};
|
||||
|
||||
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
|
||||
|
|
|
@ -138,13 +138,14 @@ const char* GetOpName(opcodetype opcode)
|
|||
case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY";
|
||||
case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG";
|
||||
case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY";
|
||||
case OP_CHECKBLOCKATHEIGHT : return "OP_CHECKBLOCKATHEIGHT";
|
||||
|
||||
// expanson
|
||||
case OP_NOP1 : return "OP_NOP1";
|
||||
case OP_NOP2 : return "OP_NOP2";
|
||||
case OP_NOP3 : return "OP_NOP3";
|
||||
case OP_NOP4 : return "OP_NOP4";
|
||||
case OP_NOP5 : return "OP_NOP5";
|
||||
//case OP_NOP5 : return "OP_NOP5"; Generic anti-replay protection using Script
|
||||
case OP_NOP6 : return "OP_NOP6";
|
||||
case OP_NOP7 : return "OP_NOP7";
|
||||
case OP_NOP8 : return "OP_NOP8";
|
||||
|
|
|
@ -162,6 +162,7 @@ enum opcodetype
|
|||
OP_NOP3 = 0xb2,
|
||||
OP_NOP4 = 0xb3,
|
||||
OP_NOP5 = 0xb4,
|
||||
OP_CHECKBLOCKATHEIGHT = OP_NOP5,
|
||||
OP_NOP6 = 0xb5,
|
||||
OP_NOP7 = 0xb6,
|
||||
OP_NOP8 = 0xb7,
|
||||
|
|
|
@ -67,6 +67,10 @@ const char* ScriptErrorString(const ScriptError serror)
|
|||
return "NOPx reserved for soft-fork upgrades";
|
||||
case SCRIPT_ERR_PUBKEYTYPE:
|
||||
return "Public key is neither compressed or uncompressed";
|
||||
case SCRIPT_ERR_CHECKBLOCKATHEIGHT:
|
||||
return "Transaction failed to pass replay prevention checks";
|
||||
case SCRIPT_ERR_CHECKBLOCKATHEIGHT_UNVERIFIED:
|
||||
return "OP_CHECKBLOCKATHEIGHT cannot be verified with zen-tx";
|
||||
case SCRIPT_ERR_UNKNOWN_ERROR:
|
||||
case SCRIPT_ERR_ERROR_COUNT:
|
||||
default: break;
|
||||
|
|
|
@ -51,8 +51,12 @@ typedef enum ScriptError_t
|
|||
|
||||
/* softfork safeness */
|
||||
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS,
|
||||
SCRIPT_ERR_ERROR_COUNT,
|
||||
|
||||
/* tx replay prevention */
|
||||
SCRIPT_ERR_CHECKBLOCKATHEIGHT,
|
||||
SCRIPT_ERR_CHECKBLOCKATHEIGHT_UNVERIFIED
|
||||
|
||||
SCRIPT_ERR_ERROR_COUNT
|
||||
} ScriptError;
|
||||
|
||||
#define SCRIPT_ERR_LAST SCRIPT_ERR_ERROR_COUNT
|
||||
|
|
|
@ -82,9 +82,14 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
|
|||
case TX_NONSTANDARD:
|
||||
case TX_NULL_DATA:
|
||||
return false;
|
||||
case TX_NULL_DATA_REPLAY:
|
||||
return false;
|
||||
case TX_PUBKEY:
|
||||
keyID = CPubKey(vSolutions[0]).GetID();
|
||||
return Sign1(keyID, creator, scriptPubKey, scriptSigRet);
|
||||
case TX_PUBKEY_REPLAY:
|
||||
keyID = CPubKey(vSolutions[0]).GetID();
|
||||
return Sign1(keyID, creator, scriptPubKey, scriptSigRet);
|
||||
case TX_PUBKEYHASH:
|
||||
keyID = CKeyID(uint160(vSolutions[0]));
|
||||
if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet))
|
||||
|
@ -96,12 +101,26 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
|
|||
scriptSigRet << ToByteVector(vch);
|
||||
}
|
||||
return true;
|
||||
case TX_PUBKEYHASH_REPLAY:
|
||||
keyID = CKeyID(uint160(vSolutions[0]));
|
||||
if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet))
|
||||
return false;
|
||||
else
|
||||
{
|
||||
CPubKey vch;
|
||||
creator.KeyStore().GetPubKey(keyID, vch);
|
||||
scriptSigRet << ToByteVector(vch);
|
||||
}
|
||||
return true;
|
||||
case TX_SCRIPTHASH:
|
||||
return creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptSigRet);
|
||||
|
||||
case TX_MULTISIG:
|
||||
scriptSigRet << OP_0; // workaround CHECKMULTISIG bug
|
||||
return (SignN(vSolutions, creator, scriptPubKey, scriptSigRet));
|
||||
case TX_MULTISIG_REPLAY:
|
||||
scriptSigRet << OP_0; // workaround CHECKMULTISIG bug
|
||||
return (SignN(vSolutions, creator, scriptPubKey, scriptSigRet));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -227,12 +246,23 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatur
|
|||
if (sigs1.size() >= sigs2.size())
|
||||
return PushAll(sigs1);
|
||||
return PushAll(sigs2);
|
||||
case TX_NULL_DATA_REPLAY:
|
||||
// Don't know anything about this, assume bigger one is correct:
|
||||
if (sigs1.size() >= sigs2.size())
|
||||
return PushAll(sigs1);
|
||||
return PushAll(sigs2);
|
||||
case TX_PUBKEY:
|
||||
case TX_PUBKEY_REPLAY:
|
||||
case TX_PUBKEYHASH:
|
||||
// Signatures are bigger than placeholders or empty scripts:
|
||||
if (sigs1.empty() || sigs1[0].empty())
|
||||
return PushAll(sigs2);
|
||||
return PushAll(sigs1);
|
||||
case TX_PUBKEYHASH_REPLAY:
|
||||
// Signatures are bigger than placeholders or empty scripts:
|
||||
if (sigs1.empty() || sigs1[0].empty())
|
||||
return PushAll(sigs2);
|
||||
return PushAll(sigs1);
|
||||
case TX_SCRIPTHASH:
|
||||
if (sigs1.empty() || sigs1.back().empty())
|
||||
return PushAll(sigs2);
|
||||
|
@ -255,6 +285,8 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatur
|
|||
}
|
||||
case TX_MULTISIG:
|
||||
return CombineMultisig(scriptPubKey, checker, vSolutions, sigs1, sigs2);
|
||||
case TX_MULTISIG_REPLAY:
|
||||
return CombineMultisig(scriptPubKey, checker, vSolutions, sigs1, sigs2);
|
||||
}
|
||||
|
||||
return CScript();
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef vector<unsigned char> valtype;
|
||||
|
@ -26,10 +28,14 @@ const char* GetTxnOutputType(txnouttype t)
|
|||
{
|
||||
case TX_NONSTANDARD: return "nonstandard";
|
||||
case TX_PUBKEY: return "pubkey";
|
||||
case TX_PUBKEY_REPLAY: return "pubkeyreplay";
|
||||
case TX_PUBKEYHASH: return "pubkeyhash";
|
||||
case TX_PUBKEYHASH_REPLAY: return "pubkeyhashreplay";
|
||||
case TX_SCRIPTHASH: return "scripthash";
|
||||
case TX_MULTISIG: return "multisig";
|
||||
case TX_MULTISIG_REPLAY: return "multisigreplay";
|
||||
case TX_NULL_DATA: return "nulldata";
|
||||
case TX_NULL_DATA_REPLAY: return "nulldatareplay";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -45,17 +51,24 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
|
|||
{
|
||||
// Standard tx, sender provides pubkey, receiver adds signature
|
||||
mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
|
||||
mTemplates.insert(make_pair(TX_PUBKEY_REPLAY, CScript() << OP_PUBKEY << OP_CHECKSIG << OP_SMALLDATA << OP_SMALLDATA << OP_CHECKBLOCKATHEIGHT));
|
||||
|
||||
// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
|
||||
mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
|
||||
mTemplates.insert(make_pair(TX_PUBKEYHASH_REPLAY, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG << OP_SMALLDATA << OP_SMALLDATA << OP_CHECKBLOCKATHEIGHT));
|
||||
|
||||
// Sender provides N pubkeys, receivers provides M signatures
|
||||
mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
|
||||
mTemplates.insert(make_pair(TX_MULTISIG_REPLAY, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG << OP_SMALLDATA << OP_SMALLDATA << OP_CHECKBLOCKATHEIGHT));
|
||||
|
||||
// Empty, provably prunable, data-carrying output
|
||||
if (GetBoolArg("-datacarrier", true))
|
||||
{
|
||||
mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA));
|
||||
mTemplates.insert(make_pair(TX_NULL_DATA_REPLAY, CScript() << OP_RETURN << OP_SMALLDATA << OP_SMALLDATA << OP_SMALLDATA << OP_CHECKBLOCKATHEIGHT));
|
||||
}
|
||||
mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN));
|
||||
|
||||
}
|
||||
|
||||
// Shortcut for pay-to-script-hash, which are more constrained than the other types:
|
||||
|
@ -87,7 +100,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
|
|||
{
|
||||
// Found a match
|
||||
typeRet = tplate.first;
|
||||
if (typeRet == TX_MULTISIG)
|
||||
if (typeRet == TX_MULTISIG || typeRet == TX_MULTISIG_REPLAY)
|
||||
{
|
||||
// Additional checks for TX_MULTISIG:
|
||||
unsigned char m = vSolutionsRet.front()[0];
|
||||
|
@ -166,14 +179,24 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned c
|
|||
case TX_NONSTANDARD:
|
||||
case TX_NULL_DATA:
|
||||
return -1;
|
||||
case TX_NULL_DATA_REPLAY:
|
||||
return -1;
|
||||
case TX_PUBKEY:
|
||||
return 1;
|
||||
case TX_PUBKEY_REPLAY:
|
||||
return 1;
|
||||
case TX_PUBKEYHASH:
|
||||
return 2;
|
||||
case TX_PUBKEYHASH_REPLAY:
|
||||
return 2;
|
||||
case TX_MULTISIG:
|
||||
if (vSolutions.size() < 1 || vSolutions[0].size() < 1)
|
||||
return -1;
|
||||
return vSolutions[0][0] + 1;
|
||||
case TX_MULTISIG_REPLAY:
|
||||
if (vSolutions.size() < 1 || vSolutions[0].size() < 1)
|
||||
return -1;
|
||||
return vSolutions[0][0] + 1;
|
||||
case TX_SCRIPTHASH:
|
||||
return 1; // doesn't include args needed by the script
|
||||
}
|
||||
|
@ -186,7 +209,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
|
|||
if (!Solver(scriptPubKey, whichType, vSolutions))
|
||||
return false;
|
||||
|
||||
if (whichType == TX_MULTISIG)
|
||||
if (whichType == TX_MULTISIG || whichType == TX_MULTISIG_REPLAY)
|
||||
{
|
||||
unsigned char m = vSolutions.front()[0];
|
||||
unsigned char n = vSolutions.back()[0];
|
||||
|
@ -207,7 +230,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
|
|||
if (!Solver(scriptPubKey, whichType, vSolutions))
|
||||
return false;
|
||||
|
||||
if (whichType == TX_PUBKEY)
|
||||
if (whichType == TX_PUBKEY || whichType == TX_PUBKEY_REPLAY)
|
||||
{
|
||||
CPubKey pubKey(vSolutions[0]);
|
||||
if (!pubKey.IsValid())
|
||||
|
@ -216,7 +239,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
|
|||
addressRet = pubKey.GetID();
|
||||
return true;
|
||||
}
|
||||
else if (whichType == TX_PUBKEYHASH)
|
||||
else if (whichType == TX_PUBKEYHASH || whichType == TX_PUBKEYHASH_REPLAY)
|
||||
{
|
||||
addressRet = CKeyID(uint160(vSolutions[0]));
|
||||
return true;
|
||||
|
@ -237,12 +260,12 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto
|
|||
vector<valtype> vSolutions;
|
||||
if (!Solver(scriptPubKey, typeRet, vSolutions))
|
||||
return false;
|
||||
if (typeRet == TX_NULL_DATA){
|
||||
if (typeRet == TX_NULL_DATA || typeRet == TX_NULL_DATA_REPLAY){
|
||||
// This is data, not addresses
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeRet == TX_MULTISIG)
|
||||
if (typeRet == TX_MULTISIG || typeRet == TX_MULTISIG_REPLAY)
|
||||
{
|
||||
nRequiredRet = vSolutions.front()[0];
|
||||
for (unsigned int i = 1; i < vSolutions.size()-1; i++)
|
||||
|
@ -284,6 +307,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifdef BITCOIN_TX // zen-tx does not have access to chain state so no replay protection is possible
|
||||
bool operator()(const CKeyID &keyID) const {
|
||||
script->clear();
|
||||
*script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||
|
@ -293,8 +317,23 @@ public:
|
|||
bool operator()(const CScriptID &scriptID) const {
|
||||
script->clear();
|
||||
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
|
||||
}
|
||||
#else
|
||||
bool operator()(const CKeyID &keyID) const {
|
||||
script->clear();
|
||||
CBlockIndex *currentBlock = chainActive.Tip();
|
||||
*script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG << ToByteVector(chainActive[currentBlock->nHeight - 300]->GetBlockHash()) << chainActive[currentBlock->nHeight - 300]->nHeight << OP_CHECKBLOCKATHEIGHT;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(const CScriptID &scriptID) const {
|
||||
script->clear();
|
||||
CBlockIndex *currentBlock = chainActive.Tip();
|
||||
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL << ToByteVector(chainActive[currentBlock->nHeight - 300]->GetBlockHash()) << chainActive[currentBlock->nHeight - 300]->nHeight << OP_CHECKBLOCKATHEIGHT;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -62,10 +62,14 @@ enum txnouttype
|
|||
TX_NONSTANDARD,
|
||||
// 'standard' transaction types:
|
||||
TX_PUBKEY,
|
||||
TX_PUBKEY_REPLAY,
|
||||
TX_PUBKEYHASH,
|
||||
TX_PUBKEYHASH_REPLAY,
|
||||
TX_SCRIPTHASH,
|
||||
TX_MULTISIG,
|
||||
TX_MULTISIG_REPLAY,
|
||||
TX_NULL_DATA,
|
||||
TX_NULL_DATA_REPLAY,
|
||||
};
|
||||
|
||||
class CNoDestination {
|
||||
|
|
|
@ -50,16 +50,29 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
|
|||
case TX_NONSTANDARD:
|
||||
case TX_NULL_DATA:
|
||||
break;
|
||||
case TX_NULL_DATA_REPLAY:
|
||||
break;
|
||||
case TX_PUBKEY:
|
||||
keyID = CPubKey(vSolutions[0]).GetID();
|
||||
if (keystore.HaveKey(keyID))
|
||||
return ISMINE_SPENDABLE;
|
||||
break;
|
||||
case TX_PUBKEY_REPLAY:
|
||||
keyID = CPubKey(vSolutions[0]).GetID();
|
||||
if (keystore.HaveKey(keyID))
|
||||
return ISMINE_SPENDABLE;
|
||||
break;
|
||||
case TX_PUBKEYHASH:
|
||||
keyID = CKeyID(uint160(vSolutions[0]));
|
||||
if (keystore.HaveKey(keyID))
|
||||
return ISMINE_SPENDABLE;
|
||||
break;
|
||||
case TX_PUBKEYHASH_REPLAY:
|
||||
keyID = CKeyID(uint160(vSolutions[0]));
|
||||
if (keystore.HaveKey(keyID))
|
||||
return ISMINE_SPENDABLE;
|
||||
break;
|
||||
|
||||
case TX_SCRIPTHASH:
|
||||
{
|
||||
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
|
||||
|
@ -83,6 +96,19 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
|
|||
return ISMINE_SPENDABLE;
|
||||
break;
|
||||
}
|
||||
case TX_MULTISIG_REPLAY:
|
||||
{
|
||||
// Only consider transactions "mine" if we own ALL the
|
||||
// keys involved. Multi-signature transactions that are
|
||||
// partially owned (somebody else has a key that can spend
|
||||
// them) enable spend-out-from-under-you attacks, especially
|
||||
// in shared-wallet situations.
|
||||
vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
|
||||
if (HaveKeys(keys, keystore) == keys.size())
|
||||
return ISMINE_SPENDABLE;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (keystore.HaveWatchOnly(scriptPubKey))
|
||||
|
|
Loading…
Reference in New Issue