Skip to content

Commit

Permalink
Exchange addresses which cannot be used as an output of private trans…
Browse files Browse the repository at this point in the history
…action (#1356)

* Consensus layer of supertransparent tx

* Super transparent address prefix & RPC

* Renaming supertransparent -> exchange

* Exchange key support in bitcoin API

* Fixes and improvements

* Allow "getnewexchangeaddress" RPC to convert an existing address to exchange one

* Fixed tests

* HF blocks for exchange address introduction

* Allow OP_EXCHANGEADDR only in specific situations

* Bug fix for missing exchange addresses in "listunspent" output

* Bug fixes

* GUI-level exchange address checks when creating a transaction

* Fix for wallet.dat corruption message on startup
  • Loading branch information
psolstice authored Nov 22, 2023
1 parent 054b065 commit 2dd2abd
Show file tree
Hide file tree
Showing 23 changed files with 233 additions and 6 deletions.
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

0 comments on commit 2dd2abd

Please sign in to comment.