Skip to content

Commit

Permalink
Preventing double mint in Lelantus (#996)
Browse files Browse the repository at this point in the history
  • Loading branch information
levonpetrosyan93 authored Feb 16, 2021
1 parent 11dab2f commit 32c7c58
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 70 deletions.
74 changes: 42 additions & 32 deletions src/wallet/lelantusjoinsplitbuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,42 +351,52 @@ void LelantusJoinSplitBuilder::GenerateMints(const std::vector<CAmount>& newMint
std::vector<CAmount> newMintsAndChange(newMints);
newMintsAndChange.push_back(changeToMint);
for (CAmount mintVal : newMintsAndChange) {
hdMint.SetNull();
while (true) {
hdMint.SetNull();
lelantus::PrivateCoin newCoin(params, mintVal);
newCoin.setVersion(LELANTUS_TX_VERSION_4);
CWalletDB walletdb(pwalletMain->strWalletFile);

lelantus::PrivateCoin newCoin(params, mintVal);
newCoin.setVersion(LELANTUS_TX_VERSION_4);
CWalletDB walletdb(pwalletMain->strWalletFile);
uint160 seedID;
mintWallet.GenerateLelantusMint(walletdb, newCoin, hdMint, seedID, boost::none, true);

uint160 seedID;
mintWallet.GenerateLelantusMint(walletdb, newCoin, hdMint, seedID, boost::none, true);
Cout.emplace_back(newCoin);
auto& pubCoin = newCoin.getPublicCoin();
auto &pubCoin = newCoin.getPublicCoin();

if (!pubCoin.validate()) {
throw std::runtime_error("Unable to mint a lelantus coin.");
}
if (!pubCoin.validate()) {
throw std::runtime_error("Unable to mint a lelantus coin.");
}

// Create script for coin
CScript scriptSerializedCoin;
scriptSerializedCoin << OP_LELANTUSJMINT;
std::vector<unsigned char> vch = pubCoin.getValue().getvch();
scriptSerializedCoin.insert(scriptSerializedCoin.end(), vch.begin(), vch.end());

std::vector<unsigned char> encryptedValue = pwalletMain->EncryptMintAmount(mintVal, pubCoin.getValue());
scriptSerializedCoin.insert(scriptSerializedCoin.end(), encryptedValue.begin(), encryptedValue.end());

auto pubcoin = hdMint.GetPubcoinValue() + lelantus::Params::get_default()->get_h1() * Scalar(hdMint.GetAmount()).negate();
uint256 hashPub = primitives::GetPubCoinValueHash(pubcoin);
CDataStream ss(SER_GETHASH, 0);
ss << hashPub;
ss << seedID;
uint256 hashForRecover = Hash(ss.begin(), ss.end());
CDataStream serializedHash(SER_NETWORK, 0);
serializedHash << hashForRecover;
scriptSerializedCoin.insert(scriptSerializedCoin.end(), serializedHash.begin(), serializedHash.end());

outputs.push_back(CTxOut(0, scriptSerializedCoin));
mintCoins.push_back(hdMint);
// Create script for coin
CScript scriptSerializedCoin;
scriptSerializedCoin << OP_LELANTUSJMINT;
std::vector<unsigned char> vch = pubCoin.getValue().getvch();
scriptSerializedCoin.insert(scriptSerializedCoin.end(), vch.begin(), vch.end());

std::vector<unsigned char> encryptedValue = pwalletMain->EncryptMintAmount(mintVal, pubCoin.getValue());
scriptSerializedCoin.insert(scriptSerializedCoin.end(), encryptedValue.begin(), encryptedValue.end());

auto pubcoin = hdMint.GetPubcoinValue() +
lelantus::Params::get_default()->get_h1() * Scalar(hdMint.GetAmount()).negate();
uint256 hashPub = primitives::GetPubCoinValueHash(pubcoin);
CDataStream ss(SER_GETHASH, 0);
ss << hashPub;
ss << seedID;
uint256 hashForRecover = Hash(ss.begin(), ss.end());
// Check if there is a mint with same private data in chain, most likely Hd mint state corruption,
// If yes, try with new counter
GroupElement dummyValue;
if (lelantus::CLelantusState::GetState()->HasCoinTag(dummyValue, hashForRecover))
continue;

CDataStream serializedHash(SER_NETWORK, 0);
serializedHash << hashForRecover;
scriptSerializedCoin.insert(scriptSerializedCoin.end(), serializedHash.begin(), serializedHash.end());

Cout.emplace_back(newCoin);
outputs.push_back(CTxOut(0, scriptSerializedCoin));
mintCoins.push_back(hdMint);
break;
}
}
}

Expand Down
85 changes: 47 additions & 38 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2650,48 +2650,57 @@ CRecipient CWallet::CreateLelantusMintRecipient(
{
EnsureMintWalletAvailable();

CWalletDB walletdb(pwalletMain->strWalletFile);
uint160 seedID;
if (generate) {
// Generate and store secrets deterministically in the following function.
pwalletMain->zwallet->GenerateLelantusMint(walletdb, coin, vDMint, seedID);
}
while (true) {
CWalletDB walletdb(pwalletMain->strWalletFile);
uint160 seedID;
if (generate) {
// Generate and store secrets deterministically in the following function.
pwalletMain->zwallet->GenerateLelantusMint(walletdb, coin, vDMint, seedID);
}

// Get a copy of the 'public' portion of the coin. You should
// embed this into a Lelantus 'MINT' transaction along with a series of currency inputs
auto& pubCoin = coin.getPublicCoin();
// Get a copy of the 'public' portion of the coin. You should
// embed this into a Lelantus 'MINT' transaction along with a series of currency inputs
auto &pubCoin = coin.getPublicCoin();

if (!pubCoin.validate()) {
throw std::runtime_error("Unable to mint a lelantus coin.");
}
if (!pubCoin.validate()) {
throw std::runtime_error("Unable to mint a lelantus coin.");
}

// Create script for coin
CScript script;
// opcode is inserted as 1 byte according to file script/script.h
script << OP_LELANTUSMINT;

// and this one will write the size in different byte lengths depending on the length of vector. If vector size is <0.4c, which is 76, will write the size of vector in just 1 byte. In our case the size is always 34, so must write that 34 in 1 byte.
std::vector<unsigned char> vch = pubCoin.getValue().getvch();
script.insert(script.end(), vch.begin(), vch.end()); //this uses 34 byte

// generating schnorr proof
CDataStream serializedSchnorrProof(SER_NETWORK, PROTOCOL_VERSION);
lelantus::GenerateMintSchnorrProof(coin, serializedSchnorrProof);
script.insert(script.end(), serializedSchnorrProof.begin(), serializedSchnorrProof.end()); //this uses 98 byte

auto pubcoin = vDMint.GetPubcoinValue() + lelantus::Params::get_default()->get_h1() * Scalar(vDMint.GetAmount()).negate();
uint256 hashPub = primitives::GetPubCoinValueHash(pubcoin);
CDataStream ss(SER_GETHASH, 0);
ss << hashPub;
ss << seedID;
uint256 hashForRecover = Hash(ss.begin(), ss.end());
CDataStream serializedHash(SER_NETWORK, 0);
serializedHash << hashForRecover;
script.insert(script.end(), serializedHash.begin(), serializedHash.end());

// overall Lelantus mint script size is 1 + 34 + 98 + 32 = 165 byte
return {script, CAmount(coin.getV()), false};
// Create script for coin
CScript script;
// opcode is inserted as 1 byte according to file script/script.h
script << OP_LELANTUSMINT;

// and this one will write the size in different byte lengths depending on the length of vector. If vector size is <0.4c, which is 76, will write the size of vector in just 1 byte. In our case the size is always 34, so must write that 34 in 1 byte.
std::vector<unsigned char> vch = pubCoin.getValue().getvch();
script.insert(script.end(), vch.begin(), vch.end()); //this uses 34 byte

// generating schnorr proof
CDataStream serializedSchnorrProof(SER_NETWORK, PROTOCOL_VERSION);
lelantus::GenerateMintSchnorrProof(coin, serializedSchnorrProof);
script.insert(script.end(), serializedSchnorrProof.begin(), serializedSchnorrProof.end()); //this uses 98 byte

auto pubcoin = vDMint.GetPubcoinValue() +
lelantus::Params::get_default()->get_h1() * Scalar(vDMint.GetAmount()).negate();
uint256 hashPub = primitives::GetPubCoinValueHash(pubcoin);
CDataStream ss(SER_GETHASH, 0);
ss << hashPub;
ss << seedID;
uint256 hashForRecover = Hash(ss.begin(), ss.end());

// Check if there is a mint with same private data in chain, most likely Hd mint state corruption,
// If yes, try with new counter
GroupElement dummyValue;
if (lelantus::CLelantusState::GetState()->HasCoinTag(dummyValue, hashForRecover))
continue;

CDataStream serializedHash(SER_NETWORK, 0);
serializedHash << hashForRecover;
script.insert(script.end(), serializedHash.begin(), serializedHash.end());

// overall Lelantus mint script size is 1 + 34 + 98 + 32 = 165 byte
return {script, CAmount(coin.getV()), false};
}
}

// coinsIn has to be sorted in descending order.
Expand Down

0 comments on commit 32c7c58

Please sign in to comment.