Skip to content

Commit

Permalink
rollback support / cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
jdogresorg committed Aug 11, 2023
1 parent 7072ff6 commit 88a7112
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 65 deletions.
3 changes: 3 additions & 0 deletions indexer/includes/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
// Tracks Execution Time
require_once('profiler.php');

// Rollback code
require_once('rollback.php');

// Protocol Changes / Activation blocks
require_once('protocol_changes.php');

Expand Down
123 changes: 99 additions & 24 deletions indexer/includes/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,37 @@ function createListItem($data=null, $item=null){
}


// Delete records in lists, list_items, and list_edits tables
function deleteLists($list=null, $rollback=null){
global $mysqli;
$lists = array();
$type = gettype($list);
if($type==='array')
$lists = $list;
if($type==='string'||is_numeric($list))
array_push($lists, $list);
// Loop through lists and convert any transaction hashes to database ids
foreach($lists as $idx => $list){
$type = getType($list);
if($type=="string" && !is_numeric($list))
$lists[$idx] = createTransaction($list);
}
$tables = ['list_items', 'list_edits'];
foreach($lists as $list_id){
// Delete item from lists table
$results = $mysqli->query("DELETE FROM lists WHERE tx_hash_id='{$list_id}'");
if(!$results)
byeLog('Error while trying to delete records from the lists table');
// Deletes item from list_items and list_edits tables
foreach($tables as $table){
$results = $mysqli->query("DELETE FROM {$table} WHERE list_id='{$list_id}'");
if(!$results)
byeLog('Error while trying to delete records from the ' . $table . ' table');
}
}
}


// Handle getting token information for a given tick
function getTokenInfo($tick=null){
global $mysqli;
Expand Down Expand Up @@ -994,7 +1025,7 @@ function getAddressDebits($address=null){
return $data;
}

// Handle getting address balances for a given address
// Get address balances using credits/debits table data
function getAddressBalances($address=null){
global $mysqli;
$type = gettype($address);
Expand All @@ -1018,55 +1049,99 @@ function getAddressBalances($address=null){
} catch(Exception $e){
$balance = number_format(0,$decimal,'.','');
}
if(is_numeric($balance) && $balance>0)
// Pass forward any numeric values (including 0 balance)
if(is_numeric($balance))
$balances[$tick_id] = $balance;
}
return $balances;
}

// Create/Update records in the 'balances' table
function updateAddressBalance( $address=null){
// Get address balances using balances table data
function getAddressTableBalances($address=null){
global $mysqli;
$type = gettype($address);
if($type==='integer' || is_numeric($address))
$address_id = $address;
if($type==='string' && !is_numeric($address))
$address_id = createAddress($address);
// Assoc array to store tick/balance
$balances = array();
$results = $mysqli->query("SELECT tick_id, amount FROM balances WHERE address_id='{$address_id}'");
if($results){
if($results->num_rows){
while($row = $results->fetch_assoc()){
$row = (object) $row;
$balances[$row->tick_id] = $row->amount;
}
}
} else {
byeLog('Error while trying to lookup balances record for address=' . $address);
}
return $balances;
}


// Create/Update/Delete records in the 'balances' table
function updateAddressBalance( $address=null, $rollback=false){
global $mysqli;
// print "updateAddressBalance address={$address}\n";
$type = gettype($address);
if($type==='integer' || is_numeric($address))
$address_id = $address;
if($type==='string' && !is_numeric($address))
$address_id = createAddress($address);
// Get list of address balances based on credits/debits tables
$balances = getAddressBalances($address_id);
if(count($balances)){
foreach($balances as $tick_id => $balance){
// print "processing balance tick={$tick_id} balance={$balance}\n";
// print "processing balance address_id={$address_id} tick={$tick_id} balance={$balance}\n";
$whereSql = "address_id='{$address_id}' AND tick_id='{$tick_id}'";
// Check if we already have a record for this address/tick_id
$sql = "SELECT id FROM balances WHERE address_id='{$address_id}' AND tick_id='{$tick_id}' LIMIT 1";
$sql = "SELECT id FROM balances WHERE {$whereSql} LIMIT 1";
$results = $mysqli->query($sql);
if($results){
$update = ($results->num_rows) ? true : false;
if($update){
$sql = "UPDATE balances SET amount='{$balance}' WHERE address_id='{$address_id}' AND tick_id='{$tick_id}'";
} else {
$action = ($results->num_rows) ? 'update' : 'insert';
if($balance==0)
$action = 'delete';
// print "action={$action}\n";
if($action=='delete'){
$sql = "DELETE FROM balances WHERE {$whereSql}";
} else if($action=='update'){
$sql = "UPDATE balances SET amount='{$balance}' WHERE {$whereSql}";
} else if($action=='insert'){
$sql = "INSERT INTO balances (tick_id, address_id, amount) values ('{$tick_id}','{$address_id}','{$balance}')";
}
// Create/Update balances records
if($update||(!$update && $balance)){
$results = $mysqli->query($sql);
if(!$results){
$action = ($update) ? 'update' : 'created';
byeLog('Error while trying to ' . $action . ' balance record for address=' . $address . ' tick_id=' . $tick_id);
}
}
$results = $mysqli->query($sql);
if(!$results)
byeLog('Error while trying to ' . $action . ' balance record for address=' . $address . ' tick_id=' . $tick_id);
} else {
byeLog('Error while trying to lookup balances record for address=' . $address . ' tick_id=' . $tick_id);
}
}
}
// If this is a rollback, then handle detecting records in balances table which should not exist and delete them
if($rollback){
// Get list of address balances based on balances table
$old_balances = getAddressTableBalances($address_id);
foreach($old_balances as $tick_id => $balance){
$balance = $balances[$tick_id];
if(!isset($balance) || $balance==0){
$results = $mysqli->query("DELETE FROM balances WHERE address_id='{$address_id}' AND tick_id='{$tick_id}'");
if(!$results)
byeLog('Error while trying to delete balance record for address=' . $address . ' tick_id=' . $tick_id);
}
}
}
}



// Handle updating address balances (credits-debits=balance)
// @param {address} boolean Full update
// @param {address} string Address string
// @param {address} array Array of address strings
function updateBalances( $address=null ){
// @param {address} boolean Full update
// @param {address} string Address string
// @param {address} array Array of address strings
// @param {rollback} boolean Rollback
function updateBalances( $address=null, $rollback=false ){
global $mysqli;
$addrs = [];
$type = gettype($address);
Expand All @@ -1089,15 +1164,15 @@ function updateBalances( $address=null ){
}
// Loop through addresses and update balance list
foreach($addrs as $address)
updateAddressBalance($address);
updateAddressBalance($address, $rollback);
}


// Handle updating token information (supply, price, etc)
// @param {tickers} boolean Full update
// @param {tickers} string Ticker
// @param {tickers} array Array of Tickers
function updateTokens( $tickers=null){
function updateTokens( $tickers=null, $rollback=true){
global $mysqli;
$tokens = [];
$type = gettype($tickers);
Expand Down
81 changes: 81 additions & 0 deletions indexer/includes/rollback.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php
/*********************************************************************
* rollback.php - Handles rolling back database updates safely
********************************************************************/
function btnsRollback($block_index=null){
global $mysqli;

$block_index = (int) $block_index;

$tables = [
'blocks',
'credits',
'debits',
'issues',
'lists',
'mints',
'sends',
'tokens',
'transactions'
];

// Arrays to track address/tick/transaction ids
$addresses = array();
$tickers = array();
$transactions = array();

// Loop through all database tables
foreach($tables as $table){

// Get list of any addresses or tickers associated with the rollback blocks
if(in_array($table, array('credits','debits'))){
$results = $mysqli->query("SELECT address_id, tick_id FROM {$table} WHERE block_index>{$block_index}");
if($results){
if($results->num_rows){
while($row = $results->fetch_assoc()){
$row = (object) $row;
if(!in_array($row->address_id, $addresses))
array_push($addresses, $row->address_id);
if(!in_array($row->tick_id, $tickers))
array_push($tickers, $row->tick_id);
}
}
} else {
byeLog('Error while trying to lookup rollback data in the ' . $table);
}
}

// Get list of transactions associated with the rollback blocks
if($table=='transactions'){
$results = $mysqli->query("SELECT tx_hash_id FROM transactions WHERE block_index>{$block_index}");
if($results){
if($results->num_rows){
while($row = $results->fetch_assoc()){
$row = (object) $row;
if(!in_array($row->tx_hash_id, $transactions))
array_push($transactions, $row->tx_hash_id);
}
}
}
}

// Delete data from rollback blocks
$results = $mysqli->query("DELETE FROM {$table} m WHERE block_index>{$block_index}");
if(!$results)
byeLog("Error while trying to rollback {$table} table to block {$block_index}");
}

// Update address balances to get back to sane balances based on credits/debits
updateBalances($addresses, true);

// Update token information
updateTokens($tickers, true);

// Delete items from list_{items,edits} tables
deleteLists($transactions, true);

// Notify user rollback is complete
byeLog("Rollback to block {$block_index} complete.");
}

?>
52 changes: 11 additions & 41 deletions indexer/indexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,42 +46,21 @@
// Load config (only after $network is defined)
require_once('includes/config.php');

// Print indexer version number so it shows up in debug logs
print "BTNS Indexer v" . VERSION_STRING . "\n";

// Set database name from global var CP_DATA
$dbase = CP_DATA;

// Initialize the database connection
initDB();

// Create a lock file, and bail if we detect an instance is already running
// createLockFile();

// Print indexer version number so it shows up in debug logs
print "BTNS Indexer v" . VERSION_STRING . "\n";
createLockFile();

// Handle rollbacks
if($rollback){
$block_index = $mysqli->real_escape_string($rollback);
$tables = [
'blocks',
'credits',
'debits',
'issues',
'mints',
'sends',
'tokens',
'transactions'
];
foreach($tables as $table){
$results = $mysqli->query("DELETE FROM {$table} WHERE block_index>{$block_index}");
if(!$results)
byeLog("Error while trying to rollback {$table} table to block {$block_index}");
}
// Add code here update balances table using credits/debits to get back to sane balances after rollback
// ... coming soon
// Add code here to handle deleting items from list_{items,edits} tables using
// ... coming soon
byeLog("Rollback to block {$block_index} complete.");
}
if($rollback)
btnsRollback($rollback);

// If no block given, load last block from state file, or use first block with BTNX tx
if(!$block){
Expand All @@ -91,11 +70,10 @@
}

// Get the current block index from status info
$sql = "SELECT block_index FROM {$dbase}.blocks ORDER BY block_index DESC limit 1";
$results = $mysqli->query($sql);
$results = $mysqli->query("SELECT block_index FROM {$dbase}.blocks ORDER BY block_index DESC limit 1");
if($results){
$row = $results->fetch_assoc();
$current = $row['block_index'];
$row = (object) $results->fetch_assoc();
$current = $row->block_index;
} else {
byeLog('Error while trying to lookup current block');
}
Expand All @@ -104,10 +82,8 @@
// Prevents issue where tokens might be missed because we are still in middle of parsing in a block
$service = 'counterparty'; // counterparty / dogeparty
$lockfile = '/var/tmp/' . $service . '2mysql-' . $network . '.lock';
if(file_exists($lockfile)){
removeLockFile();
bye("found {$service} parsing a block... exiting");
}
if(file_exists($lockfile))
byeLog("found {$service} parsing a block... exiting");

// Loop through the blocks until we are current
while($block <= $current){
Expand Down Expand Up @@ -189,12 +165,6 @@
// Handle processing the specific BTNS ACTION commands
btnsAction($action, $params, $data, $error);
}

// Handle updating balances for any addresses used in this block (probably unneccessary since we update balances as we parse)
// updateBalances($addresses);

// Handle updating token data (amount minted, etc)
// updateTokens($tickers);
}
} else {
byeLog("Error while trying to lookup BTNS broadcasts");
Expand Down

0 comments on commit 88a7112

Please sign in to comment.