diff --git a/haf b/haf index f77f308210a2b051517dc08a986bab9fc1afe98d..6e97cb607fec227d6640918bcad759c4f5d52d92 160000 --- a/haf +++ b/haf @@ -1 +1 @@ -Subproject commit f77f308210a2b051517dc08a986bab9fc1afe98d +Subproject commit 6e97cb607fec227d6640918bcad759c4f5d52d92 diff --git a/postgrest/hafah_REST/accounts/get_acc_op_types.sql b/postgrest/hafah_REST/accounts/get_acc_op_types.sql index ce4069da601464596ff7f77368ff166a7ff5043e..e0a34c760f57a5c76fcb463af6872610276eff3d 100644 --- a/postgrest/hafah_REST/accounts/get_acc_op_types.sql +++ b/postgrest/hafah_REST/accounts/get_acc_op_types.sql @@ -82,16 +82,11 @@ SET from_collapse_limit = 16 AS $$ DECLARE - _account_id INT = (SELECT av.id FROM hive.accounts_view av WHERE av.name = "account-name"); + _account_id INT := hafah_backend.get_account_id("account-name", TRUE); BEGIN - IF _account_id IS NULL THEN - PERFORM hafah_backend.rest_raise_missing_account("account-name"); - END IF; - PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=2"}]', true); RETURN hafah_backend.get_acc_op_types(_account_id); - END $$; diff --git a/postgrest/hafah_REST/accounts/get_ops_by_account.sql b/postgrest/hafah_REST/accounts/get_ops_by_account.sql index 5c6ebb595a39ce8aacdeb2ff66ca9745628e0f7f..eb604ee57fc77c3b4c7db1b0af896da6e85f22c3 100644 --- a/postgrest/hafah_REST/accounts/get_ops_by_account.sql +++ b/postgrest/hafah_REST/accounts/get_ops_by_account.sql @@ -23,6 +23,28 @@ SET ROLE hafah_owner; schema: type: string description: Account to get operations for. + - in: query + name: transacting-account-name + required: false + schema: + type: string + default: NULL + description: | + Account to filter operations by, if provided only operations where the account is an author will be returned. + - in: query + name: participation-mode + required: false + schema: + $ref: '#/components/schemas/hafah_backend.participation_mode' + default: all + description: | + filter operations by: + + * `include` - List only operations where transacting_account_id was the author. + + * `exclude` - List only operations where transacting_account_id was not the author. + + * `all` - No filtering, transacting_account_id must be NULL. - in: query name: operation-types required: false @@ -98,14 +120,18 @@ SET ROLE hafah_owner; Result contains total number of operations, total pages, and the list of operations. - * Returns `hafah_backend.operation_history` + * Returns `hafah_backend.account_operation_history` content: application/json: schema: - $ref: '#/components/schemas/hafah_backend.operation_history' + $ref: '#/components/schemas/hafah_backend.account_operation_history' example: { "total_operations": 219867, "total_pages": 73289, + "block_range": { + "from": 1, + "to": 5000000 + }, "operations_result": [ { "op": { @@ -181,6 +207,8 @@ SET ROLE hafah_owner; DROP FUNCTION IF EXISTS hafah_endpoints.get_ops_by_account; CREATE OR REPLACE FUNCTION hafah_endpoints.get_ops_by_account( "account-name" TEXT, + "transacting-account-name" TEXT = NULL, + "participation-mode" hafah_backend.participation_mode = 'all', "operation-types" TEXT = NULL, "page" INT = NULL, "page-size" INT = 100, @@ -188,78 +216,45 @@ CREATE OR REPLACE FUNCTION hafah_endpoints.get_ops_by_account( "from-block" TEXT = NULL, "to-block" TEXT = NULL ) -RETURNS hafah_backend.operation_history +RETURNS hafah_backend.account_operation_history -- openapi-generated-code-end LANGUAGE 'plpgsql' STABLE SET JIT = OFF SET join_collapse_limit = 16 SET from_collapse_limit = 16 -SET enable_hashjoin = OFF +SET plan_cache_mode = force_custom_plan AS $$ DECLARE _block_range hive.blocks_range := hive.convert_to_blocks_range("from-block","to-block"); - _account_id INT = (SELECT av.id FROM hive.accounts_view av WHERE av.name = "account-name"); - _ops_count INT; - _from INT; - _to INT; - _operation_types INT[] := (SELECT string_to_array("operation-types", ',')::INT[]); - _result hafah_backend.operation[]; - - __total_pages INT; - __offset INT; - __limit INT; + _account_id INT := hafah_backend.get_account_id("account-name", TRUE); + _transacting_account_id INT := hafah_backend.get_account_id("transacting-account-name", FALSE); + _operation_types INT[] := hafah_backend.get_operation_types("operation-types", "participation-mode" = 'all'); BEGIN - IF _account_id IS NULL THEN - PERFORM hafah_backend.rest_raise_missing_account("account-name"); - END IF; - + PERFORM hafah_backend.validate_participation_mode("participation-mode","transacting-account-name"); PERFORM hafah_python.validate_limit("page-size", 1000, 'page-size'); PERFORM hafah_python.validate_negative_limit("page-size", 'page-size'); PERFORM hafah_python.validate_negative_page("page"); - - -----------PAGING LOGIC---------------- - SELECT count, from_seq, to_seq - INTO _ops_count, _from, _to - FROM hafah_backend.account_range(_operation_types, _account_id, _block_range.first_block, _block_range.last_block); - - SELECT total_pages, offset_filter, limit_filter - INTO __total_pages, __offset, __limit - FROM hafah_backend.calculate_pages(_ops_count, "page", 'desc', "page-size"); - - IF (_block_range.last_block <= hive.app_get_irreversible_block() AND _block_range.last_block IS NOT NULL) OR ("page" IS NOT NULL AND __total_pages != "page") THEN + + IF (_block_range.last_block <= hive.app_get_irreversible_block() AND _block_range.last_block IS NOT NULL) THEN PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=31536000"}]', true); ELSE PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=2"}]', true); END IF; - _result := array_agg(row ORDER BY row.operation_id::BIGINT DESC) FROM ( - SELECT - ba.op, - ba.block, - ba.trx_id, - ba.op_pos, - ba.op_type_id, - ba.timestamp, - ba.virtual_op, - ba.operation_id, - ba.trx_in_block - FROM hafah_backend.get_ops_by_account( - _account_id, - _operation_types, - _from, - _to, - "data-size-limit", - __offset, - __limit - ) ba - ) row; - - RETURN ( - COALESCE(_ops_count,0), - COALESCE(__total_pages,0), - COALESCE(_result, '{}'::hafah_backend.operation[]) - )::hafah_backend.operation_history; + RETURN hafah_backend.get_ops_by_account( + _account_id, + -- in the current implementation, we only support a single transacting account, + -- we may extend this in the future to support multiple accounts + ARRAY[_transacting_account_id], + _operation_types, + _block_range.first_block, + _block_range.last_block, + "page", + "data-size-limit", + "page-size", + "participation-mode" +); -- ops_count returns number of operations found with current filter -- to count total_pages we need to check if there was a rest from division by "page-size", if there was the page count is +1 diff --git a/postgrest/hafah_REST/blocks/get_block.sql b/postgrest/hafah_REST/blocks/get_block.sql index d5cc80899569a922c7a352df25c349d4d05b9129..fa76a9bb41b40e23b6cb8faaa18a0bd766ad695e 100644 --- a/postgrest/hafah_REST/blocks/get_block.sql +++ b/postgrest/hafah_REST/blocks/get_block.sql @@ -166,18 +166,16 @@ SET from_collapse_limit = 16 AS $$ DECLARE - __block INT := hive.convert_to_block_num("block-num"); - __block_num BIGINT = NULL; - __exception_message TEXT; + __block INT := hive.convert_to_block_num("block-num"); + __block_num BIGINT := NULL; + __exception_message TEXT := NULL; BEGIN -- Required argument: block-num - IF __block IS NULL THEN - PERFORM hafah_backend.rest_raise_missing_arg('block-num'); - ELSE - __block_num = __block::BIGINT; - IF __block_num < 0 THEN - __block_num := __block_num + ((POW(2, 31) - 1) :: BIGINT); - END IF; + PERFORM hafah_backend.is_block_missing(__block); + + __block_num = __block::BIGINT; + IF __block_num < 0 THEN + __block_num := __block_num + ((POW(2, 31) - 1) :: BIGINT); END IF; IF __block <= hive.app_get_irreversible_block() AND __block IS NOT NULL THEN diff --git a/postgrest/hafah_REST/blocks/get_block_header.sql b/postgrest/hafah_REST/blocks/get_block_header.sql index 3f3c01726fe90c2fb3ee1cb3b53aba3da8815558..372226f55ee9acdb9e30998f3d6cdc228e14ca5f 100644 --- a/postgrest/hafah_REST/blocks/get_block_header.sql +++ b/postgrest/hafah_REST/blocks/get_block_header.sql @@ -63,18 +63,16 @@ SET from_collapse_limit = 16 AS $$ DECLARE - __block INT := hive.convert_to_block_num("block-num"); - __block_num BIGINT = NULL; + __block INT := hive.convert_to_block_num("block-num"); + __block_num BIGINT := NULL; BEGIN -- Required argument: block-num - IF __block IS NULL THEN - PERFORM hafah_backend.rest_raise_missing_arg('block-num'); - ELSE - __block_num = __block::BIGINT; - IF __block_num < 0 THEN - __block_num := __block_num + ((POW(2, 31) - 1) :: BIGINT); - END IF; - END IF; + PERFORM hafah_backend.is_block_missing(__block); + + __block_num = __block::BIGINT; + IF __block_num < 0 THEN + __block_num := __block_num + ((POW(2, 31) - 1) :: BIGINT); + END IF; IF __block <= hive.app_get_irreversible_block() AND __block IS NOT NULL THEN PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=31536000"}]', true); diff --git a/postgrest/hafah_REST/blocks/get_block_range.sql b/postgrest/hafah_REST/blocks/get_block_range.sql index 9d5043ed01fd504d724054a3b65b73a978c69622..629e727867b2da3aef7eda79922f6b4c37f77308 100644 --- a/postgrest/hafah_REST/blocks/get_block_range.sql +++ b/postgrest/hafah_REST/blocks/get_block_range.sql @@ -297,29 +297,22 @@ AS $$ DECLARE _block_range hive.blocks_range := hive.convert_to_blocks_range("from-block","to-block"); - __block_num BIGINT = NULL; - __end_block_num BIGINT = NULL; + __block_num BIGINT := NULL; + __end_block_num BIGINT := NULL; BEGIN -- Required argument: block-num - IF _block_range.first_block IS NULL THEN - PERFORM hafah_backend.rest_raise_missing_arg('from-block'); - ELSE - __block_num = _block_range.first_block::BIGINT; - IF __block_num < 0 THEN - __block_num := __block_num + ((POW(2, 31) - 1) :: BIGINT); - END IF; - END IF; + PERFORM hafah_backend.is_block_missing(_block_range.first_block, 'from-block'); - IF _block_range.last_block IS NULL THEN - PERFORM hafah_backend.rest_raise_missing_arg('to-block'); - ELSE - __end_block_num = _block_range.last_block::BIGINT; - IF __end_block_num < 0 THEN - __end_block_num := __end_block_num + ((POW(2, 31) - 1) :: BIGINT); - ELSE + __block_num = _block_range.first_block::BIGINT; + IF __block_num < 0 THEN + __block_num := __block_num + ((POW(2, 31) - 1) :: BIGINT); + END IF; - END IF; + PERFORM hafah_backend.is_block_missing(_block_range.last_block, 'to-block'); + __end_block_num = _block_range.last_block::BIGINT; + IF __end_block_num < 0 THEN + __end_block_num := __end_block_num + ((POW(2, 31) - 1) :: BIGINT); END IF; IF _block_range.last_block <= hive.app_get_irreversible_block() AND _block_range.last_block IS NOT NULL THEN diff --git a/postgrest/hafah_REST/blocks/get_ops_by_block_paging.sql b/postgrest/hafah_REST/blocks/get_ops_by_block_paging.sql index 672daa0ca6afaa31f50c80c45f4f8d8df8921ee9..3f03da12e4de52fd8d2066cd468febf90c2df6f4 100644 --- a/postgrest/hafah_REST/blocks/get_ops_by_block_paging.sql +++ b/postgrest/hafah_REST/blocks/get_ops_by_block_paging.sql @@ -163,37 +163,24 @@ SET from_collapse_limit = 16 AS $$ DECLARE - __block INT := hive.convert_to_block_num("block-num"); - _operation_types INT[] := (CASE WHEN "operation-types" IS NOT NULL THEN string_to_array("operation-types", ',')::INT[] ELSE NULL END); - _key_content TEXT[] := NULL; - _set_of_keys JSON := NULL; - _result hafah_backend.operation[]; - _account_id INT := NULL; + __block INT := hive.convert_to_block_num("block-num"); + _operation_types INT[] := hafah_backend.get_operation_types("operation-types", TRUE); + _account_id INT := hafah_backend.get_account_id("account-name", FALSE); + + _key_content TEXT[] := NULL; + _set_of_keys JSON := NULL; + _ops_count INT := NULL; + _total_pages INT := NULL; - _ops_count INT; - __total_pages INT; + _result hafah_backend.operation[]; BEGIN PERFORM hafah_python.validate_limit("page-size", 10000, 'page-size'); PERFORM hafah_python.validate_negative_limit("page-size", 'page-size'); PERFORM hafah_python.validate_negative_page("page"); + PERFORM hafah_backend.is_block_missing(__block); + PERFORM hafah_backend.validate_block_num(__block); - IF __block IS NULL THEN - PERFORM hafah_backend.rest_raise_missing_arg('block-num'); - END IF; - - IF NOT EXISTS (SELECT 1 FROM hive.blocks_view bv WHERE bv.num = __block) THEN - PERFORM hafah_backend.rest_raise_missing_block(__block); - END IF; - - IF "account-name" IS NOT NULL THEN - _account_id := (SELECT av.id FROM hive.accounts_view av WHERE av.name = "account-name"); - - IF _account_id IS NULL THEN - PERFORM hafah_backend.rest_raise_missing_account("account-name"); - END IF; - END IF; - - IF "path-filter" IS NOT NULL AND "path-filter" != '{}' THEN + IF hafah_backend.is_path_filter_not_empty("path-filter") THEN SELECT pvpf.param_json::JSON, pvpf.param_text::TEXT[] @@ -207,26 +194,10 @@ BEGIN PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=2"}]', true); END IF; - _ops_count := ( - SELECT hafah_backend.get_ops_by_block_count( - __block, - _operation_types, - _account_id, - _key_content, - _set_of_keys - ) - ); - - __total_pages := ( - CASE - WHEN (_ops_count % "page-size") = 0 THEN - _ops_count/"page-size" - ELSE - (_ops_count/"page-size") + 1 - END - ); + _ops_count := hafah_backend.get_ops_by_block_count(__block, _operation_types, _account_id, _key_content, _set_of_keys); + _total_pages := hafah_backend.total_pages(_ops_count, "page-size"); - PERFORM hafah_python.validate_page("page", __total_pages); + PERFORM hafah_python.validate_page("page", _total_pages); _result := array_agg(row ORDER BY (CASE WHEN "page-order" = 'desc' THEN row.operation_id::BIGINT ELSE NULL END) DESC, @@ -257,7 +228,7 @@ BEGIN RETURN ( COALESCE(_ops_count,0), - COALESCE(__total_pages,0), + COALESCE(_total_pages,0), COALESCE(_result, '{}'::hafah_backend.operation[]) )::hafah_backend.operation_history; diff --git a/postgrest/hafah_REST/hafah_openapi.sql b/postgrest/hafah_REST/hafah_openapi.sql index acf2cab33d84fe901d6cce8219f59c0b3a990794..de9b9f02c52671c5306c72290bcf827126887300 100644 --- a/postgrest/hafah_REST/hafah_openapi.sql +++ b/postgrest/hafah_REST/hafah_openapi.sql @@ -72,6 +72,17 @@ declare "all" ] }, + "hafah_backend.block_range_type": { + "type": "object", + "properties": { + "from": { + "type": "integer" + }, + "to": { + "type": "integer" + } + } + }, "hafah_backend.operation_body": { "type": "object", "x-sql-datatype": "JSON", @@ -154,6 +165,30 @@ declare } } }, + "hafah_backend.account_operation_history": { + "type": "object", + "properties": { + "total_operations": { + "type": "integer", + "description": "Total number of operations" + }, + "total_pages": { + "type": "integer", + "description": "Total number of pages" + }, + "block_range": { + "$ref": "#/components/schemas/hafah_backend.block_range_type", + "description": "Range of blocks that contains the returned pages" + }, + "operations_result": { + "type": "array", + "items": { + "$ref": "#/components/schemas/hafah_backend.operation" + }, + "description": "List of operation results" + } + } + }, "hafah_backend.operations_in_block_range": { "type": "object", "properties": { @@ -447,6 +482,14 @@ declare "$ref": "#/components/schemas/hafah_backend.fill_order" } }, + "hafah_backend.participation_mode": { + "type": "string", + "enum": [ + "include", + "exclude", + "all" + ] + }, "hafah_backend.array_of_block_range": { "type": "array", "items": { @@ -1619,6 +1662,26 @@ declare }, "description": "Account to get operations for." }, + { + "in": "query", + "name": "transacting-account-name", + "required": false, + "schema": { + "type": "string", + "default": null + }, + "description": "Account to filter operations by, if provided only operations where the account is an author will be returned.\n" + }, + { + "in": "query", + "name": "participation-mode", + "required": false, + "schema": { + "$ref": "#/components/schemas/hafah_backend.participation_mode", + "default": "all" + }, + "description": "filter operations by:\n\n * `include` - List only operations where transacting_account_id was the author.\n\n * `exclude` - List only operations where transacting_account_id was not the author.\n\n * `all` - No filtering, transacting_account_id must be NULL.\n" + }, { "in": "query", "name": "operation-types", @@ -1682,15 +1745,19 @@ declare ], "responses": { "200": { - "description": "Result contains total number of operations,\ntotal pages, and the list of operations.\n\n* Returns `hafah_backend.operation_history`\n", + "description": "Result contains total number of operations,\ntotal pages, and the list of operations.\n\n* Returns `hafah_backend.account_operation_history`\n", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/hafah_backend.operation_history" + "$ref": "#/components/schemas/hafah_backend.account_operation_history" }, "example": { "total_operations": 219867, "total_pages": 73289, + "block_range": { + "from": 1, + "to": 5000000 + }, "operations_result": [ { "op": { diff --git a/postgrest/hafah_REST/market_history/get_trade_history.sql b/postgrest/hafah_REST/market_history/get_trade_history.sql index 7a5d2c86f176a0de203b6ab7ec3b24b466d929e9..84b7e69291b3340e582f33869648cd04e014c688 100644 --- a/postgrest/hafah_REST/market_history/get_trade_history.sql +++ b/postgrest/hafah_REST/market_history/get_trade_history.sql @@ -116,14 +116,8 @@ DECLARE BEGIN PERFORM hafah_python.validate_limit("result-limit", 1000, 'result-limit'); PERFORM hafah_python.validate_negative_limit("result-limit", 'result-limit'); - - IF _block_range.first_block IS NULL THEN - PERFORM hafah_backend.rest_raise_missing_arg('from-block'); - END IF; - - IF _block_range.last_block IS NULL THEN - PERFORM hafah_backend.rest_raise_missing_arg('to-block'); - END IF; + PERFORM hafah_backend.is_block_missing(_block_range.first_block, 'from-block'); + PERFORM hafah_backend.is_block_missing(_block_range.last_block, 'to-block'); IF _block_range.last_block <= hive.app_get_irreversible_block() AND _block_range.last_block IS NOT NULL THEN PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=31536000"}]', true); diff --git a/postgrest/hafah_REST/operation_types/get_operation_keys.sql b/postgrest/hafah_REST/operation_types/get_operation_keys.sql index 572ba6fcedb23eece44530715b598deb39906d89..5b91e7d2dc3abe1fe8c70691daa5e2f280dab6fc 100644 --- a/postgrest/hafah_REST/operation_types/get_operation_keys.sql +++ b/postgrest/hafah_REST/operation_types/get_operation_keys.sql @@ -64,13 +64,18 @@ SET enable_bitmapscan = OFF AS $$ DECLARE - _example_key JSON := (SELECT ov.body FROM hive.operations_view ov WHERE ov.op_type_id = "type-id" LIMIT 1); + _example_key JSON := NULL; BEGIN + PERFORM hafah_backend.validate_op_type_id("type-id"); PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=31536000"}]', true); - IF ("type-id" > (SELECT MAX(id) FROM hafd.operation_types)) OR "type-id" < 0 THEN - PERFORM hafah_backend.rest_raise_missing_op_type("type-id"); - END IF; + _example_key := ( + SELECT + ov.body + FROM hive.operations_view ov + WHERE ov.op_type_id = "type-id" + LIMIT 1 + ); RETURN COALESCE( ( diff --git a/postgrest/hafah_REST/operations/get_operation.sql b/postgrest/hafah_REST/operations/get_operation.sql index ed758f29743648302bfe1bb3fe8b56b4d18be33f..3bcbc22fc67a6ac6d45f6bb94eb4cf300d85bd49 100644 --- a/postgrest/hafah_REST/operations/get_operation.sql +++ b/postgrest/hafah_REST/operations/get_operation.sql @@ -89,25 +89,14 @@ BEGIN IF _block_num IS NULL THEN PERFORM hafah_backend.rest_raise_missing_operation_id("operation-id"); END IF; - + IF _block_num <= hive.app_get_irreversible_block() THEN PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=31536000"}]', true); ELSE PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=2"}]', true); END IF; - RETURN ( - ov.op, - ov.block, - ov.trx_id, - ov.op_pos, - ov.op_type_id, - ov.timestamp, - ov.virtual_op, - ov.operation_id, - ov.trx_in_block - )::hafah_backend.operation - FROM hafah_backend.get_operation("operation-id") ov; + RETURN hafah_backend.get_operation("operation-id"); END $$; diff --git a/postgrest/hafah_REST/operations/get_operations.sql b/postgrest/hafah_REST/operations/get_operations.sql index c1f16c0530309bf2005c2b1d6dd9c4e2d0b50732..89fd497e0fc40b9c601bd2cfd8fce0cf974d01f9 100644 --- a/postgrest/hafah_REST/operations/get_operations.sql +++ b/postgrest/hafah_REST/operations/get_operations.sql @@ -288,12 +288,14 @@ AS $$ DECLARE _block_range hive.blocks_range := hive.convert_to_blocks_range("from-block","to-block"); - _operation_types INT[] := (CASE WHEN "operation-types" IS NOT NULL THEN string_to_array("operation-types", ',')::INT[] ELSE NULL END); - _operation_group_types BOOLEAN := (CASE WHEN "operation-group-type" = 'real' THEN FALSE WHEN "operation-group-type" = 'virtual' THEN TRUE ELSE NULL END); + _operation_types INT[] := hafah_backend.get_operation_types("operation-types", TRUE); + _operation_group_types BOOLEAN := hafah_backend.get_group_type("operation-group-type"); BEGIN PERFORM hafah_python.validate_limit("page-size", 150000, 'page-size'); PERFORM hafah_python.validate_negative_limit("page-size", 'page-size'); PERFORM hafah_python.validate_block_range( _block_range.first_block, _block_range.last_block + 1, 2001); + PERFORM hafah_backend.is_block_missing(_block_range.first_block, 'from-block'); + PERFORM hafah_backend.is_block_missing(_block_range.last_block, 'to-block'); IF _block_range.last_block <= hive.app_get_irreversible_block() AND _block_range.last_block IS NOT NULL THEN PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=31536000"}]', true); @@ -301,15 +303,6 @@ BEGIN PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=2"}]', true); END IF; - -- Required argument: to-block, from-block - IF _block_range.first_block IS NULL THEN - PERFORM hafah_backend.rest_raise_missing_arg('from-block'); - END IF; - - IF _block_range.last_block IS NULL THEN - PERFORM hafah_backend.rest_raise_missing_arg('to-block'); - END IF; - RETURN hafah_backend.get_ops_in_blocks( _block_range.first_block, _block_range.last_block, diff --git a/postgrest/hafah_REST/other/get_block.sql b/postgrest/hafah_REST/other/get_block.sql index 462249952e2f1e19a0165caa91b0bdffce701a6d..75c581f6f974839d015b7aa0cf3002015be0c66e 100644 --- a/postgrest/hafah_REST/other/get_block.sql +++ b/postgrest/hafah_REST/other/get_block.sql @@ -80,6 +80,9 @@ $$ DECLARE __block INT := hive.convert_to_block_num("block-num"); BEGIN + PERFORM hafah_backend.is_block_missing(__block); + PERFORM hafah_backend.validate_block_num(__block); + IF __block <= hive.app_get_irreversible_block() AND __block IS NOT NULL THEN PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=31536000"}]', true); ELSE diff --git a/postgrest/hafah_REST/other/get_head_block_num.sql b/postgrest/hafah_REST/other/get_head_block_num.sql index 4ad681253233ccecc8f4d0f42704a1a9ad85ef48..7c432d4ce3ce0414fbb756722d70c1bd08fa5412 100644 --- a/postgrest/hafah_REST/other/get_head_block_num.sql +++ b/postgrest/hafah_REST/other/get_head_block_num.sql @@ -43,7 +43,6 @@ $$ BEGIN PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=2"}]', true); RETURN bv.num FROM hive.blocks_view bv ORDER BY bv.num DESC LIMIT 1; - END $$; diff --git a/postgrest/hafah_REST/transactions/get_transaction.sql b/postgrest/hafah_REST/transactions/get_transaction.sql index adefe2c4c64ea809417c91cda576176edf507a0d..9cdd80b31a1593aee3d1bab477f0c0e75a068f7e 100644 --- a/postgrest/hafah_REST/transactions/get_transaction.sql +++ b/postgrest/hafah_REST/transactions/get_transaction.sql @@ -86,7 +86,7 @@ AS $$ DECLARE _transaction_json JSON := hafah_python.get_transaction_json(('\x' || "transaction-id")::BYTEA, TRUE, FALSE, "include-virtual"); - _result JSON; + _result JSON := NULL; BEGIN IF (_transaction_json->>'block_num')::INT <= hive.app_get_irreversible_block() THEN PERFORM set_config('response.headers', '[{"Cache-Control": "public, max-age=31536000"}]', true); diff --git a/postgrest/hafah_REST/types/operation.sql b/postgrest/hafah_REST/types/operation.sql index 26eb85309593ea339659d730d261032b41c43d7e..8a876d67a60b516ce187d30fe09223228de0e7fe 100644 --- a/postgrest/hafah_REST/types/operation.sql +++ b/postgrest/hafah_REST/types/operation.sql @@ -1,5 +1,22 @@ SET ROLE hafah_owner; +/** openapi:components:schemas +hafah_backend.block_range_type: + type: object + properties: + from: + type: integer + to: + type: integer + */ +-- openapi-generated-code-begin +DROP TYPE IF EXISTS hafah_backend.block_range_type CASCADE; +CREATE TYPE hafah_backend.block_range_type AS ( + "from" INT, + "to" INT +); +-- openapi-generated-code-end + /** openapi:components:schemas hafah_backend.operation_body: type: object @@ -97,6 +114,34 @@ CREATE TYPE hafah_backend.operation_history AS ( ); -- openapi-generated-code-end +/** openapi:components:schemas +hafah_backend.account_operation_history: + type: object + properties: + total_operations: + type: integer + description: Total number of operations + total_pages: + type: integer + description: Total number of pages + block_range: + $ref: '#/components/schemas/hafah_backend.block_range_type' + description: Range of blocks that contains the returned pages + operations_result: + type: array + items: + $ref: '#/components/schemas/hafah_backend.operation' + description: List of operation results + */ +-- openapi-generated-code-begin +DROP TYPE IF EXISTS hafah_backend.account_operation_history CASCADE; +CREATE TYPE hafah_backend.account_operation_history AS ( + "total_operations" INT, + "total_pages" INT, + "block_range" hafah_backend.block_range_type, + "operations_result" hafah_backend.operation[] +); +-- openapi-generated-code-end /** openapi:components:schemas hafah_backend.operations_in_block_range: type: object diff --git a/postgrest/hafah_REST/types/participation_mode.sql b/postgrest/hafah_REST/types/participation_mode.sql new file mode 100644 index 0000000000000000000000000000000000000000..1927d42a2cd9beb05116c453aba025be5f4f5ecb --- /dev/null +++ b/postgrest/hafah_REST/types/participation_mode.sql @@ -0,0 +1,21 @@ +SET ROLE hafah_owner; + +/** openapi:components:schemas +hafah_backend.participation_mode: + type: string + enum: + - include + - exclude + - all + + */ +-- openapi-generated-code-begin +DROP TYPE IF EXISTS hafah_backend.participation_mode CASCADE; +CREATE TYPE hafah_backend.participation_mode AS ENUM ( + 'include', + 'exclude', + 'all' +); +-- openapi-generated-code-end + +RESET ROLE; diff --git a/queries/hafah_rest_backend/account_history/account_history.sql b/queries/hafah_rest_backend/account_history/account_history.sql index 327d26b90636b14e4645e9aba8e85aead20057f6..7998f67854781c95c1c0a7361bb54a6bf79701f2 100644 --- a/queries/hafah_rest_backend/account_history/account_history.sql +++ b/queries/hafah_rest_backend/account_history/account_history.sql @@ -1,235 +1,105 @@ SET ROLE hafah_owner; -CREATE OR REPLACE FUNCTION hafah_python.get_account_history_json( - IN _filter_low NUMERIC, - IN _filter_high NUMERIC, - IN _account VARCHAR, - IN _start BIGINT, - IN _limit BIGINT, - IN _include_reversible BOOLEAN, - IN _is_legacy_style BOOLEAN ) -RETURNS JSONB -AS -$function$ -BEGIN - RETURN jsonb_agg( - json_build_array( - ops.operation_seq_num, - ( - CASE - WHEN _is_legacy_style THEN to_jsonb(ops) - 'operation_id' - 'operation_seq_num' - ELSE to_jsonb(ops) - 'operation_seq_num' - END - ) - ) - ) - FROM ( - SELECT - _block AS "block", - _value::JSON AS "op", - _op_in_trx AS "op_in_trx", - _timestamp AS "timestamp", - _trx_id AS "trx_id", - _trx_in_block AS "trx_in_block", - _virtual_op AS "virtual_op", - _operation_id::TEXT AS "operation_id", - _operation_seq_number AS "operation_seq_num" - FROM - hafah_python.get_account_history( - hafah_python.numeric_to_bigint(_filter_low), - hafah_python.numeric_to_bigint(_filter_high), - _account, - _start, - _limit, - _include_reversible, - _is_legacy_style - ) - ) ops; -END -$function$ -language plpgsql STABLE; - -CREATE OR REPLACE FUNCTION hafah_python.get_account_history( - IN _filter_low BIGINT, - IN _filter_high BIGINT, - IN _account VARCHAR, - IN _start BIGINT, - IN _limit BIGINT, - IN _include_reversible BOOLEAN, - IN _is_legacy_style BOOLEAN -) -RETURNS TABLE( - _trx_id TEXT, - _block INT, - _trx_in_block BIGINT, - _op_in_trx BIGINT, - _virtual_op BOOLEAN, - _timestamp TEXT, - _value TEXT, - _operation_id BIGINT, - _operation_seq_number INT -) -AS -$function$ -DECLARE - __resolved_filter SMALLINT[]; - __account_id INT; - __upper_block_limit INT; - __use_filter INT; -BEGIN - - PERFORM hafah_python.validate_limit( _limit, 1000 ); - PERFORM hafah_python.validate_start_limit( _start, _limit ); - - IF (NOT (_filter_low IS NULL AND _filter_high IS NULL)) AND COALESCE(_filter_low, 0) + COALESCE(_filter_high, 0) = 0 THEN - RETURN QUERY SELECT - NULL :: TEXT, - NULL :: INT, - NULL :: BIGINT, - NULL :: BIGINT, - NULL :: BOOLEAN, - NULL :: TEXT, - NULL :: TEXT, - NULL :: BIGINT, - NULL :: INT - LIMIT 0; - RETURN; - END IF; - - SELECT hafah_python.translate_get_account_history_filter(_filter_low, _filter_high) INTO __resolved_filter; - - IF _include_reversible THEN - SELECT num from hive.blocks_view order by num desc limit 1 INTO __upper_block_limit; - ELSE - SELECT hive.app_get_irreversible_block() INTO __upper_block_limit; - END IF; - - - IF _include_reversible THEN - SELECT INTO __account_id ( select id from hive.accounts_view where name = _account ); - ELSE - SELECT INTO __account_id ( select id from hafd.accounts where name = _account ); - END IF; - - __use_filter := array_length( __resolved_filter, 1 ); - - RETURN QUERY - WITH pre_result AS - ( - SELECT -- hafah_python.ah_get_account_history - ( - CASE - WHEN ho.trx_in_block < 0 THEN '0000000000000000000000000000000000000000' - ELSE encode( (SELECT htv.trx_hash FROM hive.transactions_view htv WHERE ho.trx_in_block >= 0 AND ds.block_num = htv.block_num AND ho.trx_in_block = htv.trx_in_block), 'hex') - END - ) AS _trx_id, - ds.block_num AS _block, - ( - CASE - WHEN ho.trx_in_block < 0 THEN 4294967295 - ELSE ho.trx_in_block - END - ) AS _trx_in_block, - ho.op_pos::BIGINT AS _op_in_trx, - hot.is_virtual AS virtual_op, - ( - CASE - WHEN _is_legacy_style THEN hive.get_legacy_style_operation(ho.body_binary)::TEXT - ELSE ho.body :: text - END - ) AS _value, - ds.operation_id AS _operation_id, - ds.account_op_seq_no AS _operation_seq_number - FROM - ( - WITH accepted_types AS MATERIALIZED - ( - SELECT ot.id FROM hafd.operation_types ot WHERE __use_filter IS NOT NULL AND ot.id=ANY(__resolved_filter) - ) - (SELECT hao.operation_id, hao.op_type_id,hao.block_num, hao.account_op_seq_no - FROM hive.account_operations_view hao - JOIN accepted_types t ON hao.op_type_id = t.id - WHERE __use_filter IS NOT NULL AND hao.account_id = __account_id AND hao.account_op_seq_no <= _start AND hao.block_num <= __upper_block_limit - ORDER BY hao.account_op_seq_no DESC - LIMIT _limit - ) - UNION ALL - (SELECT hao.operation_id, hao.op_type_id,hao.block_num, hao.account_op_seq_no - FROM hive.account_operations_view hao - WHERE __use_filter IS NULL AND hao.account_id = __account_id AND hao.account_op_seq_no <= _start AND hao.block_num <= __upper_block_limit - ORDER BY hao.account_op_seq_no DESC - LIMIT _limit - ) - - ) ds - JOIN LATERAL (SELECT hov.body, hov.body_binary, hov.op_pos, hov.trx_in_block FROM hive.operations_view hov WHERE ds.operation_id = hov.id) ho ON TRUE - JOIN LATERAL (select ot.is_virtual FROM hafd.operation_types ot WHERE ds.op_type_id = ot.id) hot on true - ORDER BY ds.account_op_seq_no ASC - - ) - SELECT -- hafah_python.ah_get_account_history - pre_result._trx_id, - pre_result._block, - pre_result._trx_in_block, - pre_result._op_in_trx, - pre_result.virtual_op, - btrim(to_json(hb.created_at)::TEXT, '"'::TEXT) AS formated_timestamp, - pre_result._value, - pre_result._operation_id, - pre_result._operation_seq_number - FROM - pre_result - JOIN hive.blocks_view hb ON hb.num = pre_result._block - ORDER BY pre_result._operation_seq_number ASC; - -END -$function$ -LANGUAGE plpgsql STABLE -SET JIT=OFF -SET join_collapse_limit=16 -SET from_collapse_limit=16 -SET plan_cache_mode=force_generic_plan -; - - - CREATE OR REPLACE FUNCTION hafah_backend.get_ops_by_account( _account_id INT, + _filter_account_ids INT [], _operations INT [], - _from INT, - _to INT, + _from_block INT, + _to_block INT, + _page INT, _body_limit INT, - _offset INT, - _limit INT + _limit INT, + _participation_mode hafah_backend.participation_mode ) -RETURNS SETOF hafah_backend.operation -- noqa: LT01, CP05 +RETURNS hafah_backend.account_operation_history -- noqa: LT01, CP05 LANGUAGE 'plpgsql' STABLE AS $$ +DECLARE + _result hafah_backend.account_operation_history; + + -- flags + _filter_by_account_ids BOOLEAN := (_filter_account_ids != ARRAY[NULL]::INT[]); + _filter_by_single_acc BOOLEAN := (CASE WHEN (_filter_account_ids != ARRAY[NULL]::INT[]) AND (array_length(_filter_account_ids, 1) = 1) THEN TRUE ELSE FALSE END); + _filter_by_op BOOLEAN := (_operations IS NOT NULL); BEGIN - IF _operations IS NULL THEN - RETURN QUERY - SELECT * FROM hafah_backend.account_history_default( + CASE + -- If no filters are applied, use the default account history function + WHEN (_participation_mode = 'all') AND (NOT _filter_by_op) THEN + _result := hafah_backend.account_history_default( + _account_id, + _from_block, + _to_block, + _page, + _body_limit, + _limit + ); + + -- If only operations are filtered, use the account history by operations function + WHEN ((_participation_mode = 'all') AND (_filter_by_op)) OR ((_participation_mode != 'all') AND (NOT _filter_by_account_ids)) THEN + _result := hafah_backend.account_history_by_operations( _account_id, - _from, - _to, + _operations, + _from_block, + _to_block, + _page, + _body_limit, + _limit, + (_participation_mode = 'all') -- include virtual operations if participation mode is 'all' + ); + -- If accounts are filtered, use the account history by accounts function (include account) + WHEN (_participation_mode = 'include') AND (_filter_by_account_ids) AND (_filter_by_single_acc) THEN + _result := hafah_backend.account_history_include_account( + _account_id, + _operations, + _filter_account_ids[1], + _from_block, + _to_block, + _page, _body_limit, - _offset, _limit - ); - END IF; - - RETURN QUERY - SELECT * FROM hafah_backend.account_history_by_operations( - _account_id, - _operations, - _from, - _to, - _body_limit, - _offset, - _limit - ); + ); + -- If accounts are filtered, use the account history by accounts function (include accounts) + WHEN (_participation_mode = 'include') AND (_filter_by_account_ids) AND (NOT _filter_by_single_acc) THEN + _result := hafah_backend.account_history_including_accounts( + _account_id, + _operations, + _filter_account_ids, + _from_block, + _to_block, + _page, + _body_limit, + _limit + ); + -- If accounts are filtered, use the account history by accounts function (exclude account) + WHEN (_participation_mode = 'exclude') AND (_filter_by_account_ids) AND (_filter_by_single_acc) THEN + _result := hafah_backend.account_history_exclude_account( + _account_id, + _operations, + _filter_account_ids[1], + _from_block, + _to_block, + _page, + _body_limit, + _limit + ); + -- If accounts are filtered, use the account history by accounts function (excluding accounts) + WHEN (_participation_mode = 'exclude') AND (_filter_by_account_ids) AND (NOT _filter_by_single_acc) THEN + _result := hafah_backend.account_history_excluding_accounts( + _account_id, + _operations, + _filter_account_ids, + _from_block, + _to_block, + _page, + _body_limit, + _limit + ); + ELSE + RAISE EXCEPTION 'Invalid parameters'; + END CASE; + RETURN _result; END $$; diff --git a/queries/hafah_rest_backend/account_history/by_operations.sql b/queries/hafah_rest_backend/account_history/by_operations.sql deleted file mode 100644 index 7453f983512e7246092349d16e66f98d0fae06d3..0000000000000000000000000000000000000000 --- a/queries/hafah_rest_backend/account_history/by_operations.sql +++ /dev/null @@ -1,68 +0,0 @@ -SET ROLE hafah_owner; - -CREATE OR REPLACE FUNCTION hafah_backend.account_history_by_operations( - _account_id INT, - _operations INT [], - _from INT, - _to INT, - _body_limit INT, - _offset INT, - _limit INT -) -RETURNS SETOF hafah_backend.operation -- noqa: LT01, CP05 -LANGUAGE 'plpgsql' STABLE -COST 10000 -SET JIT = OFF -SET join_collapse_limit = 16 -SET from_collapse_limit = 16 -AS -$$ -BEGIN - RETURN QUERY - WITH operation_range AS MATERIALIZED ( - SELECT - ls.operation_id AS id, - ls.block_num, - ov.trx_in_block, - encode(htv.trx_hash, 'hex') AS trx_hash, - ov.op_pos, - ls.op_type_id, - ov.body, - hot.is_virtual - FROM ( - SELECT aov.operation_id, aov.op_type_id, aov.block_num - FROM hive.account_operations_view aov - WHERE aov.account_id = _account_id - AND aov.op_type_id = ANY(_operations) - AND aov.account_op_seq_no >= _from - AND aov.account_op_seq_no <= _to - ORDER BY aov.account_op_seq_no DESC - LIMIT _limit - OFFSET _offset - ) ls - JOIN hive.operations_view ov ON ov.id = ls.operation_id - JOIN hafd.operation_types hot ON hot.id = ls.op_type_id - LEFT JOIN hive.transactions_view htv ON htv.block_num = ls.block_num AND htv.trx_in_block = ov.trx_in_block - ) - -- filter too long operation bodies - SELECT - (filtered_operations.composite).body, - filtered_operations.block_num, - filtered_operations.trx_hash, - filtered_operations.op_pos, - filtered_operations.op_type_id, - filtered_operations.created_at, - filtered_operations.is_virtual, - filtered_operations.id::TEXT, - filtered_operations.trx_in_block::SMALLINT - FROM ( - SELECT hafah_backend.operation_body_filter(ov.body, ov.id, _body_limit) as composite, ov.id, ov.block_num, ov.trx_in_block, ov.trx_hash, ov.op_pos, ov.op_type_id, ov.is_virtual, hb.created_at - FROM operation_range ov - JOIN hive.blocks_view hb ON hb.num = ov.block_num - ) filtered_operations - ORDER BY filtered_operations.id DESC; - -END -$$; - -RESET ROLE; diff --git a/queries/hafah_rest_backend/account_history/count.sql b/queries/hafah_rest_backend/account_history/count.sql deleted file mode 100644 index 42f56e07be09d6309f8b0096f174b45fc58d328c..0000000000000000000000000000000000000000 --- a/queries/hafah_rest_backend/account_history/count.sql +++ /dev/null @@ -1,36 +0,0 @@ -SET ROLE hafah_owner; - --- used in account page endpoint -CREATE OR REPLACE FUNCTION hafah_backend.get_account_operations_count( - _operations INT [], - _account_id INT, - _from INT, - _to INT -) -RETURNS BIGINT -- noqa: LT01, CP05 -LANGUAGE 'plpgsql' STABLE -SET from_collapse_limit = 16 -SET join_collapse_limit = 16 -SET enable_hashjoin = OFF -SET JIT = OFF -AS -$$ -BEGIN - IF _operations IS NULL THEN - RETURN _to - _from + 1; - END IF; - - RETURN ( - -- using hive_account_operations_uq2, we are forcing planner to use this index on (account_id,operation_id), it achives better performance results - SELECT COUNT(*) - FROM hive.account_operations_view aov - WHERE aov.account_id = _account_id - AND aov.op_type_id = ANY(_operations) - AND aov.account_op_seq_no >= _from - AND aov.account_op_seq_no <= _to - ); - -END -$$; - -RESET ROLE; diff --git a/queries/hafah_rest_backend/account_history/default.sql b/queries/hafah_rest_backend/account_history/default.sql deleted file mode 100644 index dfe730f3c7a28e218cf9be2b4b201381b2e7f269..0000000000000000000000000000000000000000 --- a/queries/hafah_rest_backend/account_history/default.sql +++ /dev/null @@ -1,64 +0,0 @@ -SET ROLE hafah_owner; - -CREATE OR REPLACE FUNCTION hafah_backend.account_history_default( - _account_id INT, - _from INT, - _to INT, - _body_limit INT, - _offset INT, - _limit INT -) -RETURNS SETOF hafah_backend.operation -- noqa: LT01, CP05 -LANGUAGE 'plpgsql' STABLE -COST 10000 -SET JIT = OFF -SET join_collapse_limit = 16 -SET from_collapse_limit = 16 -AS -$$ -BEGIN - RETURN QUERY - WITH operation_range AS MATERIALIZED ( - SELECT - ls.operation_id AS id, - ls.block_num, - ov.trx_in_block, - encode(htv.trx_hash, 'hex') AS trx_hash, - ov.op_pos, - ls.op_type_id, - ov.body, - hot.is_virtual - FROM ( - SELECT aov.operation_id, aov.op_type_id, aov.block_num - FROM hive.account_operations_view aov - WHERE aov.account_id = _account_id - AND aov.account_op_seq_no >= _from - AND aov.account_op_seq_no <= _to - _offset - ORDER BY aov.account_op_seq_no DESC - LIMIT _limit - ) ls - JOIN hive.operations_view ov ON ov.id = ls.operation_id - JOIN hafd.operation_types hot ON hot.id = ls.op_type_id - LEFT JOIN hive.transactions_view htv ON htv.block_num = ls.block_num AND htv.trx_in_block = ov.trx_in_block - ) - -- filter too long operation bodies - SELECT - (filtered_operations.composite).body, - filtered_operations.block_num, - filtered_operations.trx_hash, - filtered_operations.op_pos, - filtered_operations.op_type_id, - filtered_operations.created_at, - filtered_operations.is_virtual, - filtered_operations.id::TEXT, - filtered_operations.trx_in_block::SMALLINT - FROM ( - SELECT hafah_backend.operation_body_filter(ov.body, ov.id, _body_limit) as composite, ov.id, ov.block_num, ov.trx_in_block, ov.trx_hash, ov.op_pos, ov.op_type_id, ov.is_virtual, hb.created_at - FROM operation_range ov - JOIN hive.blocks_view hb ON hb.num = ov.block_num - ) filtered_operations - ORDER BY filtered_operations.id DESC; -END -$$; - -RESET ROLE; diff --git a/queries/hafah_rest_backend/account_history/filtering_functions/by_operations.sql b/queries/hafah_rest_backend/account_history/filtering_functions/by_operations.sql new file mode 100644 index 0000000000000000000000000000000000000000..9e7568756597281ffc21abaf5fa1d6b922553dff --- /dev/null +++ b/queries/hafah_rest_backend/account_history/filtering_functions/by_operations.sql @@ -0,0 +1,108 @@ +SET ROLE hafah_owner; + +CREATE OR REPLACE FUNCTION hafah_backend.account_history_by_operations( + _account_id INT, + _operations INT [], + _from_block INT, + _to_block INT, + _page INT, + _body_limit INT, + _limit INT, + _include_virtual BOOLEAN +) +RETURNS hafah_backend.account_operation_history -- noqa: LT01, CP05 +LANGUAGE 'plpgsql' STABLE +COST 10000 +SET JIT = OFF +SET join_collapse_limit = 16 +SET from_collapse_limit = 16 +AS +$$ +DECLARE + _result hafah_backend.operation[]; + _account_range hafah_backend.account_filter_return; + _calculate_pages hafah_backend.calculate_pages_return; + _ops_count INT; +BEGIN + + -----------PAGING LOGIC---------------- + _account_range := hafah_backend.account_range(_operations, _account_id, _from_block, _to_block); + + _ops_count := hafah_backend.get_account_operations_count(_operations, _account_id, _account_range.from_seq, _account_range.to_seq, _include_virtual); + + _calculate_pages := hafah_backend.calculate_pages(_ops_count, _page, 'desc', _limit); + + -- Fetching operations + WITH operation_range AS MATERIALIZED ( + SELECT + ls.operation_id AS id, + ls.block_num, + ov.trx_in_block, + encode(htv.trx_hash, 'hex') AS trx_hash, + ov.op_pos, + ls.op_type_id, + ov.body, + hot.is_virtual + FROM ( + SELECT aov.operation_id, aov.op_type_id, aov.block_num + FROM hive.account_operations_view aov + WHERE aov.account_id = _account_id + AND (_operations IS NULL OR aov.op_type_id = ANY(_operations)) + AND (_include_virtual OR aov.transacting_account_id IS NOT NULL) + AND aov.account_op_seq_no >= _account_range.from_seq + AND aov.account_op_seq_no <= _account_range.to_seq + ORDER BY aov.account_op_seq_no DESC + LIMIT _calculate_pages.limit_filter + OFFSET _calculate_pages.offset_filter + ) ls + JOIN hive.operations_view ov ON ov.id = ls.operation_id + JOIN hafd.operation_types hot ON hot.id = ls.op_type_id + LEFT JOIN hive.transactions_view htv ON htv.block_num = ls.block_num AND htv.trx_in_block = ov.trx_in_block + ), + -- filter too long operation bodies + result_query AS ( + SELECT + (filtered_operations.composite).body, + filtered_operations.block_num, + filtered_operations.trx_hash, + filtered_operations.op_pos, + filtered_operations.op_type_id, + filtered_operations.created_at, + filtered_operations.is_virtual, + filtered_operations.id, + filtered_operations.trx_in_block + FROM ( + SELECT hafah_backend.operation_body_filter(ov.body, ov.id, _body_limit) as composite, ov.id, ov.block_num, ov.trx_in_block, ov.trx_hash, ov.op_pos, ov.op_type_id, ov.is_virtual, hb.created_at + FROM operation_range ov + JOIN hive.blocks_view hb ON hb.num = ov.block_num + ) filtered_operations + ORDER BY filtered_operations.id DESC + ) + SELECT array_agg(rows ORDER BY rows.id::BIGINT DESC) + INTO _result + FROM ( + SELECT + s.body, + s.block_num, + s.trx_hash, + s.op_pos, + s.op_type_id, + s.created_at, + s.is_virtual, + s.id::TEXT, + s.trx_in_block::SMALLINT + FROM result_query s + ) rows; + + ---------------------------------------- + RETURN ( + COALESCE(_ops_count,0), + COALESCE(_calculate_pages.total_pages,0), + (_account_range.from_block, _account_range.to_block)::hafah_backend.block_range_type, + COALESCE(_result, '{}'::hafah_backend.operation[]) + )::hafah_backend.account_operation_history; + +END +$$; + +RESET ROLE; diff --git a/queries/hafah_rest_backend/account_history/filtering_functions/default.sql b/queries/hafah_rest_backend/account_history/filtering_functions/default.sql new file mode 100644 index 0000000000000000000000000000000000000000..3f8334022874b93ab4017c18ef9e4fad953da892 --- /dev/null +++ b/queries/hafah_rest_backend/account_history/filtering_functions/default.sql @@ -0,0 +1,104 @@ +SET ROLE hafah_owner; + +CREATE OR REPLACE FUNCTION hafah_backend.account_history_default( + _account_id INT, + _from_block INT, + _to_block INT, + _page INT, + _body_limit INT, + _limit INT +) +RETURNS hafah_backend.account_operation_history -- noqa: LT01, CP05 +LANGUAGE 'plpgsql' STABLE +COST 10000 +SET JIT = OFF +SET join_collapse_limit = 16 +SET from_collapse_limit = 16 +AS +$$ +DECLARE + _result hafah_backend.operation[]; + _account_range hafah_backend.account_filter_return; + _calculate_pages hafah_backend.calculate_pages_return; + _ops_count INT; +BEGIN + + -----------PAGING LOGIC---------------- + _account_range := hafah_backend.account_range(NULL, _account_id, _from_block, _to_block); + + -- always include virtual operations + _ops_count := hafah_backend.get_account_operations_count(NULL, _account_id, _account_range.from_seq, _account_range.to_seq, TRUE); + + _calculate_pages := hafah_backend.calculate_pages(_ops_count, _page, 'desc', _limit); + + -- Fetching operations + WITH operation_range AS MATERIALIZED ( + SELECT + ls.operation_id AS id, + ls.block_num, + ov.trx_in_block, + encode(htv.trx_hash, 'hex') AS trx_hash, + ov.op_pos, + ls.op_type_id, + ov.body, + hot.is_virtual + FROM ( + SELECT aov.operation_id, aov.op_type_id, aov.block_num + FROM hive.account_operations_view aov + WHERE aov.account_id = _account_id + AND aov.account_op_seq_no >= _account_range.from_seq + AND aov.account_op_seq_no <= _account_range.to_seq - _calculate_pages.offset_filter + ORDER BY aov.account_op_seq_no DESC + LIMIT _calculate_pages.limit_filter + ) ls + JOIN hive.operations_view ov ON ov.id = ls.operation_id + JOIN hafd.operation_types hot ON hot.id = ls.op_type_id + LEFT JOIN hive.transactions_view htv ON htv.block_num = ls.block_num AND htv.trx_in_block = ov.trx_in_block + ), + -- filter too long operation bodies + result_query AS ( + SELECT + (filtered_operations.composite).body, + filtered_operations.block_num, + filtered_operations.trx_hash, + filtered_operations.op_pos, + filtered_operations.op_type_id, + filtered_operations.created_at, + filtered_operations.is_virtual, + filtered_operations.id::TEXT, + filtered_operations.trx_in_block::SMALLINT + FROM ( + SELECT hafah_backend.operation_body_filter(ov.body, ov.id, _body_limit) as composite, ov.id, ov.block_num, ov.trx_in_block, ov.trx_hash, ov.op_pos, ov.op_type_id, ov.is_virtual, hb.created_at + FROM operation_range ov + JOIN hive.blocks_view hb ON hb.num = ov.block_num + ) filtered_operations + ORDER BY filtered_operations.id DESC + ) + SELECT array_agg(rows ORDER BY rows.id::BIGINT DESC) + INTO _result + FROM ( + SELECT + s.body, + s.block_num, + s.trx_hash, + s.op_pos, + s.op_type_id, + s.created_at, + s.is_virtual, + s.id::TEXT, + s.trx_in_block::SMALLINT + FROM result_query s + ) rows; + + ---------------------------------------- + RETURN ( + COALESCE(_ops_count,0), + COALESCE(_calculate_pages.total_pages,0), + (_account_range.from_block, _account_range.to_block)::hafah_backend.block_range_type, + COALESCE(_result, '{}'::hafah_backend.operation[]) + )::hafah_backend.account_operation_history; + +END +$$; + +RESET ROLE; diff --git a/queries/hafah_rest_backend/account_history/filtering_functions/exclude_account.sql b/queries/hafah_rest_backend/account_history/filtering_functions/exclude_account.sql new file mode 100644 index 0000000000000000000000000000000000000000..d574202cc7c6780a51a10a7ac620f0de09ff0173 --- /dev/null +++ b/queries/hafah_rest_backend/account_history/filtering_functions/exclude_account.sql @@ -0,0 +1,258 @@ +SET ROLE hafah_owner; + +CREATE OR REPLACE FUNCTION hafah_backend.account_history_exclude_account( + _account_id INT, + _operations INT [], + _transacting_account_id INT, + _from_block INT, + _to_block INT, + _page INT, + _body_limit INT, + _limit INT +) +RETURNS hafah_backend.account_operation_history -- noqa: LT01, CP05 +LANGUAGE 'plpgsql' STABLE +COST 10000 +SET JIT = OFF +SET join_collapse_limit = 16 +SET from_collapse_limit = 16 +AS +$$ +DECLARE + _result hafah_backend.operation[]; + _account_range hafah_backend.account_filter_return; + __max_page_count INT := 10; + + __total_pages INT; + __min_block_num INT; + __count INT; +BEGIN + -----------PAGING LOGIC---------------- + _account_range := hafah_backend.account_range(_operations, _account_id, _from_block, _to_block); + + -- Fetching operations + WITH operation_range AS MATERIALIZED ( + SELECT + ls.operation_id AS id, + ls.block_num, + ls.op_type_id, + ls.account_op_seq_no, + ROW_NUMBER() OVER (ORDER BY ls.operation_id DESC) AS row_num -- used to determine if last 2 records are in the same block (when page is saturated) + FROM ( + SELECT aov.operation_id, aov.op_type_id, aov.block_num, aov.account_op_seq_no + FROM hive.account_operations_view aov + WHERE aov.account_id = _account_id + AND aov.transacting_account_id != _transacting_account_id + AND aov.transacting_account_id IS NOT NULL -- for future compatibility + AND (_operations IS NULL OR aov.op_type_id = ANY(_operations)) + AND aov.account_op_seq_no >= _account_range.from_seq + AND aov.account_op_seq_no <= _account_range.to_seq + ORDER BY aov.account_op_seq_no DESC + LIMIT (__max_page_count * _limit) + 1 -- by default operation filter is limited to 10 pages + -- The +1 is to check if there are more operations in block that are not included in the current page-range + ) ls + ), + -----------PAGING LOGIC---------------- + -- Calculating pages based on result set (operation_range) + -- there is corner case when the last two operations are in the same block + -- when generating next batch of pages, we may miss operations made in the same block + -- to prevent this, we check if the last two operations are in the same block + -- if there at least 2 operations in the same block, we fetch all operations in that block + -- and add them to the result set + -- and calculate pages based on the new result set + check_if_saturated AS ( -- if not saturated, returns empty + SELECT ( + CASE + WHEN MAX(row_num) = (__max_page_count * _limit) + 1 THEN + (__max_page_count * _limit) + 1 + ELSE + NULL + END + ) AS count + FROM operation_range + ), + if_saturated_find_last_two_ops AS ( -- if not saturated, returns empty + SELECT + orr.block_num, + orr.account_op_seq_no + FROM operation_range orr + WHERE orr.row_num IN ( + (SELECT count FROM check_if_saturated), + (SELECT count - 1 FROM check_if_saturated) + ) + ), + block_check AS MATERIALIZED ( -- if not saturated, returns empty + SELECT ( + CASE + WHEN COUNT(DISTINCT block_num) = 1 THEN + MIN(block_num) + ELSE + NULL + END + ) AS block_num, + ( + CASE + WHEN COUNT(DISTINCT block_num) = 1 THEN + MIN(account_op_seq_no) + ELSE + NULL + END + ) AS account_op_seq_no + FROM if_saturated_find_last_two_ops + ), + -- returns empty if the last two operations are not in the same block + -- if the last two operations are in the same block, next CTE returns all operations in that block + find_all_records_for_page AS ( -- if not saturated, returns empty + SELECT + aov.operation_id AS id, + aov.op_type_id, + aov.block_num + FROM hive.account_operations_view aov + WHERE aov.account_id = _account_id + AND aov.transacting_account_id != _transacting_account_id + AND aov.transacting_account_id IS NOT NULL -- for future compatibility + AND (_operations IS NULL OR aov.op_type_id = ANY(_operations)) + AND aov.account_op_seq_no >= _account_range.from_seq + AND ( + (SELECT account_op_seq_no FROM block_check) IS NOT NULL + AND aov.account_op_seq_no <= (SELECT account_op_seq_no FROM block_check) + ) + AND ( + (SELECT block_num FROM block_check) IS NOT NULL + AND aov.block_num = (SELECT block_num FROM block_check) + ) + ORDER BY aov.account_op_seq_no DESC + ), + union_operations AS MATERIALIZED ( + SELECT + id, + block_num, + op_type_id + FROM operation_range + WHERE row_num <= (__max_page_count * _limit) -- limit to the maximum number of rows for the page and remove the extra row + -- if block_check is not NULL, exclude the operations from last block + -- operations from excluded block are fetched in find_all_records_for_page + UNION ALL + + SELECT + id, + block_num, + op_type_id + FROM find_all_records_for_page + ), + min_block_num AS ( + SELECT + MIN(block_num) AS block_num + FROM union_operations + ), + count_blocks AS MATERIALIZED ( + SELECT + COUNT(*) AS count + FROM union_operations + ), + calculate_pages AS MATERIALIZED ( + SELECT + total_pages, + offset_filter, + limit_filter + FROM hafah_backend.calculate_pages( + (SELECT count FROM count_blocks)::INT, + _page, + 'desc', + _limit + ) + ), + filter_page AS ( + SELECT * + FROM union_operations + ORDER BY id DESC + OFFSET (SELECT offset_filter FROM calculate_pages) + LIMIT (SELECT limit_filter FROM calculate_pages) + ), + -----------END PAGING LOGIC---------------- + -- join the operations with other necessary tables + join_tables AS ( + SELECT + ls.id, + ls.block_num, + ov.trx_in_block, + -- subquery is more stable than using LEFT JOIN with hive.transactions_view + (SELECT encode(htv.trx_hash, 'hex') FROM hive.transactions_view htv WHERE htv.block_num = ls.block_num AND htv.trx_in_block = ov.trx_in_block) AS trx_hash, + ov.op_pos, + ls.op_type_id, + ov.body, + hot.is_virtual + FROM ( + SELECT aov.id, aov.op_type_id, aov.block_num + FROM filter_page aov + ) ls + JOIN hive.operations_view ov ON ov.id = ls.id + JOIN hafd.operation_types hot ON hot.id = ls.op_type_id + --LEFT JOIN hive.transactions_view htv ON htv.block_num = ls.block_num AND htv.trx_in_block = ov.trx_in_block + ), + -- filter too long operation bodies + result_query AS ( + SELECT + (filtered_operations.composite).body, + filtered_operations.block_num, + filtered_operations.trx_hash, + filtered_operations.op_pos, + filtered_operations.op_type_id, + filtered_operations.created_at, + filtered_operations.is_virtual, + filtered_operations.id, + filtered_operations.trx_in_block + FROM ( + SELECT hafah_backend.operation_body_filter(ov.body, ov.id, _body_limit) as composite, ov.id, ov.block_num, ov.trx_in_block, ov.trx_hash, ov.op_pos, ov.op_type_id, ov.is_virtual, hb.created_at + FROM join_tables ov + JOIN hive.blocks_view hb ON hb.num = ov.block_num + ) filtered_operations + ORDER BY filtered_operations.id DESC + ) + SELECT + (SELECT count FROM count_blocks), + (SELECT total_pages FROM calculate_pages), + (SELECT block_num FROM min_block_num), + ( + SELECT array_agg(rows ORDER BY rows.id::BIGINT DESC) + FROM ( + SELECT + s.body, + s.block_num, + s.trx_hash, + s.op_pos, + s.op_type_id, + s.created_at, + s.is_virtual, + s.id::TEXT, + s.trx_in_block::SMALLINT + FROM result_query s + ) rows + ) + INTO __count, __total_pages, __min_block_num, _result; + + -- 1. If the min block number is NULL - the result is empty - there are no results for whole provided range + -- 2. If the min block number is NOT NULL and pages are not fully saturated it means there is no more blocks to fetch + -- 3. (ELSE) If the min block number is NOT NULL - the result is not empty - there are results for the provided range + -- and the min block number can be used as filter in the next API call (as a to-block parameter) + _account_range.from_block := ( + CASE + WHEN __min_block_num IS NULL THEN _account_range.from_block + WHEN __min_block_num IS NOT NULL AND __min_block_num = 1 THEN 1 + WHEN __min_block_num IS NOT NULL AND __min_block_num != 1 AND __count < __max_page_count * _limit THEN _account_range.from_block + ELSE __min_block_num - 1 + END + ); + + ---------------------------------------- + RETURN ( + COALESCE(__count,0), + COALESCE(__total_pages,0), + (_account_range.from_block, _account_range.to_block)::hafah_backend.block_range_type, + COALESCE(_result, '{}'::hafah_backend.operation[]) + )::hafah_backend.account_operation_history; + +END +$$; + +RESET ROLE; diff --git a/queries/hafah_rest_backend/account_history/filtering_functions/excluding_accounts.sql b/queries/hafah_rest_backend/account_history/filtering_functions/excluding_accounts.sql new file mode 100644 index 0000000000000000000000000000000000000000..b4cfb0b804fbd771a93e927dadc2ff810ed0b3a0 --- /dev/null +++ b/queries/hafah_rest_backend/account_history/filtering_functions/excluding_accounts.sql @@ -0,0 +1,274 @@ +SET ROLE hafah_owner; + +CREATE OR REPLACE FUNCTION hafah_backend.account_history_excluding_accounts( + _account_id INT, + _operations INT [], + _transacting_account_ids INT [], + _from_block INT, + _to_block INT, + _page INT, + _body_limit INT, + _limit INT +) +RETURNS hafah_backend.account_operation_history -- noqa: LT01, CP05 +LANGUAGE 'plpgsql' STABLE +COST 10000 +SET JIT = OFF +SET join_collapse_limit = 16 +SET from_collapse_limit = 16 +AS +$$ +DECLARE + _result hafah_backend.operation[]; + _account_range hafah_backend.account_filter_return; + __max_page_count INT := 10; + + __total_pages INT; + __min_block_num INT; + __count INT; +BEGIN + -----------PAGING LOGIC---------------- + _account_range := hafah_backend.account_range(_operations, _account_id, _from_block, _to_block); + + -- Fetching operations + WITH /* excluded_ids AS MATERIALIZED ( + SELECT unnest(_transacting_account_ids) AS id + ), */ + operation_range AS MATERIALIZED ( + SELECT + ls.operation_id AS id, + ls.block_num, + ls.op_type_id, + ls.account_op_seq_no, + ROW_NUMBER() OVER (ORDER BY ls.operation_id DESC) AS row_num -- used to determine if last 2 records are in the same block (when page is saturated) + FROM ( + SELECT aov.operation_id, aov.op_type_id, aov.block_num, aov.account_op_seq_no + FROM hive.account_operations_view aov + WHERE aov.account_id = _account_id + AND (_operations IS NULL OR aov.op_type_id = ANY(_operations)) + AND aov.account_op_seq_no >= _account_range.from_seq + AND aov.account_op_seq_no <= _account_range.to_seq + AND aov.transacting_account_id != ANY(_transacting_account_ids) -- exclude all transacting accounts + AND aov.transacting_account_id IS NOT NULL -- for future compatibility + /* + AND NOT EXISTS ( + SELECT 1 FROM excluded_ids e + WHERE e.id = aov.transacting_account_id + ) + */ + ORDER BY aov.account_op_seq_no DESC + LIMIT (__max_page_count * _limit) + 1 -- by default operation filter is limited to 10 pages + -- The +1 is to check if there are more operations in block that are not included in the current page-range + ) ls + ), + -----------PAGING LOGIC---------------- + -- Calculating pages based on result set (operation_range) + -- there is corner case when the last two operations are in the same block + -- when generating next batch of pages, we may miss operations made in the same block + -- to prevent this, we check if the last two operations are in the same block + -- if there at least 2 operations in the same block, we fetch all operations in that block + -- and add them to the result set + -- and calculate pages based on the new result set + check_if_saturated AS ( -- if not saturated, returns empty + SELECT ( + CASE + WHEN MAX(row_num) = (__max_page_count * _limit) + 1 THEN + (__max_page_count * _limit) + 1 + ELSE + NULL + END + ) AS count + FROM operation_range + ), + if_saturated_find_last_two_ops AS ( -- if not saturated, returns empty + SELECT + orr.block_num, + orr.account_op_seq_no + FROM operation_range orr + WHERE orr.row_num IN ( + (SELECT count FROM check_if_saturated), + (SELECT count - 1 FROM check_if_saturated) + ) + ), + block_check AS MATERIALIZED ( -- if not saturated, returns empty + SELECT ( + CASE + WHEN COUNT(DISTINCT block_num) = 1 THEN + MIN(block_num) + ELSE + NULL + END + ) AS block_num, + ( + CASE + WHEN COUNT(DISTINCT block_num) = 1 THEN + MIN(account_op_seq_no) + ELSE + NULL + END + ) AS account_op_seq_no + FROM if_saturated_find_last_two_ops + ), + -- returns empty if the last two operations are not in the same block + -- if the last two operations are in the same block, next CTE returns all operations in that block + find_all_records_for_page AS ( -- if not saturated, returns empty + SELECT + aov.operation_id AS id, + aov.op_type_id, + aov.block_num + FROM hive.account_operations_view aov + WHERE aov.account_id = _account_id + AND (_operations IS NULL OR aov.op_type_id = ANY(_operations)) + AND aov.transacting_account_id != ANY(_transacting_account_ids) -- exclude all transacting accounts + AND aov.transacting_account_id IS NOT NULL -- for future compatibility + AND aov.account_op_seq_no >= _account_range.from_seq +-- AND aov.account_op_seq_no <= _account_range.to_seq + AND ( + (SELECT account_op_seq_no FROM block_check) IS NOT NULL + AND aov.account_op_seq_no <= (SELECT account_op_seq_no FROM block_check) + ) + AND ( + (SELECT block_num FROM block_check) IS NOT NULL + AND aov.block_num = (SELECT block_num FROM block_check) + ) + /* + AND NOT EXISTS ( + SELECT 1 FROM excluded_ids e + WHERE e.id = aov.transacting_account_id + ) + */ + ORDER BY aov.account_op_seq_no DESC + ), + union_operations AS MATERIALIZED ( + SELECT + id, + block_num, + op_type_id + FROM operation_range + WHERE row_num <= (__max_page_count * _limit) -- limit to the maximum number of rows for the page and remove the extra row + -- if block_check is not NULL, exclude the operations from last block + -- operations from excluded block are fetched in find_all_records_for_page + UNION ALL + + SELECT + id, + block_num, + op_type_id + FROM find_all_records_for_page + ), + min_block_num AS ( + SELECT + MIN(block_num) AS block_num + FROM union_operations + ), + count_blocks AS MATERIALIZED ( + SELECT + COUNT(*) AS count + FROM union_operations + ), + calculate_pages AS MATERIALIZED ( + SELECT + total_pages, + offset_filter, + limit_filter + FROM hafah_backend.calculate_pages( + (SELECT count FROM count_blocks)::INT, + _page, + 'desc', + _limit + ) + ), + filter_page AS ( + SELECT * + FROM union_operations + ORDER BY id DESC + OFFSET (SELECT offset_filter FROM calculate_pages) + LIMIT (SELECT limit_filter FROM calculate_pages) + ), + -----------END PAGING LOGIC---------------- + -- join the operations with other necessary tables + join_tables AS ( + SELECT + ls.id, + ls.block_num, + ov.trx_in_block, + -- subquery is more stable than using LEFT JOIN with hive.transactions_view + (SELECT encode(htv.trx_hash, 'hex') FROM hive.transactions_view htv WHERE htv.block_num = ls.block_num AND htv.trx_in_block = ov.trx_in_block) AS trx_hash, + ov.op_pos, + ls.op_type_id, + ov.body, + hot.is_virtual + FROM ( + SELECT aov.id, aov.op_type_id, aov.block_num + FROM filter_page aov + ) ls + JOIN hive.operations_view ov ON ov.id = ls.id + JOIN hafd.operation_types hot ON hot.id = ls.op_type_id + --LEFT JOIN hive.transactions_view htv ON htv.block_num = ls.block_num AND htv.trx_in_block = ov.trx_in_block + ), + -- filter too long operation bodies + result_query AS ( + SELECT + (filtered_operations.composite).body, + filtered_operations.block_num, + filtered_operations.trx_hash, + filtered_operations.op_pos, + filtered_operations.op_type_id, + filtered_operations.created_at, + filtered_operations.is_virtual, + filtered_operations.id, + filtered_operations.trx_in_block + FROM ( + SELECT hafah_backend.operation_body_filter(ov.body, ov.id, _body_limit) as composite, ov.id, ov.block_num, ov.trx_in_block, ov.trx_hash, ov.op_pos, ov.op_type_id, ov.is_virtual, hb.created_at + FROM join_tables ov + JOIN hive.blocks_view hb ON hb.num = ov.block_num + ) filtered_operations + ORDER BY filtered_operations.id DESC + ) + SELECT + (SELECT count FROM count_blocks), + (SELECT total_pages FROM calculate_pages), + (SELECT block_num FROM min_block_num), + ( + SELECT array_agg(rows ORDER BY rows.id::BIGINT DESC) + FROM ( + SELECT + s.body, + s.block_num, + s.trx_hash, + s.op_pos, + s.op_type_id, + s.created_at, + s.is_virtual, + s.id::TEXT, + s.trx_in_block::SMALLINT + FROM result_query s + ) rows + ) + INTO __count, __total_pages, __min_block_num, _result; + + -- 1. If the min block number is NULL - the result is empty - there are no results for whole provided range + -- 2. If the min block number is NOT NULL and pages are not fully saturated it means there is no more blocks to fetch + -- 3. (ELSE) If the min block number is NOT NULL - the result is not empty - there are results for the provided range + -- and the min block number can be used as filter in the next API call (as a to-block parameter) + _account_range.from_block := ( + CASE + WHEN __min_block_num IS NULL THEN _account_range.from_block + WHEN __min_block_num IS NOT NULL AND __min_block_num = 1 THEN 1 + WHEN __min_block_num IS NOT NULL AND __min_block_num != 1 AND __count < __max_page_count * _limit THEN _account_range.from_block + ELSE __min_block_num - 1 + END + ); + + ---------------------------------------- + RETURN ( + COALESCE(__count,0), + COALESCE(__total_pages,0), + (_account_range.from_block, _account_range.to_block)::hafah_backend.block_range_type, + COALESCE(_result, '{}'::hafah_backend.operation[]) + )::hafah_backend.account_operation_history; + +END +$$; + +RESET ROLE; diff --git a/queries/hafah_rest_backend/account_history/filtering_functions/include_account.sql b/queries/hafah_rest_backend/account_history/filtering_functions/include_account.sql new file mode 100644 index 0000000000000000000000000000000000000000..d1175d1cae869127d5087e7a62638c90c2145893 --- /dev/null +++ b/queries/hafah_rest_backend/account_history/filtering_functions/include_account.sql @@ -0,0 +1,256 @@ +SET ROLE hafah_owner; + +CREATE OR REPLACE FUNCTION hafah_backend.account_history_include_account( + _account_id INT, + _operations INT [], + _transacting_account_id INT, + _from_block INT, + _to_block INT, + _page INT, + _body_limit INT, + _limit INT +) +RETURNS hafah_backend.account_operation_history -- noqa: LT01, CP05 +LANGUAGE 'plpgsql' STABLE +COST 10000 +SET JIT = OFF +SET join_collapse_limit = 16 +SET from_collapse_limit = 16 +AS +$$ +DECLARE + _result hafah_backend.operation[]; + _account_range hafah_backend.account_filter_return; + __max_page_count INT := 10; + + __total_pages INT; + __min_block_num INT; + __count INT; +BEGIN + -----------PAGING LOGIC---------------- + _account_range := hafah_backend.account_range(_operations, _account_id, _from_block, _to_block); + + -- Fetching operations + WITH operation_range AS MATERIALIZED ( + SELECT + ls.operation_id AS id, + ls.block_num, + ls.op_type_id, + ls.account_op_seq_no, + ROW_NUMBER() OVER (ORDER BY ls.operation_id DESC) AS row_num -- used to determine if last 2 records are in the same block (when page is saturated) + FROM ( + SELECT aov.operation_id, aov.op_type_id, aov.block_num, aov.account_op_seq_no + FROM hive.account_operations_view aov + WHERE aov.account_id = _account_id + AND aov.transacting_account_id = _transacting_account_id + AND (_operations IS NULL OR aov.op_type_id = ANY(_operations)) + AND aov.account_op_seq_no >= _account_range.from_seq + AND aov.account_op_seq_no <= _account_range.to_seq + ORDER BY aov.account_op_seq_no DESC + LIMIT (__max_page_count * _limit) + 1 -- by default operation filter is limited to 10 pages + -- The +1 is to check if there are more operations in block that are not included in the current page-range + ) ls + ), + -----------PAGING LOGIC---------------- + -- Calculating pages based on result set (operation_range) + -- there is corner case when the last two operations are in the same block + -- when generating next batch of pages, we may miss operations made in the same block + -- to prevent this, we check if the last two operations are in the same block + -- if there at least 2 operations in the same block, we fetch all operations in that block + -- and add them to the result set + -- and calculate pages based on the new result set + check_if_saturated AS ( -- if not saturated, returns empty + SELECT ( + CASE + WHEN MAX(row_num) = (__max_page_count * _limit) + 1 THEN + (__max_page_count * _limit) + 1 + ELSE + NULL + END + ) AS count + FROM operation_range + ), + if_saturated_find_last_two_ops AS ( -- if not saturated, returns empty + SELECT + orr.block_num, + orr.account_op_seq_no + FROM operation_range orr + WHERE orr.row_num IN ( + (SELECT count FROM check_if_saturated), + (SELECT count - 1 FROM check_if_saturated) + ) + ), + block_check AS MATERIALIZED ( -- if not saturated, returns empty + SELECT ( + CASE + WHEN COUNT(DISTINCT block_num) = 1 THEN + MIN(block_num) + ELSE + NULL + END + ) AS block_num, + ( + CASE + WHEN COUNT(DISTINCT block_num) = 1 THEN + MIN(account_op_seq_no) + ELSE + NULL + END + ) AS account_op_seq_no + FROM if_saturated_find_last_two_ops + ), + -- returns empty if the last two operations are not in the same block + -- if the last two operations are in the same block, next CTE returns all operations in that block + find_all_records_for_page AS ( -- if not saturated, returns empty + SELECT + aov.operation_id AS id, + aov.op_type_id, + aov.block_num + FROM hive.account_operations_view aov + WHERE aov.account_id = _account_id + AND aov.transacting_account_id = _transacting_account_id + AND (_operations IS NULL OR aov.op_type_id = ANY(_operations)) + AND aov.account_op_seq_no >= _account_range.from_seq + AND ( + (SELECT account_op_seq_no FROM block_check) IS NOT NULL + AND aov.account_op_seq_no <= (SELECT account_op_seq_no FROM block_check) + ) + AND ( + (SELECT block_num FROM block_check) IS NOT NULL + AND aov.block_num = (SELECT block_num FROM block_check) + ) + ORDER BY aov.account_op_seq_no DESC + ), + union_operations AS MATERIALIZED ( + SELECT + id, + block_num, + op_type_id + FROM operation_range + WHERE row_num <= (__max_page_count * _limit) -- limit to the maximum number of rows for the page and remove the extra row + -- if block_check is not NULL, exclude the operations from last block + -- operations from excluded block are fetched in find_all_records_for_page + UNION ALL + + SELECT + id, + block_num, + op_type_id + FROM find_all_records_for_page + ), + min_block_num AS ( + SELECT + MIN(block_num) AS block_num + FROM union_operations + ), + count_blocks AS MATERIALIZED ( + SELECT + COUNT(*) AS count + FROM union_operations + ), + calculate_pages AS MATERIALIZED ( + SELECT + total_pages, + offset_filter, + limit_filter + FROM hafah_backend.calculate_pages( + (SELECT count FROM count_blocks)::INT, + _page, + 'desc', + _limit + ) + ), + filter_page AS ( + SELECT * + FROM union_operations + ORDER BY id DESC + OFFSET (SELECT offset_filter FROM calculate_pages) + LIMIT (SELECT limit_filter FROM calculate_pages) + ), + -----------END PAGING LOGIC---------------- + -- join the operations with other necessary tables + join_tables AS ( + SELECT + ls.id, + ls.block_num, + ov.trx_in_block, + -- subquery is more stable than using LEFT JOIN with hive.transactions_view + (SELECT encode(htv.trx_hash, 'hex') FROM hive.transactions_view htv WHERE htv.block_num = ls.block_num AND htv.trx_in_block = ov.trx_in_block) AS trx_hash, + ov.op_pos, + ls.op_type_id, + ov.body, + hot.is_virtual + FROM ( + SELECT aov.id, aov.op_type_id, aov.block_num + FROM filter_page aov + ) ls + JOIN hive.operations_view ov ON ov.id = ls.id + JOIN hafd.operation_types hot ON hot.id = ls.op_type_id + --LEFT JOIN hive.transactions_view htv ON htv.block_num = ls.block_num AND htv.trx_in_block = ov.trx_in_block + ), + -- filter too long operation bodies + result_query AS ( + SELECT + (filtered_operations.composite).body, + filtered_operations.block_num, + filtered_operations.trx_hash, + filtered_operations.op_pos, + filtered_operations.op_type_id, + filtered_operations.created_at, + filtered_operations.is_virtual, + filtered_operations.id, + filtered_operations.trx_in_block + FROM ( + SELECT hafah_backend.operation_body_filter(ov.body, ov.id, _body_limit) as composite, ov.id, ov.block_num, ov.trx_in_block, ov.trx_hash, ov.op_pos, ov.op_type_id, ov.is_virtual, hb.created_at + FROM join_tables ov + JOIN hive.blocks_view hb ON hb.num = ov.block_num + ) filtered_operations + ORDER BY filtered_operations.id DESC + ) + SELECT + (SELECT count FROM count_blocks), + (SELECT total_pages FROM calculate_pages), + (SELECT block_num FROM min_block_num), + ( + SELECT array_agg(rows ORDER BY rows.id::BIGINT DESC) + FROM ( + SELECT + s.body, + s.block_num, + s.trx_hash, + s.op_pos, + s.op_type_id, + s.created_at, + s.is_virtual, + s.id::TEXT, + s.trx_in_block::SMALLINT + FROM result_query s + ) rows + ) + INTO __count, __total_pages, __min_block_num, _result; + + -- 1. If the min block number is NULL - the result is empty - there are no results for whole provided range + -- 2. If the min block number is NOT NULL and pages are not fully saturated it means there is no more blocks to fetch + -- 3. (ELSE) If the min block number is NOT NULL - the result is not empty - there are results for the provided range + -- and the min block number can be used as filter in the next API call (as a to-block parameter) + _account_range.from_block := ( + CASE + WHEN __min_block_num IS NULL THEN _account_range.from_block + WHEN __min_block_num IS NOT NULL AND __min_block_num = 1 THEN 1 + WHEN __min_block_num IS NOT NULL AND __min_block_num != 1 AND __count < __max_page_count * _limit THEN _account_range.from_block + ELSE __min_block_num - 1 + END + ); + + ---------------------------------------- + RETURN ( + COALESCE(__count,0), + COALESCE(__total_pages,0), + (_account_range.from_block, _account_range.to_block)::hafah_backend.block_range_type, + COALESCE(_result, '{}'::hafah_backend.operation[]) + )::hafah_backend.account_operation_history; + +END +$$; + +RESET ROLE; diff --git a/queries/hafah_rest_backend/account_history/filtering_functions/including_accounts.sql b/queries/hafah_rest_backend/account_history/filtering_functions/including_accounts.sql new file mode 100644 index 0000000000000000000000000000000000000000..92416304a251994624247bce4e79dc6b70033224 --- /dev/null +++ b/queries/hafah_rest_backend/account_history/filtering_functions/including_accounts.sql @@ -0,0 +1,256 @@ +SET ROLE hafah_owner; + +CREATE OR REPLACE FUNCTION hafah_backend.account_history_including_accounts( + _account_id INT, + _operations INT [], + _transacting_account_ids INT [], + _from_block INT, + _to_block INT, + _page INT, + _body_limit INT, + _limit INT +) +RETURNS hafah_backend.account_operation_history -- noqa: LT01, CP05 +LANGUAGE 'plpgsql' STABLE +COST 10000 +SET JIT = OFF +SET join_collapse_limit = 16 +SET from_collapse_limit = 16 +AS +$$ +DECLARE + _result hafah_backend.operation[]; + _account_range hafah_backend.account_filter_return; + __max_page_count INT := 10; + + __total_pages INT; + __min_block_num INT; + __count INT; +BEGIN + -----------PAGING LOGIC---------------- + _account_range := hafah_backend.account_range(_operations, _account_id, _from_block, _to_block); + + -- Fetching operations + WITH operation_range AS MATERIALIZED ( + SELECT + ls.operation_id AS id, + ls.block_num, + ls.op_type_id, + ls.account_op_seq_no, + ROW_NUMBER() OVER (ORDER BY ls.operation_id DESC) AS row_num -- used to determine if last 2 records are in the same block (when page is saturated) + FROM ( + SELECT aov.operation_id, aov.op_type_id, aov.block_num, aov.account_op_seq_no + FROM hive.account_operations_view aov + WHERE aov.account_id = _account_id + AND aov.transacting_account_id = ANY(_transacting_account_ids) + AND (_operations IS NULL OR aov.op_type_id = ANY(_operations)) + AND aov.account_op_seq_no >= _account_range.from_seq + AND aov.account_op_seq_no <= _account_range.to_seq + ORDER BY aov.account_op_seq_no DESC + LIMIT (__max_page_count * _limit) + 1 -- by default operation filter is limited to 10 pages + -- The +1 is to check if there are more operations in block that are not included in the current page-range + ) ls + ), + -----------PAGING LOGIC---------------- + -- Calculating pages based on result set (operation_range) + -- there is corner case when the last two operations are in the same block + -- when generating next batch of pages, we may miss operations made in the same block + -- to prevent this, we check if the last two operations are in the same block + -- if there at least 2 operations in the same block, we fetch all operations in that block + -- and add them to the result set + -- and calculate pages based on the new result set + check_if_saturated AS ( -- if not saturated, returns empty + SELECT ( + CASE + WHEN MAX(row_num) = (__max_page_count * _limit) + 1 THEN + (__max_page_count * _limit) + 1 + ELSE + NULL + END + ) AS count + FROM operation_range + ), + if_saturated_find_last_two_ops AS ( -- if not saturated, returns empty + SELECT + orr.block_num, + orr.account_op_seq_no + FROM operation_range orr + WHERE orr.row_num IN ( + (SELECT count FROM check_if_saturated), + (SELECT count - 1 FROM check_if_saturated) + ) + ), + block_check AS MATERIALIZED ( -- if not saturated, returns empty + SELECT ( + CASE + WHEN COUNT(DISTINCT block_num) = 1 THEN + MIN(block_num) + ELSE + NULL + END + ) AS block_num, + ( + CASE + WHEN COUNT(DISTINCT block_num) = 1 THEN + MIN(account_op_seq_no) + ELSE + NULL + END + ) AS account_op_seq_no + FROM if_saturated_find_last_two_ops + ), + -- returns empty if the last two operations are not in the same block + -- if the last two operations are in the same block, next CTE returns all operations in that block + find_all_records_for_page AS ( -- if not saturated, returns empty + SELECT + aov.operation_id AS id, + aov.op_type_id, + aov.block_num + FROM hive.account_operations_view aov + WHERE aov.account_id = _account_id + AND aov.transacting_account_id = _transacting_account_id + AND (_operations IS NULL OR aov.op_type_id = ANY(_operations)) + AND aov.account_op_seq_no >= _account_range.from_seq + AND ( + (SELECT account_op_seq_no FROM block_check) IS NOT NULL + AND aov.account_op_seq_no <= (SELECT account_op_seq_no FROM block_check) + ) + AND ( + (SELECT block_num FROM block_check) IS NOT NULL + AND aov.block_num = (SELECT block_num FROM block_check) + ) + ORDER BY aov.account_op_seq_no DESC + ), + union_operations AS MATERIALIZED ( + SELECT + id, + block_num, + op_type_id + FROM operation_range + WHERE row_num <= (__max_page_count * _limit) -- limit to the maximum number of rows for the page and remove the extra row + -- if block_check is not NULL, exclude the operations from last block + -- operations from excluded block are fetched in find_all_records_for_page + UNION ALL + + SELECT + id, + block_num, + op_type_id + FROM find_all_records_for_page + ), + min_block_num AS ( + SELECT + MIN(block_num) AS block_num + FROM union_operations + ), + count_blocks AS MATERIALIZED ( + SELECT + COUNT(*) AS count + FROM union_operations + ), + calculate_pages AS MATERIALIZED ( + SELECT + total_pages, + offset_filter, + limit_filter + FROM hafah_backend.calculate_pages( + (SELECT count FROM count_blocks)::INT, + _page, + 'desc', + _limit + ) + ), + filter_page AS ( + SELECT * + FROM union_operations + ORDER BY id DESC + OFFSET (SELECT offset_filter FROM calculate_pages) + LIMIT (SELECT limit_filter FROM calculate_pages) + ), + -----------END PAGING LOGIC---------------- + -- join the operations with other necessary tables + join_tables AS ( + SELECT + ls.id, + ls.block_num, + ov.trx_in_block, + -- subquery is more stable than using LEFT JOIN with hive.transactions_view + (SELECT encode(htv.trx_hash, 'hex') FROM hive.transactions_view htv WHERE htv.block_num = ls.block_num AND htv.trx_in_block = ov.trx_in_block) AS trx_hash, + ov.op_pos, + ls.op_type_id, + ov.body, + hot.is_virtual + FROM ( + SELECT aov.id, aov.op_type_id, aov.block_num + FROM filter_page aov + ) ls + JOIN hive.operations_view ov ON ov.id = ls.id + JOIN hafd.operation_types hot ON hot.id = ls.op_type_id + --LEFT JOIN hive.transactions_view htv ON htv.block_num = ls.block_num AND htv.trx_in_block = ov.trx_in_block + ), + -- filter too long operation bodies + result_query AS ( + SELECT + (filtered_operations.composite).body, + filtered_operations.block_num, + filtered_operations.trx_hash, + filtered_operations.op_pos, + filtered_operations.op_type_id, + filtered_operations.created_at, + filtered_operations.is_virtual, + filtered_operations.id, + filtered_operations.trx_in_block + FROM ( + SELECT hafah_backend.operation_body_filter(ov.body, ov.id, _body_limit) as composite, ov.id, ov.block_num, ov.trx_in_block, ov.trx_hash, ov.op_pos, ov.op_type_id, ov.is_virtual, hb.created_at + FROM join_tables ov + JOIN hive.blocks_view hb ON hb.num = ov.block_num + ) filtered_operations + ORDER BY filtered_operations.id DESC + ) + SELECT + (SELECT count FROM count_blocks), + (SELECT total_pages FROM calculate_pages), + (SELECT block_num FROM min_block_num), + ( + SELECT array_agg(rows ORDER BY rows.id::BIGINT DESC) + FROM ( + SELECT + s.body, + s.block_num, + s.trx_hash, + s.op_pos, + s.op_type_id, + s.created_at, + s.is_virtual, + s.id::TEXT, + s.trx_in_block::SMALLINT + FROM result_query s + ) rows + ) + INTO __count, __total_pages, __min_block_num, _result; + + -- 1. If the min block number is NULL - the result is empty - there are no results for whole provided range + -- 2. If the min block number is NOT NULL and pages are not fully saturated it means there is no more blocks to fetch + -- 3. (ELSE) If the min block number is NOT NULL - the result is not empty - there are results for the provided range + -- and the min block number can be used as filter in the next API call (as a to-block parameter) + _account_range.from_block := ( + CASE + WHEN __min_block_num IS NULL THEN _account_range.from_block + WHEN __min_block_num IS NOT NULL AND __min_block_num = 1 THEN 1 + WHEN __min_block_num IS NOT NULL AND __min_block_num != 1 AND __count < __max_page_count * _limit THEN _account_range.from_block + ELSE __min_block_num - 1 + END + ); + + ---------------------------------------- + RETURN ( + COALESCE(__count,0), + COALESCE(__total_pages,0), + (_account_range.from_block, _account_range.to_block)::hafah_backend.block_range_type, + COALESCE(_result, '{}'::hafah_backend.operation[]) + )::hafah_backend.account_operation_history; + +END +$$; + +RESET ROLE; diff --git a/queries/hafah_rest_backend/block.sql b/queries/hafah_rest_backend/blocks/block.sql similarity index 100% rename from queries/hafah_rest_backend/block.sql rename to queries/hafah_rest_backend/blocks/block.sql diff --git a/queries/hafah_rest_backend/block_header.sql b/queries/hafah_rest_backend/blocks/block_header.sql similarity index 100% rename from queries/hafah_rest_backend/block_header.sql rename to queries/hafah_rest_backend/blocks/block_header.sql diff --git a/queries/hafah_rest_backend/block_range.sql b/queries/hafah_rest_backend/blocks/block_range.sql similarity index 100% rename from queries/hafah_rest_backend/block_range.sql rename to queries/hafah_rest_backend/blocks/block_range.sql diff --git a/queries/hafah_rest_backend/acc_op_types.sql b/queries/hafah_rest_backend/operations/acc_op_types.sql similarity index 100% rename from queries/hafah_rest_backend/acc_op_types.sql rename to queries/hafah_rest_backend/operations/acc_op_types.sql diff --git a/queries/hafah_rest_backend/op_types.sql b/queries/hafah_rest_backend/operations/op_types.sql similarity index 100% rename from queries/hafah_rest_backend/op_types.sql rename to queries/hafah_rest_backend/operations/op_types.sql diff --git a/queries/hafah_rest_backend/operation.sql b/queries/hafah_rest_backend/operations/operation.sql similarity index 100% rename from queries/hafah_rest_backend/operation.sql rename to queries/hafah_rest_backend/operations/operation.sql diff --git a/queries/hafah_rest_backend/ops_in_block.sql b/queries/hafah_rest_backend/operations/ops_in_block.sql similarity index 100% rename from queries/hafah_rest_backend/ops_in_block.sql rename to queries/hafah_rest_backend/operations/ops_in_block.sql diff --git a/queries/hafah_rest_backend/utilities/account.sql b/queries/hafah_rest_backend/utilities/account.sql new file mode 100644 index 0000000000000000000000000000000000000000..f53e17de68b07fd4919f26124bf9301f88e6a82c --- /dev/null +++ b/queries/hafah_rest_backend/utilities/account.sql @@ -0,0 +1,27 @@ +SET ROLE hafah_owner; + +CREATE OR REPLACE FUNCTION hafah_backend.get_account_id(_account_name TEXT, _required BOOLEAN) +RETURNS INT STABLE +LANGUAGE 'plpgsql' +AS +$$ +DECLARE + _account_id INT := (SELECT av.id FROM hive.accounts_view av WHERE av.name = _account_name); +BEGIN + PERFORM hafah_backend.validate_account(_account_id, _account_name, _required); + + RETURN _account_id; +END +$$; + +CREATE OR REPLACE FUNCTION hafah_backend.get_account_name(_account_id INT) +RETURNS TEXT STABLE +LANGUAGE 'plpgsql' +AS +$$ +BEGIN + RETURN av.name FROM hive.accounts_view av WHERE av.id = _account_id; +END +$$; + +RESET ROLE; diff --git a/postgrest/hafah_rest_exceptions.sql b/queries/hafah_rest_backend/utilities/exceptions.sql similarity index 80% rename from postgrest/hafah_rest_exceptions.sql rename to queries/hafah_rest_backend/utilities/exceptions.sql index b058eb1c9acec27c07aee19a6674997103da78a2..f2053df428ec5942b6b7966bec3b5a5012a8abce 100644 --- a/postgrest/hafah_rest_exceptions.sql +++ b/queries/hafah_rest_backend/utilities/exceptions.sql @@ -108,4 +108,28 @@ END $$ ; +CREATE OR REPLACE FUNCTION hafah_backend.rest_raise_invalid_operation_types(_allowed_operations INT[]) +RETURNS VOID +LANGUAGE 'plpgsql' +IMMUTABLE +AS +$$ +BEGIN + RAISE EXCEPTION 'Invalid operation ID detected. Allowed IDs are: %', _allowed_operations; +END +$$ +; + +CREATE OR REPLACE FUNCTION hafah_backend.rest_raise_invalid_participation() +RETURNS VOID +LANGUAGE 'plpgsql' +IMMUTABLE +AS +$$ +BEGIN + RAISE EXCEPTION 'For participation mode ''all'', account name should not be provided'; +END +$$ +; + RESET ROLE; diff --git a/queries/hafah_rest_backend/utilities/function_helpers.sql b/queries/hafah_rest_backend/utilities/function_helpers.sql new file mode 100644 index 0000000000000000000000000000000000000000..02bbbd067fc406827ccaf0fe62c55780ae33e537 --- /dev/null +++ b/queries/hafah_rest_backend/utilities/function_helpers.sql @@ -0,0 +1,50 @@ +SET ROLE hafah_owner; + +CREATE OR REPLACE FUNCTION hafah_backend.is_block_missing(_block_num INT, _name TEXT DEFAULT 'block-num') +RETURNS VOID +LANGUAGE 'plpgsql' +IMMUTABLE +AS +$$ +BEGIN + IF _block_num IS NULL THEN + PERFORM hafah_backend.rest_raise_missing_arg(_name); + END IF; +END +$$ +; + +CREATE OR REPLACE FUNCTION hafah_backend.is_path_filter_not_empty(_path_filter TEXT[]) +RETURNS BOOLEAN +LANGUAGE 'plpgsql' +IMMUTABLE +AS +$$ +BEGIN + RETURN (_path_filter IS NOT NULL AND _path_filter != '{}'); +END +$$ +; + +CREATE OR REPLACE FUNCTION hafah_backend.get_group_type(_group_type hafah_backend.operation_group_types) +RETURNS BOOLEAN +LANGUAGE 'plpgsql' +IMMUTABLE +AS +$$ +BEGIN + RETURN ( + CASE + WHEN _group_type = 'real' THEN + FALSE + WHEN _group_type = 'virtual' THEN + TRUE + ELSE + NULL + END + ); +END +$$ +; + +RESET ROLE; diff --git a/queries/hafah_rest_backend/utilities/get_operation_types.sql b/queries/hafah_rest_backend/utilities/get_operation_types.sql new file mode 100644 index 0000000000000000000000000000000000000000..82a23031b692722127d56c24e5d27ca97bbf9a01 --- /dev/null +++ b/queries/hafah_rest_backend/utilities/get_operation_types.sql @@ -0,0 +1,41 @@ +SET ROLE hafah_owner; + +CREATE OR REPLACE FUNCTION hafah_backend.get_operation_types( + _operations TEXT, + _include_virtual BOOLEAN +) +RETURNS INT []-- noqa: LT01, CP05 +LANGUAGE 'plpgsql' STABLE +SET JIT = OFF +AS +$$ +DECLARE + _non_virtual_ops INT[]; + _all_ops INT[]; + _operation_ids INT[] := (SELECT string_to_array(_operations, ',')::INT[]); +BEGIN + IF _include_virtual IS TRUE THEN + + _all_ops := ( + SELECT array_agg(id)::INT[] + FROM hafd.operation_types + ); + + PERFORM hafah_backend.validate_operation_types(_operation_ids, _all_ops); + + RETURN _operation_ids; + END IF; + + _non_virtual_ops := ( + SELECT array_agg(id)::INT[] + FROM hafd.operation_types + WHERE is_virtual = FALSE + ); + + PERFORM hafah_backend.validate_operation_types(_operation_ids, _non_virtual_ops); + + RETURN _operation_ids; +END +$$; + +RESET ROLE; diff --git a/queries/hafah_rest_backend/path_filters.sql b/queries/hafah_rest_backend/utilities/operation_body_filter.sql similarity index 51% rename from queries/hafah_rest_backend/path_filters.sql rename to queries/hafah_rest_backend/utilities/operation_body_filter.sql index 84c63986b785e67b16dab3804cb1960b0f4ce947..e166b66add8746c383845143575b86c58392db68 100644 --- a/queries/hafah_rest_backend/path_filters.sql +++ b/queries/hafah_rest_backend/utilities/operation_body_filter.sql @@ -1,6 +1,5 @@ SET ROLE hafah_owner; - -- Function used in body returning functions that allows to limit too long operation_body (small.minion), allows FE to set desired length of operation -- Too long operations are being replaced by placeholder with possibility of opening it in another page DROP TYPE IF EXISTS hafah_backend.operation_body_filter_result CASCADE; -- noqa: LT01 @@ -35,54 +34,4 @@ BEGIN END $$; -CREATE OR REPLACE FUNCTION hafah_backend.decode_param(_encoded_param TEXT) -RETURNS TEXT -LANGUAGE 'plpgsql' -STABLE -AS -$$ -BEGIN - RETURN convert_from(decode(_encoded_param, 'base64'), 'UTF8'); -END -$$; - -CREATE OR REPLACE FUNCTION hafah_backend.parse_path_filters(_params TEXT[]) -RETURNS TABLE(param_json JSONB, param_text TEXT[]) -LANGUAGE 'plpgsql' -STABLE -AS -$$ -DECLARE - json_list JSONB := '[]'::JSONB; - text_list TEXT[] := '{}'; - param TEXT; - param_text TEXT; - key_value TEXT; - key_part TEXT[]; - value_part TEXT; -BEGIN - FOREACH param IN ARRAY _params - LOOP - -- Remove "" - param_text := hafah_backend.decode_param(param); - -- Extract everything before the first '=' as key - key_value := split_part(param_text, '=', 1); - - -- Extract everything after the first '=' as value - value_part := replace(param_text,key_value || '=',''); - - -- Split the key into parts based on '.' separator - key_part := string_to_array(key_value, '.'); - - -- Append key parts to the JSONB list - json_list := json_list || jsonb_build_array(key_part); - - -- Append the entire value part to the text array - text_list := array_append(text_list, value_part); - END LOOP; - - RETURN QUERY SELECT json_list, text_list; -END -$$; - RESET ROLE; diff --git a/queries/hafah_rest_backend/account_history/paging.sql b/queries/hafah_rest_backend/utilities/paging.sql similarity index 67% rename from queries/hafah_rest_backend/account_history/paging.sql rename to queries/hafah_rest_backend/utilities/paging.sql index 8729109ffa02a55c2bfc8d2cc2d6cf9977bdc84c..f5388b2d80ccd4b96697b5c0d3842ac8aa1decb7 100644 --- a/queries/hafah_rest_backend/account_history/paging.sql +++ b/queries/hafah_rest_backend/utilities/paging.sql @@ -81,7 +81,8 @@ $$; DROP TYPE IF EXISTS hafah_backend.account_filter_return CASCADE; CREATE TYPE hafah_backend.account_filter_return AS ( - count INT, + from_block INT, + to_block INT, from_seq INT, to_seq INT ); @@ -98,9 +99,12 @@ SET JIT = OFF AS $$ DECLARE + __to INT; + __from INT; __to_seq INT; __from_seq INT; - __count INT; + + _current_block INT := (SELECT bv.num FROM hive.blocks_view bv ORDER BY bv.num DESC LIMIT 1); BEGIN /* we are using 3 diffrent methods of fetching data, @@ -135,9 +139,81 @@ BEGIN ORDER BY aov.account_op_seq_no ASC LIMIT 1 ); - __count := hafah_backend.get_account_operations_count(_operations, _account_id, __from_seq, __to_seq); + __to := ( + CASE + WHEN (_to IS NULL) THEN + _current_block + WHEN (_to IS NOT NULL) AND (_current_block < _to) THEN + _current_block + ELSE + _to + END + ); + + __from := ( + CASE + WHEN (_from IS NULL) THEN + 1 + ELSE + _from + END + ); + + RETURN (__from, __to, __from_seq, __to_seq)::hafah_backend.account_filter_return; +END +$$; + +CREATE OR REPLACE FUNCTION hafah_backend.total_pages( + _ops_count INT, + _page_size INT +) +RETURNS INT -- noqa: LT01, CP05 +LANGUAGE 'plpgsql' IMMUTABLE +AS +$$ +BEGIN + RETURN ( + CASE + WHEN (_ops_count % _page_size) = 0 THEN + _ops_count / _page_size + ELSE + (_ops_count / _page_size) + 1 + END + ); +END +$$; + +-- used in account page endpoint (not used when filtered by accounts) +CREATE OR REPLACE FUNCTION hafah_backend.get_account_operations_count( + _operations INT [], + _account_id INT, + _from_seq INT, + _to_seq INT, + _include_virtual BOOLEAN +) +RETURNS BIGINT -- noqa: LT01, CP05 +LANGUAGE 'plpgsql' STABLE +SET from_collapse_limit = 16 +SET join_collapse_limit = 16 +SET enable_hashjoin = OFF +SET JIT = OFF +AS +$$ +BEGIN + IF _operations IS NULL AND _include_virtual THEN + RETURN _to_seq - _from_seq + 1; + END IF; + + RETURN ( + SELECT COUNT(*) + FROM hive.account_operations_view aov + WHERE aov.account_id = _account_id + AND (_operations IS NULL OR aov.op_type_id = ANY(_operations)) + AND (_include_virtual OR aov.transacting_account_id IS NOT NULL) + AND aov.account_op_seq_no >= _from_seq + AND aov.account_op_seq_no <= _to_seq + ); - RETURN (__count, __from_seq, __to_seq)::hafah_backend.account_filter_return; END $$; diff --git a/queries/hafah_rest_backend/utilities/path_filters.sql b/queries/hafah_rest_backend/utilities/path_filters.sql new file mode 100644 index 0000000000000000000000000000000000000000..e2c92e7e383528886c4f1877458987ce48d61a41 --- /dev/null +++ b/queries/hafah_rest_backend/utilities/path_filters.sql @@ -0,0 +1,53 @@ +SET ROLE hafah_owner; + +CREATE OR REPLACE FUNCTION hafah_backend.decode_param(_encoded_param TEXT) +RETURNS TEXT +LANGUAGE 'plpgsql' +STABLE +AS +$$ +BEGIN + RETURN convert_from(decode(_encoded_param, 'base64'), 'UTF8'); +END +$$; + +CREATE OR REPLACE FUNCTION hafah_backend.parse_path_filters(_params TEXT[]) +RETURNS TABLE(param_json JSONB, param_text TEXT[]) +LANGUAGE 'plpgsql' +STABLE +AS +$$ +DECLARE + json_list JSONB := '[]'::JSONB; + text_list TEXT[] := '{}'; + param TEXT; + param_text TEXT; + key_value TEXT; + key_part TEXT[]; + value_part TEXT; +BEGIN + FOREACH param IN ARRAY _params + LOOP + -- Remove "" + param_text := hafah_backend.decode_param(param); + -- Extract everything before the first '=' as key + key_value := split_part(param_text, '=', 1); + + -- Extract everything after the first '=' as value + value_part := replace(param_text,key_value || '=',''); + + -- Split the key into parts based on '.' separator + key_part := string_to_array(key_value, '.'); + + -- Append key parts to the JSONB list + json_list := json_list || jsonb_build_array(key_part); + + -- Append the entire value part to the text array + text_list := array_append(text_list, value_part); + END LOOP; + + RETURN QUERY SELECT json_list, text_list; +END +$$; + +RESET ROLE; diff --git a/queries/hafah_rest_backend/utilities/validators.sql b/queries/hafah_rest_backend/utilities/validators.sql new file mode 100644 index 0000000000000000000000000000000000000000..241284d40e5956af90d8b498f16a8e0cd2717b98 --- /dev/null +++ b/queries/hafah_rest_backend/utilities/validators.sql @@ -0,0 +1,87 @@ +SET ROLE hafah_owner; + +CREATE OR REPLACE FUNCTION hafah_backend.validate_participation_mode(_mode hafah_backend.participation_mode, _account_name TEXT) +RETURNS VOID +LANGUAGE 'plpgsql' +IMMUTABLE +AS +$$ +BEGIN + IF _mode = 'all' AND _account_name IS NOT NULL THEN + PERFORM hafah_backend.rest_raise_invalid_participation(); + END IF; +END +$$ +; + +CREATE OR REPLACE FUNCTION hafah_backend.validate_account(_account_id INT, _account_name TEXT, _required BOOLEAN) +RETURNS VOID +LANGUAGE 'plpgsql' +IMMUTABLE +AS +$$ +BEGIN + IF (_required AND _account_id IS NULL) OR (NOT _required AND _account_name IS NOT NULL AND _account_id IS NULL) THEN + PERFORM hafah_backend.rest_raise_missing_account(_account_name); + END IF; +END +$$ +; + +CREATE OR REPLACE FUNCTION hafah_backend.validate_operation_types(_operations INT[], _allowed_operations INT[]) +RETURNS VOID +LANGUAGE 'plpgsql' +IMMUTABLE +AS +$$ +BEGIN + IF (NOT _operations <@ _allowed_operations) AND _operations IS NOT NULL THEN + PERFORM hafah_backend.rest_raise_invalid_operation_types(_allowed_operations); + END IF; +END +$$ +; + +CREATE OR REPLACE FUNCTION hafah_backend.validate_block_num(_block_num INT) +RETURNS VOID +LANGUAGE 'plpgsql' +STABLE +AS +$$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM hive.blocks_view bv WHERE bv.num = _block_num) THEN + PERFORM hafah_backend.rest_raise_missing_block(_block_num); + END IF; +END +$$ +; + +CREATE OR REPLACE FUNCTION hafah_backend.validate_op_type_id(_op_type INT) +RETURNS VOID +LANGUAGE 'plpgsql' +STABLE +AS +$$ +BEGIN + IF (_op_type > (SELECT MAX(id) FROM hafd.operation_types)) OR _op_type < 0 THEN + PERFORM hafah_backend.rest_raise_missing_op_type(_op_type); + END IF; +END +$$ +; + +CREATE OR REPLACE FUNCTION hafah_backend.validate_operation_id(_operation_id BIGINT) +RETURNS VOID +LANGUAGE 'plpgsql' +STABLE +AS +$$ +BEGIN + IF (SELECT ov.block_num FROM hive.operations_view ov WHERE ov.id = _operation_id) IS NULL THEN + PERFORM hafah_backend.rest_raise_missing_operation_id(_operation_id); + END IF; +END +$$ +; + +RESET ROLE; diff --git a/scripts/install_app.sh b/scripts/install_app.sh index f721c4b95b271bd0b3c523adfedd338fd4923867..9fe5f501c6c2c0fc94468fb890a2d96eed17ea2b 100755 --- a/scripts/install_app.sh +++ b/scripts/install_app.sh @@ -79,25 +79,40 @@ psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../postgrest/hafah_R psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../postgrest/hafah_REST/types/block.sql" psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../postgrest/hafah_REST/types/transaction.sql" psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../postgrest/hafah_REST/types/fill_order.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../postgrest/hafah_REST/types/participation_mode.sql" + +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/utilities/account.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/utilities/exceptions.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/utilities/validators.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/utilities/get_operation_types.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/utilities/paging.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/utilities/path_filters.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/utilities/operation_body_filter.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/utilities/function_helpers.sql" + psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/account_history/account_history.sql" -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/account_history/by_operations.sql" -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/account_history/count.sql" -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/account_history/default.sql" -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/account_history/paging.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/account_history/filtering_functions/by_operations.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/account_history/filtering_functions/default.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/account_history/filtering_functions/excluding_accounts.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/account_history/filtering_functions/including_accounts.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/account_history/filtering_functions/include_account.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/account_history/filtering_functions/exclude_account.sql" + psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/market_history/recent_trades.sql" psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/market_history/trade_history.sql" -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/acc_op_types.sql" -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/op_types.sql" -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/operation.sql" -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/block_header.sql" -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/block_range.sql" -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/block.sql" -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/ops_in_block.sql" -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/path_filters.sql" + +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/blocks/block_header.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/blocks/block_range.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/blocks/block.sql" + +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/operations/acc_op_types.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/operations/op_types.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/operations/operation.sql" +psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../queries/hafah_rest_backend/operations/ops_in_block.sql" + psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../postgrest/hafah_endpoints.sql" psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/set_version_in_sql.pgsql" psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -c "SET custom.swagger_url = '$SWAGGER_URL';" -f "$SCRIPTPATH/../postgrest/hafah_REST/hafah_openapi.sql" -psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../postgrest/hafah_rest_exceptions.sql" psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../postgrest/hafah_REST/blocks/get_block.sql" psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../postgrest/hafah_REST/blocks/get_block_range.sql" psql "$POSTGRES_ACCESS" -v ON_ERROR_STOP=on -f "$SCRIPTPATH/../postgrest/hafah_REST/blocks/get_block_header.sql" diff --git a/scripts/openapi_rewrite.sh b/scripts/openapi_rewrite.sh index 22b4a0bae3f3a8dc1da7a30d4c25203fdc87b2d2..47eed7ee822963127b03465e3e2004c4dd028f73 100755 --- a/scripts/openapi_rewrite.sh +++ b/scripts/openapi_rewrite.sh @@ -20,6 +20,7 @@ ENDPOINTS_IN_ORDER=" ../$endpoints/types/block.sql ../$endpoints/types/transaction.sql ../$endpoints/types/fill_order.sql +../$endpoints/types/participation_mode.sql ../$endpoints/hafah_openapi.sql ../$endpoints/blocks/get_block_range.sql ../$endpoints/blocks/get_block.sql diff --git a/scripts/setup_postgres.sh b/scripts/setup_postgres.sh index 1a626ded8d82572aae4fe048461bb27919c8b523..c570be316059005ea983965ce73844213f84fb79 100755 --- a/scripts/setup_postgres.sh +++ b/scripts/setup_postgres.sh @@ -21,6 +21,7 @@ print_help () { echo " --host=VALUE Allows to specify a PostgreSQL host location (defaults to /var/run/postgresql)" echo " --port=NUMBER Allows to specify a PostgreSQL operating port (defaults to 5432)" echo " --postgres-url=URL Allows to specify a PostgreSQL URL (in opposite to separate --host and --port options)" + echo " --path-to-haf=PATH Allows to specify a path to HAF installation (defaults to the parent directory of this script)" echo " --help Display this help screen and exit" echo } @@ -35,6 +36,7 @@ supplement_builtin_roles() { POSTGRES_HOST="/var/run/postgresql" POSTGRES_PORT=5432 POSTGRES_URL="" +HAF_PATH="$SCRIPTPATH/../haf" while [ $# -gt 0 ]; do case "$1" in @@ -47,6 +49,9 @@ while [ $# -gt 0 ]; do --postgres-url=*) POSTGRES_URL="${1#*=}" ;; + --path-to-haf=*) + HAF_PATH="${1#*=}" + ;; --help) print_help exit 0 @@ -73,7 +78,7 @@ else POSTGRES_ACCESS=$POSTGRES_URL fi -"$SCRIPTPATH/../haf/scripts/create_haf_app_role.sh" --postgres-url="$POSTGRES_ACCESS" --haf-app-account="hafah_owner" -"$SCRIPTPATH/../haf/scripts/create_haf_app_role.sh" --postgres-url="$POSTGRES_ACCESS" --haf-app-account="hafah_user" +"$HAF_PATH/scripts/create_haf_app_role.sh" --postgres-url="$POSTGRES_ACCESS" --haf-app-account="hafah_owner" +"$HAF_PATH/scripts/create_haf_app_role.sh" --postgres-url="$POSTGRES_ACCESS" --haf-app-account="hafah_user" supplement_builtin_roles "$POSTGRES_ACCESS" diff --git a/tests/tavern/get_ops_by_account/positive/blocktrades_first_page.pat.json b/tests/tavern/get_ops_by_account/positive/blocktrades_first_page.pat.json index 7d011f3705fc0e77c727e8dd2386023abd5fe2c5..63896fecce6f77041fcf17d0088e2fa684170a92 100644 --- a/tests/tavern/get_ops_by_account/positive/blocktrades_first_page.pat.json +++ b/tests/tavern/get_ops_by_account/positive/blocktrades_first_page.pat.json @@ -1,6 +1,10 @@ { "total_operations": 219867, "total_pages": 10994, + "block_range": { + "from": 1, + "to": 5000000 + }, "operations_result": [ { "op": { diff --git a/tests/tavern/get_ops_by_account/positive/blocktrades_last_page.pat.json b/tests/tavern/get_ops_by_account/positive/blocktrades_last_page.pat.json index f90f0404cec229840e44cb19dae972fb85bb2116..0daef3cf7a940fd7f851c7ed4babdc8589cb9c59 100644 --- a/tests/tavern/get_ops_by_account/positive/blocktrades_last_page.pat.json +++ b/tests/tavern/get_ops_by_account/positive/blocktrades_last_page.pat.json @@ -1,6 +1,10 @@ { "total_operations": 219867, "total_pages": 10994, + "block_range": { + "from": 1, + "to": 5000000 + }, "operations_result": [ { "op": { diff --git a/tests/tavern/get_ops_by_account/positive/corner_case_transacting_account.pat.json b/tests/tavern/get_ops_by_account/positive/corner_case_transacting_account.pat.json new file mode 100644 index 0000000000000000000000000000000000000000..00831ac4a6a0a79ca1368e04deb612104c764b64 --- /dev/null +++ b/tests/tavern/get_ops_by_account/positive/corner_case_transacting_account.pat.json @@ -0,0 +1,1941 @@ +{ + "total_operations": 1007, + "total_pages": 11, + "block_range": { + "from": 4168823, + "to": 5000000 + }, + "operations_result": [ + { + "op": { + "type": "vote_operation", + "value": { + "voter": "wilfred", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4180549, + "trx_id": "d5513599594707e5e66301e46d84aa6b94235221", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T05:11:36", + "virtual_op": false, + "operation_id": "17955321234325760", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "smooth.witness", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4178023, + "trx_id": "6fe2120b5fe391fbdb8521d18511a2790ef63439", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T03:04:54", + "virtual_op": false, + "operation_id": "17944472146935808", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "smooth", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4178022, + "trx_id": "c9c2e39f80107a748ee61376ed24fb0c50e4bc6d", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T03:04:51", + "virtual_op": false, + "operation_id": "17944467851969792", + "trx_in_block": 3 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "@@ -25,16 +25,18 @@\n Please \n+re\n move you\n", + "title": "", + "author": "future24", + "permlink": "re-abit-re-future24-help-for-a-bro-in-africa-gambia-let-s-share-this-steemit-community-20160818t025421970z", + "json_metadata": "{\"tags\":[\"steemit-help\"],\"users\":[\"abit\"]}", + "parent_author": "abit", + "parent_permlink": "re-future24-help-for-a-bro-in-africa-gambia-let-s-share-this-steemit-community-20160818t012856733z" + } + }, + "block": 4177999, + "trx_id": "897bea177ddb68a9e5206f38f43945ea16f9fb49", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-08-18T03:03:42", + "virtual_op": false, + "operation_id": "17944369067720705", + "trx_in_block": 0 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "@@ -22,10 +22,15 @@\n am? \n-Re\n+Please \n move\n@@ -80,8 +80,43 @@\n article!\n+ @abit and the two others flaggers.\n", + "title": "", + "author": "future24", + "permlink": "re-abit-re-future24-help-for-a-bro-in-africa-gambia-let-s-share-this-steemit-community-20160818t025421970z", + "json_metadata": "{\"tags\":[\"steemit-help\"],\"users\":[\"abit\"]}", + "parent_author": "abit", + "parent_permlink": "re-future24-help-for-a-bro-in-africa-gambia-let-s-share-this-steemit-community-20160818t012856733z" + } + }, + "block": 4177996, + "trx_id": "55b8fd7db82c14dfdb792c86758f273ddccee774", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-08-18T03:03:33", + "virtual_op": false, + "operation_id": "17944356182820609", + "trx_in_block": 4 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "What why? Where is spam? Remove your flag men, this is a really important article!", + "title": "", + "author": "future24", + "permlink": "re-abit-re-future24-help-for-a-bro-in-africa-gambia-let-s-share-this-steemit-community-20160818t025421970z", + "json_metadata": "{\"tags\":[\"steemit-help\"]}", + "parent_author": "abit", + "parent_permlink": "re-future24-help-for-a-bro-in-africa-gambia-let-s-share-this-steemit-community-20160818t012856733z" + } + }, + "block": 4177915, + "trx_id": "0bf3a247078a4084a72e5d4c7d9c69c8d5018bec", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-08-18T02:59:24", + "virtual_op": false, + "operation_id": "17944008290467841", + "trx_in_block": 0 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "cnfund", + "approve": true, + "witness": "abit" + } + }, + "block": 4177899, + "trx_id": "f9c4114303181912292fddfc2e8d67fb065a61db", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-08-18T02:58:33", + "virtual_op": false, + "operation_id": "17943939570991116", + "trx_in_block": 0 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "What why?", + "title": "", + "author": "future24", + "permlink": "re-abit-re-future24-help-for-a-bro-in-africa-gambia-let-s-share-this-steemit-community-20160818t025421970z", + "json_metadata": "{\"tags\":[\"steemit-help\"]}", + "parent_author": "abit", + "parent_permlink": "re-future24-help-for-a-bro-in-africa-gambia-let-s-share-this-steemit-community-20160818t012856733z" + } + }, + "block": 4177816, + "trx_id": "cf75dc7aa3df2cc8e510e2ca8b34249fed3f2029", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-08-18T02:54:24", + "virtual_op": false, + "operation_id": "17943583088706305", + "trx_in_block": 2 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": ":(", + "title": "", + "author": "future24", + "permlink": "re-abit-re-future24-help-for-a-bro-in-africa-gambia-let-s-share-this-steemit-community-20160818t025355873z", + "json_metadata": "{\"tags\":[\"steemit-help\"]}", + "parent_author": "abit", + "parent_permlink": "re-future24-help-for-a-bro-in-africa-gambia-let-s-share-this-steemit-community-20160818t012856733z" + } + }, + "block": 4177808, + "trx_id": "3540e19c7f122b82c914fce3b721cca68c55316f", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-08-18T02:54:00", + "virtual_op": false, + "operation_id": "17943548728967169", + "trx_in_block": 0 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "Good post.", + "title": "", + "author": "cnfund", + "permlink": "re-abit-more-info-about-how-supercomputing-was-dominating-the-mining-queue-20160818t023833863z", + "json_metadata": "{\"tags\":[\"steem\"]}", + "parent_author": "abit", + "parent_permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4177469, + "trx_id": "4ca220d0451d714738f6a77cde61f23f3688452d", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-08-18T02:37:00", + "virtual_op": false, + "operation_id": "17942092735055105", + "trx_in_block": 3 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "好深奥", + "title": "", + "author": "oflyhigh", + "permlink": "re-abit-more-info-about-how-supercomputing-was-dominating-the-mining-queue-20160818t023407147z", + "json_metadata": "{\"tags\":[\"steem\"]}", + "parent_author": "abit", + "parent_permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4177413, + "trx_id": "5f8b65daf407cd9c241348fea07b9204431cbd3e", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-08-18T02:34:12", + "virtual_op": false, + "operation_id": "17941852216885505", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "cnfund", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4177411, + "trx_id": "fd8dcdb936ea07b2c9879f7a064a09a244e9f2c7", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T02:34:06", + "virtual_op": false, + "operation_id": "17941843626950656", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "oflyhigh", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4177331, + "trx_id": "cd1ad5c664a2618d9f7eb65745420be244b92fd6", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T02:30:06", + "virtual_op": false, + "operation_id": "17941500029567488", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "sweetsssj", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4176288, + "trx_id": "0ae8cd08368b59aaed9194828a1f8958f85b2bf9", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T01:37:45", + "virtual_op": false, + "operation_id": "17937020378678016", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "beowulfoflegend", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4176086, + "trx_id": "bad6d9079a59b46ba3d99eb1005127ff93e1d38d", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T01:27:36", + "virtual_op": false, + "operation_id": "17936152795283456", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "johnbradshaw", + "author": "abit", + "weight": 5000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4175958, + "trx_id": "b599d4c642abfb9b2e05dc8d2f0d29072a6c13d2", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T01:21:09", + "virtual_op": false, + "operation_id": "17935603039470592", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "thebotkiller", + "author": "abit", + "weight": 5000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4175954, + "trx_id": "c99499099a5e7302e73e8e8a1eb61414c84b1722", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T01:20:57", + "virtual_op": false, + "operation_id": "17935585859601152", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "vote", + "author": "abit", + "weight": 5000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4175951, + "trx_id": "a8293a43ed064e0420955b612d5f59e0c01b9686", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T01:20:48", + "virtual_op": false, + "operation_id": "17935572974699264", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "unicornfarts", + "author": "abit", + "weight": 5000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4175949, + "trx_id": "f75d2ab3c7efdd4637419f0862c2fcd0a8b0f2ba", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T01:20:42", + "virtual_op": false, + "operation_id": "17935564384764416", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "the.whale", + "author": "abit", + "weight": 5000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4175942, + "trx_id": "c211cf0909d0c2eaf60c6a8b683b79646fbfd428", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T01:20:21", + "virtual_op": false, + "operation_id": "17935534319993088", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "kissmybutt", + "author": "abit", + "weight": 5000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4175936, + "trx_id": "dadb9b8432490cb7df5c1d9701ca424f20929513", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T01:20:03", + "virtual_op": false, + "operation_id": "17935508550189568", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "iloveporn", + "author": "abit", + "weight": 5000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4175933, + "trx_id": "e9d28dad939a41cc3a542ab7d2a1ab2bacbdd031", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T01:19:54", + "virtual_op": false, + "operation_id": "17935495665287168", + "trx_in_block": 0 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "someguy123", + "approve": true, + "witness": "abit" + } + }, + "block": 4175931, + "trx_id": "af0cdbbacc531612c132376646f0e10369f4dd84", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-08-18T01:19:48", + "virtual_op": false, + "operation_id": "17935487075352588", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "jimmytwoshoes", + "author": "abit", + "weight": 5000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4175930, + "trx_id": "ef73c9df04c601a3066ce468ee98e3080c2b4505", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T01:19:45", + "virtual_op": false, + "operation_id": "17935482780386048", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "fuck.off", + "author": "abit", + "weight": 5000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4175923, + "trx_id": "a4d76fe4893a119c3c91900baf1ae558a26a5f58", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T01:19:24", + "virtual_op": false, + "operation_id": "17935452715614208", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "the.bot", + "author": "abit", + "weight": 5000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4175922, + "trx_id": "726f9aa74db51fd03774c97d52c45bd0575224b3", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T01:19:21", + "virtual_op": false, + "operation_id": "17935448420648192", + "trx_in_block": 3 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "complexring", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4175904, + "trx_id": "934fd359021968bfdd2cbcf9b076e43f72c4e091", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-18T01:18:27", + "virtual_op": false, + "operation_id": "17935371111237632", + "trx_in_block": 7 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "@@ -449,17 +449,19 @@\n he exist\n-s\n+ing\n APIs co\n", + "title": "", + "author": "arhag", + "permlink": "re-abit-more-info-about-how-supercomputing-was-dominating-the-mining-queue-20160817t205401396z", + "json_metadata": "{\"tags\":[\"steem\"],\"users\":[\"supercomputing\"]}", + "parent_author": "abit", + "parent_permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4175841, + "trx_id": "e19ff8fd7af31528b0ed3cf909d261c31e014208", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-08-18T01:15:18", + "virtual_op": false, + "operation_id": "17935100528296193", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "murh", + "author": "abit", + "weight": 3301, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4173263, + "trx_id": "16448d8c45ac9913ee5bdd8f7e26ac4149c7cdcf", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T23:06:00", + "virtual_op": false, + "operation_id": "17924028102608384", + "trx_in_block": 3 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "ubg", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4173117, + "trx_id": "8526ac2d81195029b97a9ab7350aef9e3476e5b0", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T22:58:42", + "virtual_op": false, + "operation_id": "17923401037381632", + "trx_in_block": 0 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "Upvoted, thanks for this article! It would be really nice if you would upvote my new article here everyone, with which i will support a friend in gambia: https://steemit.com/steemit-help/@future24/help-for-a-bro-in-africa-gambia-let-s-share-this-steemit-community", + "title": "", + "author": "future24", + "permlink": "re-abit-more-info-about-how-supercomputing-was-dominating-the-mining-queue-20160817t223353721z", + "json_metadata": "{\"tags\":[\"steem\"],\"links\":[\"https://steemit.com/steemit-help/@future24/help-for-a-bro-in-africa-gambia-let-s-share-this-steemit-community\"]}", + "parent_author": "abit", + "parent_permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4172624, + "trx_id": "5e6deaad2db89a5638720105613f906a64bb21b6", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-08-17T22:33:57", + "virtual_op": false, + "operation_id": "17921283618504705", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "future24", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4172554, + "trx_id": "b497671e6e3d6efd5a58d2180d918439df6a1bf8", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T22:30:27", + "virtual_op": false, + "operation_id": "17920982970793984", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "picokernel", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4171752, + "trx_id": "f217ba8648f509b092346b6f06c4bd0a3e750a88", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T21:50:12", + "virtual_op": false, + "operation_id": "17917538407024640", + "trx_in_block": 4 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "kennyskitchen", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4171719, + "trx_id": "c59b6600d68ea981e222e7de251ebf499a1c4f59", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T21:48:33", + "virtual_op": false, + "operation_id": "17917396673102848", + "trx_in_block": 3 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "goose", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4171414, + "trx_id": "ded9cf3ec6836371294baebb5fd9139a966a0f76", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T21:33:15", + "virtual_op": false, + "operation_id": "17916086708078080", + "trx_in_block": 5 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "doggnostic", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170809, + "trx_id": "2af2db3cfe109bffd30cb846a7315578fc1c621d", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T21:02:51", + "virtual_op": false, + "operation_id": "17913488252862720", + "trx_in_block": 1 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "> she can simply recover the public key (which is needed to put into the PoW operation) with the same method used in step 6\n\n> because a transaction contains only a PoW operation requires no signature (which is another hole in the old algo which got fixed in new algo)\n\nGood point.\n\nThat means the implementation of the exploit was actually easier than I thought because it didn't require messing around the libsecp256k1 function implementations. The exists APIs could have been used to get the active public key, and that's most likely what was used by @supercomputing.", + "title": "", + "author": "arhag", + "permlink": "re-abit-more-info-about-how-supercomputing-was-dominating-the-mining-queue-20160817t205401396z", + "json_metadata": "{\"tags\":[\"steem\"],\"users\":[\"supercomputing\"]}", + "parent_author": "abit", + "parent_permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170633, + "trx_id": "dd415881c4c12104206e0b0b9e537732e517dbed", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-08-17T20:54:03", + "virtual_op": false, + "operation_id": "17912732338619905", + "trx_in_block": 4 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "helen.tan", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170627, + "trx_id": "38039d31a66da9c06c42ddcddbc1088becf0d2ad", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:53:45", + "virtual_op": false, + "operation_id": "17912706568815104", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "moon", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170626, + "trx_id": "06bd5756758c14cf92252941ce9cde0787448027", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:53:42", + "virtual_op": false, + "operation_id": "17912702273848064", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "bunny", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170625, + "trx_id": "9a2729ac6f3a21e9c9b86333e8c6c59df754e64e", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:53:39", + "virtual_op": false, + "operation_id": "17912697978880000", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "daniel.pan", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170624, + "trx_id": "55d598328b622c0c3e8f87aafb6ed95e3d8e1644", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:53:36", + "virtual_op": false, + "operation_id": "17912693683915520", + "trx_in_block": 7 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "boy", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170624, + "trx_id": "26d2f4480f4919b2665d36ab8f5e1fdebdba7813", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:53:36", + "virtual_op": false, + "operation_id": "17912693683913472", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "healthcare", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170623, + "trx_id": "e1cfadaff7dd3d191ce004e2b5cefee078effe93", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:53:33", + "virtual_op": false, + "operation_id": "17912689388946432", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "mini", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170622, + "trx_id": "9f1cbc3a0cbd787b839c4cadc0effcc0e2f550f1", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:53:30", + "virtual_op": false, + "operation_id": "17912685093981440", + "trx_in_block": 7 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "soupernerd", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170622, + "trx_id": "ae5f38f37de3e7887feedae6bec141d1facb5bc6", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:53:30", + "virtual_op": false, + "operation_id": "17912685093980160", + "trx_in_block": 4 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "bue", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170622, + "trx_id": "441e33562394078a9bb23d7c29e17e66e84c4211", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:53:30", + "virtual_op": false, + "operation_id": "17912685093979136", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "weenis", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170622, + "trx_id": "9d2583236e6c09f981e716cd4ee43972c49902f1", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:53:30", + "virtual_op": false, + "operation_id": "17912685093978112", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "bue-witness", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170621, + "trx_id": "65c112b5197cdc41ddf04d1105398fe1ca5ae97b", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:53:27", + "virtual_op": false, + "operation_id": "17912680799014400", + "trx_in_block": 8 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "anonymous", + "author": "abit", + "weight": 100, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170612, + "trx_id": "c78ca80f68357fe63de38b9759bae157001b5cd0", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:53:00", + "virtual_op": false, + "operation_id": "17912642144305920", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "arhag", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170610, + "trx_id": "fc52bfa8cafa36882448cd165fe6575caef44b39", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:52:54", + "virtual_op": false, + "operation_id": "17912633554374144", + "trx_in_block": 8 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "lantto", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170584, + "trx_id": "fe485135ea19187752a31288fa4fbef482ab6008", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:51:33", + "virtual_op": false, + "operation_id": "17912521885221376", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "d-marim", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170465, + "trx_id": "3c430f9ae353f382a33abb223de641517d394c91", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:45:36", + "virtual_op": false, + "operation_id": "17912010784114176", + "trx_in_block": 3 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "roadhog", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170412, + "trx_id": "e41e67e783eaa7071685dbdf639cd8040f0747cc", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:42:57", + "virtual_op": false, + "operation_id": "17911783150846720", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "fatboy", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170164, + "trx_id": "57c8dde2f492f8d231babb9b9b06bb007d763ea5", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:30:24", + "virtual_op": false, + "operation_id": "17910717998959616", + "trx_in_block": 7 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "analyzethis", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4170010, + "trx_id": "2939bfa522efd72e3219f0fd05ca96bce51949c3", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:22:42", + "virtual_op": false, + "operation_id": "17910056573992960", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "sunshinecrypto", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169776, + "trx_id": "382ccb0068157f6acac0244d77b3e0747141388a", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:10:57", + "virtual_op": false, + "operation_id": "17909051551648000", + "trx_in_block": 7 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "jenny-talls", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169622, + "trx_id": "9844a94ffdef916e7fca95150f8b4ddb82939802", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T20:03:15", + "virtual_op": false, + "operation_id": "17908390126682624", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "xtester", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169555, + "trx_id": "0923e754731cebf0e2d5187602578a140875bc31", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:59:54", + "virtual_op": false, + "operation_id": "17908102363874304", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "akareyon", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169417, + "trx_id": "a5c6aa66d2ff8fbe0dc2d49b4aa4d21acb0049f9", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:53:00", + "virtual_op": false, + "operation_id": "17907509658386944", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "bingo-0", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169360, + "trx_id": "6b88d4f2b4e3f82fd1a71a408f8a050ff4ba2e2c", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:50:06", + "virtual_op": false, + "operation_id": "17907264845253376", + "trx_in_block": 7 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "mineralwasser", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169331, + "trx_id": "fd16f81e0af11d8e9caf7e13f9d74deb93b5d252", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:48:39", + "virtual_op": false, + "operation_id": "17907140291200000", + "trx_in_block": 3 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "boombastic", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169267, + "trx_id": "8e662a536807690328ae0bf81e6e846fd2bc213f", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:45:27", + "virtual_op": false, + "operation_id": "17906865413292288", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "barrie", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169264, + "trx_id": "8bcf684d6a4b044912e5b5481d6b4701cafb63a8", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:45:18", + "virtual_op": false, + "operation_id": "17906852528390144", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "mrs.agsexplorer", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169256, + "trx_id": "9b24f8dc5c84ec440e47dca7b777134444858b47", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:44:54", + "virtual_op": false, + "operation_id": "17906818168651776", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "rubybian", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169253, + "trx_id": "26feb906bdf2dbaeb150378a2fe438ec883ddca4", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:44:45", + "virtual_op": false, + "operation_id": "17906805283750400", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "post-successful", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169209, + "trx_id": "6f67360bc47ea12751c4b484dc72fa11bf7926dd", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:42:33", + "virtual_op": false, + "operation_id": "17906616305188864", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "litali", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169200, + "trx_id": "48f812a1d3432b19cead461b11f2fbcd8d26d27f", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:42:06", + "virtual_op": false, + "operation_id": "17906577650483712", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "alenevaa", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169197, + "trx_id": "bf1e7f5633b2a9bbb2685501d9cb61463fd28a1e", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:41:57", + "virtual_op": false, + "operation_id": "17906564765581824", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "michaeldodridge", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169143, + "trx_id": "c3343d0825bac9cb45c53f04494439860b6b21fb", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:39:15", + "virtual_op": false, + "operation_id": "17906332837347328", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "cortegam", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169141, + "trx_id": "55511109529e1210724df33b6a11d1a11f9ff70c", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:39:09", + "virtual_op": false, + "operation_id": "17906324247412736", + "trx_in_block": 0 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "> But there is a hole in the description, because the private key of a given signature shouldn't be so quickly to be resolved -- it's the nature of ECC algo.\n\nIs it bad?", + "title": "", + "author": "vi1son", + "permlink": "re-abit-more-info-about-how-supercomputing-was-dominating-the-mining-queue-20160817t193631631z", + "json_metadata": "{\"tags\":[\"steem\"]}", + "parent_author": "abit", + "parent_permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169089, + "trx_id": "bc2e2374fdca3bda39956135c015adffaab8dcc1", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-08-17T19:36:33", + "virtual_op": false, + "operation_id": "17906100909113345", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "gsdalex", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169053, + "trx_id": "4d80b57eabf70c2ad2c791122b49f20908ab6623", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:34:45", + "virtual_op": false, + "operation_id": "17905946290292992", + "trx_in_block": 7 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "runridefly", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169036, + "trx_id": "f915034739a322a79cdee3fcb75893a6531a6d03", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:33:54", + "virtual_op": false, + "operation_id": "17905873275846912", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "james-show", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169023, + "trx_id": "65802b72a716770e5ce79b4ff7b1b149844678d8", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:33:15", + "virtual_op": false, + "operation_id": "17905817441272320", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "vi1son", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4169015, + "trx_id": "910c1025d284a3ba733901a3cf025eae71f58fe2", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:32:51", + "virtual_op": false, + "operation_id": "17905783081533440", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "mahekg", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168991, + "trx_id": "d72e7e898dd751996095b140a680e1e8fe971740", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:31:39", + "virtual_op": false, + "operation_id": "17905680002319872", + "trx_in_block": 3 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "eileenbeach", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168934, + "trx_id": "ff0c6209649fc1780aeb3265569e7c67f3b1eb6c", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:28:48", + "virtual_op": false, + "operation_id": "17905435189182720", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "mrainp", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168920, + "trx_id": "164cbb960dcd21ae4e7168da594a07af8a94ecfb", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:28:06", + "virtual_op": false, + "operation_id": "17905375059641600", + "trx_in_block": 3 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "imp3", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168916, + "trx_id": "dfb7215af4bb9e9531acd9c4bfc956df7012e503", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:27:54", + "virtual_op": false, + "operation_id": "17905357879772928", + "trx_in_block": 4 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "budda", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168899, + "trx_id": "839fee965784bef49507c7c4ea37e0e8669728e5", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:27:03", + "virtual_op": false, + "operation_id": "17905284865327616", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "natalyt", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168897, + "trx_id": "76accba01ed499271558a77195a6fdf9caf8dca6", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:26:57", + "virtual_op": false, + "operation_id": "17905276275393792", + "trx_in_block": 3 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "noodhoog", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168897, + "trx_id": "84cd3e893638a3dc27a7f6740a1a173af4a343c6", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:26:57", + "virtual_op": false, + "operation_id": "17905276275392512", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "longtech", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168896, + "trx_id": "f80ce10451e7dc879020d461479080b6ca03e351", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:26:54", + "virtual_op": false, + "operation_id": "17905271980425728", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "future-shock", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168895, + "trx_id": "24d35725edfac839e706e664e683f6424251c21c", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:26:51", + "virtual_op": false, + "operation_id": "17905267685458432", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "immortality", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168891, + "trx_id": "fef6020765a93c5791d945cc1b1d588f2b65e469", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:26:39", + "virtual_op": false, + "operation_id": "17905250505588736", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "longevity", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168890, + "trx_id": "89d2893d86392360c1e945667a740998d4c8f9cb", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:26:36", + "virtual_op": false, + "operation_id": "17905246210621440", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "rashka", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168889, + "trx_id": "0d01eb4ad15f3537b4eac76cff25cc945ed74941", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:26:33", + "virtual_op": false, + "operation_id": "17905241915654144", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "zite", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168888, + "trx_id": "9d9307ba12c74879e423bfa2eb58cc9817e2875b", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:26:30", + "virtual_op": false, + "operation_id": "17905237620686848", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "gamerate", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168884, + "trx_id": "492f92cb048cc4299b79675d21f735617a85bb44", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:26:18", + "virtual_op": false, + "operation_id": "17905220440817664", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "creatorgalaxy", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168883, + "trx_id": "8c8fe0c7af0da6d18639d77d69f3fc6c297e65a9", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:26:15", + "virtual_op": false, + "operation_id": "17905216145850368", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "yarly11", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168824, + "trx_id": "2db3ea74c66360a05357291caae34cbf21281f61", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:23:18", + "virtual_op": false, + "operation_id": "17904962742785536", + "trx_in_block": 12 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "yarly4", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168824, + "trx_id": "e80709bbc27f8d015b07ca28fef70e763bad07dc", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:23:18", + "virtual_op": false, + "operation_id": "17904962742785024", + "trx_in_block": 11 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "yarly7", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168824, + "trx_id": "aa14551c25b73bf2b6348bb6c0731fc83f253e24", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:23:18", + "virtual_op": false, + "operation_id": "17904962742784512", + "trx_in_block": 10 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "yarly10", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168824, + "trx_id": "b4ecdb973b47497bf16cf2670e21634ae28990e0", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:23:18", + "virtual_op": false, + "operation_id": "17904962742784000", + "trx_in_block": 9 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "yarly", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168824, + "trx_id": "474028851579507d32efe9332fe215faa3e893bf", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:23:18", + "virtual_op": false, + "operation_id": "17904962742783488", + "trx_in_block": 8 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "yarly12", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168824, + "trx_id": "1b7fbca1616974eaec66e911719a47b30b2f5cf9", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:23:18", + "virtual_op": false, + "operation_id": "17904962742782976", + "trx_in_block": 7 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "yarly5", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168824, + "trx_id": "4c723177bfa892670b9e6c0a8bfd691cee6697db", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:23:18", + "virtual_op": false, + "operation_id": "17904962742782464", + "trx_in_block": 6 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "yarly2", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168824, + "trx_id": "54e5dc8744d77965716ef57afab36efc59a4c919", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:23:18", + "virtual_op": false, + "operation_id": "17904962742781952", + "trx_in_block": 5 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "yarly3", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168824, + "trx_id": "05fc229b1687887f481b792a1319aea775c3d307", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:23:18", + "virtual_op": false, + "operation_id": "17904962742781440", + "trx_in_block": 4 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "orly", + "author": "abit", + "weight": 10000, + "permlink": "more-info-about-how-supercomputing-was-dominating-the-mining-queue" + } + }, + "block": 4168824, + "trx_id": "e7863cdd210b9907d52515e981d9bb6511147e2d", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-08-17T19:23:18", + "virtual_op": false, + "operation_id": "17904962742780928", + "trx_in_block": 3 + } + ] +} \ No newline at end of file diff --git a/tests/tavern/get_ops_by_account/positive/corner_case_transacting_account.tavern.yaml b/tests/tavern/get_ops_by_account/positive/corner_case_transacting_account.tavern.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c7710ef42b705b3b069ca83c3a229484a1a6d794 --- /dev/null +++ b/tests/tavern/get_ops_by_account/positive/corner_case_transacting_account.tavern.yaml @@ -0,0 +1,26 @@ +--- + test_name: Hafah PostgREST + + marks: + - patterntest + + includes: + - !include ../../common.yaml + + stages: + - name: test + request: + url: "{service.proto:s}://{service.server:s}:{service.port}/rpc/get_ops_by_account" + method: POST + headers: + content-type: application/json + accept: application/json + json: + account-name: "abit" + transacting-account-name: "abit" + participation-mode: "exclude" + page: 1 + response: + status_code: 200 + verify_response_with: + function: validate_response:compare_rest_response_with_pattern diff --git a/tests/tavern/get_ops_by_account/positive/filter_by_op.pat.json b/tests/tavern/get_ops_by_account/positive/filter_by_op.pat.json index ce745ec504b224355d017d853d2081f6a52d993a..3bc8043b07cbabfd401e08eba2ea1b18c1ce212a 100644 --- a/tests/tavern/get_ops_by_account/positive/filter_by_op.pat.json +++ b/tests/tavern/get_ops_by_account/positive/filter_by_op.pat.json @@ -1,6 +1,10 @@ { "total_operations": 7947, "total_pages": 398, + "block_range": { + "from": 1, + "to": 5000000 + }, "operations_result": [ { "op": { diff --git a/tests/tavern/get_ops_by_account/positive/filter_by_range.pat.json b/tests/tavern/get_ops_by_account/positive/filter_by_range.pat.json index e1864a764df93a2852423ea4db4f43c82ae35d06..3301992975960644b7dad92ff3b2d93fe3524f48 100644 --- a/tests/tavern/get_ops_by_account/positive/filter_by_range.pat.json +++ b/tests/tavern/get_ops_by_account/positive/filter_by_range.pat.json @@ -1,6 +1,10 @@ { "total_operations": 158805, "total_pages": 7941, + "block_range": { + "from": 1, + "to": 4000000 + }, "operations_result": [ { "op": { diff --git a/tests/tavern/get_ops_by_account/positive/filter_by_transacting_account.pat.json b/tests/tavern/get_ops_by_account/positive/filter_by_transacting_account.pat.json new file mode 100644 index 0000000000000000000000000000000000000000..f8f3fda67f125ed93159c960a6355b4069124a6b --- /dev/null +++ b/tests/tavern/get_ops_by_account/positive/filter_by_transacting_account.pat.json @@ -0,0 +1,218 @@ +{ + "total_operations": 11, + "total_pages": 1, + "block_range": { + "from": 1, + "to": 5000000 + }, + "operations_result": [ + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "gtg", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 3784059, + "trx_id": "d448cf3bbe37ea308d8905ea580b79ff25b3dc43", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-08-04T08:58:27", + "virtual_op": false, + "operation_id": "16252409651136780", + "trx_in_block": 5 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "gtg", + "author": "blocktrades", + "weight": 10000, + "permlink": "bitcoin-payments-accepted-in-20s-soon-to-be-6s" + } + }, + "block": 2887502, + "trx_id": "9729bd39ffede1ad4d461474012f2868b7d4d8a2", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-07-03T22:43:48", + "virtual_op": false, + "operation_id": "12401726657134592", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "gtg", + "author": "blocktrades", + "weight": 10000, + "permlink": "-blocktrades-adds-support-for-directly-buyingselling-steem" + } + }, + "block": 2840599, + "trx_id": "2bf9b0850282ed3867d9a2470dc8d6af55aa4ea4", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-07-02T07:34:18", + "virtual_op": false, + "operation_id": "12200279806050304", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "gtg", + "author": "blocktrades", + "weight": 10000, + "permlink": "openledger-pre-sale-of-dao-tokens-is-now-live" + } + }, + "block": 2840592, + "trx_id": "5179c7745a8a8e729cf8faab0461e97bc1219ba3", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-07-02T07:33:57", + "virtual_op": false, + "operation_id": "12200249741280256", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "gtg", + "author": "blocktrades", + "weight": 10000, + "permlink": "is-vote-changing-an-important-feature-for-steem" + } + }, + "block": 2840592, + "trx_id": "4fdfc9bcbc84c0b7d798cf3f0c606269043f14ff", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-07-02T07:33:57", + "virtual_op": false, + "operation_id": "12200249741279744", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "gtg", + "author": "blocktrades", + "weight": 10000, + "permlink": "tax-issues-facing-us-based-cryptocurrency-holders-and-miners-intro-and-irs-guidelines" + } + }, + "block": 2840592, + "trx_id": "88d2f81c37e1288448b334042c3eee02df6395d9", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-07-02T07:33:57", + "virtual_op": false, + "operation_id": "12200249741279232", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "gtg", + "author": "blocktrades", + "weight": 10000, + "permlink": "tax-issues-facing-us-based-cryptocurrency-holders-and-miners-part-1-capital-gainslosses-for-crypto" + } + }, + "block": 2840591, + "trx_id": "9174e0f692023ecf91d632f9949a94c808302119", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-07-02T07:33:54", + "virtual_op": false, + "operation_id": "12200245446311936", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "gtg", + "author": "blocktrades", + "weight": 10000, + "permlink": "i-find-steemit-posts-with-summariesanalysis-of-3rd-party-content-more-compelling" + } + }, + "block": 2840590, + "trx_id": "e909cd27baf117473ec99ec9b875a6916927c55e", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-07-02T07:33:51", + "virtual_op": false, + "operation_id": "12200241151344640", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "gtg", + "author": "blocktrades", + "weight": 10000, + "permlink": "blocktrades-now-offering-steem-power-for-cryptocurrency" + } + }, + "block": 2840585, + "trx_id": "8726774b3c8d53e9dcb870db6b3561a217871d27", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-07-02T07:33:36", + "virtual_op": false, + "operation_id": "12200219676508160", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "gtg", + "author": "blocktrades", + "weight": 10000, + "permlink": "dao-gateway-on-openledger-is-fully-operational" + } + }, + "block": 2839669, + "trx_id": "c02b46ca3eced3c6bd5cc43df2114143ce6c2105", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-07-02T06:47:45", + "virtual_op": false, + "operation_id": "12196285486465536", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "gtg", + "author": "blocktrades", + "weight": 10000, + "permlink": "blocktrades-supports-buyingselling-dao-coins-without-exchange-risk" + } + }, + "block": 2839669, + "trx_id": "2b79214e07cffd28c8ee026055d92767229d8b45", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-07-02T06:47:45", + "virtual_op": false, + "operation_id": "12196285486465024", + "trx_in_block": 0 + } + ] +} \ No newline at end of file diff --git a/tests/tavern/get_ops_by_account/positive/filter_by_transacting_account.tavern.yaml b/tests/tavern/get_ops_by_account/positive/filter_by_transacting_account.tavern.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1d0d9a4a6a92c3f21043e81ebb2c877e7784ec4c --- /dev/null +++ b/tests/tavern/get_ops_by_account/positive/filter_by_transacting_account.tavern.yaml @@ -0,0 +1,25 @@ +--- + test_name: Hafah PostgREST + + marks: + - patterntest + + includes: + - !include ../../common.yaml + + stages: + - name: test + request: + url: "{service.proto:s}://{service.server:s}:{service.port}/rpc/get_ops_by_account" + method: POST + headers: + content-type: application/json + accept: application/json + json: + account-name: "blocktrades" + transacting-account-name: "gtg" + participation-mode: "include" + response: + status_code: 200 + verify_response_with: + function: validate_response:compare_rest_response_with_pattern diff --git a/tests/tavern/get_ops_by_account/positive/transacting_account_exclude.pat.json b/tests/tavern/get_ops_by_account/positive/transacting_account_exclude.pat.json new file mode 100644 index 0000000000000000000000000000000000000000..a401c9095c37cdf66ffe38d48d5ec58d61d0e8a5 --- /dev/null +++ b/tests/tavern/get_ops_by_account/positive/transacting_account_exclude.pat.json @@ -0,0 +1,2167 @@ +{ + "total_operations": 1000, + "total_pages": 10, + "block_range": { + "from": 4514574, + "to": 5000000 + }, + "operations_result": [ + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "mrwang", + "memo": "a79c09cd-0084-4cd4-ae63-bf6d2514fef9", + "amount": { + "nai": "@@000000013", + "amount": "1633", + "precision": 3 + } + } + }, + "block": 4999997, + "trx_id": "e75f833ceb62570c25504b55d0f23d86d9d76423", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-15T19:47:12", + "virtual_op": false, + "operation_id": "21474823595099394", + "trx_in_block": 3 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "condra", + "memo": "f2c3419c-9522-4bb3-aa41-472c8388f215", + "amount": { + "nai": "@@000000013", + "amount": "400000", + "precision": 3 + } + } + }, + "block": 4999607, + "trx_id": "2577322e21a18f9085cc5dba366b5292824f906b", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-15T19:26:42", + "virtual_op": false, + "operation_id": "21473148557854722", + "trx_in_block": 5 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "thisisbenbrick", + "memo": "0722aa82-10d0-4001-a286-130b633d58df", + "amount": { + "nai": "@@000000013", + "amount": "431587", + "precision": 3 + } + } + }, + "block": 4996294, + "trx_id": "01de85e061adde226a7ce59071973c9daaeca8ba", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-15T16:34:45", + "virtual_op": false, + "operation_id": "21458919331201538", + "trx_in_block": 1 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "roelandp", + "memo": "298eafac-95bb-4f27-b256-72df9d0ef222", + "amount": { + "nai": "@@000000021", + "amount": "148056", + "precision": 3 + } + } + }, + "block": 4996091, + "trx_id": "b1aa3d5f9308acd9cde664936c1b08eea77c3e49", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-15T16:24:36", + "virtual_op": false, + "operation_id": "21458047452841986", + "trx_in_block": 4 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "It just occurred to me that witness updates like this could make for interesting video material. Maybe a segment in our SteemSmart show?", + "title": "", + "author": "piedpiper", + "permlink": "re-blocktrades-witness-report-for-blocktrades-for-last-week-of-august-20160915t161707257z", + "json_metadata": "{\"tags\":[\"witness-category\"]}", + "parent_author": "blocktrades", + "parent_permlink": "witness-report-for-blocktrades-for-last-week-of-august" + } + }, + "block": 4995964, + "trx_id": "3ae419f4d37860c7d2dabec530ccf3ad79e79784", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-15T16:18:15", + "virtual_op": false, + "operation_id": "21457501991994369", + "trx_in_block": 2 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "macksby", + "memo": "92bff29e-2ee9-4ea4-ace1-6bb0c1331f67", + "amount": { + "nai": "@@000000013", + "amount": "50000", + "precision": 3 + } + } + }, + "block": 4994778, + "trx_id": "f2c59d933ba1fc08bd6f4c79ec52a0aeed6bb9e9", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-15T15:18:54", + "virtual_op": false, + "operation_id": "21452408160780290", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "good-karma", + "memo": "0c9ca5aa-53c2-445e-912f-d2cf6b111229", + "amount": { + "nai": "@@000000013", + "amount": "1500000", + "precision": 3 + } + } + }, + "block": 4994642, + "trx_id": "15a50977270c3b7e4e97715989f03099375b59bb", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-15T15:12:06", + "virtual_op": false, + "operation_id": "21451824045228034", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "blackjincrypto", + "memo": "7639138f-62f0-492a-b8e7-bad1add506dd", + "amount": { + "nai": "@@000000021", + "amount": "24838", + "precision": 3 + } + } + }, + "block": 4993122, + "trx_id": "a6e9f45a9b8fb6cd565936f64e13ae5b12ac94f2", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-15T13:56:03", + "virtual_op": false, + "operation_id": "21445295694938114", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "pakganern", + "author": "blocktrades", + "weight": 10000, + "permlink": "witness-report-for-blocktrades-for-last-week-of-august" + } + }, + "block": 4987311, + "trx_id": "b89762657465f62895ff3c8acab554a69a5bab1e", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-09-15T09:04:45", + "virtual_op": false, + "operation_id": "21420337639982848", + "trx_in_block": 4 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "schro", + "memo": "d70dfb8e-277a-4070-9812-b38cf01579cf", + "amount": { + "nai": "@@000000021", + "amount": "126225", + "precision": 3 + } + } + }, + "block": 4986680, + "trx_id": "68656136a5cff365a0c5ab873a60a3cf7528e47c", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-15T08:32:54", + "virtual_op": false, + "operation_id": "21417627515617282", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "aaronkoenig", + "memo": "b4c499fb-5deb-44dc-9646-e7feb3927e12", + "amount": { + "nai": "@@000000013", + "amount": "48000", + "precision": 3 + } + } + }, + "block": 4985503, + "trx_id": "2c918ba666c06946a792657b27ed18af196dfab5", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-15T07:34:00", + "virtual_op": false, + "operation_id": "21412572339109890", + "trx_in_block": 0 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "jeremyfromwi", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4984971, + "trx_id": "21b12d2246e1ceb0cedba3b04a504bb58d52b0d5", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-15T07:07:15", + "virtual_op": false, + "operation_id": "21410287416508684", + "trx_in_block": 1 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "cryptomental", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4984843, + "trx_id": "8c311c43f7dd1ddaa713aa6cb2f9d8dde96141bd", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-15T07:00:51", + "virtual_op": false, + "operation_id": "21409737660695052", + "trx_in_block": 1 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "I am not sure, but it is not funny anymore because I see the 12 points every time. Maybe my brain just recorded the pattern and remember it. I can not switch back again.\nAlso is truth that I work with crystal simetry pattern daily, so, maybe my brain is trained to see patterns and keep it. Also, in this occasion, I knew what was looking for...\nThis make me think how different we perceive the universe around us... Even when everybody is seeing the same thing...", + "title": "", + "author": "quigua", + "permlink": "re-blocktrades-re-quigua-re-blocktrades-re-ilovesteemit-can-you-see-all-twelve-dots-in-this-image-nope-you-cannot-20160915t054809925z", + "json_metadata": "{\"tags\":[\"optical\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-quigua-re-blocktrades-re-ilovesteemit-can-you-see-all-twelve-dots-in-this-image-nope-you-cannot-20160915t050900278z" + } + }, + "block": 4983394, + "trx_id": "5824e046a9235be9ba99440e3e8eb99860e8a63a", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-15T05:48:15", + "virtual_op": false, + "operation_id": "21403514253082625", + "trx_in_block": 0 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "therajmahal", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4982233, + "trx_id": "7df8d151a3c06fb31a6b902d80885d4a6ca5c1ab", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-15T04:50:09", + "virtual_op": false, + "operation_id": "21398527796051980", + "trx_in_block": 0 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "At first, I couldn't see more than 4 points at the same time. But, then I follow all points with the eyes making a circle pattern during 15 seconds, and surprising!. I can see the 12 points, and now I can not see the blinking points again. That is really crazy...", + "title": "", + "author": "quigua", + "permlink": "re-blocktrades-re-ilovesteemit-can-you-see-all-twelve-dots-in-this-image-nope-you-cannot-20160915t044305573z", + "json_metadata": "{\"tags\":[\"optical\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-ilovesteemit-can-you-see-all-twelve-dots-in-this-image-nope-you-cannot-20160915t034947752z" + } + }, + "block": 4982094, + "trx_id": "40ae1f796b8d0409a799fe62b3b423972b83c9e5", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-15T04:43:12", + "virtual_op": false, + "operation_id": "21397930795598081", + "trx_in_block": 1 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "rishi556", + "memo": " 50c8c0c0-c9f6-41c3-ace1-791567e5c1ba", + "amount": { + "nai": "@@000000013", + "amount": "105", + "precision": 3 + } + } + }, + "block": 4980808, + "trx_id": "083fc2207f29a78e907e5865911cf7d62910fd21", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-15T03:38:48", + "virtual_op": false, + "operation_id": "21392407467656706", + "trx_in_block": 3 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "stackcats", + "memo": "b163536b-4058-4187-96c6-ac9f573088ae", + "amount": { + "nai": "@@000000021", + "amount": "57873", + "precision": 3 + } + } + }, + "block": 4980626, + "trx_id": "5ac596f0c2b61dcffc2aed0d70858b0d36d32b91", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-15T03:29:42", + "virtual_op": false, + "operation_id": "21391625783608834", + "trx_in_block": 3 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "acidyo", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4980106, + "trx_id": "cf47b1bdf8d3f9a84cbb88931900242a7725f7c1", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-15T03:03:39", + "virtual_op": false, + "operation_id": "21389392400613388", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "ranko-k", + "memo": "59824378-b1af-44ab-a181-a150ace86aa5", + "amount": { + "nai": "@@000000021", + "amount": "29421", + "precision": 3 + } + } + }, + "block": 4977199, + "trx_id": "015a69792b3d1ce6964a656f829d1d0b9ac1fd18", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-15T00:38:18", + "virtual_op": false, + "operation_id": "21376906930683906", + "trx_in_block": 0 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "theb0red1", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4974156, + "trx_id": "706d118879820969cd2056bf7c72cb16f952a952", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-14T22:06:00", + "virtual_op": false, + "operation_id": "21363837345203724", + "trx_in_block": 3 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "@@ -399,16 +399,17 @@\n nts will\n+,\n too, an\n", + "title": "", + "author": "slammr76", + "permlink": "re-blocktrades-re-funny-why-are-bitcoin-steem-and-other-cryptocoins-worth-real-money-20160914t203223509z", + "json_metadata": "{\"tags\":[\"money\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-funny-why-are-bitcoin-steem-and-other-cryptocoins-worth-real-money-20160914t164907864z" + } + }, + "block": 4972329, + "trx_id": "f66ba76505558234d1532ad9030d349236b7ab85", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-14T20:34:30", + "virtual_op": false, + "operation_id": "21355990439952897", + "trx_in_block": 1 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "Right now, Steem has value because you can exchange it for bitcoin. I don't have to exchange bitcoin for fiat for it to have value for me, even though the merchants accepting it are likely exchanging it for fiat. I can and do spend bitcoin all the time. I've never exchanged it for fiat but have always exchanged dollars for bitcoin. I prefer to have my weath stored in bitcoin. Someday, the merchants will too, and few will care what the exchange rate for bitcoin vs fiat is.", + "title": "", + "author": "slammr76", + "permlink": "re-blocktrades-re-funny-why-are-bitcoin-steem-and-other-cryptocoins-worth-real-money-20160914t203223509z", + "json_metadata": "{\"tags\":[\"money\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-funny-why-are-bitcoin-steem-and-other-cryptocoins-worth-real-money-20160914t164907864z" + } + }, + "block": 4972289, + "trx_id": "34146d0288ea9070d2da2be5e191bb7c1885724e", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-14T20:32:30", + "virtual_op": false, + "operation_id": "21355818641261057", + "trx_in_block": 1 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "kranartz", + "memo": " 7702e0f5-8ab7-46d1-bbda-29d21c368160", + "amount": { + "nai": "@@000000013", + "amount": "1123", + "precision": 3 + } + } + }, + "block": 4971843, + "trx_id": "6412bb9abeec1f6e5027c11a165fa919fc233f03", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T20:10:12", + "virtual_op": false, + "operation_id": "21353903085846530", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "fatboy", + "memo": " 771b11ac-47f7-4b2f-b50a-71266e9b4026", + "amount": { + "nai": "@@000000013", + "amount": "28350", + "precision": 3 + } + } + }, + "block": 4971577, + "trx_id": "8b39da57e049a4f2d28eac4565183904a8765648", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T19:56:54", + "virtual_op": false, + "operation_id": "21352760624547330", + "trx_in_block": 3 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "fatboy", + "memo": " 9c0bf334-d8df-40bb-8002-409c03cf230f", + "amount": { + "nai": "@@000000021", + "amount": "22663", + "precision": 3 + } + } + }, + "block": 4971507, + "trx_id": "f817700f21c9c97b06eb61beb6eff948fb7ad07c", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T19:53:24", + "virtual_op": false, + "operation_id": "21352459976835074", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "anasz", + "memo": " 9f6e1027-9d03-4785-90d4-04fe4dd29b5e", + "amount": { + "nai": "@@000000013", + "amount": "16000", + "precision": 3 + } + } + }, + "block": 4971215, + "trx_id": "a21a7bfb210848c0c7d73d4a799a3cd6757d66d3", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T19:38:48", + "virtual_op": false, + "operation_id": "21351205846385154", + "trx_in_block": 1 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "gomeravibz", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4971093, + "trx_id": "f0a69def7eba73ee3ccd9a92dd915ee3eacc2b6a", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-14T19:32:39", + "virtual_op": false, + "operation_id": "21350681860375564", + "trx_in_block": 2 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "shaneradliff", + "memo": "7f208b66-5a77-48a5-92fb-bd9fbc5fd50c", + "amount": { + "nai": "@@000000013", + "amount": "1300", + "precision": 3 + } + } + }, + "block": 4970678, + "trx_id": "0fa231ee90f74305cab404aaabac6654050b5df9", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T19:11:21", + "virtual_op": false, + "operation_id": "21348899448947458", + "trx_in_block": 2 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "anasz", + "memo": "314ae1d5-c9e9-4a21-8cb0-48cf6d2a3ccb", + "amount": { + "nai": "@@000000013", + "amount": "10000", + "precision": 3 + } + } + }, + "block": 4969806, + "trx_id": "3fee1c9286faaa4abec711e275f0e897ada9cb5f", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T18:25:36", + "virtual_op": false, + "operation_id": "21345154237465602", + "trx_in_block": 2 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "Well, fiat money is worth something because you can use it to pay taxes.", + "title": "", + "author": "svamiva", + "permlink": "re-blocktrades-re-funny-why-are-bitcoin-steem-and-other-cryptocoins-worth-real-money-20160914t175725339z", + "json_metadata": "{\"tags\":[\"money\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-funny-why-are-bitcoin-steem-and-other-cryptocoins-worth-real-money-20160914t164907864z" + } + }, + "block": 4969274, + "trx_id": "c6f8881aba4bc494f32cfffc15ad122b44c8d124", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-14T17:57:33", + "virtual_op": false, + "operation_id": "21342869314864129", + "trx_in_block": 3 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "Thank you so much! I will remember this \").", + "title": "", + "author": "funny", + "permlink": "re-blocktrades-re-funny-re-blocktrades-re-funny-why-are-bitcoin-steem-and-other-cryptocoins-worth-real-money-20160914t174018203z", + "json_metadata": "{\"tags\":[\"money\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-funny-re-blocktrades-re-funny-why-are-bitcoin-steem-and-other-cryptocoins-worth-real-money-20160914t171338026z" + } + }, + "block": 4968948, + "trx_id": "26b80037ce89febd1c653fcbbbdf09faefc325b1", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-14T17:40:24", + "virtual_op": false, + "operation_id": "21341469155525121", + "trx_in_block": 1 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "You´re welcome :). Hehe, it´s always the last bit missing. Like the comments in this example :D", + "title": "", + "author": "lenatramper", + "permlink": "re-blocktrades-re-lenatramper-re-blocktrades-re-lenatramper-if-the-whole-earth-would-speak-like-adolf-hitler-20160914t173256475z", + "json_metadata": "{\"tags\":[\"adolf\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-lenatramper-re-blocktrades-re-lenatramper-if-the-whole-earth-would-speak-like-adolf-hitler-20160914t164216448z" + } + }, + "block": 4968805, + "trx_id": "2daac261fcc68c2461adc8524261bc11de614f3e", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-14T17:32:48", + "virtual_op": false, + "operation_id": "21340854975201281", + "trx_in_block": 0 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "good-karma", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4968789, + "trx_id": "07faf6e609944fa59dd2bdfe8fbc0c2270e83a4a", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-14T17:32:00", + "virtual_op": false, + "operation_id": "21340786255724556", + "trx_in_block": 0 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "mikemacintire", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4968546, + "trx_id": "ddfd6daa8550483afc8158b9926c7d1b1fa1f731", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-14T17:19:15", + "virtual_op": false, + "operation_id": "21339742578672396", + "trx_in_block": 2 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "Hey @blocktrades, thanks for reading \"). \n\nAgreed, that was the point I was making, but with that statement, I meant it as you need a bridge to start. If everyone decided to adopt crypto tomorrow, and picked a few coins that we all used, then yes, you're right, it could work without fiat. \n\nAny chance you can upvote the post, I'm trying to build my account with steemit.", + "title": "", + "author": "funny", + "permlink": "re-blocktrades-re-funny-why-are-bitcoin-steem-and-other-cryptocoins-worth-real-money-20160914t170230207z", + "json_metadata": "{\"tags\":[\"money\"],\"users\":[\"blocktrades\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-funny-why-are-bitcoin-steem-and-other-cryptocoins-worth-real-money-20160914t164907864z" + } + }, + "block": 4968231, + "trx_id": "f205fbcc8aee61419ad54982f3d594de07450a2a", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-14T17:02:36", + "virtual_op": false, + "operation_id": "21338389663973377", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "ziogio", + "memo": "92754b24-3405-434f-88ca-07ad5e74b089", + "amount": { + "nai": "@@000000013", + "amount": "10000", + "precision": 3 + } + } + }, + "block": 4967726, + "trx_id": "f620062399c31a45b339fb6ca2d8d3980a6bf747", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T16:37:03", + "virtual_op": false, + "operation_id": "21336220705488898", + "trx_in_block": 0 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "Hi blocktrades,\n\nyou can check it here in the comments :)\nhttps://steemit.com/language/@lenatramper/top-10-hardest-languages-in-the-world-ranking", + "title": "", + "author": "lenatramper", + "permlink": "re-blocktrades-re-lenatramper-if-the-whole-earth-would-speak-like-adolf-hitler-20160914t160355256z", + "json_metadata": "{\"tags\":[\"adolf\"],\"links\":[\"https://steemit.com/language/@lenatramper/top-10-hardest-languages-in-the-world-ranking\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-lenatramper-if-the-whole-earth-would-speak-like-adolf-hitler-20160914t155903950z" + } + }, + "block": 4967060, + "trx_id": "120b4917d02d628ae830fb480c1d049274489445", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-14T16:03:45", + "virtual_op": false, + "operation_id": "21333360257271553", + "trx_in_block": 4 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "juliac", + "memo": "0dba7938-f263-4da8-820e-8d883ddacff7", + "amount": { + "nai": "@@000000013", + "amount": "13334", + "precision": 3 + } + } + }, + "block": 4965170, + "trx_id": "515fca2bc83c7f460f18ec52457c0cd672826358", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T14:29:12", + "virtual_op": false, + "operation_id": "21325242769080322", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "blockchaingirl", + "memo": "6ebbb676-3fd8-4713-acbe-2f1d4da1a0b0", + "amount": { + "nai": "@@000000013", + "amount": "800000", + "precision": 3 + } + } + }, + "block": 4964407, + "trx_id": "cf4264043076e5af34318b9db392ccf469a66563", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T13:51:00", + "virtual_op": false, + "operation_id": "21321965709034498", + "trx_in_block": 2 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "solarguy", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4959174, + "trx_id": "6bd2741483e2659c957cb574b7fec5ba1fec25de", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-14T09:28:45", + "virtual_op": false, + "operation_id": "21299490145174284", + "trx_in_block": 2 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "royalmacro", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4957445, + "trx_id": "582159a7d6fa988594fe6f947bc73998fc655b14", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-14T08:02:00", + "virtual_op": false, + "operation_id": "21292064146721292", + "trx_in_block": 5 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "kaylinart", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4957104, + "trx_id": "8ce909995eaf91a2ed2676932000a4c7d4af80ad", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-14T07:44:54", + "virtual_op": false, + "operation_id": "21290599562870796", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "anasz", + "memo": "b16de592-76a1-485d-b8c8-4e8692f9803e", + "amount": { + "nai": "@@000000013", + "amount": "23", + "precision": 3 + } + } + }, + "block": 4954441, + "trx_id": "169310b87bfd1fde6ff207ea39ec09df7760391e", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T05:31:21", + "virtual_op": false, + "operation_id": "21279162064964098", + "trx_in_block": 5 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "bryner", + "memo": "4c93872a-7b82-42f3-b094-a54ab719c34c", + "amount": { + "nai": "@@000000021", + "amount": "8350", + "precision": 3 + } + } + }, + "block": 4952702, + "trx_id": "d7474d597f3cd27c895b477b1a7b1046888851d7", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T04:04:06", + "virtual_op": false, + "operation_id": "21271693116833794", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "theb0red1", + "author": "blocktrades", + "weight": 10000, + "permlink": "re-timcliff-poloniex-transfers-disabled-for-steem-and-sbd-20160914t013605178z" + } + }, + "block": 4952567, + "trx_id": "182a7686346f290e1bf55d4a8e2474e5bd33697e", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-09-14T03:57:21", + "virtual_op": false, + "operation_id": "21271113296249344", + "trx_in_block": 1 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "themanualbot", + "memo": "a4f54c6d-bc77-404f-b4ce-8e3433fcc928", + "amount": { + "nai": "@@000000013", + "amount": "484185", + "precision": 3 + } + } + }, + "block": 4950730, + "trx_id": "aa54b0534c3e747051b5e88b620d9f8cd337c983", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T02:25:15", + "virtual_op": false, + "operation_id": "21263223441327618", + "trx_in_block": 3 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "Ok, thanks!", + "title": "", + "author": "timcliff", + "permlink": "re-blocktrades-re-timcliff-poloniex-transfers-disabled-for-steem-and-sbd-20160914t015047838z", + "json_metadata": "{\"tags\":[\"poloniex\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-timcliff-poloniex-transfers-disabled-for-steem-and-sbd-20160914t013605178z" + } + }, + "block": 4950046, + "trx_id": "a1467edcb59b7cf3cff77cd83c4af3ada0c4ffb6", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-14T01:50:51", + "virtual_op": false, + "operation_id": "21260285683696129", + "trx_in_block": 1 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "forrestwillie", + "memo": "34edb0f7-ad6f-4927-84ea-d5faf99f9e6e", + "amount": { + "nai": "@@000000021", + "amount": "66281", + "precision": 3 + } + } + }, + "block": 4949192, + "trx_id": "693841b51e3fe86929f71d5491fba422acbbae54", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T01:08:03", + "virtual_op": false, + "operation_id": "21256617781624834", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "shaneradliff", + "memo": "d1d42c18-b06f-4124-9c81-a4593dcea8cd", + "amount": { + "nai": "@@000000013", + "amount": "13000", + "precision": 3 + } + } + }, + "block": 4948215, + "trx_id": "1134409013de5dd70173d087edfe9763baec70ec", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-14T00:19:03", + "virtual_op": false, + "operation_id": "21252421598576642", + "trx_in_block": 0 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "wefdi", + "approve": false, + "witness": "blocktrades" + } + }, + "block": 4947057, + "trx_id": "b375567add15744604a2ab38d76ebeb82cb0e897", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-13T23:21:00", + "virtual_op": false, + "operation_id": "21247448026448908", + "trx_in_block": 2 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "lindee-hamner", + "memo": "3ecd1ed7-da35-4497-8106-92e26eff9103", + "amount": { + "nai": "@@000000021", + "amount": "22367", + "precision": 3 + } + } + }, + "block": 4946739, + "trx_id": "74bf8f7190a6a7e7c51692c27a0a8523e33e7821", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T23:05:03", + "virtual_op": false, + "operation_id": "21246082226847746", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "thisisbenbrick", + "memo": "2e62fd10-bd7b-43ee-a2a2-33324e576e93", + "amount": { + "nai": "@@000000021", + "amount": "766741", + "precision": 3 + } + } + }, + "block": 4946006, + "trx_id": "65c72bcffb05588cd274257969bee51f07d266d2", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T22:28:21", + "virtual_op": false, + "operation_id": "21242934015819778", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "mrwang", + "memo": "a79c09cd-0084-4cd4-ae63-bf6d2514fef9", + "amount": { + "nai": "@@000000013", + "amount": "71475", + "precision": 3 + } + } + }, + "block": 4945986, + "trx_id": "555cc791ad80d0f025e6f7fa5965657c1e54b063", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T22:27:18", + "virtual_op": false, + "operation_id": "21242848116474882", + "trx_in_block": 2 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "yassinebentour", + "memo": "b9b4574a-220d-49ee-bf4e-6889df4e387c", + "amount": { + "nai": "@@000000021", + "amount": "1775", + "precision": 3 + } + } + }, + "block": 4944673, + "trx_id": "4e78a7c63beff4ffa7c715eae5c523231918d939", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T21:21:30", + "virtual_op": false, + "operation_id": "21237208824414978", + "trx_in_block": 2 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "furion", + "memo": "cffb8a8e-1f88-4654-945a-8888dcdac62a", + "amount": { + "nai": "@@000000021", + "amount": "50000", + "precision": 3 + } + } + }, + "block": 4943900, + "trx_id": "ee10f9357650aeaebfc4b34429b5465642edc41b", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T20:42:45", + "virtual_op": false, + "operation_id": "21233888814694402", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "dmilash", + "author": "blocktrades", + "weight": 10000, + "permlink": "witness-report-for-blocktrades-for-last-week-of-august" + } + }, + "block": 4943618, + "trx_id": "c8bfb1ae741895c5d9ff90381faef14cb2bd9dc0", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-09-13T20:28:39", + "virtual_op": false, + "operation_id": "21232677633917440", + "trx_in_block": 1 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "vetvso", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4943513, + "trx_id": "18d12216537678f958f3e7e0c6550367d01a76fa", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-13T20:23:21", + "virtual_op": false, + "operation_id": "21232226662351116", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "vetvso", + "author": "blocktrades", + "weight": 10000, + "permlink": "re-alexgaud-re-blocktrades-witness-report-for-blocktrades-for-last-week-of-august-20160902t150436912z" + } + }, + "block": 4943368, + "trx_id": "fb9ee97aaafe8d8f0581a269d5f0ff2ca4c9ffa7", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-09-13T20:16:06", + "virtual_op": false, + "operation_id": "21231603892094208", + "trx_in_block": 3 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "vetvso", + "author": "blocktrades", + "weight": 10000, + "permlink": "witness-report-for-blocktrades-for-last-week-of-august" + } + }, + "block": 4943233, + "trx_id": "596e450517c6f651b77492fc6a9ad3b1e46cf271", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-09-13T20:09:21", + "virtual_op": false, + "operation_id": "21231024071507968", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "novium", + "memo": " d48f47eb-5900-4a70-bfb3-a821ba90de30", + "amount": { + "nai": "@@000000013", + "amount": "40000", + "precision": 3 + } + } + }, + "block": 4942698, + "trx_id": "c38b4cf8d260ec43b48c55f3613428e5adb3dc93", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T19:42:33", + "virtual_op": false, + "operation_id": "21228726264005378", + "trx_in_block": 2 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "vasco-el-jim", + "memo": "edc7fa29-31d9-42a0-bf53-353d6c01f1c0", + "amount": { + "nai": "@@000000021", + "amount": "50000", + "precision": 3 + } + } + }, + "block": 4941972, + "trx_id": "c4aaf923be6e77740477d13d2e904cae164caa05", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T19:06:06", + "virtual_op": false, + "operation_id": "21225608117747714", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "trevorjenglish", + "memo": " be40e871-d9c4-494f-a85d-92deae85c97c", + "amount": { + "nai": "@@000000021", + "amount": "52407", + "precision": 3 + } + } + }, + "block": 4940759, + "trx_id": "d2bc1229e4138b9692170b2d23c5f71084dae5c0", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T18:05:21", + "virtual_op": false, + "operation_id": "21220398322417666", + "trx_in_block": 0 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "pathtomydream", + "author": "blocktrades", + "weight": 10000, + "permlink": "witness-report-for-blocktrades-for-last-week-of-august" + } + }, + "block": 4940594, + "trx_id": "e05d94343442b1ee91910b7c17eef0f2a71eb392", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-09-13T17:57:06", + "virtual_op": false, + "operation_id": "21219689652814336", + "trx_in_block": 1 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "expedition", + "author": "blocktrades", + "weight": 10000, + "permlink": "re-alexgaud-re-blocktrades-witness-report-for-blocktrades-for-last-week-of-august-20160902t150436912z" + } + }, + "block": 4939847, + "trx_id": "f4f666caa5199e09e0c8f29a3e8d8aa40b4b859f", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-09-13T17:19:36", + "virtual_op": false, + "operation_id": "21216481312243712", + "trx_in_block": 0 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "@@ -73,16 +73,23 @@\n ugh the \n+coffee \n ocean. J\n", + "title": "", + "author": "phenom", + "permlink": "re-blocktrades-re-smailer-my-daytime-latte-art-tryings-blocktrades-fan-art-20160913t151302200z", + "json_metadata": "{\"tags\":[\"food\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-smailer-my-daytime-latte-art-tryings-blocktrades-fan-art-20160913t131146653z" + } + }, + "block": 4937336, + "trx_id": "d41b3134e9d0ac8a797d4a534498aa158faa12f5", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-13T15:13:45", + "virtual_op": false, + "operation_id": "21205696649364225", + "trx_in_block": 2 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "haha, Initially I thought that this blue thing is a whale floating through the ocean. Just realized that it's a symbol of blocktrades platform", + "title": "", + "author": "phenom", + "permlink": "re-blocktrades-re-smailer-my-daytime-latte-art-tryings-blocktrades-fan-art-20160913t151302200z", + "json_metadata": "{\"tags\":[\"food\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-smailer-my-daytime-latte-art-tryings-blocktrades-fan-art-20160913t131146653z" + } + }, + "block": 4937323, + "trx_id": "7755512e9ec8350494408daa261d7627a04ac87a", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-13T15:13:06", + "virtual_op": false, + "operation_id": "21205640814789377", + "trx_in_block": 2 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "wefdi", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4937041, + "trx_id": "6cefffe540c5636c5fa9cef5bd29c57cc434121f", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-13T14:58:57", + "virtual_op": false, + "operation_id": "21204429634011148", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "karsmaxanes", + "memo": " 9823b75c-e743-47e4-bbe9-7d0e9edd914b", + "amount": { + "nai": "@@000000013", + "amount": "44000", + "precision": 3 + } + } + }, + "block": 4936718, + "trx_id": "2be56fa88ee477504d54ab8fb0ee410812daba21", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T14:42:42", + "virtual_op": false, + "operation_id": "21203042359575554", + "trx_in_block": 2 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "mrwang", + "memo": "a79c09cd-0084-4cd4-ae63-bf6d2514fef9", + "amount": { + "nai": "@@000000013", + "amount": "2821", + "precision": 3 + } + } + }, + "block": 4936102, + "trx_id": "b254e42f7ee58f909bcb190d6c58d925543cc0a9", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T14:11:48", + "virtual_op": false, + "operation_id": "21200396659720706", + "trx_in_block": 1 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "blockchaingirl", + "memo": "039abf1c-19ed-4242-a267-78bcaf29b6df", + "amount": { + "nai": "@@000000013", + "amount": "800000", + "precision": 3 + } + } + }, + "block": 4935965, + "trx_id": "58943fa0fc2612ecbb0c4733fc85bca2a048a651", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T14:04:57", + "virtual_op": false, + "operation_id": "21199808249201154", + "trx_in_block": 1 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "I see, but I talk about two hidden from eye edges. \nhttps://s9.postimg.org/nny820clr/2016_09_13_1630.png\nIn 3D they will be visible :) or just duplicate symbols Bitcoin and BitShares?", + "title": "", + "author": "smailer", + "permlink": "re-blocktrades-re-smailer-re-blocktrades-re-smailer-my-daytime-latte-art-tryings-blocktrades-fan-art-20160913t133200497z", + "json_metadata": "{\"tags\":[\"food\"],\"image\":[\"https://s9.postimg.org/nny820clr/2016_09_13_1630.png\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-smailer-re-blocktrades-re-smailer-my-daytime-latte-art-tryings-blocktrades-fan-art-20160913t132556933z" + } + }, + "block": 4935318, + "trx_id": "d600c1d7a2674b8044a9b419792bb5ee88fc8c60", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-13T13:32:12", + "virtual_op": false, + "operation_id": "21197029405360897", + "trx_in_block": 2 + }, + { + "op": { + "type": "vote_operation", + "value": { + "voter": "anasz", + "author": "blocktrades", + "weight": 10000, + "permlink": "witness-report-for-blocktrades-for-last-week-of-august" + } + }, + "block": 4935292, + "trx_id": "18c58076b75a07a6bfaebdf28ef0ac0d926fd6b8", + "op_pos": 0, + "op_type_id": 0, + "timestamp": "2016-09-13T13:30:54", + "virtual_op": false, + "operation_id": "21196917736211200", + "trx_in_block": 1 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "@@ -25,16 +25,30 @@\n missing \n+classic movie \n lol than\n", + "title": "", + "author": "fairz", + "permlink": "re-blocktrades-re-fairz-it-the-clown-parody-20160913t132321388z", + "json_metadata": "{\"tags\":[\"animation\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-fairz-it-the-clown-parody-20160913t114531552z" + } + }, + "block": 4935240, + "trx_id": "e20a2875cef6b7b4b2f8d15af33e57b7f0fae003", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-13T13:28:18", + "virtual_op": false, + "operation_id": "21196694397912577", + "trx_in_block": 3 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "they dont no what there missing lol thanks for looking", + "title": "", + "author": "fairz", + "permlink": "re-blocktrades-re-fairz-it-the-clown-parody-20160913t132321388z", + "json_metadata": "{\"tags\":[\"animation\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-fairz-it-the-clown-parody-20160913t114531552z" + } + }, + "block": 4935141, + "trx_id": "35a2153fd3a78744bc149d1b1a77a65aee3d7ff7", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-13T13:23:21", + "virtual_op": false, + "operation_id": "21196269196150785", + "trx_in_block": 5 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "Glad to hear you! thank you!\nWhen I find the way make more square block I will try add more details.\nCan you say me what the money symbols on another one edges ?", + "title": "", + "author": "smailer", + "permlink": "re-blocktrades-re-smailer-my-daytime-latte-art-tryings-blocktrades-fan-art-20160913t131414898z", + "json_metadata": "{\"tags\":[\"food\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-smailer-my-daytime-latte-art-tryings-blocktrades-fan-art-20160913t131146653z" + } + }, + "block": 4934964, + "trx_id": "09845865ddf51d5b61350e7f8864b7bb01a5d514", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-13T13:14:27", + "virtual_op": false, + "operation_id": "21195508986938625", + "trx_in_block": 3 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "onetree", + "memo": "f5b4968c-2ecc-4bbd-a85c-af1c1541657d", + "amount": { + "nai": "@@000000013", + "amount": "94576", + "precision": 3 + } + } + }, + "block": 4934174, + "trx_id": "b1ef0b05d1913f8c51e52deb7ac2cb836ee222d0", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T12:34:39", + "virtual_op": false, + "operation_id": "21192115962774274", + "trx_in_block": 1 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "Sent a followup email, specifically mentioning your comments (as an influential member here) and got this promising response :)\n
", + "title": "", + "author": "ausbitbank", + "permlink": "re-blocktrades-re-krystle-re-blocktrades-re-eight-rad-re-hugothepoet-brexit-rap-battles-of-history-the-irony-of-it-all-new-original-rap-20160913t095540421z", + "json_metadata": "{\"tags\":[\"hugothepoet\"],\"image\":[\"https://www.steemimg.com/images/2016/09/13/hugoemailb7efe.png\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-krystle-re-blocktrades-re-eight-rad-re-hugothepoet-brexit-rap-battles-of-history-the-irony-of-it-all-new-original-rap-20160910t185524205z" + } + }, + "block": 4931009, + "trx_id": "9e67c8a0528a527dce8c6cb450ff236b47822dde", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-13T09:55:45", + "virtual_op": false, + "operation_id": "21178522391282177", + "trx_in_block": 1 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "slowwalker", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4929491, + "trx_id": "38b0d2d1b33b3f6cd6392a5dd7daf938a4713873", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-13T08:39:33", + "virtual_op": false, + "operation_id": "21172002630926348", + "trx_in_block": 0 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "stranger27", + "approve": false, + "witness": "blocktrades" + } + }, + "block": 4929005, + "trx_id": "625f1260e080e0f9840bb58d793472dc60cb5dcf", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-13T08:15:15", + "virtual_op": false, + "operation_id": "21169915276821004", + "trx_in_block": 1 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "tinfoilfedora", + "memo": "d03e7b59-2dfb-4e08-8506-5f435cbb522e", + "amount": { + "nai": "@@000000013", + "amount": "25000", + "precision": 3 + } + } + }, + "block": 4927062, + "trx_id": "0b6a7666a14a312f9976e01375656f6a7da1bcd6", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T06:37:48", + "virtual_op": false, + "operation_id": "21161570155364354", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "tinfoilfedora", + "memo": "d03e7b59-2dfb-4e08-8506-5f435cbb522e", + "amount": { + "nai": "@@000000013", + "amount": "120000", + "precision": 3 + } + } + }, + "block": 4926872, + "trx_id": "f21ce3eb2aa1aa47a2f5ba389dd6bac96742016b", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T06:28:18", + "virtual_op": false, + "operation_id": "21160754111578370", + "trx_in_block": 1 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "levycore", + "memo": "5bf6f52a-01f2-4552-93a9-2318ab9c79ae", + "amount": { + "nai": "@@000000013", + "amount": "34000", + "precision": 3 + } + } + }, + "block": 4926832, + "trx_id": "a9449ff148d769878770031c14af8f608522384c", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T06:26:15", + "virtual_op": false, + "operation_id": "21160582312886274", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "krypto", + "memo": "931ded82-cd09-4e3f-80ff-5fbba1b54cf7", + "amount": { + "nai": "@@000000021", + "amount": "40000", + "precision": 3 + } + } + }, + "block": 4925898, + "trx_id": "41a66f6d678d0e06100cd62e8a155d70d768a410", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T05:39:27", + "virtual_op": false, + "operation_id": "21156570813432066", + "trx_in_block": 1 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "kennyskitchen", + "memo": "6230441c-1436-4bd7-826b-e78cc86666d0", + "amount": { + "nai": "@@000000013", + "amount": "40151", + "precision": 3 + } + } + }, + "block": 4924611, + "trx_id": "2e75bc3b9f7405317797a37f6908a2042d13c7e1", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T04:34:54", + "virtual_op": false, + "operation_id": "21151043190522882", + "trx_in_block": 2 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "luzcypher", + "memo": "4435fc70-df36-43e3-b79c-dcb75118a90e", + "amount": { + "nai": "@@000000013", + "amount": "25000", + "precision": 3 + } + } + }, + "block": 4922698, + "trx_id": "2a67c362d3d6f1199611a000dbbd899d13d8f46e", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T02:58:33", + "virtual_op": false, + "operation_id": "21142826918084610", + "trx_in_block": 0 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "Nice article !", + "title": "", + "author": "alex2016", + "permlink": "re-blocktrades-blocktrades-witness-report-for-3rd-week-of-august-20160913t025753852z", + "json_metadata": "{\"tags\":[\"witness-category\"]}", + "parent_author": "blocktrades", + "parent_permlink": "blocktrades-witness-report-for-3rd-week-of-august" + } + }, + "block": 4922686, + "trx_id": "e5538ed98b4286b3b220c9525582eb606eab8f6c", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-13T02:57:57", + "virtual_op": false, + "operation_id": "21142775378478337", + "trx_in_block": 3 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "luzcypher", + "memo": "12Ht7fzVCmUP8BWx1Nk3ThrsLvEoFhmfm2", + "amount": { + "nai": "@@000000013", + "amount": "23000", + "precision": 3 + } + } + }, + "block": 4922068, + "trx_id": "7b52cc62c5804b40262a74bcb8e8c711253b2462", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T02:27:00", + "virtual_op": false, + "operation_id": "21140121088688898", + "trx_in_block": 2 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "luzcypher", + "memo": "af2ed608-e7fa-4cdc-80b4-bb049c6125e2", + "amount": { + "nai": "@@000000013", + "amount": "24000", + "precision": 3 + } + } + }, + "block": 4921719, + "trx_id": "625f4822303c979e3f404010c59ae992b27458c3", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T02:09:21", + "virtual_op": false, + "operation_id": "21138622145102338", + "trx_in_block": 1 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "luzcypher", + "memo": "b1737718-81cc-46f4-a5de-7cfc25158ab9", + "amount": { + "nai": "@@000000013", + "amount": "30", + "precision": 3 + } + } + }, + "block": 4921269, + "trx_id": "3bd88459ff9b3d0b50dd0ee010ccc99e4da46e0c", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T01:46:51", + "virtual_op": false, + "operation_id": "21136689409818626", + "trx_in_block": 0 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "luzcypher", + "memo": "51d939e0-9466-4204-b8ba-ea064ed5ad89", + "amount": { + "nai": "@@000000013", + "amount": "1000", + "precision": 3 + } + } + }, + "block": 4921209, + "trx_id": "ab006f1ed38e768fe8336c08821b9ec832453c2c", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T01:43:51", + "virtual_op": false, + "operation_id": "21136431711781890", + "trx_in_block": 2 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "luzcypher", + "memo": "", + "amount": { + "nai": "@@000000013", + "amount": "1000", + "precision": 3 + } + } + }, + "block": 4920877, + "trx_id": "dc7615967a6dfec38df66ea9a28cbd14db4a47d5", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T01:27:12", + "virtual_op": false, + "operation_id": "21135005782639106", + "trx_in_block": 1 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "johan-nygren", + "memo": " a68ac41f-bc4b-402f-915c-990c5b9de41f", + "amount": { + "nai": "@@000000013", + "amount": "93910", + "precision": 3 + } + } + }, + "block": 4919627, + "trx_id": "1072dabfad4c459ee6da08e1b6ddaa7d1cc8033f", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-13T00:24:33", + "virtual_op": false, + "operation_id": "21129637073519106", + "trx_in_block": 1 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "chitty", + "approve": false, + "witness": "blocktrades" + } + }, + "block": 4915116, + "trx_id": "ebab7cd7f222d5f85539361487fca9e895c1a41c", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-12T20:38:15", + "virtual_op": false, + "operation_id": "21110262476047372", + "trx_in_block": 2 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "soulsistashakti", + "memo": "1af846c5-640b-4296-9d37-ded453c0eeb1", + "amount": { + "nai": "@@000000013", + "amount": "10000", + "precision": 3 + } + } + }, + "block": 4914007, + "trx_id": "b75309ee66fab08cb0ec535595273d5e28b0eb38", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-12T19:42:36", + "virtual_op": false, + "operation_id": "21105499357316354", + "trx_in_block": 3 + }, + { + "op": { + "type": "account_witness_vote_operation", + "value": { + "account": "kingscrown", + "approve": true, + "witness": "blocktrades" + } + }, + "block": 4913253, + "trx_id": "beba6063fd4603a157805cfd5280f25747713bf7", + "op_pos": 0, + "op_type_id": 12, + "timestamp": "2016-09-12T19:04:51", + "virtual_op": false, + "operation_id": "21102260951976204", + "trx_in_block": 4 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "condra", + "memo": "158a5398-1542-4822-a47b-f144ddeef74f", + "amount": { + "nai": "@@000000013", + "amount": "295891", + "precision": 3 + } + } + }, + "block": 4912869, + "trx_id": "f56aa2a449bc90c025c49e489f624aa9bba54917", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-12T18:45:30", + "virtual_op": false, + "operation_id": "21100611684533250", + "trx_in_block": 3 + }, + { + "op": { + "type": "transfer_operation", + "value": { + "to": "blocktrades", + "from": "lauralemons", + "memo": "0bd788bc-09bc-45d4-a385-9656061a8418", + "amount": { + "nai": "@@000000021", + "amount": "46672", + "precision": 3 + } + } + }, + "block": 4912813, + "trx_id": "6a466c13ddfcf1a6ec5cacc51d2c0a4c257f420b", + "op_pos": 0, + "op_type_id": 2, + "timestamp": "2016-09-12T18:42:42", + "virtual_op": false, + "operation_id": "21100371166363650", + "trx_in_block": 0 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "@@ -187,8 +187,74 @@\n mpatible\n+, I have buy some steem too with what I have earn here to power up\n", + "title": "", + "author": "alex.gaud", + "permlink": "re-blocktrades-re-alexgaud-re-blocktrades-witness-report-for-blocktrades-for-last-week-of-august-20160912t173311407z", + "json_metadata": "{\"tags\":[\"witness-category\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-alexgaud-re-blocktrades-witness-report-for-blocktrades-for-last-week-of-august-20160902t150436912z" + } + }, + "block": 4911452, + "trx_id": "f21a1ad86d4cd8ab3cb2ee01c8ba60bfd26a57d4", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-12T17:34:24", + "virtual_op": false, + "operation_id": "21094525715874305", + "trx_in_block": 2 + }, + { + "op": { + "type": "comment_operation", + "value": { + "body": "Thank you sir for your answer, sorry if I will ask you again, actually I can't install the steem wallet on my computer I'm using windows 7, I have error message saying that it was not compatible", + "title": "", + "author": "alex.gaud", + "permlink": "re-blocktrades-re-alexgaud-re-blocktrades-witness-report-for-blocktrades-for-last-week-of-august-20160912t173311407z", + "json_metadata": "{\"tags\":[\"witness-category\"]}", + "parent_author": "blocktrades", + "parent_permlink": "re-alexgaud-re-blocktrades-witness-report-for-blocktrades-for-last-week-of-august-20160902t150436912z" + } + }, + "block": 4911428, + "trx_id": "d21600ff9e8b63ec977e0eb41e72907ed55e20cd", + "op_pos": 0, + "op_type_id": 1, + "timestamp": "2016-09-12T17:33:12", + "virtual_op": false, + "operation_id": "21094422636658945", + "trx_in_block": 1 + } + ] +} \ No newline at end of file diff --git a/tests/tavern/get_ops_by_account/positive/transacting_account_exclude.tavern.yaml b/tests/tavern/get_ops_by_account/positive/transacting_account_exclude.tavern.yaml new file mode 100644 index 0000000000000000000000000000000000000000..71a64b9fee0c0ed97d9926ea7a9a921a6ce36ef2 --- /dev/null +++ b/tests/tavern/get_ops_by_account/positive/transacting_account_exclude.tavern.yaml @@ -0,0 +1,25 @@ +--- + test_name: Hafah PostgREST + + marks: + - patterntest + + includes: + - !include ../../common.yaml + + stages: + - name: test + request: + url: "{service.proto:s}://{service.server:s}:{service.port}/rpc/get_ops_by_account" + method: POST + headers: + content-type: application/json + accept: application/json + json: + account-name: "blocktrades" + transacting-account-name: "blocktrades" + participation-mode: "exclude" + response: + status_code: 200 + verify_response_with: + function: validate_response:compare_rest_response_with_pattern