diff --git a/planetmint/backend/tarantool/init.lua b/planetmint/backend/tarantool/init.lua index a97e802..c633c28 100644 --- a/planetmint/backend/tarantool/init.lua +++ b/planetmint/backend/tarantool/init.lua @@ -1,107 +1,198 @@ box.cfg{listen = 3303} -abci_chains = box.schema.create_space('abci_chains', { if_not_exists = true }) -abci_chains:format({ - { name = 'id', type = 'string' }, - { name = 'height', type = 'unsigned' }, - { name = 'is_synced', type = 'boolean' } -}) -abci_chains:create_index('id', { parts = {'id'}}) -abci_chains:create_index('height', { parts = {'height'}}) +function init() + -- ABCI chains + abci_chains = box.schema.create_space('abci_chains', { if_not_exists = true }) + abci_chains:format({ + { name = 'id', type = 'string' }, + { name = 'height', type = 'unsigned' }, + { name = 'is_synced', type = 'boolean' } + }) + abci_chains:create_index('id', { parts = {'id'}}) + abci_chains:create_index('height', { parts = {'height'}}) --- Transactions -transactions = box.schema.create_space('transactions', { if_not_exists = true }) -transactions:format({ - { name = 'id', type = 'string' }, - { name = 'operation', type = 'string' }, - { name = 'version', type = 'string' }, - { name = 'metadata', type = 'string' }, - { name = 'assets', type = 'array' }, - { name = 'inputs', type = 'array', is_nullable = true }, - { name = 'scripts', type = 'map', is_nullable = true } -}) -transactions:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) -transactions:create_index('transactions_by_asset', { parts = { - { field = 'assets[*].id', type = 'string', is_nullable = true }, - { field = 'assets[*].data', type = 'string', is_nullable = true } -}}) -transactions:create_index('spending_transaction_by_id_and_output_index', { parts = { - { field = 'inputs[*].fulfills["transaction_id"]', type = 'string' }, - { field = 'inputs[*].fulfills["output_index"]', type = 'unsigned' } -}}) + -- Transactions + transactions = box.schema.create_space('transactions', { if_not_exists = true }) + transactions:format({ + { name = 'id', type = 'string' }, + { name = 'operation', type = 'string' }, + { name = 'version', type = 'string' }, + { name = 'metadata', type = 'string' }, + { name = 'assets', type = 'array' }, + { name = 'inputs', type = 'array' }, + { name = 'scripts', type = 'map', is_nullable = true } + }) + transactions:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) + transactions:create_index('transactions_by_asset', { parts = { + { field = 'assets[*].id', type = 'string', is_nullable = true }, + { field = 'assets[*].data', type = 'string', is_nullable = true } + }}) + transactions:create_index('spending_transaction_by_id_and_output_index', { parts = { + { field = 'inputs[*].fulfills["transaction_id"]', type = 'string' }, + { field = 'inputs[*].fulfills["output_index"]', type = 'unsigned' } + }}) --- Outputs -outputs = box.schema.create_space('outputs', { if_not_exists = true }) -outputs:format({ - { name = 'id', type = 'string' }, - { name = 'amount' , type='unsigned' }, - { name = 'public_keys', type='array' }, - { name = 'condition', type = 'map' }, - { name = 'output_index', type='number' }, - { name = 'transaction_id' , foreign_key = { space = 'transactions', field = 'id' }} -}) -outputs:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) -outputs:create_index('transaction_id', { parts = {{ field = 'id', type = 'string' }}}) -outputs:create_index('public_keys', { unique = false, parts = {{field = 'public_keys[*]', type = 'string' }}}) + -- Outputs + outputs = box.schema.create_space('outputs', { if_not_exists = true }) + outputs:format({ + { name = 'id', type = 'string' }, + { name = 'amount' , type = 'unsigned' }, + { name = 'public_keys', type = 'array' }, + { name = 'condition', type = 'map' }, + { name = 'output_index', type = 'number' }, + { name = 'transaction_id' , type = 'string' } + }) + outputs:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) + outputs:create_index('transaction_id', { unique = false, parts = {{ field = 'id', type = 'string' }}}) + outputs:create_index('public_keys', { unique = false, parts = {{field = 'public_keys[*]', type = 'string' }}}) --- Precommits -pre_commits = box.schema.create_space('pre_commits', { if_not_exists = true }) -pre_commits:format({ - { name = 'id', type = 'string' }, - { name = 'height', type = 'unsigned' }, - { name = 'transaction_ids', type = 'array'} -}) -pre_commits:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) -pre_commits:create_index('height', { parts = {{ field = 'height', type = 'unsigned' }}}) + -- Precommits + pre_commits = box.schema.create_space('pre_commits', { if_not_exists = true }) + pre_commits:format({ + { name = 'id', type = 'string' }, + { name = 'height', type = 'unsigned' }, + { name = 'transaction_ids', type = 'array'} + }) + pre_commits:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) + pre_commits:create_index('height', { parts = {{ field = 'height', type = 'unsigned' }}}) --- Blocks -blocks = box.schema.create_space('blocks', { if_not_exists = true }) -blocks:format({ - { name = 'id', type = 'string' }, - { name = 'app_hash', type = 'string' }, - { name = 'height', type = 'unsigned' }, - { name = 'transaction_ids', type = 'array' } -}) -blocks:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) -blocks:create_index('height', { parts = {{ field = 'height', type = 'unsigned' }}}) -blocks:create_index('block_by_transaction_id', { parts = {{ field = 'transaction_ids[*]', type = 'string' }}}) + -- Blocks + blocks = box.schema.create_space('blocks', { if_not_exists = true }) + blocks:format({ + { name = 'id', type = 'string' }, + { name = 'app_hash', type = 'string' }, + { name = 'height', type = 'unsigned' }, + { name = 'transaction_ids', type = 'array' } + }) + blocks:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) + blocks:create_index('height', { parts = {{ field = 'height', type = 'unsigned' }}}) + blocks:create_index('block_by_transaction_id', { parts = {{ field = 'transaction_ids[*]', type = 'string' }}}) --- UTXO -utxos = box.schema.create_space('utxos', { if_not_exists = true }) -utxos:format({ - { name = 'id', type = 'string' }, - { name = 'transaction_id', foreign_key = { space = 'transactions', field = 'id' } }, - { name = 'output_index', type = 'unsigned' }, - { name = 'utxo', type = 'map' } -}) -utxos:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) -utxos:create_index('utxo_by_transaction_id_and_output_index', { parts = { - { field = 'transaction_id', type = 'string' }, - { field = 'output_index', type = 'unsigned' } -}}) + -- UTXO + utxos = box.schema.create_space('utxos', { if_not_exists = true }) + utxos:format({ + { name = 'id', type = 'string' }, + { name = 'transaction_id', type = 'string' }, + { name = 'output_index', type = 'unsigned' }, + { name = 'utxo', type = 'map' } + }) + utxos:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) + utxos:create_index('utxo_by_transaction_id_and_output_index', { parts = { + { field = 'transaction_id', type = 'string' }, + { field = 'output_index', type = 'unsigned' } + }}) --- Elections -elections = box.schema.create_space('elections', { if_not_exists = true }) -elections:format({ - { name = 'id', type = 'string' }, - { name = 'height', type = 'unsigned' }, - { name = 'is_concluded', type = 'boolean' } -}) -elections:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) -elections:create_index('height', { parts = {{ field = 'height', type = 'unsigned' }}}) + -- Elections + elections = box.schema.create_space('elections', { if_not_exists = true }) + elections:format({ + { name = 'id', type = 'string' }, + { name = 'height', type = 'unsigned' }, + { name = 'is_concluded', type = 'boolean' } + }) + elections:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) + elections:create_index('height', { parts = {{ field = 'height', type = 'unsigned' }}}) --- Validators -validator_sets = box.schema.create_space('validator_sets', { if_not_exists = true }) -validator_sets:format({ - { name = 'id', type = 'string' }, - { name = 'height', type = 'unsigned' }, - { name = 'set', type = 'array' } -}) -validator_sets:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) \ No newline at end of file + -- Validators + validator_sets = box.schema.create_space('validator_sets', { if_not_exists = true }) + validator_sets:format({ + { name = 'id', type = 'string' }, + { name = 'height', type = 'unsigned' }, + { name = 'set', type = 'array' } + }) + validator_sets:create_index('id', { parts = {{ field = 'id', type = 'string' }}}) +end + +function drop() + box.space.abci_chains:drop() + box.space.blocks:drop() + box.space.elections:drop() + box.space.pre_commits:drop() + box.space.utxos:drop() + box.space.validator_sets:drop() + box.space.transactions:drop() + box.space.outputs:drop() +end + +function indexed_pattern_search(space_name, field_no, pattern) + if (box.space[space_name] == nil) then + print("Error: Failed to find the specified space") + return nil + end + local index_no = -1 + for i=0,box.schema.INDEX_MAX,1 do + if (box.space[space_name].index[i] == nil) then break end + if (box.space[space_name].index[i].type == "TREE" + and box.space[space_name].index[i].parts[1].fieldno == field_no + and (box.space[space_name].index[i].parts[1].type == "scalar" + or box.space[space_name].index[i].parts[1].type == "string")) then + index_no = i + break + end + end + if (index_no == -1) then + print("Error: Failed to find an appropriate index") + return nil + end + local index_search_key = "" + local index_search_key_length = 0 + local last_character = "" + local c = "" + local c2 = "" + for i=1,string.len(pattern),1 do + c = string.sub(pattern, i, i) + if (last_character ~= "%") then + if (c == '^' or c == "$" or c == "(" or c == ")" or c == "." + or c == "[" or c == "]" or c == "*" or c == "+" + or c == "-" or c == "?") then + break + end + if (c == "%") then + c2 = string.sub(pattern, i + 1, i + 1) + if (string.match(c2, "%p") == nil) then break end + index_search_key = index_search_key .. c2 + else + index_search_key = index_search_key .. c + end + end + last_character = c + end + index_search_key_length = string.len(index_search_key) + local result_set = {} + local number_of_tuples_in_result_set = 0 + local previous_tuple_field = "" + while true do + local number_of_tuples_since_last_yield = 0 + local is_time_for_a_yield = false + for _,tuple in box.space[space_name].index[index_no]: + pairs(index_search_key,{iterator = box.index.GE}) do + if (string.sub(tuple[field_no], 1, index_search_key_length) + > index_search_key) then + break + end + number_of_tuples_since_last_yield = number_of_tuples_since_last_yield + 1 + if (number_of_tuples_since_last_yield >= 10 + and tuple[field_no] ~= previous_tuple_field) then + index_search_key = tuple[field_no] + is_time_for_a_yield = true + break + end + previous_tuple_field = tuple[field_no] + if (string.match(tuple[field_no], pattern) ~= nil) then + number_of_tuples_in_result_set = number_of_tuples_in_result_set + 1 + result_set[number_of_tuples_in_result_set] = tuple + end + end + if (is_time_for_a_yield ~= true) then + break + end + require('fiber').yield() + end + return result_set +end \ No newline at end of file