@ -1046,3 +1046,279 @@ int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_
return ( 0 ) ;
}
/*
komodo_checkPOW ( fast ) is called early in the process and should only refer to data immediately available . it is a filter to prevent bad blocks from going into the local DB
komodo_checkPOW ( slow ) is called right before connecting blocks so all prior blocks can be assumed to be there and all checks must pass
commission must be in coinbase . vout [ 1 ] and must be > = 10000 sats
PoS stake must be without txfee and in the last tx in the block at vout [ 0 ]
PoW mining on PoS chain must solve a harder diff that adjusts , but never less than KOMODO_POWMINMULT
*/
# define KOMODO_POWMINMULT 16
uint64_t komodo_commission ( const CBlock * pblock )
{
int32_t i , j , n = 0 , txn_count ; uint64_t commission , total = 0 ;
txn_count = pblock - > vtx . size ( ) ;
for ( i = 0 ; i < txn_count ; i + + )
{
n = pblock - = > vtx [ i ] . vout . size ( ) ;
for ( j = 0 ; j < n ; j + + )
{
//fprintf(stderr,"(%d %.8f).%d ",i,dstr(block.vtx[i].vout[j].nValue),j);
if ( i ! = 0 | | j ! = 1 )
total + = pblock - > vtx [ i ] . vout [ j ] . nValue ;
}
}
//fprintf(stderr,"txn.%d n.%d commission total %.8f -> %.8f\n",txn_count,n,dstr(total),dstr((total * ASSETCHAINS_COMMISSION) / COIN));
commission = ( ( total * ASSETCHAINS_COMMISSION ) / COIN ) ;
if ( commission < 10000 )
commission = 0 ;
return ( commission ) ;
}
uint32_t komodo_stake ( int32_t validateflag , arith_uint256 bnTarget , int32_t nHeight , uint256 txid , int32_t vout , uint32_t blocktime , uint32_t prevtime , char * destaddr )
{
CBlockIndex * pindex ; uint8_t hashbuf [ 128 ] ; char address [ 64 ] ; bits256 addrhash ; arith_uint256 hashval ; uint256 hash , pasthash ; int64_t diff = 0 ; int32_t segid , minage , i , iter = 0 ; uint32_t txtime , winner = 0 ; uint64_t value , coinage , supply = ASSETCHAINS_SUPPLY + nHeight * ASSETCHAINS_REWARD / SATOSHIDEN ;
txtime = komodo_txtime ( & value , txid , vout , address ) ;
if ( value = = 0 | | txtime = = 0 )
return ( 0 ) ;
if ( ( minage = nHeight * 3 ) > 6000 )
minage = 6000 ;
if ( blocktime > txtime + minage & & ( pindex = komodo_chainactive ( nHeight > 200 ? nHeight - 200 : 1 ) ) ! = 0 )
{
vcalc_sha256 ( 0 , ( uint8_t * ) & addrhash , ( uint8_t * ) address , ( int32_t ) strlen ( address ) ) ;
segid = ( ( nHeight + addrhash . uints [ 0 ] ) & 0x3f ) ;
pasthash = pindex - > GetBlockHash ( ) ;
memcpy ( hashbuf , & pasthash , sizeof ( pasthash ) ) ;
memcpy ( & hashbuf [ sizeof ( pasthash ) ] , & addrhash , sizeof ( addrhash ) ) ;
vcalc_sha256 ( 0 , ( uint8_t * ) & hash , hashbuf , ( int32_t ) sizeof ( uint256 ) * 2 ) ;
//fprintf(stderr,"(%s) vs. (%s) %s %.8f txtime.%u\n",address,destaddr,hash.ToString().c_str(),dstr(value),txtime);
for ( iter = 0 ; iter < 3600 ; iter + + )
{
diff = ( iter + blocktime - txtime - minage ) ;
if ( diff > 3600 * 24 )
break ;
coinage = ( value * diff ) * ( ( diff > > 16 ) + 1 ) ;
hashval = arith_uint256 ( supply * 64 ) * ( UintToArith256 ( hash ) / arith_uint256 ( coinage + 1 ) ) ;
if ( hashval < = bnTarget )
{
winner = 1 ;
if ( validateflag = = 0 )
{
blocktime + = iter ;
blocktime + = segid * 2 ;
}
break ;
}
if ( validateflag ! = 0 )
{
for ( i = 31 ; i > = 24 ; i - - )
fprintf ( stderr , " %02x " , ( ( uint8_t * ) & hashval ) [ i ] ) ;
fprintf ( stderr , " vs target " ) ;
for ( i = 31 ; i > = 24 ; i - - )
fprintf ( stderr , " %02x " , ( ( uint8_t * ) & bnTarget ) [ i ] ) ;
fprintf ( stderr , " segid.%d iter.%d winner.%d coinage.%llu %d ht.%d gap.%d %.8f diff.%d \n " , segid , iter , winner , ( long long ) coinage , ( int32_t ) ( blocktime - txtime ) , nHeight , ( int32_t ) ( blocktime - prevtime ) , dstr ( value ) , ( int32_t ) diff ) ;
break ;
}
}
//fprintf(stderr,"iterated until i.%d winner.%d\n",i,winner);
if ( 0 )
{
for ( i = 31 ; i > = 24 ; i - - )
fprintf ( stderr , " %02x " , ( ( uint8_t * ) & hashval ) [ i ] ) ;
fprintf ( stderr , " vs " ) ;
for ( i = 31 ; i > = 24 ; i - - )
fprintf ( stderr , " %02x " , ( ( uint8_t * ) & bnTarget ) [ i ] ) ;
fprintf ( stderr , " segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u %.8f diff.%d \n " , segid , iter , winner , ( long long ) coinage , ( int32_t ) ( blocktime - txtime ) , nHeight , blocktime , dstr ( value ) , ( int32_t ) diff ) ;
}
}
if ( nHeight < 2 )
return ( blocktime ) ;
return ( blocktime * winner ) ;
}
arith_uint256 komodo_PoWtarget ( int32_t * percPoSp , arith_uint256 target , int32_t height , int32_t goalperc )
{
CBlockIndex * pindex ; arith_uint256 bnTarget , hashval , sum , ave ; bool fNegative , fOverflow ; int32_t i , n , ht , percPoS , diff ;
* percPoSp = percPoS = 0 ;
sum = arith_uint256 ( 0 ) ;
ave = sum ;
for ( i = n = 0 ; i < 100 ; i + + )
{
ht = height - 100 + i ;
if ( ( pindex = komodo_chainactive ( ht ) ) ! = 0 )
{
bnTarget . SetCompact ( pindex - > nBits , & fNegative , & fOverflow ) ;
bnTarget = ( bnTarget / arith_uint256 ( KOMODO_POWMINMULT ) ) ;
hashval = UintToArith256 ( pindex - > GetBlockHash ( ) ) ;
if ( hashval < = bnTarget ) // PoW is never as easy as PoS/64, some PoS will be counted as PoW
{
sum + = hashval ;
n + + ;
} else percPoS + + ;
}
}
* percPoSp = percPoS ;
target = ( target / arith_uint256 ( KOMODO_POWMINMULT ) ) ;
if ( n > 0 )
{
ave = ( sum / arith_uint256 ( n ) ) ;
if ( ave > target )
ave = target ;
} else return ( target ) ;
if ( percPoS < goalperc ) // increase PoW diff -> lower bnTarget
{
bnTarget = ( ave * arith_uint256 ( goalperc ) ) / arith_uint256 ( percPoS + goalperc ) ;
if ( 0 )
{
for ( i = 31 ; i > = 24 ; i - - )
fprintf ( stderr , " %02x " , ( ( uint8_t * ) & ave ) [ i ] ) ;
fprintf ( stderr , " increase diff -> " ) ;
for ( i = 31 ; i > = 24 ; i - - )
fprintf ( stderr , " %02x " , ( ( uint8_t * ) & bnTarget ) [ i ] ) ;
fprintf ( stderr , " floor diff " ) ;
for ( i = 31 ; i > = 24 ; i - - )
fprintf ( stderr , " %02x " , ( ( uint8_t * ) & target ) [ i ] ) ;
fprintf ( stderr , " ht.%d percPoS.%d vs goal.%d -> diff %d \n " , height , percPoS , goalperc , goalperc - percPoS ) ;
}
}
else if ( percPoS > goalperc ) // decrease PoW diff -> raise bnTarget
{
bnTarget = ( ( ave * arith_uint256 ( goalperc ) ) + ( target * arith_uint256 ( percPoS ) ) ) / arith_uint256 ( percPoS + goalperc ) ;
if ( 0 )
{
for ( i = 31 ; i > = 24 ; i - - )
fprintf ( stderr , " %02x " , ( ( uint8_t * ) & ave ) [ i ] ) ;
fprintf ( stderr , " decrease diff -> " ) ;
for ( i = 31 ; i > = 24 ; i - - )
fprintf ( stderr , " %02x " , ( ( uint8_t * ) & bnTarget ) [ i ] ) ;
fprintf ( stderr , " floor diff " ) ;
for ( i = 31 ; i > = 24 ; i - - )
fprintf ( stderr , " %02x " , ( ( uint8_t * ) & target ) [ i ] ) ;
fprintf ( stderr , " ht.%d percPoS.%d vs goal.%d -> diff %d \n " , height , percPoS , goalperc , goalperc - percPoS ) ;
}
}
else bnTarget = ave ; // recent ave is perfect
return ( bnTarget ) ;
}
int32_t komodo_is_PoSblock ( int32_t slowflag , CBlock * pblock , arith_uint256 bnTarget )
{
CBlockIndex * previndex ; char voutaddr [ 64 ] , destaddr [ 64 ] ; uint256 txid ; uint32_t prevtime = 0 ; int32_t vout , txn_count , isPoS = 0 ;
txn_count = pblock - > vtx . size ( ) ;
if ( txn_count > 1 )
{
if ( prevtime = = 0 )
{
if ( ( previndex = mapBlockIndex [ pblock - > hashPrevBlock ] ) ! = 0 )
prevtime = ( uint32_t ) previndex - > nTime ;
}
txid = pblock - > vtx [ txn_count - 1 ] . vin [ 0 ] . prevout . hash ;
vout = pblock - > vtx [ txn_count - 1 ] . vin [ 0 ] . prevout . n ;
if ( prevtime ! = 0 )
{
eligible = komodo_stake ( 1 , bnTarget , height , txid , vout , pblock - > nTime , prevtime , ( char * ) " " ) ;
if ( eligible = = 0 | | eligible > pblock - > nTime )
{
fprintf ( stderr , " komodo_is_PoSblock PoS failure ht.%d eligible.%u vs blocktime.%u, lag.%d -> check to see if it is PoW block \n " , height , eligible , ( uint32_t ) block . nTime , ( int32_t ) ( eligible - block . nTime ) ) ;
} else isPoS = 1 ;
}
else if ( slowflag = = 0 ) // maybe previous block is not seen yet, do the best approx
{
txtime = komodo_txtime ( & value , txid , vout , destaddr ) ;
if ( ExtractDestination ( pblock - > vtx [ txn_count - 1 ] . vout [ 0 ] . scriptPubKey , voutaddress ) )
{
strcpy ( voutaddr , CBitcoinAddress ( address ) . ToString ( ) . c_str ( ) ) ;
if ( strcmp ( destaddr , voutaddr ) = = 0 & & pblock - > vtx [ txn_count - 1 ] . vout [ 0 ] . value = = value )
isPoS = 1 ; // close enough for a pre-filter
else fprintf ( stderr , " komodo_is_PoSblock ht.%d (%s) ! = ( % s ) or % .8f ! = % .8f \ n " ,height,destaddr,voutaddr,dstr(value),dstr(pblock->vtx[txn_count-1].vout[0].value)) ;
} else fprintf ( stderr , " komodo_is_PoSblock ht.%d couldnt extract voutaddress \n " , height ) ;
} else return ( - 1 ) ;
}
return ( isPoS ) ;
}
int32_t komodo_checkPOW ( int32_t slowflag , CBlock * pblock , int32_t height )
{
arith_uint256 bnTarget , bhash ; bool fNegative , fOverflow ; uint8_t * script , pubkey33 [ 33 ] , pubkeys [ 64 ] [ 33 ] ; int32_t i , PoSperc , is_PoSblock = 0 , n , failed = 0 , notaryid = - 1 ; int64_t checktoshis , value ;
if ( height = = 0 )
return ( 0 ) ;
if ( ! CheckEquihashSolution ( pblock , Params ( ) ) )
{
fprintf ( stderr , " komodo_checkPOW slowflag.%d ht.%d CheckEquihashSolution failed \n " , slowflag , height ) ;
return ( - 1 ) ;
}
bnTarget . SetCompact ( pblock - > nBits , & fNegative , & fOverflow ) ;
bhash = UintToArith256 ( pblock - > GetHash ( ) ) ;
komodo_block2pubkey33 ( pubkey33 , pblock ) ;
if ( slowflag ! = 0 )
{
if ( ! CheckProofOfWork ( height , pubkey33 , bhash , pblock - > nBits , Params ( ) . GetConsensus ( ) , pblock - > nTime ) )
return state . DoS ( 1 , error ( " CheckBlock: proof of work failed " ) , REJECT_INVALID , " high-hash " ) ;
}
if ( bhash > bnTarget )
{
failed = 1 ;
if ( ASSETCHAINS_SYMBOL [ 0 ] = = 0 ) // for the fast case
{
if ( ( n = komodo_notaries ( pubkeys , height , pblock - > nTime ) ) > 0 )
{
for ( i = 0 ; i < n ; i + + )
if ( memcmp ( pubkey33 , pubkeys [ i ] , 33 ) = = 0 )
{
notaryid = i ;
break ;
}
}
}
}
else if ( ASSETCHAINS_STAKED ! = 0 & & height > = 2 ) // must PoS or have at least 16x better PoW
{
if ( ( is_PoSblock = komodo_is_PoSblock ( slowflag , & block , bnTarget ) ) = = 0 )
{
if ( ASSETCHAINS_STAKED = = 100 | | height < = 100 ) // only PoS allowed!
return ( - 1 ) ;
else
{
if ( slowflag ! = 0 )
bnTarget = komodo_PoWtarget ( & PoSperc , bnTarget , height , ASSETCHAINS_STAKED ) ;
else bnTarget = ( bnTarget / arith_uint256 ( KOMODO_POWMINMULT ) ) ; // lower bound
if ( bhash > bnTarget )
{
for ( i = 31 ; i > = 16 ; i - - )
fprintf ( stderr , " %02x " , ( ( uint8_t * ) & hashval ) [ i ] ) ;
fprintf ( stderr , " > " ) ;
for ( i = 31 ; i > = 16 ; i - - )
fprintf ( stderr , " %02x " , ( ( uint8_t * ) & bnTarget ) [ i ] ) ;
fprintf ( stderr , " ht.%d PoW diff violation PoSperc.%d vs goalperc.%d \n " , height , PoSperc , ( int32_t ) ASSETCHAINS_STAKED ) ;
return ( - 1 ) ;
}
}
} else if ( isPoSblock < 0 )
return ( - 1 ) ;
}
if ( failed = = 0 & & ASSETCHAINS_OVERRIDE_PUBKEY33 [ 0 ] ! = 0 & & ASSETCHAINS_COMMISSION ! = 0 )
{
checktoshis = komodo_commission ( pblock ) ;
if ( checktoshis > 10000 & & pblock - > vtx [ 0 ] . vout . size ( ) ! = 2 )
return ( - 1 ) ;
else if ( checktoshis ! = 0 )
{
script = ( uint8_t * ) pblock - > vtx [ 0 ] . vout [ 1 ] . scriptPubKey . data ( ) ;
if ( script [ 0 ] ! = 33 | | script [ 34 ] ! = OP_CHECKSIG | | memcmp ( script + 1 , ASSETCHAINS_OVERRIDE_PUBKEY33 , 33 ) ! = 0 )
return ( - 1 ) ;
if ( pblock - > vtx [ 0 ] . vout [ 1 ] . nValue ! = checktoshis )
{
fprintf ( stderr , " checktoshis %.8f vs actual vout[1] %.8f \n " , dstr ( checktoshis ) , dstr ( pblock - > vtx [ 0 ] . vout [ 1 ] . nValue ) ) ;
return ( - 1 ) ;
}
}
}
//fprintf(stderr,"komodo_checkPOW slowflag.%d ht.%d notaryid.%d failed.%d\n",slowflag,height,notaryid,failed);
if ( failed ! = 0 & & notaryid < 0 )
return ( - 1 ) ;
else return ( 0 ) ;
}