Skip to content

Commit

Permalink
consensus: store entity ids instead of consensus addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
jberci committed Aug 30, 2024
1 parent 0fb06a5 commit e0ea8d8
Show file tree
Hide file tree
Showing 26 changed files with 9,135 additions and 18,097 deletions.
37 changes: 34 additions & 3 deletions analyzer/consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,22 @@ func (m *processor) ProcessBlock(ctx context.Context, uheight uint64) error {
}

func (m *processor) queueBlockInserts(batch *storage.QueryBatch, data *consensusBlockData) error {
// Prepare a mapping of node consensus addresses.
//
// CometBFT (formerly Tendermint) uses a truncated hash of the entity's
// public key as its address format. Specifically, the address (a
// [20]byte) is the first 20 bytes of the SHA-256 of the public key.
// Since CometBFT is what oasis-core uses for its consensus mechanism,
// these addresses are what we're given in the block metadata for the
// proposer and signers. Because the address derivation is one-way, we
// need a map to convert them to Oasis-style addresses (base64 public
// keys).
consensusToEntity := map[string]signature.PublicKey{}
for _, n := range data.Nodes {
consensusAddress := crypto.PublicKeyToCometBFT(common.Ptr(n.Consensus.ID)).Address().String()
consensusToEntity[consensusAddress] = n.EntityID
}

var cmtMeta cometbft.BlockMeta
if err := cbor.Unmarshal(data.BlockHeader.Meta, &cmtMeta); err != nil {
m.logger.Warn("could not unmarshal block meta, may be incompatible version",
Expand All @@ -384,7 +400,15 @@ func (m *processor) queueBlockInserts(batch *storage.QueryBatch, data *consensus

var proposerAddr *string
if cmtMeta.Header != nil {
proposerAddr = common.Ptr(cmtMeta.Header.ProposerAddress.String())
entity, ok := consensusToEntity[cmtMeta.Header.ProposerAddress.String()]
if !ok {
m.logger.Warn("could not convert block proposer address to entity id (address not found)",
"height", data.BlockHeader.Height,
"proposer", cmtMeta.Header.ProposerAddress.String(),
)
} else {
proposerAddr = common.Ptr(entity.String())
}
}
batch.Queue(
queries.ConsensusBlockUpsert,
Expand All @@ -407,7 +431,15 @@ func (m *processor) queueBlockInserts(batch *storage.QueryBatch, data *consensus
if cs.Absent() {
continue
}
prevSigners = append(prevSigners, cs.ValidatorAddress.String())
entity, ok := consensusToEntity[cs.ValidatorAddress.String()]
if !ok {
m.logger.Warn("could not convert block signer address to entity id (address not found)",
"height", data.BlockHeader.Height,
"signer", cs.ValidatorAddress.String(),
)
continue
}
prevSigners = append(prevSigners, entity.String())
}
batch.Queue(
queries.ConsensusBlockSignersUpsert,
Expand Down Expand Up @@ -684,7 +716,6 @@ func (m *processor) queueNodeEvents(batch *storage.QueryBatch, data *registryDat
nodeEvent.P2PID.String(),
nodeEvent.P2PAddresses,
nodeEvent.ConsensusID.String(),
crypto.PublicKeyToCometBFT(common.Ptr(nodeEvent.ConsensusID)).Address().String(),
strings.Join(nodeEvent.ConsensusAddresses, ","), // TODO: store as array
nodeEvent.VRFPubKey,
strings.Join(nodeEvent.Roles, ","), // TODO: store as array
Expand Down
8 changes: 8 additions & 0 deletions analyzer/consensus/data_fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ func fetchAllData(ctx context.Context, cc nodeapi.ConsensusApiLite, network sdkC

// fetchBlockData retrieves data about a consensus block at the provided block height.
func fetchBlockData(ctx context.Context, cc nodeapi.ConsensusApiLite, height int64) (*consensusBlockData, error) {
nodes, err := cc.GetNodes(ctx, height)
if err != nil {
return nil, err
}

block, err := cc.GetBlock(ctx, height)
if err != nil {
return nil, err
Expand All @@ -119,6 +124,7 @@ func fetchBlockData(ctx context.Context, cc nodeapi.ConsensusApiLite, height int

return &consensusBlockData{
Height: height,
Nodes: nodes,
BlockHeader: block,
Epoch: epoch,
GasLimit: params.MaxBlockGas,
Expand Down Expand Up @@ -355,6 +361,8 @@ type allData struct {
type consensusBlockData struct {
Height int64

Nodes []nodeapi.Node

BlockHeader *consensus.Block
Epoch beacon.EpochTime
GasLimit uint64
Expand Down
2 changes: 0 additions & 2 deletions analyzer/consensus/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (

"github.com/oasisprotocol/oasis-core/go/common/cbor"
"github.com/oasisprotocol/oasis-core/go/common/entity"
"github.com/oasisprotocol/oasis-core/go/consensus/cometbft/crypto"

"github.com/oasisprotocol/nexus/analyzer/queries"
"github.com/oasisprotocol/nexus/common"
Expand Down Expand Up @@ -120,7 +119,6 @@ func (mg *GenesisProcessor) addRegistryBackendMigrations(batch *storage.QueryBat
node.P2P.ID.String(),
nil,
node.Consensus.ID.String(),
crypto.PublicKeyToCometBFT(common.Ptr(node.Consensus.ID)).Address().String(),
nil,
nil,
node.Roles.String(),
Expand Down
13 changes: 6 additions & 7 deletions analyzer/queries/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ var (
height = excluded.height`

ConsensusBlockUpsert = `
INSERT INTO chain.blocks (height, block_hash, time, num_txs, namespace, version, state_root, epoch, gas_limit, size_limit, proposer_node_consensus_pubkey_address)
INSERT INTO chain.blocks (height, block_hash, time, num_txs, namespace, version, state_root, epoch, gas_limit, size_limit, proposer_entity_id)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
ON CONFLICT (height) DO UPDATE
SET
Expand All @@ -180,16 +180,16 @@ var (
epoch = excluded.epoch,
gas_limit = excluded.gas_limit,
size_limit = excluded.size_limit,
proposer_node_consensus_pubkey_address = excluded.proposer_node_consensus_pubkey_address`
proposer_entity_id = excluded.proposer_entity_id`

ConsensusBlockSignersUpsert = `
INSERT INTO chain.blocks
(height, signer_node_consensus_pubkey_addresses)
(height, signer_entity_ids)
VALUES
($1, $2)
ON CONFLICT (height) DO UPDATE
SET
signer_node_consensus_pubkey_addresses = excluded.signer_node_consensus_pubkey_addresses`
signer_entity_ids = excluded.signer_entity_ids`

ConsensusEpochUpsert = `
INSERT INTO chain.epochs AS old (id, start_height, end_height)
Expand Down Expand Up @@ -333,8 +333,8 @@ var (
start_block = LEAST(old.start_block, excluded.start_block)`

ConsensusNodeUpsert = `
INSERT INTO chain.nodes (id, entity_id, expiration, tls_pubkey, tls_next_pubkey, tls_addresses, p2p_pubkey, p2p_addresses, consensus_pubkey, consensus_pubkey_address, consensus_address, vrf_pubkey, roles, software_version, voting_power)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
INSERT INTO chain.nodes (id, entity_id, expiration, tls_pubkey, tls_next_pubkey, tls_addresses, p2p_pubkey, p2p_addresses, consensus_pubkey, consensus_address, vrf_pubkey, roles, software_version, voting_power)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
ON CONFLICT (id) DO UPDATE
SET
entity_id = excluded.entity_id,
Expand All @@ -345,7 +345,6 @@ var (
p2p_pubkey = excluded.p2p_pubkey,
p2p_addresses = excluded.p2p_addresses,
consensus_pubkey = excluded.consensus_pubkey,
consensus_pubkey_address = excluded.consensus_pubkey_address,
consensus_address = excluded.consensus_address,
vrf_pubkey = excluded.vrf_pubkey,
roles = excluded.roles,
Expand Down
41 changes: 35 additions & 6 deletions api/spec/v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1385,11 +1385,13 @@ components:
description: The Merkle root of the state tree after applying the block.
example: *state_root_1
proposer:
allOf: [$ref: '#/components/schemas/NodeInfo']
allOf: [$ref: '#/components/schemas/EntityInfo']
description: The entity that proposed this block.
signers:
type: array
items:
allOf: [$ref: '#/components/schemas/NodeInfo']
allOf: [$ref: '#/components/schemas/EntityInfo']
description: A list of the entities that signed the block.
# TODO: Not available on backend
# size:
# type: integer
Expand All @@ -1404,16 +1406,43 @@ components:
description: |
A consensus block.
NodeInfo:
EntityInfo:
type: object
description: Light-weight entity information, containing only its ID, address and registry metadata.
properties:
node_id:
type: string
entity_id:
type: string
description: The ID of the entity owning the node; this corresponds to the entity's public key in base64.
example: "`TqUyj5Q+9vZtqu10yw6Zw7HEX3Ywe0JQA9vHyzY47TU=`"
entity_address:
type: string
entity_metadata: {}
description: Address of the entity owning the node, in Bech32 format (`oasis1...`).
example: "`oasis1qzzd6khm3acqskpxlk9vd5044cmmcce78y5l6000`"
entity_metadata:
description: |
Metadata about an entity, if available. See [the metadata registry](https://github.com/oasisprotocol/metadata-registry) for details.
When available, it is an object with some subset of the following fields:
- `v`: The version of the metadata structure (always present).
- `serial`: The serial number of the metadata statement (always present).
- `name`: The name of the entity.
- `url`: The URL associated with the entity.
- `email`: The email address associated with the entity.
- `keybase`: Tne entity's keybase.io handle.
- `twitter`: The twitter handle associated with the entity.
example: |
An entity with all fields:
{
"v": 1,
"serial": 1,
"name": "Entity display name",
"url": "https://example.org/entity",
"email": "[email protected]",
"keybase": "keybase_handle",
"twitter": "twitter_handle"
}
Delegation:
type: object
Expand Down
30 changes: 10 additions & 20 deletions storage/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,24 +289,22 @@ func (c *StorageClient) Status(ctx context.Context) (*Status, error) {
return &s, nil
}

type nodeInfoRow struct {
NodeID *string
type entityInfoRow struct {
EntityID *string
EntityAddress *string
EntityMetadata *json.RawMessage
}

func nodeInfoFromRow(r nodeInfoRow) (apiTypes.NodeInfo, error) {
func entityInfoFromRow(r entityInfoRow) apiTypes.EntityInfo {
var entityMetadataAny any
if r.EntityMetadata != nil {
entityMetadataAny = *r.EntityMetadata
}
return apiTypes.NodeInfo{
return apiTypes.EntityInfo{
EntityAddress: r.EntityAddress,
EntityId: r.EntityID,
EntityMetadata: &entityMetadataAny,
NodeId: r.NodeID,
}, nil
}
}

// Blocks returns a list of consensus blocks.
Expand Down Expand Up @@ -338,9 +336,8 @@ func (c *StorageClient) Blocks(ctx context.Context, r apiTypes.GetConsensusBlock
}
for res.rows.Next() {
var b Block
var metadata json.RawMessage
var proposerRow nodeInfoRow
var signerRows []nodeInfoRow
var proposerRow entityInfoRow
var signerRows []entityInfoRow
if err := res.rows.Scan(
&b.Height,
&b.Hash,
Expand All @@ -350,24 +347,17 @@ func (c *StorageClient) Blocks(ctx context.Context, r apiTypes.GetConsensusBlock
&b.SizeLimit,
&b.Epoch,
&b.StateRoot,
&metadata,
&proposerRow,
&signerRows,
); err != nil {
return nil, wrapError(err)
}
b.Timestamp = b.Timestamp.UTC()
proposer, err := nodeInfoFromRow(proposerRow)
if err != nil {
return nil, fmt.Errorf("converting block %d proposer: %w", b.Height, err)
}
proposer := entityInfoFromRow(proposerRow)
b.Proposer = &proposer
signers := make([]apiTypes.NodeInfo, 0, len(signerRows))
for i, signerRow := range signerRows {
signer, err := nodeInfoFromRow(signerRow)
if err != nil {
return nil, fmt.Errorf("converting block %d signer %d: %w", b.Height, i, err)
}
signers := make([]apiTypes.EntityInfo, 0, len(signerRows))
for _, signerRow := range signerRows {
signer := entityInfoFromRow(signerRow)
signers = append(signers, signer)
}
b.Signers = &signers
Expand Down
19 changes: 7 additions & 12 deletions storage/client/queries/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,24 @@ const (
size_limit,
epoch,
state_root,
metadata,
ROW(
proposer_node.id,
proposer_node.entity_id,
proposer_entity.id,
proposer_entity.address,
proposer_entity.meta
) AS proposer_node_info,
ARRAY(
) AS proposer_entity_info,
ARRAY( -- Select block signers and map them into an array column named signer_node_infos.
SELECT
ROW(
signer_node.id,
signer_node.entity_id,
signer_entity.id,
signer_entity.address,
signer_entity.meta
)
FROM UNNEST(signer_node_consensus_pubkey_addresses) AS signer_node_consensus_pubkey_address
LEFT JOIN chain.nodes AS signer_node ON signer_node.consensus_pubkey_address = signer_node_consensus_pubkey_address
LEFT JOIN chain.entities AS signer_entity ON signer_entity.id = signer_node.entity_id
FROM UNNEST(signer_entity_ids) AS signer_entity_id
LEFT JOIN chain.entities AS signer_entity ON signer_entity.id = signer_entity_id
ORDER BY signer_entity.address
) AS signer_node_infos
FROM chain.blocks
LEFT JOIN chain.nodes AS proposer_node ON proposer_node.consensus_pubkey_address = proposer_node_consensus_pubkey_address
LEFT JOIN chain.entities AS proposer_entity ON proposer_entity.id = proposer_node.entity_id
LEFT JOIN chain.entities AS proposer_entity ON proposer_entity.id = proposer_entity_id
WHERE
($1::bigint IS NULL OR height >= $1::bigint) AND
($2::bigint IS NULL OR height <= $2::bigint) AND
Expand Down
4 changes: 2 additions & 2 deletions storage/migrations/00_consensus.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ CREATE TABLE chain.blocks
metadata JSONB

-- added in 31_block_meta.up.sql
-- proposer_node_consensus_pubkey_address TEXT,
-- signer_node_consensus_pubkey_addresses TEXT[]
-- proposer_entity_id base64_ed25519_pubkey,
-- signer_entity_ids base64_ed25519_pubkey[]
);
CREATE INDEX ix_blocks_time ON chain.blocks (time);
-- CREATE INDEX ix_blocks_block_hash ON chain.blocks (block_hash); -- Needed to lookup blocks by hash. -- added in 21_consensus_block_hash.up.sql
Expand Down
12 changes: 5 additions & 7 deletions storage/migrations/31_block_meta.up.sql
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
BEGIN;

ALTER TABLE chain.nodes
ADD COLUMN consensus_pubkey_address TEXT;

CREATE INDEX ix_nodes_consensus_pubkey_address ON chain.nodes (consensus_pubkey_address);

ALTER TABLE chain.blocks
ALTER COLUMN block_hash DROP NOT NULL,
ALTER COLUMN time DROP NOT NULL,
ALTER COLUMN num_txs DROP NOT NULL,
ALTER COLUMN namespace DROP NOT NULL,
ALTER COLUMN version DROP NOT NULL,
ALTER COLUMN state_root DROP NOT NULL,
ADD COLUMN proposer_node_consensus_pubkey_address TEXT,
ADD COLUMN signer_node_consensus_pubkey_addresses TEXT[];
ADD COLUMN proposer_entity_id base64_ed25519_pubkey,
ADD COLUMN signer_entity_ids base64_ed25519_pubkey[];

CREATE INDEX ix_blocks_proposer_entity_id ON chain.blocks (proposer_entity_id);
CREATE INDEX ix_blocks_signer_entity_ids ON chain.blocks USING gin(signer_entity_ids);

COMMIT;
Binary file modified tests/e2e_regression/damask/rpc-cache/emerald/db.pmt
Binary file not shown.
Git LFS file not shown
Git LFS file not shown
Loading

0 comments on commit e0ea8d8

Please sign in to comment.