Merge pull request #780 from jl777/example0

code complete core AE
This commit is contained in:
jl777
2018-07-19 22:14:17 -11:00
committed by GitHub

View File

@@ -20,6 +20,9 @@
#include "../script/standard.h"
#include "../base58.h"
// debugging: need to clarify tx, inputTx, origTx
// code rpc
/*
Assets can be created or transferred.
@@ -61,66 +64,69 @@
vout.n-2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid]
buyoffer:
vins.*: normal inputs (bid + change)
vout.0: amount of bid to unspendable
vout.1: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
cancelbuy:
vin.0: normal input
vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
vout.1: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['o']
fillbuy:
vin.0: normal input
vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
vin.2: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
vout.0: remaining amount of bid to unspendable
vout.1: vin.1 value to signer of vin.2
vout.2: vin.2 assetoshis to original pubkey
vout.3: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
selloffer:
vin.0: normal input
vin.1: valid CC output for sale
vout.0: vin.1 assetoshis output to CC to unspendable
vout.1: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required]
buyoffer:
vins.*: normal inputs (bid + change)
vout.0: amount of bid to unspendable
vout.1: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required]
vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
exchange:
vin.0: normal input
vin.1: valid CC output
vout.0: vin.1 assetoshis output to CC to unspendable
vout.1: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required]
cancelbuy:
vin.0: normal input
vin.1: unspendable.(vout.1 from buyoffer)
vout.0: vin.1 value to original pubkey
vout.1: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['o'] [assetid]
vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
cancel:
vin.0: normal input
vin.1: unspendable.(vout.1 from exchange or selloffer)
vout.0: vin.1 assetoshis to original pubkey CC
vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
vout.1: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
fillsell:
vin.0: normal input
vin.1: unspendable.(vout.1 assetoshis from selloffer)
vin.2: normal output that satisfies selloffer
vout.0: vin.1 assetoshis to signer of vin.2
vout.1: vin.2 value to original pubkey
vout.2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid]
fillbuy:
vin.0: normal input
vin.1: unspendable.(vout.1 from buyoffer)
vin.2: valid CC output satisfies buyoffer
vout.0: vin.1 value to signer of vin.2
vout.1: vin.2 assetoshis to original pubkey
vout.2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid]
vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0]
vin.2: normal output that satisfies selloffer (*tx.vin[2])->nValue
vout.0: remaining assetoshis -> unspendable
vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any
vout.2: vin.2 value to original pubkey [origpubkey]
vout.3: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey]
fillexchange:
vin.0: normal input
vin.1: unspendable.(vout.1 assetoshis from exchange)
vin.2: valid CC output that satisfies exchange
vout.0: vin.1 assetoshis to signer of vin.2
vout.1: vin.2 assetoshis to original pubkey
vout.2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin1] [assetid vin2]
vin.1: unspendable.(vout.0 assetoshis from exchange) exchangeTx.vout[0]
vin.2: valid CC assetid2 output that satisfies exchange (*tx.vin[2])->nValue
vout.0: remaining assetoshis -> unspendable
vout.1: vin.1 assetoshis to signer of vin.2 exchangeTx.vout[0].nValue -> any
vout.2: vin.2 assetoshis2 to original pubkey [origpubkey]
vout.3: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey]
*/
@@ -150,6 +156,60 @@ signEncodeTx "$transferTx"
const char *Unspendableaddr = "RHTcNNYXEZhLGRcXspA2H4gw2v4u6w8MNp";
bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey)
{
CTxDestination address;
if ( ExtractDestination(scriptPubKey,address) != 0 )
{
strcpy(destaddr,(char *)CBitcoinAddress(address).ToString().c_str());
return(true);
}
return(false);
}
uint8_t DecodeOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,uint64_t &price,std::vector<uint8_t> &origpubkey)
{
std::vector<uint8_t> vopret; uint8_t funcid=0,*script;
GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
memset(&assetid,0,sizeof(assetid));
memset(&assetid2,0,sizeof(assetid2));
price = 0;
if ( script[0] == EVAL_ASSETS )
{
funcid = script[1];
switch ( funcid )
{
case 'o':
return(funcid);
break;
case 't': case 'x':
if ( E_UNMARSHAL(vopret, ss >> assetid) != 0 )
return(funcid);
break;
case 's': case 'b': case 'S': case 'B':
if ( E_UNMARSHAL(vopret, ss >> assetid; ss >> price; ss >> origpubkey) != 0 )
return(funcid);
break;
case 'E': case 'e':
if ( E_UNMARSHAL(vopret, ss >> assetid; ss >> assetid2; ss >> price; ss >> origpubkey) != 0 )
return(funcid);
break;
}
}
return(funcid);
}
bool Getorigaddr(char *destaddr,CTransaction& tx)
{
uint256 assetid,assetid2; uint64_t price,nValue=0; int32_t n; uint8_t funcid; std::vector<uint8_t> origpubkey; CScript script;
n = tx.vout.size();
if ( n == 0 || (funcid= DecodeOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,price,origpubkey)) == 0 )
return(false);
script = CScript() << origpubkey << OP_CHECKSIG;
return(Getscriptaddress(destaddr,script));
}
CC* GetCryptoCondition(CScript const& scriptSig)
{
auto pc = scriptSig.begin();
@@ -176,158 +236,102 @@ bool IsAssetInput(CScript const& scriptSig)
return out;
}
uint8_t DecodeOpRet(CScript const& scriptPubKey,uint256 &assetid,uint256 &assetid2,uint64_t &amount)
uint64_t IsAssetvout(uint64_t &price,std::vector<uint8_t> &origpubkey,CTransaction& tx,int32_t v,uint256 refassetid)
{
std::vector<uint8_t> vopret; uint8_t funcid=0,*script;
GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
memset(&assetid,0,sizeof(assetid));
memset(&assetid2,0,sizeof(assetid2));
amount = 0;
if ( script[0] == EVAL_ASSETS )
uint256 assetid,assetid2; uint64_t nValue=0; int32_t n; uint8_t funcid;
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
{
funcid = script[1];
switch ( funcid )
{
case 't': case 'o': case 'x': case 'S': case 'B':
if ( E_UNMARSHAL(vopret, ss >> assetid) != 0 )
return(funcid);
break;
case 's': case 'b':
if ( E_UNMARSHAL(vopret, ss >> assetid; ss >> amount) != 0 )
return(funcid);
break;
case 'E':
if ( E_UNMARSHAL(vopret, ss >> assetid; ss >> assetid2) != 0 )
return(funcid);
break;
case 'e':
if ( E_UNMARSHAL(vopret, ss >> assetid; ss >> assetid2; ss >> amount) != 0 )
return(funcid);
break;
}
}
return(funcid);
}
uint64_t IsAssetTx(uint64_t *origamountp,uint8_t funcid,int32_t vini,uint256 assetid,CTransaction& inputTx,int32_t v)
{
// Either the tx will be a CREATE (no CC inputs) or a TRANSFER (1 or more CC inputs)
uint256 inputassetid,inputassetid2; uint64_t amount; uint8_t inputfuncid; unsigned int i,n,r = 0;
if ( origamountp != 0 )
*origamountp = 0;
for (i=0; i<inputTx.vin.size(); i++)
r += IsAssetInput(inputTx.vin[i].scriptSig) ? 1 : 0;
if ( r > 0 )
{
n = inputTx.vout.size();
if ( (inputfuncid= DecodeOpRet(inputTx.vout[n-1].scriptPubKey,inputassetid,inputassetid2,amount)) == 0 )
return(0);
if ( origamountp != 0 )
*origamountp = amount;
n = tx.vout.size();
if ( v >= n-1 )
return(0);
if ( inputfuncid != 'E' || vini == 1 )
nValue = tx.vout[v].nValue;
if ( (funcid= DecodeOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,price,origpubkey)) == 0 )
return(0);
if ( funcid == 'c' )
{
if ( inputassetid == assetid )
return(inputTx.vout[v].nValue);
if ( refassetid == tx.GetHash() && v == 0 )
return(nValue);
}
else // 'E' and vini == 2
else if ( funcid != 'E' )
{
if ( inputassetid2 == assetid )
return(inputTx.vout[v].nValue);
if ( assetid == refassetid )
return(nValue);
}
else if ( funcid == 'E' )
{
if ( v < 2 && assetid == refassetid )
return(nValue);
else if ( v == 2 && assetid2 == refassetid )
return(nValue);
}
}
else if ( r == 0 ) // It's a CREATE, compare hash directly
{
if ( assetid == inputTx.GetHash() && v == 0 ) // asset create must use vout[0] for the asset
return(inputTx.vout[v].nValue);
}
// should never happen
fprintf(stderr, "Illegal state detected, mixed inputs for asset at: %s\n",assetid.GetHex().data());
return(0);
}
uint64_t AssetIsvalidCCvin(Eval* eval,uint8_t funcid,uint256 assetid,uint64_t *origamountp,uint64_t *origoutp,char *origaddr,const CTransaction &tx,int32_t vini)
uint64_t AssetValidatevin(Eval* eval,char *origaddr,CTransaction &tx,CTransaction &vinTx)
{
CTransaction inputTx,origTx; CTxDestination address; uint256 hashBlock; int32_t v; uint64_t nValue = 0;
v = tx.vin[vini].prevout.n;
if ( eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash,inputTx,hashBlock) == 0 )
uint256 hashBlock; char destaddr[64];
origaddr[0] = destaddr[0] = 0;
if ( tx.vin[1].prevout.n != 0 )
return eval->Invalid("vin1 needs to be buyvin.vout[0]");
else if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
return eval->Invalid("always should find vin, but didnt");
if ( IsAssetInput(tx.vin[vini].scriptSig) != 0 )
{
if ( (nValue= IsAssetTx(origamountp,funcid,vini,assetid,inputTx,v)) == 0 )
return eval->Invalid("Non asset / wrong asset input tx");
}
if ( origaddr != 0 && origoutp != 0 )
{
origaddr[0] = 0;
*origoutp = 0;
if ( eval->GetTxUnconfirmed(inputTx.vin[vini].prevout.hash,origTx,hashBlock) == 0 )
return eval->Invalid("always should find origtx, but didnt for cancelbuy");
if ( ExtractDestination(origTx.vout[inputTx.vin[vini].prevout.n].scriptPubKey,address) == 0 )
return eval->Invalid("no origtx address for cancelbuy");
strcpy(origaddr,(char *)CBitcoinAddress(address).ToString().c_str());
*origoutp = inputTx.vout[0].nValue;
}
return(nValue);
else if ( Getscriptaddress(destaddr,vinTx.vout[0].scriptPubKey) == 0 || strcmp(destaddr,Unspendableaddr) != 0 )
return eval->Invalid("invalid vin unspendableaddr");
else if ( vinTx.vout[0].nValue < 10000 )
return eval->Invalid("invalid dust for buyvin");
else if ( Getorigaddr(origaddr,vinTx) == 0 )
return eval->Invalid("couldnt get origaddr for buyvin");
else return(vinTx.vout[0].nValue);
}
uint64_t AssetIsvalidvin(Eval* eval,uint64_t *origamountp,uint64_t *origoutp,char *origaddr,const CTransaction &tx,int32_t vini)
uint64_t AssetValidateBuyvin(Eval* eval,uint64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *origaddr,CTransaction &tx)
{
CTransaction inputTx,origTx; CTxDestination address; uint256 hashBlock,inputassetid,inputassetid2; int32_t v; uint8_t inputfuncid; uint64_t amount,nValue = 0;
v = tx.vin[vini].prevout.n;
*origamountp = 0;
if ( eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash,inputTx,hashBlock) == 0 )
return eval->Invalid("always should find vin, but didnt");
if ( (inputfuncid= DecodeOpRet(inputTx.vout[inputTx.vout.size()-1].scriptPubKey,inputassetid,inputassetid2,amount)) == 0 )
CTransaction vinTx; uint64_t nValue; uint256 assetid,assetid2;
if ( (nValue= AssetValidatevin(eval,origaddr,tx,vinTx)) == 0 )
return(0);
nValue = inputTx.vout[v].nValue;
*origamountp = amount;
if ( origaddr != 0 && origoutp != 0 )
else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 )
return eval->Invalid("invalid CC vout0 for buyvin");
else if ( DecodeOpRet(vinTx.vout[vinTx.vout.size()-1].scriptPubKey,assetid,assetid2,tmpprice,tmporigpubkey) == 0 )
return eval->Invalid("invalid opreturn for buyvin");
else return(nValue);
}
uint64_t AssetValidateSellvin(Eval* eval,uint64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *origaddr,CTransaction &tx,uint256 assetid)
{
CTransaction vinTx; uint64_t nValue,assetoshis;
if ( (nValue= AssetValidatevin(eval,origaddr,tx,vinTx)) == 0 )
return(0);
if ( (assetoshis= IsAssetvout(tmpprice,tmporigpubkey,vinTx,0,assetid)) != 0 )
return eval->Invalid("invalid missing CC vout0 for sellvin");
else return(assetoshis);
}
bool ValidateRemainder(uint64_t remaining_price,uint64_t remaining_nValue,uint64_t orig_nValue,uint64_t received,uint64_t paid,uint64_t totalprice)
{
uint64_t price,recvprice;
if ( orig_nValue == 0 || received == 0 || paid == 0 || totalprice == 0 )
return(false);
else if ( remaining_price < (totalprice - received) )
return(false);
else if ( remaining_nValue < (orig_nValue - paid) )
return(false);
else if ( remaining_nValue > 0 )
{
origaddr[0] = 0;
*origoutp = 0;
if ( eval->GetTxUnconfirmed(inputTx.vin[vini].prevout.hash,origTx,hashBlock) == 0 )
return eval->Invalid("always should find origtx, but didnt for cancelbuy");
if ( ExtractDestination(origTx.vout[inputTx.vin[vini].prevout.n].scriptPubKey,address) == 0 )
return eval->Invalid("no origtx address for cancelbuy");
strcpy(origaddr,(char *)CBitcoinAddress(address).ToString().c_str());
*origoutp = inputTx.vout[0].nValue;
price = (totalprice * COIN) / orig_nValue;
recvprice = (received * COIN) / paid;
if ( recvprice < price )
return(false);
}
return(nValue);
return(true);
}
uint64_t Assetvini2val(Eval* eval,int32_t needasset,uint256 assetid,const CTransaction &tx)
{
CTransaction inputTx; uint256 hashBlock;
if ( needasset != 0 && IsAssetInput(tx.vin[2].scriptSig) == 0 )
return eval->Invalid("vini2 is not CC");
if ( eval->GetTxUnconfirmed(tx.vin[2].prevout.hash,inputTx,hashBlock) == 0 )
return eval->Invalid("always should find vin, but didnt");
return(inputTx.vout[tx.vin[2].prevout.n].nValue);
}
uint64_t AssetIsvalidvout0(Eval* eval,const CTransaction &tx,uint64_t nValue)
{
CTxDestination address;
if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
return eval->Invalid("illegal normal vout0");
if ( tx.vout[0].nValue != nValue )
return eval->Invalid("nValue mismatch vout0");
if ( ExtractDestination(tx.vout[0].scriptPubKey,address) == 0 )
return eval->Invalid("no vout0 address");
if ( strcmp(Unspendableaddr,(char *)CBitcoinAddress(address).ToString().c_str()) != 0 )
return eval->Invalid("invalid vout0 address");
return(nValue);
}
bool AssetValidate(Eval* eval,const CTransaction &tx,int32_t numvouts,uint8_t funcid,uint256 assetid,uint256 assetid2,uint64_t amount)
bool AssetValidate(Eval* eval,CTransaction &tx,int32_t numvouts,uint8_t funcid,uint256 assetid,uint256 assetid2,uint64_t remaining_price,std::vector<uint8_t> origpubkey)
{
static uint256 zero;
CTxDestination address; uint256 hashBlock; int32_t i,r,numvins; uint64_t nValue,vini2val,origamount,outputs,assetoshis,origout; char origaddr[64];
CTxDestination address; CTransaction vinTx; uint256 hashBlock; int32_t i,numvins; uint64_t nValue,assetoshis,outputs,inputs,tmpprice,ignore; std::vector<uint8_t> tmporigpubkey,ignorepubkey; char destaddr[64],origaddr[64];
numvins = tx.vin.size();
assetoshis = outputs = 0;
outputs = inputs = 0;
if ( IsAssetInput(tx.vin[0].scriptSig) != 0 )
return eval->Invalid("illegal asset vin0");
if ( funcid != 'c' && assetid == zero )
@@ -339,8 +343,7 @@ bool AssetValidate(Eval* eval,const CTransaction &tx,int32_t numvouts,uint8_t fu
//vout.0: issuance assetoshis to CC
//vout.1: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['c'] [{"<assetname>":"<description>"}]
if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
return eval->Invalid("illegal normal vout0 for createasset");
return eval->Invalid("unexpected AssetValidate for createasset");
break;
case 't': // transfer
@@ -349,208 +352,203 @@ bool AssetValidate(Eval* eval,const CTransaction &tx,int32_t numvouts,uint8_t fu
//vout.0 to n-2: assetoshis output to CC
//vout.n-2: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid]
for (r=0,i=1; i<numvins; i++)
for (i=1; i<numvins; i++)
{
if ( (nValue= AssetIsvalidCCvin(eval,funcid,assetid,0,0,0,tx,i)) != 0 )
assetoshis += nValue;
else return eval->Invalid("illegal normal vin for transfer");
if ( IsAssetInput(tx.vin[i].scriptSig) != 0 )
{
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
return eval->Invalid("always should find vin, but didnt");
else if ( (assetoshis= IsAssetvout(tmpprice,tmporigpubkey,vinTx,tx.vin[i].prevout.n,assetid)) != 0 )
inputs += assetoshis;
}
}
for (i=0; i<numvouts-1; i++)
{
if ( tx.vout[i].scriptPubKey.IsPayToCryptoCondition() != 0 )
outputs += tx.vout[i].nValue;
else if ( i < numvouts-2 )
return eval->Invalid("non-CC asset transfer vout");
if ( (assetoshis= IsAssetvout(tmpprice,tmporigpubkey,tx,i,assetid)) != 0 )
outputs += assetoshis;
}
if ( assetoshis == 0 )
if ( inputs == 0 )
return eval->Invalid("no asset inputs for transfer");
else if ( assetoshis != outputs )
else if ( inputs != outputs )
return eval->Invalid("mismatched inputs and outputs for transfer");
break;
case 's': // selloffer
//vin.0: normal input
//vin.1: valid CC output for sale
//vout.0: vin.1 assetoshis output to CC to unspendable
//vout.1: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required]
if ( amount == 0 )
return eval->Invalid("illegal null amount for selloffer");
if ( (nValue= AssetIsvalidCCvin(eval,funcid,assetid,0,0,0,tx,1)) != 0 )
{
if ( AssetIsvalidvout0(eval,tx,nValue) != nValue )
return(false);
} else return eval->Invalid("illegal standard vini.1 for selloffer");
break;
case 'b': // buyoffer
//vins.*: normal inputs (bid + change)
//vout.0: amount of bid to unspendable
//vout.1: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required]
if ( amount == 0 )
// vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
if ( remaining_price == 0 )
return eval->Invalid("illegal null amount for buyoffer");
for (i=1; i<numvins; i++)
{
if ( (nValue= AssetIsvalidCCvin(eval,funcid,assetid,0,0,0,tx,i)) != 0 )
if ( IsAssetInput(tx.vin[i].scriptSig) != 0 )
return eval->Invalid("invalid CC vin for buyoffer");
}
for (i=0; i<numvouts-1; i++)
{
if ( tx.vout[i].scriptPubKey.IsPayToCryptoCondition() != 0 )
return eval->Invalid("invalid CC vout for buyoffer");
if ( i == 0 )
{
if ( Getscriptaddress(destaddr,tx.vout[i].scriptPubKey) == 0 || strcmp(destaddr,Unspendableaddr) != 0 )
return eval->Invalid("invalid vout0 destaddr for buyoffer");
else if ( tx.vout[i].nValue < 10000 )
return eval->Invalid("invalid vout0 dust for buyoffer");
}
}
break;
case 'e': // exchange
//vin.0: normal input
//vin.1: valid CC output
//vout.0: vin.1 assetoshis output to CC to unspendable
//vout.1: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required]
if ( assetid2 == zero )
return eval->Invalid("illegal assetid2 for exchange");
if ( amount == 0 )
return eval->Invalid("illegal null amount for exchange");
if ( (nValue= AssetIsvalidCCvin(eval,funcid,assetid,0,0,0,tx,1)) != 0 )
{
if ( AssetIsvalidvout0(eval,tx,nValue) != nValue )
return(false);
} else return eval->Invalid("illegal standard vini.1 for exchange");
break;
case 'o': // cancelbuy
//vin.0: normal input
//vin.1: unspendable.(vout.1 from buyoffer)
//vout.0: vin.1 value to original pubkey
//vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
//vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
//vout.1: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['o'] [assetid]
if ( AssetIsvalidCCvin(eval,funcid,assetid,&origamount,&origout,origaddr,tx,1) != 0 )
return eval->Invalid("illegal CC vin.1 for cancelbuy");
if ( strcmp(Unspendableaddr,(char *)CBitcoinAddress(address).ToString().c_str()) != 0 )
return eval->Invalid("invalid vout0 address for cancelbuy");
if ( origout != tx.vout[0].nValue )
return eval->Invalid("mismatched value for cancelbuy");
if ( ExtractDestination(tx.vout[0].scriptPubKey,address) == 0 )
return eval->Invalid("no vout0 address for cancelbuy");
if ( strcmp(origaddr,(char *)CBitcoinAddress(address).ToString().c_str()) != 0 )
return eval->Invalid("mismatched origaddr for cancelbuy");
//vout.n-1: opreturn [EVAL_ASSETS] ['o']
if ( (nValue= AssetValidateBuyvin(eval,tmpprice,tmporigpubkey,origaddr,tx)) == 0 )
return(false);
else if ( nValue != tx.vout[0].nValue )
return eval->Invalid("mismatched refund for cancelbuy");
else if ( Getscriptaddress(destaddr,tx.vout[0].scriptPubKey) == 0 || strcmp(destaddr,origaddr) != 0 )
return eval->Invalid("invalid vout0 destaddr for cancelbuy");
break;
case 'B': // fillbuy:
//vin.0: normal input
//vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
//vin.2: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
//vout.0: remaining amount of bid to unspendable
//vout.1: vin.1 value to signer of vin.2
//vout.2: vin.2 assetoshis to original pubkey
//vout.3: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
if ( (nValue= AssetValidateBuyvin(eval,tmpprice,tmporigpubkey,origaddr,tx)) == 0 )
return(false);
else if ( tmporigpubkey != origpubkey )
return eval->Invalid("mismatched origpubkeys for fillbuy");
else if ( IsAssetInput(tx.vin[2].scriptSig) != 0 )
{
if ( eval->GetTxUnconfirmed(tx.vin[2].prevout.hash,vinTx,hashBlock) == 0 )
return eval->Invalid("always should find vin, but didnt");
else if ( (assetoshis= IsAssetvout(ignore,ignorepubkey,vinTx,tx.vin[2].prevout.n,assetid)) != 0 )
{
if ( tx.vout[2].nValue != assetoshis )
return eval->Invalid("mismatched assetoshis for fillbuy");
else if ( Getscriptaddress(destaddr,tx.vout[2].scriptPubKey) == 0 || strcmp(destaddr,origaddr) != 0 )
return eval->Invalid("mismatched vout2 destaddr for fillbuy");
else if ( ValidateRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,assetoshis,tmpprice) == false )
return eval->Invalid("mismatched remainder for fillbuy");
else if ( remaining_price != 0 )
{
if ( remaining_price < 10000 )
return eval->Invalid("dust vout0 to unspendableaddr for fillbuy");
else if ( Getscriptaddress(destaddr,tx.vout[0].scriptPubKey) == 0 || strcmp(destaddr,Unspendableaddr) != 0 )
return eval->Invalid("mismatched vout0 unspendableaddr for fillbuy");
}
} else return eval->Invalid("vin2 not enough asset2 for fillbuy");
} else return eval->Invalid("vin2 not asset for fillbuy");
break;
case 's': // selloffer
case 'e': // exchange
//vin.0: normal input
//vin.1: valid CC output for sale
//vout.0: vin.1 assetoshis output to CC to unspendable
//vout.1: normal output for change (if any)
//'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
//'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
if ( remaining_price == 0 )
return eval->Invalid("illegal null remaining_price for selloffer");
if ( IsAssetInput(tx.vin[1].scriptSig) != 0 )
{
if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
return eval->Invalid("always should find vin, but didnt");
else if ( (assetoshis= IsAssetvout(tmpprice,tmporigpubkey,vinTx,tx.vin[1].prevout.n,assetid)) == 0 )
return eval->Invalid("illegal missing assetvin for selloffer");
else if ( Getscriptaddress(destaddr,tx.vout[0].scriptPubKey) == 0 || strcmp(destaddr,Unspendableaddr) != 0 )
return eval->Invalid("mismatched vout0 unspendableaddr for selloffer");
else if ( tx.vout[0].nValue != assetoshis )
return eval->Invalid("mismatched assetoshis for selloffer");
} else return eval->Invalid("illegal normal vin1 for selloffer");
break;
case 'x': // cancel
//vin.0: normal input
//vin.1: unspendable.(vout.1 from exchange or selloffer)
//vout.0: vin.1 assetoshis to original pubkey CC
//vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
//vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
//vout.1: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
if ( (nValue= AssetIsvalidCCvin(eval,funcid,assetid,&origamount,&origout,origaddr,tx,1)) != 0 )
{
if ( origout != tx.vout[0].nValue )
return eval->Invalid("mismatched value for cancel");
if ( ExtractDestination(tx.vout[0].scriptPubKey,address) == 0 )
return eval->Invalid("no vout0 address for cancel");
if ( strcmp(origaddr,(char *)CBitcoinAddress(address).ToString().c_str()) != 0 )
return eval->Invalid("mismatched origaddr for cancel");
} else return eval->Invalid("illegal standard vini.1 for cancel");
if ( (assetoshis= AssetValidateSellvin(eval,tmpprice,tmporigpubkey,origaddr,tx,assetid)) == 0 )
return(false);
else if ( assetoshis != tx.vout[0].nValue )
return eval->Invalid("mismatched refund for cancelbuy");
else if ( Getscriptaddress(destaddr,tx.vout[0].scriptPubKey) == 0 || strcmp(destaddr,origaddr) != 0 )
return eval->Invalid("invalid vout0 destaddr for cancel");
break;
case 'S': // fillsell
case 'E': // fillexchange
//vin.0: normal input
//vin.1: unspendable.(vout.1 assetoshis from selloffer)
//vin.2: normal output that satisfies selloffer, need to extract from opreturn
//vout.0: vin.1 assetoshis to signer of vin.2
//vout.1: vin.2 value to original pubkey
//vout.2: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid]
if ( (vini2val= Assetvini2val(eval,0,assetid,tx)) == 0 )
return eval->Invalid("no vini2val for fillsell");
if ( (nValue= AssetIsvalidCCvin(eval,funcid,assetid,&origamount,&origout,origaddr,tx,1)) != 0 )
//vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0]
//'S'.vin.2: normal output that satisfies selloffer (*tx.vin[2])->nValue
//'E'.vin.2: valid CC assetid2 output that satisfies exchange (*tx.vin[2])->nValue
//vout.0: remaining assetoshis -> unspendable
//vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any
//'S'.vout.2: vin.2 value to original pubkey [origpubkey]
//'E'.vout.2: vin.2 assetoshis2 to original pubkey [origpubkey]
//vout.3: normal output for change (if any)
//'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey]
//'E'.vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey]
if ( (assetoshis= AssetValidateSellvin(eval,tmpprice,tmporigpubkey,origaddr,tx,assetid)) == 0 )
return(false);
else if ( tmporigpubkey != origpubkey )
return eval->Invalid("mismatched origpubkeys for fillsell");
else if ( funcid == 'S' && IsAssetInput(tx.vin[2].scriptSig) != 0 )
return eval->Invalid("invalid vin2 is CC for fillsell");
else if ( funcid == 'E' && IsAssetInput(tx.vin[2].scriptSig) == 0 )
return eval->Invalid("invalid vin2 is CC for fillexchange");
else if ( eval->GetTxUnconfirmed(tx.vin[2].prevout.hash,vinTx,hashBlock) == 0 )
return eval->Invalid("always should find vin, but didnt");
else if ( funcid == 'E' && (IsAssetvout(ignore,ignorepubkey,vinTx,tx.vin[2].prevout.n,assetid2) != tx.vout[2].nValue || tx.vout[2].nValue == 0) )
return eval->Invalid("invalid asset2 vin value for fillexchange");
else if ( funcid == 'E' && IsAssetvout(ignore,ignorepubkey,tx,2,assetid2) == 0 )
return eval->Invalid("invalid asset2 voutvalue for fillexchange");
else if ( Getscriptaddress(destaddr,tx.vout[2].scriptPubKey) == 0 || strcmp(destaddr,origaddr) != 0 )
return eval->Invalid("mismatched vout2 destaddr for fill");
else if ( vinTx.vout[tx.vin[2].prevout.n].nValue != tx.vout[2].nValue )
return eval->Invalid("mismatched vout2 nValue for fill");
else if ( ValidateRemainder(remaining_price,tx.vout[0].nValue,assetoshis,tx.vout[2].nValue,tx.vout[1].nValue,tmpprice) == false )
return eval->Invalid("mismatched remainder for fill");
else if ( IsAssetvout(ignore,ignorepubkey,vinTx,tx.vin[1].prevout.n,assetid) == 0 )
return eval->Invalid("mismatched vout0 unspendableaddr for fill");
else if ( remaining_price != 0 )
{
if ( origout != tx.vout[0].nValue )
return eval->Invalid("mismatched value for fillsell");
// vout address can be any
if ( ExtractDestination(tx.vout[1].scriptPubKey,address) == 0 )
return eval->Invalid("no vout0 address for fillsell");
if ( strcmp(origaddr,(char *)CBitcoinAddress(address).ToString().c_str()) != 0 )
return eval->Invalid("mismatched origaddr for fillsell");
if ( vini2val < origamount )
return eval->Invalid("not enough paid for fillsell");
if ( vini2val != tx.vout[1].nValue )
return eval->Invalid("mismatched vout.1 val for fillsell");
} else return eval->Invalid("illegal standard vini.1 for fillsell");
break;
case 'B': // fillbuy:
//vin.0: normal input
//vin.1: unspendable.(vout.1 from buyoffer)
//vin.2: valid CC output satisfies buyoffer
//vout.0: vin.1 value to signer of vin.2
//vout.1: vin.2 assetoshis to original pubkey
//vout.2: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid]
if ( (vini2val= Assetvini2val(eval,1,assetid,tx)) == 0 )
return eval->Invalid("no vini2val for fillbuy");
if ( (nValue= AssetIsvalidvin(eval,&origamount,&origout,origaddr,tx,1)) != 0 )
{
if ( origout != tx.vout[0].nValue )
return eval->Invalid("mismatched value for fillbuy");
if ( ExtractDestination(tx.vout[1].scriptPubKey,address) == 0 )
return eval->Invalid("no vout0 address for fillbuy");
if ( strcmp(origaddr,(char *)CBitcoinAddress(address).ToString().c_str()) != 0 )
return eval->Invalid("mismatched origaddr for fillbuy");
if ( vini2val < origamount )
return eval->Invalid("not enough paid for fillbuy");
if ( vini2val != tx.vout[1].nValue )
return eval->Invalid("mismatched vout.1 val for fillbuy");
} else return eval->Invalid("illegal CC vini.1 for fillbuy");
break;
case 'E': // fillexchange:
//vin.0: normal input
/*vin.1: unspendable.(vout.1 assetoshis from exchange)
vin.2: valid CC output that satisfies exchange
vout.0: vin.1 assetoshis to signer of vin.2
vout.1: vin.2 assetoshis to original pubkey*/
//vout.2: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin1] [assetid vin2]
if ( assetid2 == zero )
return eval->Invalid("illegal assetid2 for fillexchange");
if ( Getscriptaddress(destaddr,tx.vout[0].scriptPubKey) == 0 || strcmp(destaddr,Unspendableaddr) != 0 )
return eval->Invalid("mismatched vout0 unspendableaddr for fill");
else if ( IsAssetvout(ignore,ignorepubkey,tx,0,assetid) == 0 )
return eval->Invalid("not asset vout0 to unspendableaddr for fill");
else if ( funcid == 'S' && remaining_price < 10000 )
return eval->Invalid("dust vout0 to unspendableaddr for fill");
}
break;
}
return(true);
}
bool ProcessAssets(Eval* eval, std::vector<uint8_t> paramsNull, const CTransaction &tx, unsigned int nIn)
bool ProcessAssets(Eval* eval, std::vector<uint8_t> paramsNull,const CTransaction &ctx, unsigned int nIn)
{
static uint256 zero;
CTransaction createTx; uint256 assetid,assetid2,hashBlock; uint8_t funcid; int32_t i,n; uint64_t amount;
CTransaction createTx; uint256 assetid,assetid2,hashBlock; uint8_t funcid; int32_t i,n; uint64_t amount; std::vector<uint8_t> origpubkey;
CTransaction tx = *(CTransaction *)&ctx;
if ( paramsNull.size() != 0 ) // Don't expect params
return eval->Invalid("Cannot have params");
if ( (n= tx.vout.size()) == 0 )
return eval->Invalid("no-vouts");
if ( (funcid= DecodeOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,amount)) == 0 )
if ( (funcid= DecodeOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,amount,origpubkey)) == 0 )
return eval->Invalid("Invalid opreturn payload");
if ( eval->GetTxUnconfirmed(assetid,createTx,hashBlock) == 0 )
return eval->Invalid("cant find asset create txid");
if ( assetid2 != zero && eval->GetTxUnconfirmed(assetid2,createTx,hashBlock) == 0 )
return eval->Invalid("cant find asset2 create txid");
return(AssetValidate(eval,tx,n,funcid,assetid,assetid2,amount));
// We're unable to ensure that all the outputs have the correct
// CC Eval code because we only have the scriptPubKey which is a hash of the
// condition. The effect of this is that we cannot control the outputs, and therefore
// are able to burn or "lose" units or our colored token and they will be spent as the
// regular chain token, but we are able to control when units of the asset get
// created because we can always vet the inputs.
//
// Units of the colored token are always 1:1 with the token being input. No additional
// supply is created on the chain. Effectively, fees are paid in units of the colored token.
// This leaves something to be desired, because for example, if you wanted to create a
// single asset that could transfer ownership, usually you would create an asset with a
// supply of 1, ie can not be divided further, however in this case there would be nothing
// left to pay fees with. Implementing separate supply and other details are possible, by
// encoding further information in the OP_RETURNS.
// It's valid
//return true;
return(AssetValidate(eval,tx,n,funcid,assetid,assetid2,amount,origpubkey));
}