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

Exchange addresses which cannot be used as an output of private transaction #1356

Merged
merged 13 commits into from
Nov 22, 2023
Merged
1 change: 1 addition & 0 deletions qa/rpc-tests/test_framework/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ def __new__(cls, n):
OP_SMALLINTEGER = CScriptOp(0xfa)
OP_PUBKEYS = CScriptOp(0xfb)
OP_PUBKEYHASH = CScriptOp(0xfd)
OP_SUPERSTRANSPARENTPUBKEYHASH = CScriptOp(0xe0)
OP_PUBKEY = CScriptOp(0xfe)

OP_INVALIDOPCODE = CScriptOp(0xff)
Expand Down
18 changes: 17 additions & 1 deletion src/base58.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ class CBitcoinAddressVisitor : public boost::static_visitor<bool>
CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {}

bool operator()(const CKeyID& id) const { return addr->Set(id); }
bool operator()(const CExchangeKeyID& id) const { return addr->Set(id); }
bool operator()(const CScriptID& id) const { return addr->Set(id); }
bool operator()(const CNoDestination& no) const { return false; }
};
Expand All @@ -234,6 +235,18 @@ bool CBitcoinAddress::Set(const CKeyID& id)
return true;
}

bool CBitcoinAddress::Set(const CExchangeKeyID& id)
{
SetData(Params().Base58Prefix(CChainParams::EXCHANGE_PUBKEY_ADDRESS), &id, 20);
return true;
}

bool CBitcoinAddress::SetExchange(const CKeyID& id)
{
SetData(Params().Base58Prefix(CChainParams::EXCHANGE_PUBKEY_ADDRESS), &id, 20);
return true;
}

bool CBitcoinAddress::Set(const CScriptID& id)
{
SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20);
Expand All @@ -254,7 +267,8 @@ bool CBitcoinAddress::IsValid(const CChainParams& params) const
{
bool fCorrectSize = vchData.size() == 20;
bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) ||
vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS) ||
vchVersion == params.Base58Prefix(CChainParams::EXCHANGE_PUBKEY_ADDRESS);
return fCorrectSize && fKnownVersion;
}

Expand All @@ -268,6 +282,8 @@ CTxDestination CBitcoinAddress::Get() const
return CKeyID(id);
else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS))
return CScriptID(id);
else if (vchVersion == Params().Base58Prefix(CChainParams::EXCHANGE_PUBKEY_ADDRESS))
return CExchangeKeyID(id);
else
return CNoDestination();
}
Expand Down
10 changes: 9 additions & 1 deletion src/base58.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,22 @@ class CBase58Data
class CBitcoinAddress : public CBase58Data {
public:
bool Set(const CKeyID &id);
bool Set(const CExchangeKeyID &id);
bool Set(const CScriptID &id);
bool Set(const CTxDestination &dest);
bool SetExchange(const CKeyID &id);
bool IsValid() const;
bool IsValid(const CChainParams &params) const;

CBitcoinAddress() {}
CBitcoinAddress(const CTxDestination &dest) { Set(dest); }
CBitcoinAddress(const std::string& strAddress) { SetString(strAddress); }
CBitcoinAddress(const std::string& strAddress) {
SetString(strAddress);
if (vchData.size() != 20) {
// give the address second chance and try exchange address format with 3 byte prefix
SetString(strAddress.c_str(), 3);
}
}
CBitcoinAddress(const char* pszAddress) { SetString(pszAddress); }

CTxDestination Get() const;
Expand Down
14 changes: 14 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ class CMainParams : public CChainParams {
// Note that of those with the service bits flag, most only support a subset of possible options
base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned char > (1, 82);
base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned char > (1, 7);
base58Prefixes[EXCHANGE_PUBKEY_ADDRESS] = {0x01, 0xb9, 0xbb}; // EXX prefix for the address
base58Prefixes[SECRET_KEY] = std::vector < unsigned char > (1, 210);
base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container < std::vector < unsigned char > > ();
base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container < std::vector < unsigned char > > ();
Expand Down Expand Up @@ -472,6 +473,9 @@ class CMainParams : public CChainParams {
consensus.nPPSwitchTime = 1635228000; // Tue Oct 26 2021 06:00:00 GMT+0000
consensus.nPPBlockNumber = 419264;
consensus.nInitialPPDifficulty = 0x1b1774cd; // 40GH/s

// exchange address
consensus.nExchangeAddressStartBlock = consensus.nSparkStartBlock;
}
virtual bool SkipUndoForBlock(int nHeight) const
{
Expand Down Expand Up @@ -660,6 +664,7 @@ class CTestNetParams : public CChainParams {

base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned char > (1, 65);
base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned char > (1, 178);
base58Prefixes[EXCHANGE_PUBKEY_ADDRESS] = {0x01, 0xb9, 0xbb}; // EXT prefix for the address
base58Prefixes[SECRET_KEY] = std::vector < unsigned char > (1, 185);
base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container < std::vector < unsigned char > > ();
base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container < std::vector < unsigned char > > ();
Expand Down Expand Up @@ -763,6 +768,9 @@ class CTestNetParams : public CChainParams {
consensus.nPPSwitchTime = 1630069200; // August 27 2021, 13:00 UTC
consensus.nPPBlockNumber = 37305;
consensus.nInitialPPDifficulty = 0x1d016e81; // 10MH/s

// exchange address
consensus.nExchangeAddressStartBlock = 147000;
}
};

Expand Down Expand Up @@ -918,6 +926,7 @@ class CDevNetParams : public CChainParams {

base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned char > (1, 66);
base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned char > (1, 179);
base58Prefixes[EXCHANGE_PUBKEY_ADDRESS] = {0x01, 0xb9, 0x8e}; // EXD prefix for the address
base58Prefixes[SECRET_KEY] = std::vector < unsigned char > (1, 186);
base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xD0).convert_to_container < std::vector < unsigned char > > ();
base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x95).convert_to_container < std::vector < unsigned char > > ();
Expand Down Expand Up @@ -996,6 +1005,9 @@ class CDevNetParams : public CChainParams {
consensus.nPPSwitchTime = 1631261566; // immediately after network start
consensus.nPPBlockNumber = 1;
consensus.nInitialPPDifficulty = 0x2000ffff;

// exchange address
consensus.nExchangeAddressStartBlock = 2500;
}
};

Expand Down Expand Up @@ -1158,6 +1170,7 @@ class CRegTestParams : public CChainParams {
};
base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned char > (1, 65);
base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned char > (1, 178);
base58Prefixes[EXCHANGE_PUBKEY_ADDRESS] = {0x01, 0xb9, 0xac}; // EXR prefix for the address
base58Prefixes[SECRET_KEY] = std::vector < unsigned char > (1, 239);
base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container < std::vector < unsigned char > > ();
base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container < std::vector < unsigned char > > ();
Expand All @@ -1182,6 +1195,7 @@ class CRegTestParams : public CChainParams {
consensus.nLelantusStartBlock = 400;
consensus.nLelantusFixesStartBlock = 400;
consensus.nSparkStartBlock = 1000;
consensus.nExchangeAddressStartBlock = 1000;
consensus.nLelantusGracefulPeriod = 1500;
consensus.nZerocoinV2MintMempoolGracefulPeriod = 1;
consensus.nZerocoinV2MintGracefulPeriod = 1;
Expand Down
1 change: 1 addition & 0 deletions src/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class CChainParams
SECRET_KEY,
EXT_PUBLIC_KEY,
EXT_SECRET_KEY,
EXCHANGE_PUBKEY_ADDRESS,

MAX_BASE58_TYPES
};
Expand Down
3 changes: 3 additions & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,9 @@ struct Params {
// Number of blocks with allowed zerocoin to sigma remint transaction (after nSigmaStartBlock)
int nZerocoinToSigmaRemintWindowSize;

// Number of block that introduces ability to specify super-transparent addresses
int nExchangeAddressStartBlock;

/** switch to MTP time */
uint32_t nMTPSwitchTime;
/** number of block when MTP switch occurs or 0 if not clear yet */
Expand Down
3 changes: 3 additions & 0 deletions src/elysium/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ bool SafeSolver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<st
// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
mTemplates.insert(std::make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));

// Modification of the previous one but with super transparent address
mTemplates.insert(std::make_pair(TX_PUBKEYHASH, CScript() << OP_EXCHANGEADDR << OP_DUP << OP_HASH160 << OP_EQUALVERIFY << OP_CHECKSIG));

// Sender provides N pubkeys, receivers provides M signatures
mTemplates.insert(std::make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));

Expand Down
9 changes: 9 additions & 0 deletions src/pubkey.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ class CKeyID : public uint160
CKeyID(const uint160& in) : uint160(in) {}
};

/** A reference to a CKey: the Hash160 of its serialized public key, special case for exchange key */

class CExchangeKeyID : public uint160
{
public:
CExchangeKeyID() : uint160() {}
CExchangeKeyID(const uint160& in) : uint160(in) {}
};

typedef uint256 ChainCode;

/** An encapsulated public key. */
Expand Down
11 changes: 11 additions & 0 deletions src/rpc/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,17 @@ class DescribeAddressVisitor : public boost::static_visitor<UniValue>
return obj;
}

UniValue operator()(const CExchangeKeyID &keyID) const {
UniValue obj(UniValue::VOBJ);
CPubKey vchPubKey;
obj.push_back(Pair("isscript", false));
if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
obj.push_back(Pair("exchangepubkey", HexStr(vchPubKey)));
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
}
return obj;
}

UniValue operator()(const CScriptID &scriptID) const {
UniValue obj(UniValue::VOBJ);
CScript subscript;
Expand Down
10 changes: 10 additions & 0 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&

try
{
bool fFirstOpCode = true;
while (pc < pend)
{
bool fExec = !count(vfExec.begin(), vfExec.end(), false);
Expand Down Expand Up @@ -345,6 +346,13 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
case OP_NOP:
break;

case OP_EXCHANGEADDR:
// allow OP_EXCHANGEADDR only at the beginning of the script
if (!fFirstOpCode)
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
// otherwise NOOP
break;

case OP_CHECKLOCKTIMEVERIFY:
{
if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) {
Expand Down Expand Up @@ -1030,6 +1038,8 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
// Size limits
if (stack.size() + altstack.size() > 1000)
return set_error(serror, SCRIPT_ERR_STACK_SIZE);

fFirstOpCode = false;
}
}
catch (...)
Expand Down
1 change: 1 addition & 0 deletions src/script/ismine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
break;
}
case TX_PUBKEYHASH:
case TX_EXCHANGEADDRESS:
keyID = CKeyID(uint160(vSolutions[0]));
if (sigversion != SIGVERSION_BASE) {
CPubKey pubkey;
Expand Down
14 changes: 14 additions & 0 deletions src/script/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ const char* GetOpName(opcodetype opcode)
case OP_SPARKMINT : return "OP_SPARKMINT";
case OP_SPARKSMINT : return "OP_SPARKSMINT";
case OP_SPARKSPEND : return "OP_SPARKSPEND";
// Super transparent txout script prefix
case OP_EXCHANGEADDR : return "OP_EXCHANGEADDR";

// Note:
// The template matching params OP_SMALLINTEGER/etc are defined in opcodetype enum
Expand Down Expand Up @@ -251,6 +253,18 @@ bool CScript::IsPayToPublicKeyHash() const
(*this)[24] == OP_CHECKSIG);
}

bool CScript::IsPayToExchangeAddress() const
{
// Extra-fast test for pay-to-pubkey-hash CScripts:
return (this->size() == 26 &&
(*this)[0] == OP_EXCHANGEADDR &&
(*this)[1] == OP_DUP &&
(*this)[2] == OP_HASH160 &&
(*this)[3] == 0x14 &&
(*this)[24] == OP_EQUALVERIFY &&
(*this)[25] == OP_CHECKSIG);
}

bool CScript::IsPayToScriptHash() const
{
// Extra-fast test for pay-to-script-hash CScripts:
Expand Down
4 changes: 4 additions & 0 deletions src/script/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ enum opcodetype
OP_SPARKMINT = 0xd1,
OP_SPARKSMINT = 0xd2,
OP_SPARKSPEND = 0xd3,

// basically NOP but identifies that sunsequent txout script contains super transparent address
OP_EXCHANGEADDR = 0xe0
};

const char* GetOpName(opcodetype opcode);
Expand Down Expand Up @@ -660,6 +663,7 @@ class CScript : public CScriptBase
bool IsNormalPaymentScript() const;

bool IsPayToPublicKeyHash() const;
bool IsPayToExchangeAddress() const;

bool IsPayToScriptHash() const;
bool IsPayToWitnessScriptHash() const;
Expand Down
2 changes: 2 additions & 0 deletions src/script/sign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
keyID = CPubKey(vSolutions[0]).GetID();
return Sign1(keyID, creator, scriptPubKey, ret, sigversion);
case TX_PUBKEYHASH:
case TX_EXCHANGEADDRESS:
keyID = CKeyID(uint160(vSolutions[0]));
if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion))
return false;
Expand Down Expand Up @@ -325,6 +326,7 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature
return sigs2;
case TX_PUBKEY:
case TX_PUBKEYHASH:
case TX_EXCHANGEADDRESS:
// Signatures are bigger than placeholders or empty scripts:
if (sigs1.script.empty() || sigs1.script[0].empty())
return sigs2;
Expand Down
23 changes: 23 additions & 0 deletions src/script/standard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const char* GetTxnOutputType(txnouttype t)
case TX_LELANTUSJMINT: return "lelantusmint";
case TX_SPARKMINT: return "sparkmint";
case TX_SPARKSMINT: return "sparksmint";
case TX_EXCHANGEADDRESS: return "exchangeaddress";
}
return NULL;
}
Expand All @@ -56,6 +57,9 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
mTemplates.insert(std::make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));

// Super transparent address txout, same as previous one but with a prefix opcode to indicate it
mTemplates.insert(std::make_pair(TX_EXCHANGEADDRESS, CScript() << OP_EXCHANGEADDR << OP_DUP << OP_HASH160 << OP_EQUALVERIFY << OP_CHECKSIG));

// Sender provides N pubkeys, receivers provides M signatures
mTemplates.insert(std::make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
}
Expand All @@ -80,6 +84,14 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
return true;
}

if (scriptPubKey.IsPayToExchangeAddress())
{
typeRet = TX_EXCHANGEADDRESS;
std::vector<unsigned char> hashBytes(scriptPubKey.begin()+4, scriptPubKey.begin()+24);
vSolutionsRet.push_back(hashBytes);
return true;
}

// Zerocoin
if (scriptPubKey.IsZerocoinMint())
{
Expand Down Expand Up @@ -266,6 +278,11 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
addressRet = CKeyID(uint160(vSolutions[0]));
return true;
}
else if (whichType == TX_EXCHANGEADDRESS)
{
addressRet = CExchangeKeyID(uint160(vSolutions[0]));
return true;
}
else if (whichType == TX_SCRIPTHASH)
{
addressRet = CScriptID(uint160(vSolutions[0]));
Expand Down Expand Up @@ -335,6 +352,12 @@ class CScriptVisitor : public boost::static_visitor<bool>
return true;
}

bool operator()(const CExchangeKeyID &keyID) const {
script->clear();
*script << OP_EXCHANGEADDR << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
return true;
}

bool operator()(const CScriptID &scriptID) const {
script->clear();
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
Expand Down
6 changes: 4 additions & 2 deletions src/script/standard.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ enum txnouttype
TX_LELANTUSMINT,
TX_LELANTUSJMINT,
TX_SPARKMINT,
TX_SPARKSMINT
TX_SPARKSMINT,
TX_EXCHANGEADDRESS
};

class CNoDestination {
Expand All @@ -72,9 +73,10 @@ class CNoDestination {
* * CNoDestination: no destination set
* * CKeyID: TX_PUBKEYHASH destination
* * CScriptID: TX_SCRIPTHASH destination
* * CExchangeKeyID: CKeyID for exchange key
* A CTxDestination is the internal data type encoded in a CBitcoinAddress
*/
typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
typedef boost::variant<CNoDestination, CKeyID, CScriptID, CExchangeKeyID> CTxDestination;

const char* GetTxnOutputType(txnouttype t);

Expand Down
Loading
Loading