@@ -555,6 +555,9 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state)
555
555
bool fIsBDAP = false ;
556
556
// Check for negative or overflow output values
557
557
CAmount nValueOut = 0 ;
558
+ CAmount nStandardOut = 0 ;
559
+ CAmount nCreditsOut = 0 ;
560
+ CAmount nDataBurned = 0 ;
558
561
for (const CTxOut& txout : tx.vout ) {
559
562
if (txout.nValue < 0 )
560
563
return state.DoS (100 , false , REJECT_INVALID, " bad-txns-vout-negative" );
@@ -569,8 +572,14 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state)
569
572
if (!fluid.ValidationProcesses (state, txout.scriptPubKey , txout.nValue ))
570
573
return state.DoS (100 , false , REJECT_INVALID, " bad-txns-fluid-validate-failure" );
571
574
}
572
- if (txout.IsBDAP ())
575
+ if (txout.IsBDAP ()) {
573
576
fIsBDAP = true ;
577
+ nCreditsOut += txout.nValue ;
578
+ } else if (txout.IsData ()) {
579
+ nDataBurned += txout.nValue ;
580
+ } else {
581
+ nStandardOut += txout.nValue ;
582
+ }
574
583
}
575
584
576
585
// Check for duplicate inputs
@@ -580,7 +589,8 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state)
580
589
if (!vInOutPoints.insert (txin.prevout ).second )
581
590
return state.DoS (100 , false , REJECT_INVALID, " bad-txns-inputs-duplicate" );
582
591
}
583
-
592
+ CAmount nStandardIn = 0 ;
593
+ CAmount nCreditsIn = 0 ;
584
594
std::vector<Coin> vBdapCoins;
585
595
if (tx.IsCoinBase ()) {
586
596
if (tx.vin [0 ].scriptSig .size () < 2 || tx.vin [0 ].scriptSig .size () > 100 )
@@ -595,6 +605,9 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state)
595
605
if (coin.out .IsBDAP ()) {
596
606
vBdapCoins.push_back (coin);
597
607
fIsBDAP = true ;
608
+ nCreditsIn += coin.out .nValue ;
609
+ } else {
610
+ nStandardIn += coin.out .nValue ;
598
611
}
599
612
}
600
613
}
@@ -606,14 +619,34 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state)
606
619
if (fIsBDAP && tx.nVersion != BDAP_TX_VERSION)
607
620
return state.DoS (100 , false , REJECT_INVALID, " incorrect-bdap-tx-version" );
608
621
609
- if (fIsBDAP && !CheckBDAPTxCreditUsage (tx, vBdapCoins))
622
+ if (fIsBDAP && !CheckBDAPTxCreditUsage (tx, vBdapCoins, nStandardIn, nCreditsIn, nStandardOut, nCreditsOut, nDataBurned ))
610
623
return state.DoS (100 , false , REJECT_INVALID, " bad-bdap-credit-use" );
611
624
612
625
return true ;
613
626
}
614
627
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)
616
630
{
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;
617
650
std::vector<std::pair<CServiceCredit, CDynamicAddress>> vInputInfo;
618
651
for (const Coin& coin : vBdapCoins) {
619
652
int opCode1 = -1 ; int opCode2 = -1 ;
@@ -623,10 +656,12 @@ bool CheckBDAPTxCreditUsage(const CTransaction& tx, const std::vector<Coin>& vBd
623
656
std::string strOpType = GetBDAPOpTypeString (opCode1, opCode2);
624
657
CServiceCredit credit (strOpType, coin.out .nValue , vvchOpParameters);
625
658
vInputInfo.push_back (std::make_pair (credit, address));
659
+ mapInputs.insert ({address, credit});
626
660
LogPrint (" bdap" , " %s -- BDAP Input strOpType %s, opCode1 %d, opCode2 %d, nValue %d, address %s\n " , __func__,
627
661
strOpType, opCode1, opCode2, FormatMoney (coin.out .nValue ), address.ToString ());
628
662
}
629
- std::vector<std::pair<CServiceCredit, CDynamicAddress>> vOutputInfo;
663
+
664
+ std::multimap<CDynamicAddress, CServiceCredit> mapOutputs;
630
665
for (const CTxOut& txout : tx.vout ) {
631
666
if (txout.IsBDAP ()) {
632
667
int opCode1 = -1 ; int opCode2 = -1 ;
@@ -635,50 +670,97 @@ bool CheckBDAPTxCreditUsage(const CTransaction& tx, const std::vector<Coin>& vBd
635
670
CDynamicAddress address = GetScriptAddress (txout.scriptPubKey );
636
671
std::string strOpType = GetBDAPOpTypeString (opCode1, opCode2);
637
672
CServiceCredit credit (strOpType, txout.nValue , vvchOpParameters);
638
- vOutputInfo. push_back ( std::make_pair (credit, address) );
673
+ mapOutputs. insert ({address, credit} );
639
674
LogPrint (" bdap" , " %s -- BDAP Output strOpType %s, opCode1 %d, opCode2 %d, nValue %d, address %s\n " , __func__,
640
675
strOpType, opCode1, opCode2, FormatMoney (txout.nValue ), address.ToString ());
641
676
} else if (txout.IsData ()) {
642
677
CDynamicAddress address;
643
678
CServiceCredit credit (" data" , txout.nValue );
644
- vOutputInfo. push_back ( std::make_pair (credit, address) );
679
+ mapOutputs. insert ({address, credit} );
645
680
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});
646
685
}
647
686
}
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
- */
681
687
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
+ }
682
764
return true ;
683
765
}
684
766
@@ -1169,7 +1251,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
1169
1251
if (nBDAPBurn > 0 )
1170
1252
nFees += nBDAPBurn;
1171
1253
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));
1173
1255
}
1174
1256
// nModifiedFees includes any fee deltas from PrioritiseTransaction
1175
1257
CAmount nModifiedFees = nFees;
0 commit comments