Skip to content

Commit 83c1b58

Browse files
authored
Merge pull request #1698 from cyrossignol/contract-fee
Add support for type-specific contract fee amounts
2 parents b996056 + f1b5e3e commit 83c1b58

File tree

10 files changed

+101
-18
lines changed

10 files changed

+101
-18
lines changed

src/main.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,14 +1193,7 @@ bool CTransaction::CheckContracts(const MapPrevTx& inputs) const
11931193
return DoS(100, error("%s: contract in non-standard tx", __func__));
11941194
}
11951195

1196-
const auto is_valid_burn_output = [](const CTxOut& output) {
1197-
return output.scriptPubKey[0] == OP_RETURN
1198-
&& output.nValue >= NN::Contract::BURN_AMOUNT;
1199-
};
1200-
1201-
if (std::none_of(vout.begin(), vout.end(), is_valid_burn_output)) {
1202-
return DoS(100, error("%s: no sufficient burn output", __func__));
1203-
}
1196+
int64_t required_burn_fee = 0;
12041197

12051198
for (const auto& contract : GetContracts()) {
12061199
if (contract.m_version <= 1) {
@@ -1216,6 +1209,24 @@ bool CTransaction::CheckContracts(const MapPrevTx& inputs) const
12161209
if (contract.RequiresMasterKey() && !HasMasterKeyInput(inputs)) {
12171210
return DoS(100, error("%s: contract requires master key", __func__));
12181211
}
1212+
1213+
required_burn_fee += contract.RequiredBurnAmount();
1214+
}
1215+
1216+
int64_t supplied_burn_fee = 0;
1217+
1218+
for (const auto& output : vout) {
1219+
if (output.scriptPubKey[0] == OP_RETURN) {
1220+
supplied_burn_fee += output.nValue;
1221+
}
1222+
}
1223+
1224+
if (supplied_burn_fee < required_burn_fee) {
1225+
return DoS(100, error(
1226+
"%s: insufficient burn output. Required: %s, supplied: %s",
1227+
__func__,
1228+
FormatMoney(required_burn_fee),
1229+
FormatMoney(supplied_burn_fee)));
12191230
}
12201231

12211232
return true;

src/neuralnet/beacon.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,16 @@ class BeaconPayload : public IContractPayload
279279
return m_beacon.ToString();
280280
}
281281

282+
//!
283+
//! \brief Get the burn fee amount required to send a particular contract.
284+
//!
285+
//! \return Burn fee in units of 1/100000000 GRC.
286+
//!
287+
int64_t RequiredBurnAmount() const override
288+
{
289+
return 0.5 * COIN;
290+
}
291+
282292
//!
283293
//! \brief Sign the beacon with its private key.
284294
//!

src/neuralnet/contract/contract.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ class EmptyPayload : public IContractPayload
4545
return "";
4646
}
4747

48+
int64_t RequiredBurnAmount() const override
49+
{
50+
return MAX_MONEY;
51+
}
52+
4853
ADD_CONTRACT_PAYLOAD_SERIALIZE_METHODS;
4954

5055
template <typename Stream, typename Operation>
@@ -110,6 +115,11 @@ class LegacyPayload : public IContractPayload
110115
return m_value;
111116
}
112117

118+
int64_t RequiredBurnAmount() const override
119+
{
120+
return Contract::STANDARD_BURN_AMOUNT;
121+
}
122+
113123
ADD_CONTRACT_PAYLOAD_SERIALIZE_METHODS;
114124

115125
template <typename Stream, typename Operation>
@@ -383,7 +393,7 @@ void NN::RevertContracts(const std::vector<Contract>& contracts)
383393
// Class: Contract
384394
// -----------------------------------------------------------------------------
385395

386-
constexpr int64_t Contract::BURN_AMOUNT; // for clang
396+
constexpr int64_t Contract::STANDARD_BURN_AMOUNT; // for clang
387397

388398
Contract::Contract()
389399
: m_version(Contract::CURRENT_VERSION)
@@ -568,6 +578,11 @@ const CPubKey& Contract::ResolvePublicKey() const
568578
return m_public_key.Key();
569579
}
570580

581+
int64_t Contract::RequiredBurnAmount() const
582+
{
583+
return m_body.m_payload->RequiredBurnAmount();
584+
}
585+
571586
bool Contract::WellFormed() const
572587
{
573588
return m_version > 0 && m_version <= Contract::CURRENT_VERSION

src/neuralnet/contract/contract.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ class Contract
150150
//! \brief The amount of coin set for a burn output in a transaction that
151151
//! broadcasts a contract in units of 1/100000000 GRC.
152152
//!
153-
static constexpr int64_t BURN_AMOUNT = 0.5 * COIN;
153+
static constexpr int64_t STANDARD_BURN_AMOUNT = 0.5 * COIN;
154154

155155
//!
156156
//! \brief A contract type from a transaction message.
@@ -563,6 +563,13 @@ class Contract
563563
//!
564564
const CPubKey& ResolvePublicKey() const;
565565

566+
//!
567+
//! \brief Get the burn fee amount required to send a particular contract.
568+
//!
569+
//! \return Burn fee in units of 1/100000000 GRC.
570+
//!
571+
int64_t RequiredBurnAmount() const;
572+
566573
//!
567574
//! \brief Determine whether the instance represents a complete contract.
568575
//!

src/neuralnet/contract/message.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,19 @@ bool SelectMasterInputOutput(CCoinControl& coin_control)
5252
//!
5353
//! \param wtx_new A new transaction with a contract.
5454
//! \param reserve_key Key reserved for any change.
55-
//! \param admin \c true for an administrative contract.
55+
//! \param burn_fee Total burn fee required for contracts in the transaction.
5656
//!
5757
//! \return \c true if coin selection succeeded.
5858
//!
59-
bool CreateContractTx(CWalletTx& wtx_out, CReserveKey reserve_key, bool admin)
59+
bool CreateContractTx(CWalletTx& wtx_out, CReserveKey reserve_key, int64_t burn_fee)
6060
{
6161
CCoinControl coin_control_out;
6262
int64_t applied_fee_out; // Unused
63+
bool admin = false;
64+
65+
for (const auto& contract : wtx_out.vContracts) {
66+
admin |= contract.RequiresMasterKey();
67+
}
6368

6469
// Configure inputs/outputs for the address associated with the master key.
6570
// Nodes validate administrative contracts by checking that the containing
@@ -79,7 +84,7 @@ bool CreateContractTx(CWalletTx& wtx_out, CReserveKey reserve_key, bool admin)
7984
scriptPubKey << OP_RETURN;
8085

8186
return pwalletMain->CreateTransaction(
82-
{ std::make_pair(std::move(scriptPubKey), Contract::BURN_AMOUNT) },
87+
{ std::make_pair(std::move(scriptPubKey), burn_fee) },
8388
wtx_out,
8489
reserve_key,
8590
applied_fee_out,
@@ -90,12 +95,11 @@ bool CreateContractTx(CWalletTx& wtx_out, CReserveKey reserve_key, bool admin)
9095
//! \brief Send a transaction that contains a contract.
9196
//!
9297
//! \param wtx_new A new transaction with a contract.
93-
//! \param admin \c true for an administrative contract.
9498
//!
9599
//! \return An empty string when successful or a description of the error that
96100
//! occurred. TODO: Refactor to remove string-based signaling.
97101
//!
98-
std::string SendContractTx(CWalletTx& wtx_new, const bool admin)
102+
std::string SendContractTx(CWalletTx& wtx_new)
99103
{
100104
CReserveKey reserve_key(pwalletMain);
101105

@@ -112,14 +116,19 @@ std::string SendContractTx(CWalletTx& wtx_new, const bool admin)
112116
}
113117

114118
int64_t balance = pwalletMain->GetBalance();
119+
int64_t burn_fee = 0;
120+
121+
for (const auto& contract : wtx_new.vContracts) {
122+
burn_fee += contract.RequiredBurnAmount();
123+
}
115124

116-
if (balance < COIN || balance < Contract::BURN_AMOUNT + nTransactionFee) {
125+
if (balance < COIN || balance < burn_fee + nTransactionFee) {
117126
std::string strError = _("Balance too low to create a contract.");
118127
LogPrintf("%s: %s", __func__, strError);
119128
return strError;
120129
}
121130

122-
if (!CreateContractTx(wtx_new, reserve_key, admin)) {
131+
if (!CreateContractTx(wtx_new, reserve_key, burn_fee)) {
123132
std::string strError = _("Error: Transaction creation failed.");
124133
LogPrintf("%s: %s", __func__, strError);
125134
return strError;
@@ -175,7 +184,7 @@ std::pair<CWalletTx, std::string> NN::SendContract(Contract contract)
175184

176185
wtx.vContracts.emplace_back(std::move(contract));
177186

178-
std::string error = SendContractTx(wtx, contract.RequiresMasterKey());
187+
std::string error = SendContractTx(wtx);
179188

180189
return std::make_pair(std::move(wtx), std::move(error));
181190
}

src/neuralnet/contract/payload.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ class IContractPayload
114114
//!
115115
virtual std::string LegacyValueString() const = 0;
116116

117+
//!
118+
//! \brief Get the burn fee amount required to send a particular contract.
119+
//!
120+
//! \return Burn fee in units of 1/100000000 GRC.
121+
//!
122+
virtual int64_t RequiredBurnAmount() const = 0;
123+
117124
//!
118125
//! \brief Serialize the contract to the provided file.
119126
//!

src/neuralnet/project.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,16 @@ class Project : public IContractPayload
111111
return m_url;
112112
}
113113

114+
//!
115+
//! \brief Get the burn fee amount required to send a particular contract.
116+
//!
117+
//! \return Burn fee in units of 1/100000000 GRC.
118+
//!
119+
int64_t RequiredBurnAmount() const override
120+
{
121+
return 0.5 * COIN; // TODO: reduce fee for admin contracts?
122+
}
123+
114124
//!
115125
//! \brief Get a user-friendly display name created from the project key.
116126
//!

src/test/neuralnet/beacon_tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ BOOST_AUTO_TEST_CASE(it_behaves_like_a_contract_payload)
271271
BOOST_CHECK(payload.WellFormed(NN::ContractAction::ADD) == true);
272272
BOOST_CHECK(payload.LegacyKeyString() == cpid.ToString());
273273
BOOST_CHECK(payload.LegacyValueString() == payload.m_beacon.ToString());
274+
BOOST_CHECK(payload.RequiredBurnAmount() > 0);
274275
}
275276

276277
BOOST_AUTO_TEST_CASE(it_checks_whether_the_payload_is_well_formed_for_add)

src/test/neuralnet/contract_tests.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ class TestPayload : public NN::IContractPayload
3939
return m_data;
4040
}
4141

42+
int64_t RequiredBurnAmount() const override
43+
{
44+
return NN::Contract::STANDARD_BURN_AMOUNT;
45+
}
46+
4247
ADD_CONTRACT_PAYLOAD_SERIALIZE_METHODS;
4348

4449
template <typename Stream, typename Operation>
@@ -942,6 +947,13 @@ BOOST_AUTO_TEST_CASE(it_determines_whether_a_legacy_v1_contract_is_valid)
942947
BOOST_CHECK(contract.Validate() == false);
943948
}
944949

950+
BOOST_AUTO_TEST_CASE(it_determines_the_requred_burn_fee)
951+
{
952+
const NN::Contract contract = TestMessage::Current();
953+
954+
BOOST_CHECK(contract.RequiredBurnAmount() > 0);
955+
}
956+
945957
BOOST_AUTO_TEST_CASE(it_provides_access_to_the_contract_payload)
946958
{
947959
const NN::Contract contract = TestMessage::Current();

src/test/neuralnet/project_tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ BOOST_AUTO_TEST_CASE(it_behaves_like_a_contract_payload)
104104
BOOST_CHECK(project.WellFormed(NN::ContractAction::ADD) == true);
105105
BOOST_CHECK(project.LegacyKeyString() == "Enigma");
106106
BOOST_CHECK(project.LegacyValueString() == "http://enigma.test/@");
107+
BOOST_CHECK(project.RequiredBurnAmount() > 0);
107108
}
108109

109110
BOOST_AUTO_TEST_CASE(it_checks_whether_the_payload_is_well_formed_for_add)

0 commit comments

Comments
 (0)