Skip to content

Commit

Permalink
add DESTROY support
Browse files Browse the repository at this point in the history
  • Loading branch information
jdogresorg committed Jan 30, 2024
1 parent 5257899 commit ccfaadc
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 7 deletions.
23 changes: 20 additions & 3 deletions docs/actions/DESTROY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,19 @@ This command permanently destroys `token` supply
| `VERSION` | String | Broadcast Format Version |
| `TICK` | String | 1 to 250 characters in length |
| `AMOUNT` | String | Amount of `tokens` to destroy |
| `MEMO` | String | An optional memo to include |

## Formats

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

### Version `1`
- `VERSION|TICK|AMOUNT|TICK|AMOUNT|MEMO`

### Version `2`
- `VERSION|TICK|AMOUNT|MEMO|TICK|AMOUNT|MEMO`


## Examples
```
Expand All @@ -20,11 +28,20 @@ This example destroys 1 BRRR token from the broadcasting address
```

```
bt:DESTROY|0|BRRR|1|GAS|10
bt:DESTROY|1|BRRR|1|GAS|10
This example destroys 1 BRRR token and 10 GAS tokens from the broadcasting address
```

```
bt:DESTROY|2|BRRR|1|foo|GAS|10|bar
This example destroys 1 BRRR token with the memo `foo`, and 10 GAS tokens with the memo `bar` from the broadcasting address
```

## Rules
- Any destroyed `token` supply should be debited from broadcasting address balances

## Notes
## Notes
- Format version `0` allows for a single destroy
- Format version `1` allows for repeating `TICK` and `AMOUNT` params to enable multiple destroys
- Format version `2` allows for repeating `TICK`, `AMOUNT`, and `MEMO` params to enable multiple destroys
- Format version `0` and `1` allow for a single optional `MEMO` field to be included as the last PARAM
165 changes: 162 additions & 3 deletions indexer/includes/actions/destroy.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,170 @@
* - VERSION - Broadcast Format Version
* - TICK - 1 to 250 characters in length
* - AMOUNT - Amount of tokens to destroy
* - MEMO - An optional memo to include
*
* FORMATS:
* 0 =VERSION|TICK|AMOUNT
* - 0 = Single Destroy
* - 1 = Multi-Destroy (Brief)
* - 2 = Multi-Destroy (Full)
*
********************************************************************/
function btnsDestroy($params=null, $data=null, $error=null){
global $mysqli;
// Coming soon
global $mysqli, $tickers, $addresses;

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

/*****************************************************************
* DEBUGGING - Force params
****************************************************************/
// $str = '0|BRRR|1|foo';
// $str = '1|BRRR|1|GAS|10|bar';
// $str = '2|BRRR|1|foo|GAS|10|bar';
// $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)';

// Array of destroys [TICK, AMOUNT, MEMO]
$destroys = array();

// Extract memo
$memo = NULL;
$last = count($params) - 1;
foreach($params as $idx => $param)
if($idx==$last && (($format==0 && $idx==3) || ($format==1 && $idx%2==1)))
$memo = $param;

// Build out array of destroys
$lastIdx = count($params) - 1;
foreach($params as $idx => $param){

// Single Destroy
if($format==0 && $idx==0)
array_push($destroys,[$params[1], $params[2], $memo]);

// Multi-Destroy (Brief)
if($format==1 && $idx>1 && $idx%2==1)
array_push($destroys,[$params[1], $params[$idx-1], $memo]);

// Multi-Destroy (Full)
if($format==2 && $idx>0 && $idx%3==1 && $idx < $lastIdx)
array_push($destroys,[$params[$idx], $params[$idx+1], $params[$idx+2]]);
}

// Get token data for every TICK (reduces duplicated sql queries)
$ticks = [];
foreach($destroys as $destroy){
$tick = $destroy[0];
if(!$ticks[$tick])
$ticks[$tick] = getTokenInfo($tick);
}

// Get source address balances
$balances = getAddressBalances($data->SOURCE);

// Store original error value
$origError = $error;

// Add SOURCE address to the addresses array
$addresses[$data->SOURCE] = 1;

// Array of debits
$debits = [];

// Loop through destroys and process each
foreach($destroys as $info){

// Reset $error to the original value
$error = $origError;

// Copy base BTNS Transacation data object
$destroy = $data;

// Update BTNS transaction data object with destroy values
$destroy->TICK = $info[0];
$destroy->AMOUNT = $info[1];
$destroy->MEMO = $info[2];

// Get information on BTNS token
$btInfo = $ticks[$destroy->TICK];

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

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

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

// Verify AMOUNT format
if(!$error && isset($destroy->AMOUNT) && !isValidAmountFormat($divisible, $destroy->AMOUNT))
$error = "invalid: AMOUNT (format)";

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

// Verify no pipe in MEMO (BTNS uses pipe as field delimiter)
if(!$error && strpos($destroy->MEMO,'|')!==false)
$error = 'invalid: MEMO (pipe)';

// Verify no semicolon in MEMO (BTNS uses semicolon as action delimiter)
if(!$error && strpos($destroy->MEMO,';')!==false)
$error = 'invalid: MEMO (semicolon)';

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

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

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

// Print status message
print "\n\t DESTROY : {$destroy->TICK} : {$destroy->AMOUNT} : {$destroy->MEMO} : {$destroy->STATUS}";

// Create record in transfers table
createDestroy($destroy);

// If this was a valid transaction, then create debit record
if($status=='valid'){

// Add ticker to tickers array
$tickers[$destroy->TICK] = 1;

// Add ticker and amount to debits array
array_push($debits, array($destroy->TICK, $destroy->AMOUNT));
}
}

// Consolidate the debit records to write as few records as possible
$debits = consolidateCreditDebitRecords('debits', $debits);

// Create records in debits table
foreach($debits as $debit){
[$tick, $amount] = $debit;
createDebit('DESTROY', $data->BLOCK_INDEX, $data->TX_HASH, $tick, $amount, $data->SOURCE);
}

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

// Update supply for tokens
updateTokens(array_keys($ticks));
}

?>
50 changes: 50 additions & 0 deletions indexer/includes/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,56 @@ function createSend( $data=null ){
}
}

// Create record in `destroys` table
function createDestroy( $data=null ){
global $mysqli;
$tick_id = createTicker($data->TICK);
$source_id = createAddress($data->SOURCE);
$tx_hash_id = createTransaction($data->TX_HASH);
$status_id = createStatus($data->STATUS);
$memo_id = createMemo($data->MEMO);
$tx_index = $mysqli->real_escape_string($data->TX_INDEX);
$amount = $mysqli->real_escape_string($data->AMOUNT);
$block_index = $mysqli->real_escape_string($data->BLOCK_INDEX);
$amount = $mysqli->real_escape_string($data->AMOUNT);
// Check if record already exists
$sql = "SELECT
tx_index
FROM
destroys
WHERE
tick_id='{$tick_id}' AND
source_id='{$source_id}' AND
amount='{$amount}' AND
tx_hash_id='{$tx_hash_id}'";
$results = $mysqli->query($sql);
if($results){
if($results->num_rows){
// UPDATE record
$sql = "UPDATE
destroys
SET
tx_index='{$tx_index}',
block_index='{$block_index}',
memo_id='{$memo_id}',
status_id='{$status_id}'
WHERE
tick_id='{$tick_id}' AND
source_id='{$source_id}' AND
amount='{$amount}' AND
tx_hash_id='{$tx_hash_id}'";
} else {
// INSERT record
$sql = "INSERT INTO destroys (tx_index, tick_id, source_id, amount, memo_id, tx_hash_id, block_index, status_id) values ('{$tx_index}','{$tick_id}', '{$source_id}', '{$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 destroys table');
} else {
byeLog('Error while trying to lookup record in destroys table');
}
}

// Create / Update record in `tokens` table
function createToken( $data=null ){
global $mysqli;
Expand Down
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 @@
'BATCH' => array(0, 10, 0, 9999999, 999999999),
'BET' => array(0, 10, 0, 9999999, 999999999),
'CALLBACK' => array(0, 10, 0, 9999999, 999999999),
'DESTROY' => array(0, 10, 0, 9999999, 999999999),
'DESTROY' => array(0, 10, 0, 789742, 2473585),
'DISPENSER' => array(0, 10, 0, 9999999, 999999999),
'DIVIDEND' => array(0, 10, 0, 9999999, 999999999),
'ISSUE' => array(0, 10, 0, 789742, 2473585),
Expand Down
1 change: 1 addition & 0 deletions indexer/includes/rollback.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function btnsRollback($block_index=null){
'blocks',
'credits',
'debits',
'destroys',
'issues',
'lists',
'mints',
Expand Down
20 changes: 20 additions & 0 deletions indexer/sql/destroys.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
DROP TABLE IF EXISTS destroys;
CREATE TABLE destroys (
tx_index INTEGER UNSIGNED, -- Unique transaction index
tick_id INTEGER UNSIGNED, -- id of record in index_ticks table
amount VARCHAR(250), -- Amount of token to destroy
source_id INTEGER UNSIGNED, -- id of record in index_addresses table
tx_hash_id INTEGER UNSIGNED, -- id of record in index_transactions
block_index INTEGER UNSIGNED, -- block index of DESTROY transaction
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 destroys (tx_index);
CREATE INDEX tick_id ON destroys (tick_id);
CREATE INDEX source_id ON destroys (source_id);
CREATE INDEX tx_hash_id ON destroys (tx_hash_id);
CREATE INDEX block_index ON destroys (block_index);
CREATE INDEX memo_id ON destroys (memo_id);
CREATE INDEX status_id ON destroys (status_id);

0 comments on commit ccfaadc

Please sign in to comment.