From 7e3b29aaa66cd643f0d984ae16c3287546ecb0e9 Mon Sep 17 00:00:00 2001 From: Warren He Date: Fri, 5 Jan 2024 16:13:32 -0800 Subject: [PATCH 01/15] storage: eden use eden request types --- storage/oasis/nodeapi/eden/node.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/storage/oasis/nodeapi/eden/node.go b/storage/oasis/nodeapi/eden/node.go index 62e70750e..409314415 100644 --- a/storage/oasis/nodeapi/eden/node.go +++ b/storage/oasis/nodeapi/eden/node.go @@ -14,8 +14,6 @@ import ( consensus "github.com/oasisprotocol/nexus/coreapi/v22.2.11/consensus/api" consensusTx "github.com/oasisprotocol/nexus/coreapi/v22.2.11/consensus/api/transaction" genesis "github.com/oasisprotocol/nexus/coreapi/v22.2.11/genesis/api" - governance "github.com/oasisprotocol/nexus/coreapi/v22.2.11/governance/api" - scheduler "github.com/oasisprotocol/nexus/coreapi/v22.2.11/scheduler/api" "github.com/oasisprotocol/nexus/storage/oasis/connections" "github.com/oasisprotocol/nexus/storage/oasis/nodeapi" @@ -191,7 +189,7 @@ func (c *ConsensusApiLite) GetValidators(ctx context.Context, height int64) ([]n func (c *ConsensusApiLite) GetCommittees(ctx context.Context, height int64, runtimeID common.Namespace) ([]nodeapi.Committee, error) { var rsp []*schedulerEden.Committee - if err := c.grpcConn.Invoke(ctx, "/oasis-core.Scheduler/GetCommittees", &scheduler.GetCommitteesRequest{ + if err := c.grpcConn.Invoke(ctx, "/oasis-core.Scheduler/GetCommittees", &schedulerEden.GetCommitteesRequest{ Height: height, RuntimeID: runtimeID, }, &rsp); err != nil { @@ -206,7 +204,7 @@ func (c *ConsensusApiLite) GetCommittees(ctx context.Context, height int64, runt func (c *ConsensusApiLite) GetProposal(ctx context.Context, height int64, proposalID uint64) (*nodeapi.Proposal, error) { var rsp *governanceEden.Proposal - if err := c.grpcConn.Invoke(ctx, "/oasis-core.Governance/Proposal", &governance.ProposalQuery{ + if err := c.grpcConn.Invoke(ctx, "/oasis-core.Governance/Proposal", &governanceEden.ProposalQuery{ Height: height, ProposalID: proposalID, }, &rsp); err != nil { From 4b7452adc1780b01b6ff74205cb0d9be53ac2762 Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 4 Jan 2024 17:25:33 -0800 Subject: [PATCH 02/15] storage: extract roothash messages --- storage/oasis/nodeapi/api.go | 2 ++ storage/oasis/nodeapi/cobalt/convert.go | 14 +++++++++++++- storage/oasis/nodeapi/damask/convert.go | 1 + storage/oasis/nodeapi/eden/convert.go | 13 +++++++++++++ storage/oasis/nodeapi/eden/node.go | 2 ++ 5 files changed, 31 insertions(+), 1 deletion(-) diff --git a/storage/oasis/nodeapi/api.go b/storage/oasis/nodeapi/api.go index 9d48bda8b..87661913b 100644 --- a/storage/oasis/nodeapi/api.go +++ b/storage/oasis/nodeapi/api.go @@ -19,6 +19,7 @@ import ( genesis "github.com/oasisprotocol/nexus/coreapi/v22.2.11/genesis/api" governance "github.com/oasisprotocol/nexus/coreapi/v22.2.11/governance/api" registry "github.com/oasisprotocol/nexus/coreapi/v22.2.11/registry/api" + "github.com/oasisprotocol/nexus/coreapi/v22.2.11/roothash/api/message" scheduler "github.com/oasisprotocol/nexus/coreapi/v22.2.11/scheduler/api" staking "github.com/oasisprotocol/nexus/coreapi/v22.2.11/staking/api" "github.com/oasisprotocol/nexus/storage/oasis/connections" @@ -203,6 +204,7 @@ type ExecutorCommittedEvent struct { RuntimeID coreCommon.Namespace Round uint64 NodeID *signature.PublicKey // Available starting in Damask. + Messages []message.Message } type MessageEvent struct { diff --git a/storage/oasis/nodeapi/cobalt/convert.go b/storage/oasis/nodeapi/cobalt/convert.go index bd982476c..c034234e2 100644 --- a/storage/oasis/nodeapi/cobalt/convert.go +++ b/storage/oasis/nodeapi/cobalt/convert.go @@ -15,6 +15,7 @@ import ( genesis "github.com/oasisprotocol/nexus/coreapi/v22.2.11/genesis/api" governance "github.com/oasisprotocol/nexus/coreapi/v22.2.11/governance/api" registry "github.com/oasisprotocol/nexus/coreapi/v22.2.11/registry/api" + "github.com/oasisprotocol/nexus/coreapi/v22.2.11/roothash/api/message" scheduler "github.com/oasisprotocol/nexus/coreapi/v22.2.11/scheduler/api" staking "github.com/oasisprotocol/nexus/coreapi/v22.2.11/staking/api" upgrade "github.com/oasisprotocol/nexus/coreapi/v22.2.11/upgrade/api" @@ -332,12 +333,23 @@ func convertRoothashEvent(e roothashCobalt.Event) nodeapi.Event { "err", err, ) } - + messages := make([]message.Message, len(computeBody.Messages)) + for i, messageCobalt := range computeBody.Messages { + messageCBOR := cbor.Marshal(messageCobalt) + if err := cbor.Unmarshal(messageCBOR, &messages[i]); err != nil { + logger.Error("convert event: roothash executor committed: compute body message error unmarshaling", + "event", e, + "message_index", i, + "err", err, + ) + } + } ret = nodeapi.Event{ RoothashExecutorCommitted: &nodeapi.ExecutorCommittedEvent{ RuntimeID: e.RuntimeID, Round: computeBody.Header.Round, NodeID: &e.ExecutorCommitted.Commit.Signature.PublicKey, + Messages: messages, }, RawBody: common.TryAsJSON(e.ExecutorCommitted), Type: apiTypes.ConsensusEventTypeRoothashExecutorCommitted, diff --git a/storage/oasis/nodeapi/damask/convert.go b/storage/oasis/nodeapi/damask/convert.go index ad64f0bc9..2db34f08f 100644 --- a/storage/oasis/nodeapi/damask/convert.go +++ b/storage/oasis/nodeapi/damask/convert.go @@ -161,6 +161,7 @@ func convertRoothashEvent(e roothashDamask.Event) nodeapi.Event { RuntimeID: e.RuntimeID, Round: e.ExecutorCommitted.Commit.Header.Round, NodeID: &e.ExecutorCommitted.Commit.NodeID, + Messages: e.ExecutorCommitted.Commit.Messages, }, RawBody: common.TryAsJSON(e.ExecutorCommitted), Type: apiTypes.ConsensusEventTypeRoothashExecutorCommitted, diff --git a/storage/oasis/nodeapi/eden/convert.go b/storage/oasis/nodeapi/eden/convert.go index 6a3efba8d..d70e06a96 100644 --- a/storage/oasis/nodeapi/eden/convert.go +++ b/storage/oasis/nodeapi/eden/convert.go @@ -13,6 +13,7 @@ import ( genesis "github.com/oasisprotocol/nexus/coreapi/v22.2.11/genesis/api" governance "github.com/oasisprotocol/nexus/coreapi/v22.2.11/governance/api" registry "github.com/oasisprotocol/nexus/coreapi/v22.2.11/registry/api" + "github.com/oasisprotocol/nexus/coreapi/v22.2.11/roothash/api/message" scheduler "github.com/oasisprotocol/nexus/coreapi/v22.2.11/scheduler/api" staking "github.com/oasisprotocol/nexus/coreapi/v22.2.11/staking/api" upgrade "github.com/oasisprotocol/nexus/coreapi/v22.2.11/upgrade/api" @@ -404,11 +405,23 @@ func convertRoothashEvent(e roothashEden.Event) nodeapi.Event { ret := nodeapi.Event{} switch { case e.ExecutorCommitted != nil: + messages := make([]message.Message, len(e.ExecutorCommitted.Commit.Messages)) + for i, messageEden := range e.ExecutorCommitted.Commit.Messages { + messageCBOR := cbor.Marshal(messageEden) + if err := cbor.Unmarshal(messageCBOR, &messages[i]); err != nil { + logger.Error("convert event: roothash executor committed: commit message error unmarshaling", + "event", e, + "message_index", i, + "err", err, + ) + } + } ret = nodeapi.Event{ RoothashExecutorCommitted: &nodeapi.ExecutorCommittedEvent{ RuntimeID: e.RuntimeID, Round: e.ExecutorCommitted.Commit.Header.Header.Round, NodeID: &e.ExecutorCommitted.Commit.NodeID, + Messages: messages, }, RawBody: common.TryAsJSON(e.ExecutorCommitted), Type: apiTypes.ConsensusEventTypeRoothashExecutorCommitted, diff --git a/storage/oasis/nodeapi/eden/node.go b/storage/oasis/nodeapi/eden/node.go index 409314415..25ea048ef 100644 --- a/storage/oasis/nodeapi/eden/node.go +++ b/storage/oasis/nodeapi/eden/node.go @@ -30,6 +30,8 @@ import ( stakingEden "github.com/oasisprotocol/nexus/coreapi/v23.0/staking/api" ) +var logger = cmdCommon.RootLogger().WithModule("eden-consensus-api-lite") + // ConsensusApiLite provides low-level access to the consensus API of a // Eden node. To be able to use the old gRPC API, this struct uses gRPC // directly, skipping the convenience wrappers provided by oasis-core. From e752482a2de7c29281657657ce41d8d2eae17b00 Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 29 Feb 2024 17:03:15 -0800 Subject: [PATCH 03/15] storage: bump cache keys for roothash events --- storage/oasis/nodeapi/file/consensus.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/oasis/nodeapi/file/consensus.go b/storage/oasis/nodeapi/file/consensus.go index 160951622..27cf903fa 100644 --- a/storage/oasis/nodeapi/file/consensus.go +++ b/storage/oasis/nodeapi/file/consensus.go @@ -83,8 +83,8 @@ func (c *FileConsensusApiLite) GetBlock(ctx context.Context, height int64) (*con func (c *FileConsensusApiLite) GetTransactionsWithResults(ctx context.Context, height int64) ([]nodeapi.TransactionWithResults, error) { return kvstore.GetSliceFromCacheOrCall( c.db, height == consensus.HeightLatest, - // v2: Updated roothash events conversion to retain more data. - kvstore.GenerateCacheKey("GetTransactionsWithResults.v2", height), + // v3: Updated roothash events conversion to retain roothash messages. + kvstore.GenerateCacheKey("GetTransactionsWithResults.v3", height), func() ([]nodeapi.TransactionWithResults, error) { return c.consensusApi.GetTransactionsWithResults(ctx, height) }, @@ -133,8 +133,8 @@ func (c *FileConsensusApiLite) GovernanceEvents(ctx context.Context, height int6 func (c *FileConsensusApiLite) RoothashEvents(ctx context.Context, height int64) ([]nodeapi.Event, error) { return kvstore.GetSliceFromCacheOrCall( c.db, height == consensus.HeightLatest, - // v2: Updated roothash events conversion to retain more data. - kvstore.GenerateCacheKey("RoothashEvents.v2", height), + // v3: Updated roothash events conversion to retain roothash messages. + kvstore.GenerateCacheKey("RoothashEvents.v3", height), func() ([]nodeapi.Event, error) { return c.consensusApi.RoothashEvents(ctx, height) }, ) } From 333e4cd3303c35452c09db4766caf4b58c395ae1 Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 4 Jan 2024 17:19:17 -0800 Subject: [PATCH 04/15] storage: add roothash messages table --- .../migrations/13_roothash_messages.up.sql | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 storage/migrations/13_roothash_messages.up.sql diff --git a/storage/migrations/13_roothash_messages.up.sql b/storage/migrations/13_roothash_messages.up.sql new file mode 100644 index 000000000..fa4e3c9ea --- /dev/null +++ b/storage/migrations/13_roothash_messages.up.sql @@ -0,0 +1,31 @@ +BEGIN; + +-- Roothash messages are small structures that a runtime can send to +-- communicate with the consensus layer. They are agreed upon for each runtime +-- block. We'll see the messages themselves in the proposal for that block, +-- i.e. in the first executor commit for the round. The consensus layer +-- processes these messages when the block gets finalized, which produces a +-- result for each message. +-- +-- In Cobalt and below, the roothash consensus app emits an event for each +-- message. In Damask and up, the results are stored on chain, and you use a +-- roothash "get last round results" query to look up the results. +-- +-- This table has tracked runtimes' messages and results. Either of the +-- message or result may be absent, as they can be disseminated in different +-- consensus blocks. + +CREATE TABLE chain.roothash_messages ( + runtime runtime NOT NULL, + round UINT63 NOT NULL, + message_index UINT31 NOT NULL, + PRIMARY KEY (runtime, round, message_index), + type TEXT, + body JSONB, + error_module TEXT, + error_code UINT31, + result BYTEA, + related_accounts oasis_addr[] +); + +COMMIT; From 5e3956c688319bda817ccf81397ead4b5f412f54 Mon Sep 17 00:00:00 2001 From: Warren He Date: Tue, 13 Feb 2024 14:08:22 -0800 Subject: [PATCH 05/15] coreapi: backport new roothash message types --- .../v22.2.11/roothash/api/message/message.go | 15 ++++++++ .../backport_governance_message.patch | 37 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 scripts/vendor-oasis-core/patches/v22.2.11/backport_governance_message.patch diff --git a/coreapi/v22.2.11/roothash/api/message/message.go b/coreapi/v22.2.11/roothash/api/message/message.go index 702a27a67..06ff264f5 100644 --- a/coreapi/v22.2.11/roothash/api/message/message.go +++ b/coreapi/v22.2.11/roothash/api/message/message.go @@ -4,6 +4,7 @@ package message import ( "github.com/oasisprotocol/oasis-core/go/common/cbor" + governance "github.com/oasisprotocol/nexus/coreapi/v22.2.11/governance/api" registry "github.com/oasisprotocol/nexus/coreapi/v22.2.11/registry/api" staking "github.com/oasisprotocol/nexus/coreapi/v22.2.11/staking/api" ) @@ -12,6 +13,9 @@ import ( type Message struct { Staking *StakingMessage `json:"staking,omitempty"` Registry *RegistryMessage `json:"registry,omitempty"` + // XXX: Added by Nexus. Backported from Eden so that this structure can + // represent all oasis-core versions. + Governance *GovernanceMessage `json:"governance,omitempty"` } // ValidateBasic performs basic validation of the runtime message. @@ -42,3 +46,14 @@ type RegistryMessage struct { // ValidateBasic performs basic validation of the runtime message. // removed func + +// GovernanceMessage is a governance message that allows a runtime to perform governance operations. +type GovernanceMessage struct { + cbor.Versioned + + CastVote *governance.ProposalVote `json:"cast_vote,omitempty"` + SubmitProposal *governance.ProposalContent `json:"submit_proposal,omitempty"` +} + +// ValidateBasic performs basic validation of a governance message. +// removed func diff --git a/scripts/vendor-oasis-core/patches/v22.2.11/backport_governance_message.patch b/scripts/vendor-oasis-core/patches/v22.2.11/backport_governance_message.patch new file mode 100644 index 000000000..9781ae185 --- /dev/null +++ b/scripts/vendor-oasis-core/patches/v22.2.11/backport_governance_message.patch @@ -0,0 +1,37 @@ +diff --git a/coreapi/v22.2.11/roothash/api/message/message.go b/coreapi/v22.2.11/roothash/api/message/message.go +index 702a27a6..06ff264f 100644 +--- a/coreapi/v22.2.11/roothash/api/message/message.go ++++ b/coreapi/v22.2.11/roothash/api/message/message.go +@@ -4,6 +4,7 @@ package message + import ( + "github.com/oasisprotocol/oasis-core/go/common/cbor" + ++ governance "github.com/oasisprotocol/nexus/coreapi/v22.2.11/governance/api" + registry "github.com/oasisprotocol/nexus/coreapi/v22.2.11/registry/api" + staking "github.com/oasisprotocol/nexus/coreapi/v22.2.11/staking/api" + ) +@@ -12,6 +13,9 @@ import ( + type Message struct { + Staking *StakingMessage `json:"staking,omitempty"` + Registry *RegistryMessage `json:"registry,omitempty"` ++ // XXX: Added by Nexus. Backported from Eden so that this structure can ++ // represent all oasis-core versions. ++ Governance *GovernanceMessage `json:"governance,omitempty"` + } + + // ValidateBasic performs basic validation of the runtime message. +@@ -42,3 +46,14 @@ type RegistryMessage struct { + + // ValidateBasic performs basic validation of the runtime message. + // removed func ++ ++// GovernanceMessage is a governance message that allows a runtime to perform governance operations. ++type GovernanceMessage struct { ++ cbor.Versioned ++ ++ CastVote *governance.ProposalVote `json:"cast_vote,omitempty"` ++ SubmitProposal *governance.ProposalContent `json:"submit_proposal,omitempty"` ++} ++ ++// ValidateBasic performs basic validation of a governance message. ++// removed func From f146bc2c8768826d424b14598ddc3d7b92ea5834 Mon Sep 17 00:00:00 2001 From: Warren He Date: Fri, 19 Jan 2024 16:51:58 -0800 Subject: [PATCH 06/15] consensus: add message switch --- analyzer/consensus/messages.go | 147 ++++++++++++++++++++++++ analyzer/util/addresses/addresses.go | 12 +- analyzer/util/addresses/registration.go | 23 ++++ 3 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 analyzer/consensus/messages.go diff --git a/analyzer/consensus/messages.go b/analyzer/consensus/messages.go new file mode 100644 index 000000000..a0b753dcc --- /dev/null +++ b/analyzer/consensus/messages.go @@ -0,0 +1,147 @@ +package consensus + +import ( + "encoding/json" + + "github.com/oasisprotocol/nexus/analyzer/util/addresses" + apiTypes "github.com/oasisprotocol/nexus/api/v1/types" + "github.com/oasisprotocol/nexus/coreapi/v22.2.11/roothash/api/message" + "github.com/oasisprotocol/nexus/log" +) + +type MessageData struct { + messageType string + body json.RawMessage + relatedAddresses map[apiTypes.Address]struct{} +} + +func extractMessageData(logger *log.Logger, m message.Message) MessageData { + messageData := MessageData{ + relatedAddresses: map[apiTypes.Address]struct{}{}, + } + switch { + case m.Staking != nil: + switch { + case m.Staking.Transfer != nil: + messageData.messageType = "staking.transfer" + body, err := json.Marshal(m.Staking.Transfer) + if err != nil { + logger.Info("marshal message body failed", + "message_type", messageData.messageType, + "err", err, + ) + break + } + messageData.body = body + _, err = addresses.RegisterRelatedOCSAddress(messageData.relatedAddresses, m.Staking.Transfer.To) + if err != nil { + logger.Info("register related address 'to' failed", + "message_type", messageData.messageType, + "err", err, + ) + } + case m.Staking.Withdraw != nil: + messageData.messageType = "staking.withdraw" + body, err := json.Marshal(m.Staking.Withdraw) + if err != nil { + logger.Info("marshal message body failed", + "message_type", messageData.messageType, + "err", err, + ) + break + } + messageData.body = body + _, err = addresses.RegisterRelatedOCSAddress(messageData.relatedAddresses, m.Staking.Withdraw.From) + if err != nil { + logger.Info("register related address 'from' failed", + "message_type", messageData.messageType, + "err", err, + ) + } + case m.Staking.AddEscrow != nil: + messageData.messageType = "staking.add_escrow" + body, err := json.Marshal(m.Staking.AddEscrow) + if err != nil { + logger.Info("marshal message body failed", + "message_type", messageData.messageType, + "err", err, + ) + break + } + messageData.body = body + _, err = addresses.RegisterRelatedOCSAddress(messageData.relatedAddresses, m.Staking.AddEscrow.Account) + if err != nil { + logger.Info("register related address 'account' failed", + "message_type", messageData.messageType, + "err", err, + ) + } + case m.Staking.ReclaimEscrow != nil: + messageData.messageType = "staking.reclaim_escrow" + body, err := json.Marshal(m.Staking.ReclaimEscrow) + if err != nil { + logger.Info("marshal message body failed", + "message_type", messageData.messageType, + "err", err, + ) + break + } + messageData.body = body + _, err = addresses.RegisterRelatedOCSAddress(messageData.relatedAddresses, m.Staking.ReclaimEscrow.Account) + if err != nil { + logger.Info("register related address 'account' failed", + "message_type", messageData.messageType, + "err", err, + ) + } + default: + logger.Info("unhandled staking message", + "staking_message", m.Staking, + ) + } + case m.Registry != nil: + switch { + case m.Registry.UpdateRuntime != nil: + messageData.messageType = "registry.update_runtime" + body, err := json.Marshal(m.Registry.UpdateRuntime) + if err != nil { + logger.Info("marshal message body failed", + "message_type", messageData.messageType, + "err", err, + ) + break + } + messageData.body = body + } + case m.Governance != nil: + switch { + case m.Governance.CastVote != nil: + messageData.messageType = "governance.cast_vote" + body, err := json.Marshal(m.Governance.CastVote) + if err != nil { + logger.Info("marshal message body failed", + "message_type", messageData.messageType, + "err", err, + ) + break + } + messageData.body = body + case m.Governance.SubmitProposal != nil: + messageData.messageType = "governance.submit_proposal" + body, err := json.Marshal(m.Governance.SubmitProposal) + if err != nil { + logger.Info("marshal message body failed", + "message_type", messageData.messageType, + "err", err, + ) + break + } + messageData.body = body + } + default: + logger.Info("unhandled message", + "message", m, + ) + } + return messageData +} diff --git a/analyzer/util/addresses/addresses.go b/analyzer/util/addresses/addresses.go index 0ddfc4a6c..be13503a8 100644 --- a/analyzer/util/addresses/addresses.go +++ b/analyzer/util/addresses/addresses.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/oasisprotocol/oasis-core/go/common/crypto/address" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" sdkTypes "github.com/oasisprotocol/oasis-sdk/client-sdk/go/types" apiTypes "github.com/oasisprotocol/nexus/api/v1/types" @@ -13,7 +14,8 @@ import ( // ethAddr -> []byte len-20 slice // ecAddr -> go-ethereum type binary address // ocAddr -> oasis-core type binary oasis address -// sdkAddr -> oasis-sdk type binary oasis address +// ocsAddr -> oasis-core type binary oasis address, but staking api wrapper +// sdkAddr -> oasis-sdk type binary oasis address, but sdk wrapper // addr -> bech32 string oasis address // addrTextBytes -> bech32 []byte oasis address @@ -38,6 +40,14 @@ func FromOCAddress(ocAddr address.Address) (apiTypes.Address, error) { return FromSdkAddress(&sdkAddr) } +func FromOCSAddress(ocsAddr staking.Address) (apiTypes.Address, error) { + addrTextBytes, err := ocsAddr.MarshalText() + if err != nil { + return "", fmt.Errorf("address marshal text: %w", err) + } + return apiTypes.Address(addrTextBytes), nil +} + func FromEthAddress(ethAddr []byte) (apiTypes.Address, error) { ctx := sdkTypes.AddressV0Secp256k1EthContext ocAddr := address.NewAddress(ctx, ethAddr) diff --git a/analyzer/util/addresses/registration.go b/analyzer/util/addresses/registration.go index cac5ef52d..cdc362429 100644 --- a/analyzer/util/addresses/registration.go +++ b/analyzer/util/addresses/registration.go @@ -5,6 +5,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/address" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" sdkTypes "github.com/oasisprotocol/oasis-sdk/client-sdk/go/types" "github.com/oasisprotocol/nexus/analyzer/util/eth" @@ -112,6 +113,28 @@ func RegisterRelatedAddressSpec(addressPreimages map[apiTypes.Address]*PreimageD return addr, nil } +func RegisterRelatedOCAddress(relatedAddresses map[apiTypes.Address]struct{}, ocAddr address.Address) (apiTypes.Address, error) { + addr, err := FromOCAddress(ocAddr) + if err != nil { + return "", err + } + + relatedAddresses[addr] = struct{}{} + + return addr, nil +} + +func RegisterRelatedOCSAddress(relatedAddresses map[apiTypes.Address]struct{}, ocsAddr staking.Address) (apiTypes.Address, error) { + addr, err := FromOCSAddress(ocsAddr) + if err != nil { + return "", err + } + + relatedAddresses[addr] = struct{}{} + + return addr, nil +} + func RegisterRelatedEthAddress(addressPreimages map[apiTypes.Address]*PreimageData, relatedAddresses map[apiTypes.Address]struct{}, ethAddr []byte) (apiTypes.Address, error) { addr, err := registerEthAddress(addressPreimages, ethAddr) if err != nil { From 77da6feef60a33cbad8b398a7bc3a018e01f7a08 Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 4 Jan 2024 17:26:06 -0800 Subject: [PATCH 07/15] consensus: store scheduled roothash messages --- analyzer/consensus/consensus.go | 42 +++++++++++++++++++++++++++++++-- analyzer/queries/queries.go | 11 +++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/analyzer/consensus/consensus.go b/analyzer/consensus/consensus.go index aa5399571..adc11ab73 100644 --- a/analyzer/consensus/consensus.go +++ b/analyzer/consensus/consensus.go @@ -16,6 +16,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" sdkConfig "github.com/oasisprotocol/oasis-sdk/client-sdk/go/config" + "github.com/oasisprotocol/nexus/analyzer/util/addresses" "github.com/oasisprotocol/nexus/coreapi/v22.2.11/consensus/api/transaction" genesis "github.com/oasisprotocol/nexus/coreapi/v22.2.11/genesis/api" staking "github.com/oasisprotocol/nexus/coreapi/v22.2.11/staking/api" @@ -286,8 +287,13 @@ func (m *processor) queueDbUpdates(batch *storage.QueryBatch, data allData) erro } } - if err := m.queueRootHashEventInserts(batch, data.RootHashData); err != nil { - return err + for _, f := range []func(*storage.QueryBatch, *rootHashData) error{ + m.queueRootHashMessageUpserts, + m.queueRootHashEventInserts, + } { + if err := f(batch, data.RootHashData); err != nil { + return err + } } return nil @@ -648,6 +654,38 @@ func (m *processor) queueRegistryEventInserts(batch *storage.QueryBatch, data *r return nil } +func (m *processor) queueRootHashMessageUpserts(batch *storage.QueryBatch, data *rootHashData) error { + for _, event := range data.Events { + switch { + case event.RoothashExecutorCommitted != nil: + runtime := RuntimeFromID(event.RoothashExecutorCommitted.RuntimeID, m.network) + if runtime == nil { + break + } + round := event.RoothashExecutorCommitted.Round + for i, message := range event.RoothashExecutorCommitted.Messages { + logger := m.logger.With( + "height", data.Height, + "runtime", runtime, + "round", round, + "message_index", i, + ) + messageData := extractMessageData(logger, message) + batch.Queue(queries.ConsensusRoothashMessageScheduleUpsert, + runtime, + round, + i, + messageData.messageType, + messageData.body, + addresses.SliceFromSet(messageData.relatedAddresses), + ) + } + } + } + + return nil +} + func (m *processor) queueRootHashEventInserts(batch *storage.QueryBatch, data *rootHashData) error { for _, event := range data.Events { hash := util.SanitizeTxHash(event.TxHash.Hex()) diff --git a/analyzer/queries/queries.go b/analyzer/queries/queries.go index 3be0fb58f..6da2403b0 100644 --- a/analyzer/queries/queries.go +++ b/analyzer/queries/queries.go @@ -214,6 +214,17 @@ var ( INSERT INTO chain.events (type, body, tx_block, tx_hash, tx_index, related_accounts, roothash_runtime_id, roothash_runtime, roothash_runtime_round) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)` + ConsensusRoothashMessageScheduleUpsert = ` + INSERT INTO chain.roothash_messages + (runtime, round, message_index, type, body, related_accounts) + VALUES + ($1, $2, $3, $4, $5, $6) + ON CONFLICT (runtime, round, message_index) DO UPDATE + SET + type = excluded.type, + body = excluded.body, + related_accounts = excluded.related_accounts` + ConsensusAccountRelatedTransactionInsert = ` INSERT INTO chain.accounts_related_transactions (account_address, tx_block, tx_index) VALUES ($1, $2, $3)` From 22d0a8d228c052137cc9672fe7f5879e783038b6 Mon Sep 17 00:00:00 2001 From: Warren He Date: Fri, 5 Jan 2024 16:40:20 -0800 Subject: [PATCH 08/15] storage: add RoothashLastRoundResults --- storage/oasis/nodeapi/api.go | 2 ++ storage/oasis/nodeapi/cobalt/node.go | 8 ++++++++ storage/oasis/nodeapi/damask/node.go | 11 +++++++++++ storage/oasis/nodeapi/eden/node.go | 14 ++++++++++++++ storage/oasis/nodeapi/file/consensus.go | 11 +++++++++++ storage/oasis/nodeapi/history/history.go | 9 +++++++++ 6 files changed, 55 insertions(+) diff --git a/storage/oasis/nodeapi/api.go b/storage/oasis/nodeapi/api.go index 87661913b..1f470dd13 100644 --- a/storage/oasis/nodeapi/api.go +++ b/storage/oasis/nodeapi/api.go @@ -19,6 +19,7 @@ import ( genesis "github.com/oasisprotocol/nexus/coreapi/v22.2.11/genesis/api" governance "github.com/oasisprotocol/nexus/coreapi/v22.2.11/governance/api" registry "github.com/oasisprotocol/nexus/coreapi/v22.2.11/registry/api" + roothash "github.com/oasisprotocol/nexus/coreapi/v22.2.11/roothash/api" "github.com/oasisprotocol/nexus/coreapi/v22.2.11/roothash/api/message" scheduler "github.com/oasisprotocol/nexus/coreapi/v22.2.11/scheduler/api" staking "github.com/oasisprotocol/nexus/coreapi/v22.2.11/staking/api" @@ -58,6 +59,7 @@ type ConsensusApiLite interface { StakingEvents(ctx context.Context, height int64) ([]Event, error) GovernanceEvents(ctx context.Context, height int64) ([]Event, error) RoothashEvents(ctx context.Context, height int64) ([]Event, error) + RoothashLastRoundResults(ctx context.Context, height int64, runtimeID coreCommon.Namespace) (*roothash.RoundResults, error) GetValidators(ctx context.Context, height int64) ([]Validator, error) GetNodes(ctx context.Context, height int64) ([]Node, error) GetCommittees(ctx context.Context, height int64, runtimeID coreCommon.Namespace) ([]Committee, error) diff --git a/storage/oasis/nodeapi/cobalt/node.go b/storage/oasis/nodeapi/cobalt/node.go index 139706a45..7be9b9751 100644 --- a/storage/oasis/nodeapi/cobalt/node.go +++ b/storage/oasis/nodeapi/cobalt/node.go @@ -15,6 +15,7 @@ import ( consensusTx "github.com/oasisprotocol/nexus/coreapi/v22.2.11/consensus/api/transaction" genesis "github.com/oasisprotocol/nexus/coreapi/v22.2.11/genesis/api" governance "github.com/oasisprotocol/nexus/coreapi/v22.2.11/governance/api" + roothash "github.com/oasisprotocol/nexus/coreapi/v22.2.11/roothash/api" scheduler "github.com/oasisprotocol/nexus/coreapi/v22.2.11/scheduler/api" "github.com/oasisprotocol/nexus/storage/oasis/connections" @@ -163,6 +164,13 @@ func (c *ConsensusApiLite) RoothashEvents(ctx context.Context, height int64) ([] return events, nil } +func (c *ConsensusApiLite) RoothashLastRoundResults(ctx context.Context, height int64, runtimeID common.Namespace) (*roothash.RoundResults, error) { + // Cobalt didn't have this API. Always return empty. + // Results of roothash messages were instead reported in MessageEvent, + // which we retrieve with the other events. + return &roothash.RoundResults{}, nil +} + func (c *ConsensusApiLite) GetNodes(ctx context.Context, height int64) ([]nodeapi.Node, error) { var rsp []*nodeapi.Node // ABI is stable across Cobalt and Damask if err := c.grpcConn.Invoke(ctx, "/oasis-core.Registry/GetNodes", height, &rsp); err != nil { diff --git a/storage/oasis/nodeapi/damask/node.go b/storage/oasis/nodeapi/damask/node.go index b46037efe..e55ab804d 100644 --- a/storage/oasis/nodeapi/damask/node.go +++ b/storage/oasis/nodeapi/damask/node.go @@ -157,6 +157,17 @@ func (c *ConsensusApiLite) RoothashEvents(ctx context.Context, height int64) ([] return events, nil } +func (c *ConsensusApiLite) RoothashLastRoundResults(ctx context.Context, height int64, runtimeID common.Namespace) (*roothash.RoundResults, error) { + var rsp roothash.RoundResults + if err := c.grpcConn.Invoke(ctx, "/oasis-core.RootHash/GetLastRoundResults", &roothash.RuntimeRequest{ + Height: height, + RuntimeID: runtimeID, + }, &rsp); err != nil { + return nil, fmt.Errorf("RoothashLastRoundResults(%d, %v): %w", height, runtimeID, err) + } + return &rsp, nil +} + func (c *ConsensusApiLite) GetNodes(ctx context.Context, height int64) ([]nodeapi.Node, error) { var rsp []*nodeapi.Node if err := c.grpcConn.Invoke(ctx, "/oasis-core.Registry/GetNodes", height, &rsp); err != nil { diff --git a/storage/oasis/nodeapi/eden/node.go b/storage/oasis/nodeapi/eden/node.go index 25ea048ef..0d6f9b61e 100644 --- a/storage/oasis/nodeapi/eden/node.go +++ b/storage/oasis/nodeapi/eden/node.go @@ -14,6 +14,7 @@ import ( consensus "github.com/oasisprotocol/nexus/coreapi/v22.2.11/consensus/api" consensusTx "github.com/oasisprotocol/nexus/coreapi/v22.2.11/consensus/api/transaction" genesis "github.com/oasisprotocol/nexus/coreapi/v22.2.11/genesis/api" + roothash "github.com/oasisprotocol/nexus/coreapi/v22.2.11/roothash/api" "github.com/oasisprotocol/nexus/storage/oasis/connections" "github.com/oasisprotocol/nexus/storage/oasis/nodeapi" @@ -161,6 +162,19 @@ func (c *ConsensusApiLite) RoothashEvents(ctx context.Context, height int64) ([] return events, nil } +func (c *ConsensusApiLite) RoothashLastRoundResults(ctx context.Context, height int64, runtimeID common.Namespace) (*roothash.RoundResults, error) { + // We are extending the Damask vendored structure to make it compatible + // with new features from Eden. + var rsp roothash.RoundResults + if err := c.grpcConn.Invoke(ctx, "/oasis-core.RootHash/GetLastRoundResults", &roothashEden.RuntimeRequest{ + Height: height, + RuntimeID: runtimeID, + }, &rsp); err != nil { + return nil, fmt.Errorf("RoothashLastRoundResults(%d, %v): %w", height, runtimeID, err) + } + return &rsp, nil +} + func (c *ConsensusApiLite) GetNodes(ctx context.Context, height int64) ([]nodeapi.Node, error) { var rsp []*nodeapi.Node // ABI is stable across Eden and Damask if err := c.grpcConn.Invoke(ctx, "/oasis-core.Registry/GetNodes", height, &rsp); err != nil { diff --git a/storage/oasis/nodeapi/file/consensus.go b/storage/oasis/nodeapi/file/consensus.go index 27cf903fa..b3b65ea15 100644 --- a/storage/oasis/nodeapi/file/consensus.go +++ b/storage/oasis/nodeapi/file/consensus.go @@ -10,6 +10,7 @@ import ( beacon "github.com/oasisprotocol/nexus/coreapi/v22.2.11/beacon/api" consensus "github.com/oasisprotocol/nexus/coreapi/v22.2.11/consensus/api" genesis "github.com/oasisprotocol/nexus/coreapi/v22.2.11/genesis/api" + roothash "github.com/oasisprotocol/nexus/coreapi/v22.2.11/roothash/api" "github.com/oasisprotocol/nexus/cache/kvstore" "github.com/oasisprotocol/nexus/common" @@ -139,6 +140,16 @@ func (c *FileConsensusApiLite) RoothashEvents(ctx context.Context, height int64) ) } +func (c *FileConsensusApiLite) RoothashLastRoundResults(ctx context.Context, height int64, runtimeID coreCommon.Namespace) (*roothash.RoundResults, error) { + return kvstore.GetFromCacheOrCall( + c.db, height == consensus.HeightLatest, + kvstore.GenerateCacheKey("RoothashLastRoundResults", height, runtimeID), + func() (*roothash.RoundResults, error) { + return c.consensusApi.RoothashLastRoundResults(ctx, height, runtimeID) + }, + ) +} + func (c *FileConsensusApiLite) GetNodes(ctx context.Context, height int64) ([]nodeapi.Node, error) { return kvstore.GetSliceFromCacheOrCall( c.db, height == consensus.HeightLatest, diff --git a/storage/oasis/nodeapi/history/history.go b/storage/oasis/nodeapi/history/history.go index 666fbdf32..2b997dae0 100644 --- a/storage/oasis/nodeapi/history/history.go +++ b/storage/oasis/nodeapi/history/history.go @@ -9,6 +9,7 @@ import ( beacon "github.com/oasisprotocol/nexus/coreapi/v22.2.11/beacon/api" consensus "github.com/oasisprotocol/nexus/coreapi/v22.2.11/consensus/api" genesis "github.com/oasisprotocol/nexus/coreapi/v22.2.11/genesis/api" + roothash "github.com/oasisprotocol/nexus/coreapi/v22.2.11/roothash/api" "github.com/oasisprotocol/nexus/config" "github.com/oasisprotocol/nexus/storage/oasis/connections" @@ -191,6 +192,14 @@ func (c *HistoryConsensusApiLite) RoothashEvents(ctx context.Context, height int return api.RoothashEvents(ctx, height) } +func (c *HistoryConsensusApiLite) RoothashLastRoundResults(ctx context.Context, height int64, runtimeID common.Namespace) (*roothash.RoundResults, error) { + api, err := c.APIForHeight(height) + if err != nil { + return nil, fmt.Errorf("getting api for height %d: %w", height, err) + } + return api.RoothashLastRoundResults(ctx, height, runtimeID) +} + func (c *HistoryConsensusApiLite) GetNodes(ctx context.Context, height int64) ([]nodeapi.Node, error) { api, err := c.APIForHeight(height) if err != nil { From 1e023341edae84bb2c4c84991d71a7730b1ac2f5 Mon Sep 17 00:00:00 2001 From: Warren He Date: Tue, 9 Apr 2024 15:16:18 -0700 Subject: [PATCH 09/15] consensus: register runtime itself as related to roothash messages --- analyzer/consensus/consensus.go | 17 +++++++++++++ analyzer/consensus/messages.go | 2 ++ analyzer/util/addresses/addresses.go | 6 +++++ analyzer/util/addresses/registration.go | 33 +++++++++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/analyzer/consensus/consensus.go b/analyzer/consensus/consensus.go index adc11ab73..801fdfdaf 100644 --- a/analyzer/consensus/consensus.go +++ b/analyzer/consensus/consensus.go @@ -671,6 +671,23 @@ func (m *processor) queueRootHashMessageUpserts(batch *storage.QueryBatch, data "message_index", i, ) messageData := extractMessageData(logger, message) + // The runtime has its own staking account, which is what + // performs these actions, e.g. when sending or receiving the + // consensus token. Register that as related to the message. + if _, err := addresses.RegisterRelatedRuntimeAddress(messageData.addressPreimages, messageData.relatedAddresses, event.RoothashExecutorCommitted.RuntimeID); err != nil { + logger.Info("register runtime address failed", + "runtime_id", event.RoothashExecutorCommitted.RuntimeID, + "err", err, + ) + } + for addr, preimageData := range messageData.addressPreimages { + batch.Queue(queries.AddressPreimageInsert, + addr, + preimageData.ContextIdentifier, + preimageData.ContextVersion, + preimageData.Data, + ) + } batch.Queue(queries.ConsensusRoothashMessageScheduleUpsert, runtime, round, diff --git a/analyzer/consensus/messages.go b/analyzer/consensus/messages.go index a0b753dcc..89291d94b 100644 --- a/analyzer/consensus/messages.go +++ b/analyzer/consensus/messages.go @@ -12,11 +12,13 @@ import ( type MessageData struct { messageType string body json.RawMessage + addressPreimages map[apiTypes.Address]*addresses.PreimageData relatedAddresses map[apiTypes.Address]struct{} } func extractMessageData(logger *log.Logger, m message.Message) MessageData { messageData := MessageData{ + addressPreimages: map[apiTypes.Address]*addresses.PreimageData{}, relatedAddresses: map[apiTypes.Address]struct{}{}, } switch { diff --git a/analyzer/util/addresses/addresses.go b/analyzer/util/addresses/addresses.go index be13503a8..6d19ff654 100644 --- a/analyzer/util/addresses/addresses.go +++ b/analyzer/util/addresses/addresses.go @@ -3,6 +3,7 @@ package addresses import ( "fmt" + coreCommon "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/common/crypto/address" staking "github.com/oasisprotocol/oasis-core/go/staking/api" sdkTypes "github.com/oasisprotocol/oasis-sdk/client-sdk/go/types" @@ -54,6 +55,11 @@ func FromEthAddress(ethAddr []byte) (apiTypes.Address, error) { return FromOCAddress(ocAddr) } +func FromRuntimeID(id coreCommon.Namespace) (apiTypes.Address, error) { + ocsAddr := staking.NewRuntimeAddress(id) + return FromOCSAddress(ocsAddr) +} + func SliceFromSet(accounts map[apiTypes.Address]struct{}) []string { addrs := make([]string, len(accounts)) i := 0 diff --git a/analyzer/util/addresses/registration.go b/analyzer/util/addresses/registration.go index cdc362429..979aedefa 100644 --- a/analyzer/util/addresses/registration.go +++ b/analyzer/util/addresses/registration.go @@ -3,6 +3,7 @@ package addresses import ( "fmt" + coreCommon "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/crypto/address" staking "github.com/oasisprotocol/oasis-core/go/staking/api" @@ -91,6 +92,27 @@ func registerEthAddress(addressPreimages map[apiTypes.Address]*PreimageData, eth return addr, nil } +func registerRuntimeAddress(addressPreimages map[apiTypes.Address]*PreimageData, id coreCommon.Namespace) (apiTypes.Address, error) { + addr, err := FromRuntimeID(id) + if err != nil { + return "", err + } + + if _, ok := addressPreimages[addr]; !ok { + data, err1 := id.MarshalBinary() + if err1 != nil { + return "", err1 + } + addressPreimages[addr] = &PreimageData{ + ContextIdentifier: staking.AddressRuntimeV0Context.Identifier, + ContextVersion: int(staking.AddressRuntimeV0Context.Version), + Data: data, + } + } + + return addr, nil +} + func RegisterRelatedSdkAddress(relatedAddresses map[apiTypes.Address]struct{}, sdkAddr *sdkTypes.Address) (apiTypes.Address, error) { addr, err := FromSdkAddress(sdkAddr) if err != nil { @@ -145,3 +167,14 @@ func RegisterRelatedEthAddress(addressPreimages map[apiTypes.Address]*PreimageDa return addr, nil } + +func RegisterRelatedRuntimeAddress(addressPreimages map[apiTypes.Address]*PreimageData, relatedAddresses map[apiTypes.Address]struct{}, id coreCommon.Namespace) (apiTypes.Address, error) { + addr, err := registerRuntimeAddress(addressPreimages, id) + if err != nil { + return "", err + } + + relatedAddresses[addr] = struct{}{} + + return addr, nil +} From da54ac6be25da6ba125ad7d48e14bc04ca2b7037 Mon Sep 17 00:00:00 2001 From: Warren He Date: Fri, 5 Jan 2024 16:43:34 -0800 Subject: [PATCH 10/15] consensus: fetch roothash last round results --- analyzer/consensus/data_fetch.go | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/analyzer/consensus/data_fetch.go b/analyzer/consensus/data_fetch.go index 95096b850..8ba6e006d 100644 --- a/analyzer/consensus/data_fetch.go +++ b/analyzer/consensus/data_fetch.go @@ -9,6 +9,7 @@ import ( beacon "github.com/oasisprotocol/nexus/coreapi/v22.2.11/beacon/api" consensus "github.com/oasisprotocol/nexus/coreapi/v22.2.11/consensus/api" + roothash "github.com/oasisprotocol/nexus/coreapi/v22.2.11/roothash/api" "github.com/oasisprotocol/nexus/storage/oasis/nodeapi" ) @@ -50,7 +51,7 @@ func fetchAllData(ctx context.Context, cc nodeapi.ConsensusApiLite, network sdkC return nil, err } - rootHashData, err := fetchRootHashData(ctx, cc, height) + rootHashData, err := fetchRootHashData(ctx, cc, network, height) if err != nil { return nil, err } @@ -276,16 +277,33 @@ func fetchGovernanceData(ctx context.Context, cc nodeapi.ConsensusApiLite, heigh }, nil } -// fetchRootHashData retrieves roothash events at the provided block height. -func fetchRootHashData(ctx context.Context, cc nodeapi.ConsensusApiLite, height int64) (*rootHashData, error) { +// fetchRootHashData retrieves roothash events and last round results at the +// provided block height. +func fetchRootHashData(ctx context.Context, cc nodeapi.ConsensusApiLite, network sdkConfig.Network, height int64) (*rootHashData, error) { events, err := cc.RoothashEvents(ctx, height) if err != nil { return nil, err } + lastRoundResults := make(map[coreCommon.Namespace]*roothash.RoundResults, len(network.ParaTimes.All)) + + for name := range network.ParaTimes.All { + var runtimeID coreCommon.Namespace + if err1 := runtimeID.UnmarshalHex(network.ParaTimes.All[name].ID); err1 != nil { + return nil, err1 + } + + res, err1 := cc.RoothashLastRoundResults(ctx, height, runtimeID) + if err1 != nil { + return nil, err1 + } + lastRoundResults[runtimeID] = res + } + return &rootHashData{ - Height: height, - Events: events, + Height: height, + Events: events, + LastRoundResults: lastRoundResults, }, nil } @@ -352,6 +370,8 @@ type rootHashData struct { Height int64 Events []nodeapi.Event + + LastRoundResults map[coreCommon.Namespace]*roothash.RoundResults } // schedulerData represents data for elected committees and validators at a given height. From 3f9dba14794e3109c7b954d05b99173d5f53dd01 Mon Sep 17 00:00:00 2001 From: Warren He Date: Fri, 5 Jan 2024 16:43:51 -0800 Subject: [PATCH 11/15] consensus: store finalized roothash messages --- analyzer/consensus/consensus.go | 46 +++++++++++++++++++++++++++++++++ analyzer/queries/queries.go | 11 ++++++++ 2 files changed, 57 insertions(+) diff --git a/analyzer/consensus/consensus.go b/analyzer/consensus/consensus.go index 801fdfdaf..091efce80 100644 --- a/analyzer/consensus/consensus.go +++ b/analyzer/consensus/consensus.go @@ -655,8 +655,15 @@ func (m *processor) queueRegistryEventInserts(batch *storage.QueryBatch, data *r } func (m *processor) queueRootHashMessageUpserts(batch *storage.QueryBatch, data *rootHashData) error { + finalized := map[coreCommon.Namespace]uint64{} + var roothashMessageEvents []nodeapi.Event for _, event := range data.Events { switch { + case event.RoothashMisc != nil: + switch event.Type { //nolint:gocritic,exhaustive // singleCaseSwitch, no special handling for other types + case apiTypes.ConsensusEventTypeRoothashFinalized: + finalized[event.RoothashMisc.RuntimeID] = *event.RoothashMisc.Round + } case event.RoothashExecutorCommitted != nil: runtime := RuntimeFromID(event.RoothashExecutorCommitted.RuntimeID, m.network) if runtime == nil { @@ -697,6 +704,45 @@ func (m *processor) queueRootHashMessageUpserts(batch *storage.QueryBatch, data addresses.SliceFromSet(messageData.relatedAddresses), ) } + case event.RoothashMessage != nil: + // Save these for after we collect all roothash finalized events. + roothashMessageEvents = append(roothashMessageEvents, event) + } + } + for _, event := range roothashMessageEvents { + runtime := RuntimeFromID(event.RoothashMessage.RuntimeID, m.network) + if runtime == nil { + continue + } + batch.Queue(queries.ConsensusRoothashMessageFinalizeUpsert, + runtime, + finalized[event.RoothashMessage.RuntimeID], + event.RoothashMessage.Index, + event.RoothashMessage.Module, + event.RoothashMessage.Code, + nil, + ) + } + for rtid, results := range data.LastRoundResults { + runtime := RuntimeFromID(rtid, m.network) + if runtime == nil { + // We shouldn't even have gathered last round results for unknown + // runtimes. But prevent nil-runtime inserts anyway. + continue + } + round, ok := finalized[rtid] + if !ok { + continue + } + for _, message := range results.Messages { + batch.Queue(queries.ConsensusRoothashMessageFinalizeUpsert, + runtime, + round, + message.Index, + message.Module, + message.Code, + cbor.Marshal(message.Result), + ) } } diff --git a/analyzer/queries/queries.go b/analyzer/queries/queries.go index 6da2403b0..f181b49a7 100644 --- a/analyzer/queries/queries.go +++ b/analyzer/queries/queries.go @@ -225,6 +225,17 @@ var ( body = excluded.body, related_accounts = excluded.related_accounts` + ConsensusRoothashMessageFinalizeUpsert = ` + INSERT INTO chain.roothash_messages + (runtime, round, message_index, error_module, error_code, result) + VALUES + ($1, $2, $3, $4, $5, $6) + ON CONFLICT (runtime, round, message_index) DO UPDATE + SET + error_module = excluded.error_module, + error_code = excluded.error_code, + result = excluded.result` + ConsensusAccountRelatedTransactionInsert = ` INSERT INTO chain.accounts_related_transactions (account_address, tx_block, tx_index) VALUES ($1, $2, $3)` From 2fb1d98d624d50b5b5d025acac409efdeb63afeb Mon Sep 17 00:00:00 2001 From: Warren He Date: Wed, 10 Apr 2024 15:20:15 -0700 Subject: [PATCH 12/15] consensus: add comments to queueRootHashMessageUpserts --- analyzer/consensus/consensus.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/analyzer/consensus/consensus.go b/analyzer/consensus/consensus.go index 091efce80..576ecac65 100644 --- a/analyzer/consensus/consensus.go +++ b/analyzer/consensus/consensus.go @@ -655,6 +655,11 @@ func (m *processor) queueRegistryEventInserts(batch *storage.QueryBatch, data *r } func (m *processor) queueRootHashMessageUpserts(batch *storage.QueryBatch, data *rootHashData) error { + // Collect (I) roothash messages being scheduled and (II) roothash + // messages being finalized. They're always scheduled in the first + // ExecutorCommitedEvent (i.e. the proposal). They're finalized in (a) + // MessageEvent in Cobalt and in (b) the last round results in Damask and + // later. finalized := map[coreCommon.Namespace]uint64{} var roothashMessageEvents []nodeapi.Event for _, event := range data.Events { @@ -662,6 +667,9 @@ func (m *processor) queueRootHashMessageUpserts(batch *storage.QueryBatch, data case event.RoothashMisc != nil: switch event.Type { //nolint:gocritic,exhaustive // singleCaseSwitch, no special handling for other types case apiTypes.ConsensusEventTypeRoothashFinalized: + // (II.a) MessageEvent does not have its own Round field, so + // use the value from the FinalizedEvent that happens at the + // same time. finalized[event.RoothashMisc.RuntimeID] = *event.RoothashMisc.Round } case event.RoothashExecutorCommitted != nil: @@ -670,6 +678,9 @@ func (m *processor) queueRootHashMessageUpserts(batch *storage.QueryBatch, data break } round := event.RoothashExecutorCommitted.Round + // (I) Extract roothash messages from the ExecutorCommittedEvent. + // Only the proposal has the messages, so the other commits will + // harmlessly skip over this part. for i, message := range event.RoothashExecutorCommitted.Messages { logger := m.logger.With( "height", data.Height, @@ -705,6 +716,7 @@ func (m *processor) queueRootHashMessageUpserts(batch *storage.QueryBatch, data ) } case event.RoothashMessage != nil: + // (II.a) Extract message results from the MessageEvents. // Save these for after we collect all roothash finalized events. roothashMessageEvents = append(roothashMessageEvents, event) } @@ -734,6 +746,7 @@ func (m *processor) queueRootHashMessageUpserts(batch *storage.QueryBatch, data if !ok { continue } + // (II.b) Extract message results from the last round results. for _, message := range results.Messages { batch.Queue(queries.ConsensusRoothashMessageFinalizeUpsert, runtime, From aa579bb38fb060250d6d60be24f9a02292ce7600 Mon Sep 17 00:00:00 2001 From: Warren He Date: Mon, 4 Mar 2024 14:26:56 -0800 Subject: [PATCH 13/15] tests: rebuild damask consensus cache --- .../damask/rpc-cache/consensus/00000-1.psg | 4 ++-- .../damask/rpc-cache/consensus/00000-1.psg.pmt | Bin 621 -> 621 bytes .../damask/rpc-cache/consensus/db.pmt | Bin 555 -> 555 bytes .../damask/rpc-cache/consensus/index.pmt | Bin 879 -> 658 bytes .../damask/rpc-cache/consensus/main.pix | 4 ++-- .../damask/rpc-cache/consensus/overflow.pix | 4 ++-- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/e2e_regression/damask/rpc-cache/consensus/00000-1.psg b/tests/e2e_regression/damask/rpc-cache/consensus/00000-1.psg index 8a229141c..228d0437f 100644 --- a/tests/e2e_regression/damask/rpc-cache/consensus/00000-1.psg +++ b/tests/e2e_regression/damask/rpc-cache/consensus/00000-1.psg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8b0d9f8e80300373db998e9332ee51ce4e1e90cb9e90376f9cf10cebd2f4ccb -size 34299824 +oid sha256:dfd4781db000c6c98add072af17abe637270fd837cc10b28bb2161c579fbcd5f +size 34533824 diff --git a/tests/e2e_regression/damask/rpc-cache/consensus/00000-1.psg.pmt b/tests/e2e_regression/damask/rpc-cache/consensus/00000-1.psg.pmt index 616fa0a9ad438d90ab4185934f71968fd4a850a9..48c47b3cfd897f93071c353097a8a9c7ed37bf22 100644 GIT binary patch delta 11 ScmaFM@|I;o78A4CMg{;JJp<_g delta 11 ScmaFM@|I;o78A49PX+)SsRShe diff --git a/tests/e2e_regression/damask/rpc-cache/consensus/db.pmt b/tests/e2e_regression/damask/rpc-cache/consensus/db.pmt index 7ab8f27cbc4a4578f90ae9ca9f0f460b8d8448e2..fd89de9194e0d230bcb5b934ad6f47ea87a9eb5a 100644 GIT binary patch delta 13 UcmZ3@vYKUs8WXF3LG&9203Iy_LI3~& delta 13 UcmZ3@vYKUs8WZb0(@ok803J^R>;M1& diff --git a/tests/e2e_regression/damask/rpc-cache/consensus/index.pmt b/tests/e2e_regression/damask/rpc-cache/consensus/index.pmt index 4566ab0b6d55a066893acb6e93a474ce61c406cb..1e0ef3d545fc9adde91c38d24ee89c67a06099f0 100644 GIT binary patch delta 25 hcmaFQHi>mZBa@)O{}x70#(!oT8UHc0G5&93004QT31I*L delta 247 zcmWlTFD%4y7{)(e!+t@yAcz-)n+bw&K@ctqf*=ZlU~^HhVY?s*W`ZCJf^b0)E(oF^ zh=L&AMiAJ!&(9+~@1N)M?%8`j^)cN)o;a+Bb+1&#g`Z*>m3mY<-#C7uX3{#W%diZ}vREFFe6d>|)qi vP8!%wtD@y7IlK6bi5}u5F5zjw3SMLCWR_O+|85g+aDaEXiMJT%2)E%6N Date: Mon, 4 Mar 2024 15:09:00 -0800 Subject: [PATCH 14/15] tests: rebuild eden consensus cache --- .../eden/rpc-cache/consensus/00000-1.psg | 4 ++-- .../eden/rpc-cache/consensus/00000-1.psg.pmt | Bin 621 -> 621 bytes .../eden/rpc-cache/consensus/db.pmt | Bin 555 -> 555 bytes .../eden/rpc-cache/consensus/index.pmt | Bin 833 -> 664 bytes .../eden/rpc-cache/consensus/main.pix | 4 ++-- .../eden/rpc-cache/consensus/overflow.pix | 4 ++-- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/e2e_regression/eden/rpc-cache/consensus/00000-1.psg b/tests/e2e_regression/eden/rpc-cache/consensus/00000-1.psg index 05c59f44d..c1b0ad74c 100644 --- a/tests/e2e_regression/eden/rpc-cache/consensus/00000-1.psg +++ b/tests/e2e_regression/eden/rpc-cache/consensus/00000-1.psg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d3da7b003e4b570bd54e69387dc1b6b50c47d525ea18fec48219bbd8e54cfe4 -size 40078199 +oid sha256:55b600d4f8e50e27c4384d9a39b64513eeae1e7bbfab2114483c5aa11b6e1742 +size 40312199 diff --git a/tests/e2e_regression/eden/rpc-cache/consensus/00000-1.psg.pmt b/tests/e2e_regression/eden/rpc-cache/consensus/00000-1.psg.pmt index 616fa0a9ad438d90ab4185934f71968fd4a850a9..48c47b3cfd897f93071c353097a8a9c7ed37bf22 100644 GIT binary patch delta 11 ScmaFM@|I;o78A4CMg{;JJp<_g delta 11 ScmaFM@|I;o78A49PX+)SsRShe diff --git a/tests/e2e_regression/eden/rpc-cache/consensus/db.pmt b/tests/e2e_regression/eden/rpc-cache/consensus/db.pmt index f184559eefff6533f6351d1d38fb431970fc0f78..56c40e2d4f72f5242c1e744d8fe8a25b36051c41 100644 GIT binary patch delta 13 UcmZ3@vYKUs8WZc=z1qqQ03cNaGynhq delta 13 UcmZ3@vYKUs8WXG2BcoUb03I6y?*IS* diff --git a/tests/e2e_regression/eden/rpc-cache/consensus/index.pmt b/tests/e2e_regression/eden/rpc-cache/consensus/index.pmt index 633a7bfee087fa80d7e58fe895e57baafdcfa7ed..512392d2f4b11e73f421d2075ab7bdacf599c8e2 100644 GIT binary patch delta 31 ncmX@eHiLCTBa^t;{}x70#(!oT8UHc0G5&93Wc Date: Fri, 15 Mar 2024 15:19:31 -0700 Subject: [PATCH 15/15] add changelog --- .changelog/599.feature.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 .changelog/599.feature.md diff --git a/.changelog/599.feature.md b/.changelog/599.feature.md new file mode 100644 index 000000000..ef1928712 --- /dev/null +++ b/.changelog/599.feature.md @@ -0,0 +1 @@ +consensus: roothash messages