Skip to content

Commit 66d1b0f

Browse files
committed
[BDAP] Add more rules when checking the validity of credit use
1 parent 2cec466 commit 66d1b0f

File tree

5 files changed

+147
-42
lines changed

5 files changed

+147
-42
lines changed

src/bdap/bdap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ static constexpr unsigned int MAX_BDAP_SIGNATURE_PROOF = 90; // TODO (bdap):
5555
static constexpr unsigned int MAX_BDAP_LINK_DATA_SIZE = 1592;
5656
static constexpr uint64_t DEFAULT_LINK_EXPIRE_TIME = 1861920000;
5757
static constexpr int32_t DEFAULT_REGISTRATION_MONTHS = 12; // 1 year
58+
static constexpr bool ENFORCE_BDAP_CREDIT_USE = false; // TODO: Change to true before release
5859
static const std::string DEFAULT_PUBLIC_DOMAIN = "bdap.io";
5960
static const std::string DEFAULT_PUBLIC_OU = "public";
6061
static const std::string DEFAULT_ADMIN_OU = "admin";

src/bdap/utils.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,9 @@ std::string GetOpCodeType(const std::string& strOpCode)
356356
else if (strOpCode == "bdap_update_link_accept") {
357357
return "link";
358358
}
359+
else if (strOpCode == "bdap_move_asset") {
360+
return "asset";
361+
}
359362
else {
360363
return "unknown";
361364
}
@@ -418,6 +421,23 @@ bool GetBDAPOpScript(const CTransactionRef& tx, CScript& scriptBDAPOp, vchCharSt
418421
return false;
419422
}
420423

424+
bool GetBDAPCreditScript(const CTransactionRef& ptx, CScript& scriptBDAPScredit)
425+
{
426+
for (unsigned int i = 0; i < ptx->vout.size(); i++)
427+
{
428+
const CTxOut& out = ptx->vout[i];
429+
int op1, op2;
430+
vchCharString vvchOpParameters;
431+
if (DecodeBDAPScript(out.scriptPubKey, op1, op2, vvchOpParameters)) {
432+
if (op1 == 5 && op2 == 15) {
433+
scriptBDAPScredit = out.scriptPubKey;
434+
return true;
435+
}
436+
}
437+
}
438+
return false;
439+
}
440+
421441
bool GetBDAPOpScript(const CTransactionRef& tx, CScript& scriptBDAPOp)
422442
{
423443
int op1, op2;

src/bdap/utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ bool GetBDAPOpScript(const CTransactionRef& tx, CScript& scriptBDAPOp, vchCharSt
5050
bool GetBDAPOpScript(const CTransactionRef& tx, CScript& scriptBDAPOp);
5151
bool GetBDAPDataScript(const CTransaction& tx, CScript& scriptBDAPData);
5252
bool GetBDAPDataScript(const CTransactionRef& ptx, CScript& scriptBDAPData);
53+
bool GetBDAPCreditScript(const CTransactionRef& ptx, CScript& scriptBDAPScredit);
5354
bool IsBDAPOperationOutput(const CTxOut& out);
5455
int GetBDAPOperationOutIndex(const CTransactionRef& tx);
5556
int GetBDAPOperationOutIndex(int nHeight, const uint256& txHash);

src/validation.cpp

Lines changed: 123 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,9 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state)
555555
bool fIsBDAP = false;
556556
// Check for negative or overflow output values
557557
CAmount nValueOut = 0;
558+
CAmount nStandardOut = 0;
559+
CAmount nCreditsOut = 0;
560+
CAmount nDataBurned = 0;
558561
for (const CTxOut& txout : tx.vout) {
559562
if (txout.nValue < 0)
560563
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
@@ -569,8 +572,14 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state)
569572
if (!fluid.ValidationProcesses(state, txout.scriptPubKey, txout.nValue))
570573
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fluid-validate-failure");
571574
}
572-
if (txout.IsBDAP())
575+
if (txout.IsBDAP()) {
573576
fIsBDAP = true;
577+
nCreditsOut += txout.nValue;
578+
} else if (txout.IsData()) {
579+
nDataBurned += txout.nValue;
580+
} else {
581+
nStandardOut += txout.nValue;
582+
}
574583
}
575584

576585
// Check for duplicate inputs
@@ -580,7 +589,8 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state)
580589
if (!vInOutPoints.insert(txin.prevout).second)
581590
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
582591
}
583-
592+
CAmount nStandardIn = 0;
593+
CAmount nCreditsIn = 0;
584594
std::vector<Coin> vBdapCoins;
585595
if (tx.IsCoinBase()) {
586596
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
@@ -595,6 +605,9 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state)
595605
if (coin.out.IsBDAP()) {
596606
vBdapCoins.push_back(coin);
597607
fIsBDAP = true;
608+
nCreditsIn += coin.out.nValue;
609+
} else {
610+
nStandardIn += coin.out.nValue;
598611
}
599612
}
600613
}
@@ -606,14 +619,34 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state)
606619
if (fIsBDAP && tx.nVersion != BDAP_TX_VERSION)
607620
return state.DoS(100, false, REJECT_INVALID, "incorrect-bdap-tx-version");
608621

609-
if (fIsBDAP && !CheckBDAPTxCreditUsage(tx, vBdapCoins))
622+
if (fIsBDAP && !CheckBDAPTxCreditUsage(tx, vBdapCoins, nStandardIn, nCreditsIn, nStandardOut, nCreditsOut, nDataBurned))
610623
return state.DoS(100, false, REJECT_INVALID, "bad-bdap-credit-use");
611624

612625
return true;
613626
}
614627

615-
bool CheckBDAPTxCreditUsage(const CTransaction& tx, const std::vector<Coin>& vBdapCoins)
628+
bool CheckBDAPTxCreditUsage(const CTransaction& tx, const std::vector<Coin>& vBdapCoins,
629+
const CAmount& nStandardIn, const CAmount& nCreditsIn, const CAmount& nStandardOut, const CAmount& nCreditsOut, const CAmount& nDataBurned)
616630
{
631+
LogPrint("bdap", "%s -- nStandardIn %d, nCreditsIn %d, nStandardOut %d, nCreditsOut %d, nDataBurned %d\n", __func__,
632+
FormatMoney(nStandardIn), FormatMoney(nCreditsIn), FormatMoney(nStandardOut), FormatMoney(nCreditsOut), FormatMoney(nDataBurned));
633+
// when there are no BDAP inputs, we do not need to check how credits are used.
634+
if (vBdapCoins.size() == 0 || nCreditsIn == 0)
635+
return true;
636+
637+
if (nStandardIn > 0 && nStandardOut > 0 && nStandardOut >= nStandardIn) {
638+
LogPrintf("%s -- Check failed. Invalid use of BDAP credits. Standard DYN output amounts exceeds or equals standard DYN input amount\n", __func__);
639+
if (ENFORCE_BDAP_CREDIT_USE)
640+
return false;
641+
}
642+
643+
if (nCreditsOut >= nCreditsIn) {
644+
LogPrintf("%s -- Check failed. Invalid use of BDAP credits. BDAP credits output amount exceeds BDAP credit input amount\n", __func__);
645+
if (ENFORCE_BDAP_CREDIT_USE)
646+
return false;
647+
}
648+
649+
std::multimap<CDynamicAddress, CServiceCredit> mapInputs;
617650
std::vector<std::pair<CServiceCredit, CDynamicAddress>> vInputInfo;
618651
for (const Coin& coin : vBdapCoins) {
619652
int opCode1 = -1; int opCode2 = -1;
@@ -623,10 +656,12 @@ bool CheckBDAPTxCreditUsage(const CTransaction& tx, const std::vector<Coin>& vBd
623656
std::string strOpType = GetBDAPOpTypeString(opCode1, opCode2);
624657
CServiceCredit credit(strOpType, coin.out.nValue, vvchOpParameters);
625658
vInputInfo.push_back(std::make_pair(credit, address));
659+
mapInputs.insert({address, credit});
626660
LogPrint("bdap", "%s -- BDAP Input strOpType %s, opCode1 %d, opCode2 %d, nValue %d, address %s\n", __func__,
627661
strOpType, opCode1, opCode2, FormatMoney(coin.out.nValue), address.ToString());
628662
}
629-
std::vector<std::pair<CServiceCredit, CDynamicAddress>> vOutputInfo;
663+
664+
std::multimap<CDynamicAddress, CServiceCredit> mapOutputs;
630665
for (const CTxOut& txout : tx.vout) {
631666
if (txout.IsBDAP()) {
632667
int opCode1 = -1; int opCode2 = -1;
@@ -635,50 +670,97 @@ bool CheckBDAPTxCreditUsage(const CTransaction& tx, const std::vector<Coin>& vBd
635670
CDynamicAddress address = GetScriptAddress(txout.scriptPubKey);
636671
std::string strOpType = GetBDAPOpTypeString(opCode1, opCode2);
637672
CServiceCredit credit(strOpType, txout.nValue, vvchOpParameters);
638-
vOutputInfo.push_back(std::make_pair(credit, address));
673+
mapOutputs.insert({address, credit});
639674
LogPrint("bdap", "%s -- BDAP Output strOpType %s, opCode1 %d, opCode2 %d, nValue %d, address %s\n", __func__,
640675
strOpType, opCode1, opCode2, FormatMoney(txout.nValue), address.ToString());
641676
} else if (txout.IsData()) {
642677
CDynamicAddress address;
643678
CServiceCredit credit("data", txout.nValue);
644-
vOutputInfo.push_back(std::make_pair(credit, address));
679+
mapOutputs.insert({address, credit});
645680
LogPrint("bdap", "%s -- BDAP Output strOpType %s, nValue %d\n", __func__, "data", FormatMoney(txout.nValue));
681+
} else {
682+
CDynamicAddress address = GetScriptAddress(txout.scriptPubKey);
683+
CServiceCredit credit("standard", txout.nValue);
684+
mapOutputs.insert({address, credit});
646685
}
647686
}
648-
/*
649-
1 - When input is a BDAP credit, make sure unconsumed coins go to a BDAP credit change ouput with the same credit input address and parameters
650-
2 - When input is a BDAP account update or delete operation, make sure deposit change goes back to input wallet address
651-
3 - When input is a BDAP link operation, make sure it is only spent by a link update or delete operations with the same input address and parameters
652-
653-
Example BDAP transactions
654-
- New Account:
655-
inputs: Can be BDAP credits or standard DYN.
656-
outputs:
657-
strOpType data, nValue 0.600006
658-
strOpType bdap_new_account, opCode1 1, opCode2 6, nValue 1.00001
659-
660-
- Update Account
661-
inputs: Restricted
662-
strOpType bdap_new_account, opCode1 1, opCode2 6, nValue 1.00001 -- uses the previous input
663-
outputs:
664-
strOpType bdap_update_account, opCode1 4, opCode2 6, nValue 0.100001 -- operation fee
665-
strOpType data, nValue 0.100001 -- burned data registration fee. Can be from credits or standard.
666-
strOpType bdap_new_account, opCode1 1, opCode2 6, nValue 0.99800998 -- optional change
667-
668-
- Delete Account
669-
inputs: Restricted
670-
strOpType bdap_update_account, opCode1 4, opCode2 6, nValue 0.100001 -- uses the previous input
671-
outputs:
672-
strOpType bdap_delete_account, opCode1 2, opCode2 6, nValue 0.100001 -- operation fee
673-
674-
- New Link:
675-
inputs: Can be BDAP credits or standard DYN.
676-
outputs:
677-
strOpType bdap_new_link_request, opCode1 1, opCode2 7, nValue 0.100001 -- deposit, can be from credits or standard.
678-
strOpType data, nValue 0.9900099 -- burned data registration fee. Can be from credits or standard.
679-
680-
*/
681687

688+
for (const std::pair<CServiceCredit, CDynamicAddress>& credit : vInputInfo) {
689+
if (credit.first.OpType == "bdap_move_asset") {
690+
// When an input is a BDAP credit, make sure unconsumed coins go to a BDAP credit change ouput with the same credit input address and parameters
691+
if (credit.first.vParameters.size() == 2) {
692+
std::vector<unsigned char> vchMoveSource = credit.first.vParameters[0];
693+
std::vector<unsigned char> vchMoveDestination = credit.first.vParameters[1];
694+
if (vchMoveSource != vchFromString(std::string("DYN")) || vchMoveDestination != vchFromString(std::string("BDAP"))) {
695+
LogPrintf("%s -- Check failed. Invalid use of BDAP credits. BDAP Credit has incorrect parameter. Move Source %s (should be DYN), Move Destination %s (should be BDAP)\n", __func__,
696+
stringFromVch(vchMoveSource), stringFromVch(vchMoveDestination));
697+
return false;
698+
}
699+
} else {
700+
LogPrintf("%s -- Check failed. Invalid use of BDAP credits. BDAP Credit has incorrect parameter count.\n", __func__);
701+
return false;
702+
}
703+
// make sure all of the credits are spent when we can't find an output address
704+
CDynamicAddress inputAddress = credit.second;
705+
std::multimap<CDynamicAddress, CServiceCredit>::iterator it = mapOutputs.find(inputAddress);
706+
if (it == mapOutputs.end()) {
707+
LogPrintf("%s -- Check failed. Invalid use of BDAP credits. Can't find credit address %s in outputs\n", __func__, inputAddress.ToString());
708+
if (ENFORCE_BDAP_CREDIT_USE)
709+
return false;
710+
711+
} else {
712+
// make sure asset doesn't move to another address, check outputs
713+
CAmount nInputAmount = 0;
714+
for (auto itr = mapInputs.find(inputAddress); itr != mapInputs.end(); itr++) {
715+
nInputAmount += itr->second.nValue;
716+
}
717+
CAmount nOutputAmount = 0;
718+
for (auto itr = mapOutputs.find(inputAddress); itr != mapOutputs.end(); itr++) {
719+
nOutputAmount += itr->second.nValue;
720+
}
721+
LogPrint("bdap", "%s -- inputAddress %s, nInputAmount %d, nOutputAmount %d, Diff %d\n", __func__,
722+
inputAddress.ToString(), FormatMoney(nInputAmount), FormatMoney(nOutputAmount), FormatMoney((nInputAmount - nOutputAmount)));
723+
724+
if (!((nInputAmount - nOutputAmount) == (nCreditsIn - nCreditsOut))) {
725+
LogPrintf("%s -- Check failed. Invalid use of BDAP credits. Fuel used %d should equal total fuel used %d\n", __func__,
726+
FormatMoney((nInputAmount - nOutputAmount)), FormatMoney((nCreditsIn - nCreditsOut)));
727+
if (ENFORCE_BDAP_CREDIT_USE)
728+
return false;
729+
}
730+
}
731+
} else if (credit.first.OpType == "bdap_new_account" || credit.first.OpType == "bdap_update_account" ||
732+
credit.first.OpType == "bdap_new_link_request" || credit.first.OpType == "bdap_new_link_accept") {
733+
// When input is a BDAP account new or update operation, make sure deposit change goes back to input wallet address
734+
// When input is a BDAP link operation, make sure it is only spent by a link update or delete operations with the same input address and parameters
735+
CDynamicAddress inputAddress = credit.second;
736+
std::multimap<CDynamicAddress, CServiceCredit>::iterator it = mapOutputs.find(inputAddress);
737+
if (it == mapOutputs.end()) {
738+
LogPrintf("%s -- Check failed. Invalid use of BDAP credits. Can't find account address %s in outputs\n", __func__, inputAddress.ToString());
739+
if (ENFORCE_BDAP_CREDIT_USE)
740+
return false;
741+
742+
} else {
743+
// make sure asset doesn't move to another address, check outputs
744+
CAmount nInputAmount = 0;
745+
for (auto itr = mapInputs.find(inputAddress); itr != mapInputs.end(); itr++) {
746+
nInputAmount += itr->second.nValue;
747+
}
748+
CAmount nOutputAmount = 0;
749+
for (auto itr = mapOutputs.find(inputAddress); itr != mapOutputs.end(); itr++) {
750+
nOutputAmount += itr->second.nValue;
751+
}
752+
LogPrint("bdap", "%s --inputAddress %s, nInputAmount %d, nOutputAmount %d, Diff %d\n", __func__,
753+
inputAddress.ToString(), FormatMoney(nInputAmount), FormatMoney(nOutputAmount), FormatMoney((nInputAmount - nOutputAmount)));
754+
755+
if (!((nInputAmount - nOutputAmount) == (nCreditsIn - nCreditsOut))) {
756+
LogPrintf("%s -- Check failed. Invalid use of BDAP credits. Fuel used %d should equal total fuel used %d\n", __func__,
757+
FormatMoney((nInputAmount - nOutputAmount)), FormatMoney((nCreditsIn - nCreditsOut)));
758+
if (ENFORCE_BDAP_CREDIT_USE)
759+
return false;
760+
}
761+
}
762+
}
763+
}
682764
return true;
683765
}
684766

@@ -1169,7 +1251,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
11691251
if (nBDAPBurn > 0)
11701252
nFees += nBDAPBurn;
11711253

1172-
LogPrint("bdap", "%s -- BDAP Burn Amount %d, Total Fees %d, BDAP Deposit Amount %d\n", __func__, FormatMoney(nBDAPBurn), FormatMoney(nFees), FormatMoney(nOpCodeAmount));
1254+
LogPrint("bdap", "%s -- BDAP Burn Data Amount %d, BDAP Op Code Amount %d\n", __func__, FormatMoney(nBDAPBurn), FormatMoney(nOpCodeAmount));
11731255
}
11741256
// nModifiedFees includes any fee deltas from PrioritiseTransaction
11751257
CAmount nModifiedFees = nFees;

src/validation.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,8 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state);
392392
* Validate the usage of BDAP credits to makes sure they are not recirculated
393393
* with standard DYN (trapped as fuel credit) and used to fund the appropriate BDAP operation.
394394
*/
395-
bool CheckBDAPTxCreditUsage(const CTransaction& tx, const std::vector<Coin>& vBdapCoins);
395+
bool CheckBDAPTxCreditUsage(const CTransaction& tx, const std::vector<Coin>& vBdapCoins,
396+
const CAmount& nStandardIn, const CAmount& nCreditsIn, const CAmount& nStandardOut, const CAmount& nCreditsOut, const CAmount& nDataBurned);
396397

397398
namespace Consensus
398399
{

0 commit comments

Comments
 (0)