Skip to content

Commit

Permalink
add basic CALLBACK support
Browse files Browse the repository at this point in the history
todo: decimal precision still needs some work
  • Loading branch information
jdogresorg committed May 20, 2024
1 parent 2e8a361 commit 553beee
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 5 deletions.
3 changes: 2 additions & 1 deletion docs/actions/CALLBACK.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ This command performs a callback on a `token`.
| --------- | ------ | ----------------------------- |
| `VERSION` | String | Broadcast Format Version |
| `TICK` | String | 1 to 250 characters in length |
| `MEMO` | String | An optional memo to include |

## Formats

### Version `0`
- `VERSION|TICK`
- `VERSION|TICK|MEMO`

## Examples
```
Expand Down
197 changes: 194 additions & 3 deletions indexer/includes/actions/callback.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,204 @@
* PARAMS:
* - VERSION - Broadcast Format Version
* - TICK - 1 to 250 characters in length
* - MEMO - An optional memo to include
*
* FORMATS:
* 0 = VERSION|TICK
* 0 = VERSION|TICK|MEMO
********************************************************************/
function btnsCallback($params=null, $data=null, $error=null){
global $mysqli, $reparse;
// Coming soon
global $mysqli, $reparse, $addresses, $tickers;

// Define list of known FORMATS
$formats = array(
0 => 'VERSION|TICK|MEMO'
);

/*****************************************************************
* DEBUGGING - Force params
****************************************************************/
// $str = "0|JDOG|1|";
// $params = explode('|',$str);

// Validate that broadcast format is known
$format = getFormatVersion($params[0]);
if(!$error && ($format===NULL || !in_array($format,array_keys($formats))))
$error = 'invalid: VERSION (unknown)';

// Parse PARAMS using given VERSION format and update BTNS transaction data object
if(!$error)
$data = setActionParams($data, $params, $formats[$format]);

// Get information on BTNS token
$btInfo = getTokenInfo($data->TICK, null, $data->BLOCK_INDEX, $data->TX_INDEX);

// Clone the raw data for storage in callbacks table
$callback = clone($data);

// Create the fees object
$fees = createFeesObject($data);

// Define placeholders for holders and balances
$holder = [];
$balances = [];

// Get list of TICK holders and SOURCE address balances
if(!$error){
$holders = getHolders($data->TICK, $data->BLOCK_INDEX, $data->TX_INDEX);
$balances = getAddressBalances($data->SOURCE, null, $data->BLOCK_INDEX, $data->TX_INDEX);
}

// Validate TICK exists
if(!$error && !$btInfo)
$error = 'invalid: TICK (unknown)';

// Verify only token OWNER can do CALLBACK
if(!$error && $data->SOURCE!=$btInfo->OWNER)
$error = 'invalid: SOURCE (not authorized)';

// Get information on CALLBACK_TICK token
if(!$error && isset($btInfo->CALLBACK_TICK))
$btInfo2 = getTokenInfo($btInfo->CALLBACK_TICK, null, $data->BLOCK_INDEX, $data->TX_INDEX);

// Validate CALLBACK_TICK exists
if(!$error && !$btInfo2)
$error = 'invalid: CALLBACK_TICK (unknown)';

// Set divisible flags
$divisible = ($btInfo->DECIMALS==0) ? 0 : 1;
$divisible2 = ($btInfo2->DECIMALS==0) ? 0 : 1;

// Populate callback object with callback data
if($btInfo){
$callback->CALLBACK_TICK = $btInfo->CALLBACK_TICK;
$callback->CALLBACK_AMOUNT = $btInfo->CALLBACK_AMOUNT;
}

/*****************************************************************
* ACTION Validations
****************************************************************/

// Verify CALLBACK is allowed
if(!$error && isset($btInfo->LOCK_CALLBACK) && $btInfo->LOCK_CALLBACK==1)
$error = "invalid: LOCK_CALLBACK";

/*****************************************************************
* FORMAT Validations
****************************************************************/

// Verify CALLBACK_BLOCK format
if(!$error && $btInfo && isset($btInfo->CALLBACK_BLOCK) && $btInfo->CALLBACK_BLOCK!=intval($btInfo->CALLBACK_BLOCK))
$error = 'invalid: CALLBACK_BLOCK (format)';

// Verify CALLBACK_AMOUNT format
if(!$error && isset($btInfo->CALLBACK_AMOUNT) && !isValidAmountFormat($divisible2, $btInfo->CALLBACK_AMOUNT))
$error = "invalid: CALLBACK_AMOUNT (format)";

/*****************************************************************
* General Validations
****************************************************************/

// Verify CALLBACK_BLOCK is less than or equal to current block index
if(!$error && $btInfo && isset($btInfo->CALLBACK_BLOCK) && $btInfo->CALLBACK_BLOCK > $data->BLOCK_INDEX)
$error = 'invalid: CALLBACK_BLOCK (block index)';

// Loop through holders and determine CALLBACK_TOTAL
$total = (object)[];
$total->TICK = 0;
$total->CALLBACK_TICK = 0;
foreach($holders as $address => $amount){
// Skip including SOURCE address in total calculations
if($address==$data->SOURCE)
continue;
$callback_amount = bcmul($amount, $btInfo->CALLBACK_AMOUNT, $btInfo2->DECIMALS);
$total->TICK = bcadd($amount, $total->TICK, $btInfo->DECIMALS);
$total->CALLBACK_TICK = bcadd($callback_amount, $total->CALLBACK_TICK, $btInfo2->DECIMALS);
}

// Verify SOURCE has enough balances to cover total callback amount
if(!$error && !hasBalance($balances, $btInfo->CALLBACK_TICK, $total->CALLBACK_TICK))
$error = 'invalid: insufficient funds (CALLBACK_TICK)';

// Adjust balances to reduce by callback total
if(!$error)
$balances = debitBalances($balances, $btInfo->CALLBACK_TICK, $data->CALLBACK_TICK);

// Calculate total number of database hits for this CALLBACK
$db_hits = count($holders) * 3; // 1 debits, 1 credits, 1 balances
$db_hits += 4; // 1 debits, 1 credits, 1 balances, 1 callback

// Determine total transaction FEE based on database hits
$fees->AMOUNT = getTransactionFee($db_hits, $fees->TICK);

// Verify SOURCE has enough balances to cover FEE AMOUNT
if(!$error && !hasBalance($balances, $fees->TICK, $fees->AMOUNT))
$error = 'invalid: insufficient funds (FEE)';

// Adjust balances to reduce by FEE amount
if(!$error)
$balances = debitBalances($balances, $fees->TICK, $fees->AMOUNT);

// Determine final status
$data->STATUS = $callback->STATUS = $status = ($error) ? $error : 'valid';

// Print status message
print "\n\t CALLBACK : {$data->TICK} : {$data->STATUS}";

// Create record in callbacks table
createCallback($callback);

// If this was a valid transaction, then add records to the credits and debits array
if($status=='valid'){

// TODO: update to support dispensers (close dispenser)

// Store the SOURCE, TICK, CALLBACK_TICK, and fee TICK in addresses list
addAddressTicker($data->SOURCE, [$data->TICK, $btInfo->CALLBACK_TICK, $fees->TICK]);

// Debit CALLBACK_TOTAL from SOURCE address
createDebit('CALLBACK', $data->BLOCK_INDEX, $data->TX_HASH, $btInfo->CALLBACK_TICK, $total->CALLBACK_TICK, $data->SOURCE);

// Credit all TICK SUPPLY to SOURCE address
createCredit('CALLBACK', $data->BLOCK_INDEX, $data->TX_HASH, $btInfo->TICK, $total->TICK, $data->SOURCE);

// Handle any transaction FEE according the users's ADDRESS preferences
processTransactionFees('CALLBACK', $fees);

// Loop through TICK holders
// TODO: Decimal precision needs a bit more work...
foreach($holders as $address => $amount){
$callback_amount = bcmul($amount, $btInfo->CALLBACK_AMOUNT, $btInfo2->DECIMALS);

// Skip including SOURCE address in credits/debits (already )
if($address==$data->SOURCE)
continue;

// Debit TICK from holders address
createDebit('CALLBACK', $data->BLOCK_INDEX, $data->TX_HASH, $data->TICK, $amount, $address);

// Credit CALLBACK_TICK to holders address except SOURCE
if($callback_amount>0)
createCredit('CALLBACK', $data->BLOCK_INDEX, $data->TX_HASH, $btInfo->CALLBACK_TICK, $callback_amount, $address);

// Store the recipient ADDRESS, TICK, and CALLBACK_TICK in addresses list
addAddressTicker($address, [$data->TICK, $btInfo->CALLBACK_TICK]);
}

}

// If this is a reparse, bail out before updating balances
if($reparse)
return;

// Update address balances
updateBalances(array_keys($addresses));

// Update supply for GAS (no other supply should change)
updateTokens($fees->TICK);

// TODO: Remove this when decimal precision is perfect better
updateTokens($tickers);

}

?>
54 changes: 54 additions & 0 deletions indexer/includes/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,9 @@ function getTokenSupply( $tick=null, $tick_id=null, $block_index=null, $tx_index
byeLog('Error while trying to get list of debits');
}
$supply = bcsub($credits, $debits, $decimals);
// print "credits={$credits}\n";
// print "debits={$debits}\n";
// print "supply={$supply}\n";
return $supply;
}

Expand Down Expand Up @@ -1858,6 +1861,10 @@ function sanityCheck( $block=null ){
$supplyA = $supply[$tick]; // Supply from tokens table
$supplyB = getTokenSupplyBalance($tick); // Supply from balances table
$supplyC = getTokenSupply($tick); // Supply from credits/debits tables
// print "\nTick={$tick} tick_id={$tick_id}\n";
// print "token supply = {$supplyA}\n";
// print "balances supply = {$supplyB}\n";
// print "credit/debit supply = {$supplyC}\n";
if($supplyA!=$supplyB)
byeLog("SanityError: balances table supply does not match token supply : {$tick} ({$supplyB} != {$supplyA})");
if($supplyA!=$supplyC)
Expand Down Expand Up @@ -2660,4 +2667,51 @@ function cleanupHolders($holders){
return $holders;
}

// Create record in `callbacks` table
function createCallback( $data=null ){
global $mysqli;
$tick_id = createTicker($data->TICK);
$callback_tick_id = createTicker($data->CALLBACK_TICK);
$source_id = createAddress($data->SOURCE);
$tx_hash_id = createTransaction($data->TX_HASH);
$memo_id = createMemo($data->MEMO);
$status_id = createStatus($data->STATUS);
$tx_index = $mysqli->real_escape_string($data->TX_INDEX);
$block_index = $mysqli->real_escape_string($data->BLOCK_INDEX);
$callback_amount = $mysqli->real_escape_string($data->CALLBACK_AMOUNT);
// Check if record already exists
$sql = "SELECT
tx_index
FROM
callbacks
WHERE
source_id='{$source_id}' AND
tx_hash_id='{$tx_hash_id}'";
$results = $mysqli->query($sql);
if($results){
if($results->num_rows){
// UPDATE record
$sql = "UPDATE
callbacks
SET
tx_index='{$tx_index}',
block_index='{$block_index}',
tick_id='{$tick_id}',
memo_id='{$memo_id}',
status_id='{$status_id}'
WHERE
source_id='{$source_id}' AND
tx_hash_id='{$tx_hash_id}'";
} else {
// INSERT record
$sql = "INSERT INTO callbacks (tx_index, source_id, tick_id, callback_tick_id, callback_amount, memo_id, tx_hash_id, block_index, status_id) values ('{$tx_index}', '{$source_id}', '{$tick_id}', '{$callback_tick_id}', '{$callback_amount}', '{$memo_id}', '{$tx_hash_id}', '{$block_index}', '{$status_id}')";
}
$results = $mysqli->query($sql);
if(!$results)
byeLog('Error while trying to create / update a record in the callbacks table');
} else {
byeLog('Error while trying to lookup record in callbacks table');
}
}

?>
2 changes: 1 addition & 1 deletion indexer/includes/protocol_changes.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
'AIRDROP' => array(0, 10, 0, 789742, 2581842),
'BATCH' => array(0, 10, 0, 789742, 2581531),
'BET' => array(0, 10, 0, 9999999, 999999999),
'CALLBACK' => array(0, 10, 0, 9999999, 999999999),
'CALLBACK' => array(0, 10, 0, 9999999, 2473585),
'DESTROY' => array(0, 10, 0, 789742, 2473585),
'DISPENSER' => array(0, 10, 0, 9999999, 999999999),
'DIVIDEND' => array(0, 10, 0, 789742, 2473585),
Expand Down
22 changes: 22 additions & 0 deletions indexer/sql/callbacks.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
DROP TABLE IF EXISTS callbacks;
CREATE TABLE callbacks (
tx_index INTEGER UNSIGNED, -- Unique transaction index
tx_hash_id INTEGER UNSIGNED, -- id of record in index_transactions
block_index INTEGER UNSIGNED, -- block index of CALLBACKS transaction
tick_id INTEGER UNSIGNED, -- id of record in index_tickers
callback_tick_id INTEGER UNSIGNED, -- id of record in index_tickers
callback_amount VARCHAR(250), -- Amount of token per unit
source_id INTEGER UNSIGNED, -- id of record in index_addresses table
memo_id INTEGER UNSIGNED, -- id of record in index_memos table
status_id INTEGER UNSIGNED -- id of record in index_statuses table
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE INDEX tx_index ON callbacks (tx_index);
CREATE INDEX source_id ON callbacks (source_id);
CREATE INDEX tick_id ON callbacks (tick_id);
CREATE INDEX callback_tick_id ON callbacks (callback_tick_id);
CREATE INDEX tx_hash_id ON callbacks (tx_hash_id);
CREATE INDEX block_index ON callbacks (block_index);
CREATE INDEX memo_id ON callbacks (memo_id);
CREATE INDEX status_id ON callbacks (status_id);

0 comments on commit 553beee

Please sign in to comment.