Skip to content

Commit

Permalink
Add Naming tree, isVerified tag for Validators
Browse files Browse the repository at this point in the history
* Added naming tree API
* Added `isVerified` to validator data responses
* Added new ENV for `explorer.verified-addresses` as a comma-deliniated list of trusted addresses

closes: #134
closes: #343
  • Loading branch information
minxylynx committed Jun 7, 2022
1 parent 51648e7 commit 10fb677
Show file tree
Hide file tree
Showing 21 changed files with 689 additions and 98 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ Ref: https://keepachangelog.com/en/1.0.0/
* Add new IBC APIs [#336](https://github.com/provenance-io/explorer-service/issues/336)
* `/api/v2/txs/ibc/chain/{ibcChain}` - txs per IBC chain id, query params supporting narrowing by channel
* `/api/v2/ibc/channels/src_port/{srcPort}/src_channel/{srcChannel}/relayers` - relayers by channel
* Add Name APIs #134
* `/api/v2/names/tree` - tree map of names on chain, including restriction and owner
* Updated Validator data responses to include `isVerified` attribute #343
* Defined by where the validator owner has a KYC attribute created by a trusted address
* Added new ENV for `explorer.verified-addresses` - is a list of trusted addresses

### Improvements
* Update vote ingestion to include Weighted Votes [#323](https://github.com/provenance-io/explorer-service/issues/323)
Expand Down Expand Up @@ -118,6 +123,12 @@ Ref: https://keepachangelog.com/en/1.0.0/
* Updated `add_tx_debug()`, `add_tx()` procedures with new ingestions
* Migration 1.63 - Add function for `uuid_or_null()` [#339](https://github.com/provenance-io/explorer-service/issues/339)
* Added function to determine if a string is a UUID, and if not, return null
* Migration 1.64 - Add naming tree #134
* Created `name` table, and inserted records
* Migration 1.65 - Add `verified` to validators #343
* Added `verified` to `validator_state` table
* Updated materialized view `current_validator_state` to include new `verified` column
* Updated the `get_validator_list()`, `get_all_validator_state()` functions to include new `verified` column

## [v4.1.0](https://github.com/provenance-io/explorer-service/releases/tag/v4.1.0) - 2022-03-24
### Release Name: Abu Bakr II
Expand Down
123 changes: 123 additions & 0 deletions database/src/main/resources/db/migration/V1_65__Add_naming_tree.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
SELECT 'Adding name table' AS comment;

CREATE TABLE IF NOT EXISTS name
(
id SERIAL PRIMARY KEY,
parent VARCHAR(550),
child VARCHAR(40),
full_name VARCHAR(600),
owner VARCHAR(128),
restricted BOOLEAN DEFAULT FALSE,
height_added INT
);

DROP INDEX IF EXISTS name_unique_idx;
CREATE UNIQUE INDEX IF NOT EXISTS name_unique_idx ON name (full_name, owner);

SELECT 'Inserting bind_names' AS comment;
WITH base AS (
SELECT tc.height,
tc.id,
CASE WHEN attr.key = 'name' THEN attr.value::text END AS full_name,
CASE WHEN attr.key = 'address' THEN attr.value::text END AS owner
FROM tx_cache tc
JOIN tx_message tm ON tm.tx_hash_id = tc.id
JOIN tx_message_type tmt on tm.tx_message_type_id = tmt.id,
jsonb_array_elements(tc.tx_v2 -> 'tx_response' -> 'logs') with ordinality logs("events", idx),
jsonb_to_recordset(logs.events -> 'events') event("type" text, "attributes" jsonb),
jsonb_to_recordset(event.attributes) attr("key" text, "value" text)
WHERE tc.error_code IS NULL
AND tmt.proto_type IN ('/provenance.name.v1.MsgBindNameRequest',
'/cosmwasm.wasm.v1beta1.MsgInstantiateContract',
'/cosmwasm.wasm.v1.MsgInstantiateContract',
'/cosmwasm.wasm.v1.MsgExecuteContract',
'/cosmwasm.wasm.v1beta1.MsgExecuteContract')
AND logs.idx - 1 = tm.msg_idx
AND event.type IN ('provenance.name.v1.EventNameBound')
ORDER BY tc.height
),
name_agg AS (
SELECT base.id,
array_agg(base.full_name) AS name_agg
FROM base
WHERE base.full_name IS NOT NULL
GROUP BY base.id
),
owner_agg AS (
SELECT base.id,
array_agg(base.owner) AS owner_agg
FROM base
WHERE base.owner IS NOT NULL
GROUP BY base.id
),
smooshed AS (
SELECT base.height,
base.id AS tx_id,
REPLACE(unnest(na.name_agg), '"', '') AS full_name,
REPLACE(unnest(oa.owner_agg), '"', '') AS owner
FROM base
JOIN name_agg na ON base.id = na.id
JOIN owner_agg oa ON base.id = oa.id
),
regexed AS (
SELECT sm.height,
sm.owner,
sm.full_name,
regexp_matches(sm.full_name, '([a-zA-Z0-9-]*)(\.[a-zA-Z0-9.-]*)') AS reg
FROM smooshed sm
)
INSERT
INTO name (parent, child, full_name, owner, height_added)
SELECT substr(reg[2], 2) AS parent,
reg[1] AS child,
r.full_name,
r.owner,
MAX(r.height)
FROM regexed r
GROUP BY parent, child, full_name, owner
ON CONFLICT (full_name, owner) DO UPDATE
SET height_added = CASE
WHEN excluded.height_added > name.height_added THEN excluded.height_added
ELSE name.height_added END;


SELECT 'Removing unbind_names' AS comment;
DELETE
FROM name
WHERE id IN (
WITH base AS (SELECT tc.height,
tc.id,
CASE WHEN attr.key = 'name' THEN attr.value::text END AS full_name,
CASE WHEN attr.key = 'address' THEN attr.value::text END AS owner
FROM tx_cache tc
JOIN tx_message tm ON tm.tx_hash_id = tc.id
JOIN tx_message_type tmt on tm.tx_message_type_id = tmt.id,
jsonb_array_elements(tc.tx_v2 -> 'tx_response' -> 'logs') with ordinality logs("events", idx),
jsonb_to_recordset(logs.events -> 'events') event("type" text, "attributes" jsonb),
jsonb_to_recordset(event.attributes) attr("key" text, "value" text)
WHERE tc.error_code IS NULL
AND tmt.proto_type IN ('/provenance.name.v1.MsgDeleteNameRequest',
'/cosmwasm.wasm.v1beta1.MsgInstantiateContract',
'/cosmwasm.wasm.v1.MsgInstantiateContract',
'/cosmwasm.wasm.v1.MsgExecuteContract',
'/cosmwasm.wasm.v1beta1.MsgExecuteContract')
AND logs.idx - 1 = msg_idx
AND event.type IN ('provenance.name.v1.EventNameUnbound')
ORDER BY tc.height
),
smooshed AS (
SELECT base.height,
base.id AS tx_id,
REPLACE(MAX(full_name), '"', '') AS full_name,
REPLACE(MAX(owner), '"', '') AS owner
FROM base
GROUP BY base.height, base.id
)
SELECT name.id
FROM smooshed sm
JOIN name ON sm.full_name = name.full_name AND sm.owner = name.owner
WHERE sm.height > name.height_added
);

DROP INDEX IF EXISTS name_unique_idx;
CREATE UNIQUE INDEX IF NOT EXISTS name_unique_idx ON name (full_name);
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
SELECT 'Adding validator verified to validator_state' AS comment;


ALTER TABLE validator_state
ADD COLUMN IF NOT EXISTS verified BOOLEAN NOT NULL DEFAULT FALSE;

SELECT 'Modify `current_validator_state` view' AS comment;
DROP MATERIALIZED VIEW IF EXISTS current_validator_state;
CREATE MATERIALIZED VIEW IF NOT EXISTS current_validator_state AS
SELECT DISTINCT ON (vs.operator_addr_id) vs.operator_addr_id,
vs.operator_address,
vs.block_height,
vs.moniker,
vs.status,
vs.jailed,
vs.token_count,
vs.json,
svc.account_address,
svc.consensus_address,
svc.consensus_pubkey,
vs.commission_rate,
vs.verified
FROM validator_state vs
JOIN staking_validator_cache svc on vs.operator_addr_id = svc.id
ORDER BY vs.operator_addr_id, vs.block_height desc
WITH DATA;


SELECT 'Modify `get_validator_list` function' AS comment;
DROP FUNCTION IF EXISTS get_validator_list(integer, varchar, text, integer, integer, text[]);
CREATE OR REPLACE FUNCTION get_validator_list(active_set integer, active_status character varying, search_state text, search_limit integer, search_offset integer, consensus_set text[] DEFAULT NULL, search_verified boolean DEFAULT NULL)
returns TABLE(operator_addr_id integer, operator_address character varying, block_height integer, moniker character varying, status character varying, jailed boolean, token_count numeric, json jsonb, account_address character varying, consensus_address character varying, consensus_pubkey character varying, commission_rate numeric, verified boolean, validator_state text)
language plpgsql
as
$$
BEGIN

RETURN QUERY
WITH active AS (
SELECT cvs.*
FROM current_validator_state cvs
WHERE cvs.status = active_status
AND cvs.jailed = false
ORDER BY cvs.token_count DESC
LIMIT active_set
),
jailed AS (
SELECT cvs.*
FROM current_validator_state cvs
WHERE cvs.jailed = true
),
candidate AS (
SELECT cvs.*
FROM current_validator_state cvs
LEFT JOIN active a ON cvs.operator_address = a.operator_address
WHERE cvs.jailed = false
AND a.operator_address IS NULL
),
state AS (
SELECT cvs.operator_address,
CASE
WHEN a.operator_address IS NOT NULL THEN 'active'
WHEN j.operator_address IS NOT NULL THEN 'jailed'
WHEN c.operator_address IS NOT NULL THEN 'candidate'
END validator_state
FROM current_validator_state cvs
LEFT JOIN active a ON cvs.operator_address = a.operator_address
LEFT JOIN jailed j ON cvs.operator_address = j.operator_address
LEFT JOIN candidate c ON cvs.operator_address = c.operator_address
)
SELECT cvs.*,
s.validator_state
FROM current_validator_state cvs
LEFT JOIN state s ON cvs.operator_address = s.operator_address
WHERE s.validator_state = search_state
AND (consensus_set IS NULL OR cvs.consensus_address = ANY (consensus_set))
AND (search_verified IS NULL OR cvs.verified = search_verified)
ORDER BY s.validator_state, cvs.token_count DESC
LIMIT search_limit OFFSET search_offset;
END
$$;


SELECT 'Modify `get_all_validator_state` function' AS comment;
DROP FUNCTION IF EXISTS get_all_validator_state(integer, varchar, text[]);
CREATE OR REPLACE FUNCTION get_all_validator_state(active_set integer, active_status character varying, consensus_set text[] DEFAULT NULL, search_verified boolean DEFAULT NULL)
returns TABLE(operator_addr_id integer, operator_address character varying, block_height integer, moniker character varying, status character varying, jailed boolean, token_count numeric, json jsonb, account_address character varying, consensus_address character varying, consensus_pubkey character varying, commission_rate numeric, verified boolean, validator_state text)
language plpgsql
as
$$
BEGIN

RETURN QUERY
WITH active AS (
SELECT cvs.*
FROM current_validator_state cvs
WHERE cvs.status = active_status
AND cvs.jailed = false
ORDER BY cvs.token_count DESC
LIMIT active_set
),
jailed AS (
SELECT cvs.*
FROM current_validator_state cvs
WHERE cvs.jailed = true
),
candidate AS (
SELECT cvs.*
FROM current_validator_state cvs
LEFT JOIN active a ON cvs.operator_address = a.operator_address
WHERE cvs.jailed = false
AND a.operator_address IS NULL
),
state AS (
SELECT cvs.operator_address,
CASE
WHEN a.operator_address IS NOT NULL THEN 'active'
WHEN j.operator_address IS NOT NULL THEN 'jailed'
WHEN c.operator_address IS NOT NULL THEN 'candidate'
END validator_state
FROM current_validator_state cvs
LEFT JOIN active a ON cvs.operator_address = a.operator_address
LEFT JOIN jailed j ON cvs.operator_address = j.operator_address
LEFT JOIN candidate c ON cvs.operator_address = c.operator_address
)
SELECT cvs.*,
s.validator_state
FROM current_validator_state cvs
LEFT JOIN state s ON cvs.operator_address = s.operator_address
WHERE (consensus_set IS NULL OR cvs.consensus_address = ANY (consensus_set))
AND (search_verified IS NULL OR cvs.verified = search_verified)
ORDER BY s.validator_state, cvs.token_count DESC;
END
$$;
1 change: 1 addition & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ services:
- EXPLORER_SWAGGER_URL=localhost:8612
- EXPLORER_SWAGGER_PROTOCOL=http
- EXPLORER_PRICING_URL=https://test.figure.tech/service-pricing-engine/external
- EXPLORER_VERIFIED_ADDRESSES=test
depends_on:
- explorer-postgres
links:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,28 @@ package io.provenance.explorer.config

import io.provenance.explorer.domain.core.Bech32
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.ConstructorBinding
import org.springframework.validation.annotation.Validated
import javax.validation.constraints.NotNull

@ConfigurationProperties(prefix = "explorer")
@Validated
class ExplorerProperties {

@NotNull
lateinit var mainnet: String

@NotNull
lateinit var pbUrl: String

@NotNull
lateinit var initialHistoricalDayCount: String

@NotNull
lateinit var spotlightTtlMs: String

@NotNull
lateinit var figmentApikey: String

@NotNull
lateinit var figmentUrl: String

@NotNull
lateinit var genesisVersionUrl: String

@NotNull
lateinit var upgradeVersionRegex: String

@NotNull
lateinit var upgradeGithubRepo: String

@NotNull
lateinit var hiddenApis: String

@NotNull
lateinit var swaggerUrl: String

@NotNull
lateinit var swaggerProtocol: String

@NotNull
lateinit var pricingUrl: String
@ConstructorBinding
class ExplorerProperties(
val mainnet: String,
val pbUrl: String,
val initialHistoricalDayCount: String,
val spotlightTtlMs: String,
val figmentApikey: String,
val figmentUrl: String,
val genesisVersionUrl: String,
val upgradeVersionRegex: String,
val upgradeGithubRepo: String,
val hiddenApis: String,
val swaggerUrl: String,
val swaggerProtocol: String,
val pricingUrl: String,
val verifiedAddresses: List<String>
) {

fun initialHistoricalDays() = initialHistoricalDayCount.toInt()

Expand Down
Loading

0 comments on commit 10fb677

Please sign in to comment.