Skip to content

Commit

Permalink
Merge pull request #616 from oasisprotocol/pro-wh/feature/roothash3
Browse files Browse the repository at this point in the history
 api: add roothash messages
  • Loading branch information
pro-wh authored Apr 18, 2024
2 parents 8024fbd + c79a2c2 commit 7b6d93f
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 8 deletions.
1 change: 1 addition & 0 deletions .changelog/616.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
api: add roothash messages
16 changes: 8 additions & 8 deletions analyzer/consensus/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

type MessageData struct {
messageType string
messageType apiTypes.RoothashMessageType
body json.RawMessage
addressPreimages map[apiTypes.Address]*addresses.PreimageData
relatedAddresses map[apiTypes.Address]struct{}
Expand All @@ -25,7 +25,7 @@ func extractMessageData(logger *log.Logger, m message.Message) MessageData {
case m.Staking != nil:
switch {
case m.Staking.Transfer != nil:
messageData.messageType = "staking.transfer"
messageData.messageType = apiTypes.RoothashMessageTypeStakingTransfer
body, err := json.Marshal(m.Staking.Transfer)
if err != nil {
logger.Info("marshal message body failed",
Expand All @@ -43,7 +43,7 @@ func extractMessageData(logger *log.Logger, m message.Message) MessageData {
)
}
case m.Staking.Withdraw != nil:
messageData.messageType = "staking.withdraw"
messageData.messageType = apiTypes.RoothashMessageTypeStakingWithdraw
body, err := json.Marshal(m.Staking.Withdraw)
if err != nil {
logger.Info("marshal message body failed",
Expand All @@ -61,7 +61,7 @@ func extractMessageData(logger *log.Logger, m message.Message) MessageData {
)
}
case m.Staking.AddEscrow != nil:
messageData.messageType = "staking.add_escrow"
messageData.messageType = apiTypes.RoothashMessageTypeStakingAddEscrow
body, err := json.Marshal(m.Staking.AddEscrow)
if err != nil {
logger.Info("marshal message body failed",
Expand All @@ -79,7 +79,7 @@ func extractMessageData(logger *log.Logger, m message.Message) MessageData {
)
}
case m.Staking.ReclaimEscrow != nil:
messageData.messageType = "staking.reclaim_escrow"
messageData.messageType = apiTypes.RoothashMessageTypeStakingReclaimEscrow
body, err := json.Marshal(m.Staking.ReclaimEscrow)
if err != nil {
logger.Info("marshal message body failed",
Expand All @@ -104,7 +104,7 @@ func extractMessageData(logger *log.Logger, m message.Message) MessageData {
case m.Registry != nil:
switch {
case m.Registry.UpdateRuntime != nil:
messageData.messageType = "registry.update_runtime"
messageData.messageType = apiTypes.RoothashMessageTypeRegistryUpdateRuntime
body, err := json.Marshal(m.Registry.UpdateRuntime)
if err != nil {
logger.Info("marshal message body failed",
Expand All @@ -118,7 +118,7 @@ func extractMessageData(logger *log.Logger, m message.Message) MessageData {
case m.Governance != nil:
switch {
case m.Governance.CastVote != nil:
messageData.messageType = "governance.cast_vote"
messageData.messageType = apiTypes.RoothashMessageTypeGovernanceCastVote
body, err := json.Marshal(m.Governance.CastVote)
if err != nil {
logger.Info("marshal message body failed",
Expand All @@ -129,7 +129,7 @@ func extractMessageData(logger *log.Logger, m message.Message) MessageData {
}
messageData.body = body
case m.Governance.SubmitProposal != nil:
messageData.messageType = "governance.submit_proposal"
messageData.messageType = apiTypes.RoothashMessageTypeGovernanceSubmitProposal
body, err := json.Marshal(m.Governance.SubmitProposal)
if err != nil {
logger.Info("marshal message body failed",
Expand Down
129 changes: 129 additions & 0 deletions api/spec/v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ x-examples:
- &epoch_2 8048966
event-type:
- &event_type_1 'staking.escrow.take'
roothash-message-type:
- &roothash_message_type_1 'staking.transfer'
entity-id:
- &entity_id_1 'gb8SHLeDc69Elk7OTfqhtVgE2sqxrBCDQI84xKR+Bjg='
node-id:
Expand Down Expand Up @@ -355,6 +357,42 @@ paths:
$ref: '#/components/schemas/ConsensusEventList'
<<: *common_error_responses

/consensus/roothash_messages:
get:
parameters:
- *limit
- *offset
- in: query
name: runtime
# There's only an index on (runtime, round) for now. Feel free to
# index on consensus height and make this parameter optional if we
# want to get recent messages from any indexed runtime.
required: true
schema:
allOf: [$ref: '#/components/schemas/Runtime']
- in: query
name: round
schema:
type: integer
format: int64
- in: query
name: type
schema:
allOf: [$ref: '#/components/schemas/RoothashMessageType']
- in: query
name: rel
schema:
allOf: [$ref: '#/components/schemas/StakingAddress']
responses:
'200':
description: |
A JSON object containing a list of roothash messages.
content:
application/json:
schema:
$ref: '#/components/schemas/RoothashMessageList'
<<: *common_error_responses

/consensus/entities:
get:
summary: Returns a list of entities registered at the consensus layer.
Expand Down Expand Up @@ -1638,6 +1676,97 @@ components:
description: |
An event emitted by the consensus layer.
RoothashMessageType:
type: string
enum:
- staking.transfer
- staking.withdraw
- staking.add_escrow
- staking.reclaim_escrow
- registry.update_runtime
- governance.cast_vote
- governance.submit_proposal
example: *roothash_message_type_1

RoothashMessageList:
allOf:
- $ref: '#/components/schemas/List'
- type: object
required: [roothash_messages]
properties:
roothash_messages:
type: array
items:
allOf: [$ref: '#/components/schemas/RoothashMessage']

RoothashMessage:
type: object
required: [runtime, round, index]
properties:
runtime:
type: string
description: |
The runtime that sent this message.
example: sapphire
round:
type: integer
format: int64
description: |
The block round when the runtime sent this message.
example: 1357490
index:
type: integer
format: int32
description: |
The 0-based index of this message in the block.
example: 0
type:
allOf: [$ref: '#/components/schemas/RoothashMessageType']
description: |
The type of thies message.
example: staking.withdraw
body:
type: object
description: |
The "body" of a message is a structure within the
`github.com/oasisprotocol/oasis-core/go/roothash/api/message`
`Message` structure
(https://pkg.go.dev/github.com/oasisprotocol/oasis-core/go/roothash/api/message#Message).
For example, if the type is `staking.withdraw`, the body is the Go
`Message` structure's `.Staking.Withdraw` field, which is a
`github.com/oasisprotocol/oasis-core/go/staking/api` `Withdraw`
structure
(https://pkg.go.dev/github.com/oasisprotocol/oasis-core/go/staking/api#Withdraw),
with `from` and `amount` fields in JSON.
example: {"from": "oasis1qqzjq6lqjf8d07ehhvu5ytc47dck8w7a6qgn7efh", "amount": "500000000"}
error_module:
type: string
description: |
If executing this message resulted in an error, this is the
error's module.
example: staking
error_code:
type: integer
format: int32
description: |
If executing this message resulted in an error, this is the
error's code.
example: 3
result:
description: |
A result value that consensus provided after executing this
message. These aren't centrally registered anywhere, so look at
the consensus apps' `ExecuteMessage`
(https://pkg.go.dev/github.com/oasisprotocol/oasis-core/go/consensus/cometbft/api#MessageSubscriber)
implementations to see what they return. For example, a
`staking.withdraw` type message gives a
`github.com/oasisprotocol/oasis-core/go/staking/api`
`WithdrawResult` structure as its result
(`https://pkg.go.dev/github.com/oasisprotocol/oasis-core/go/staking/api#WithdrawResult`)
with `owner`, `beneficiary`, `allowance`, and `amount_change`
fields.
example: {"owner":"oasis1qqzjq6lqjf8d07ehhvu5ytc47dck8w7a6qgn7efh","beneficiary":"oasis1qrd3mnzhhgst26hsp96uf45yhq6zlax0cuzdgcfc","allowance":"97000000000","amount_change":"500000000"}

EntityList:
allOf:
- $ref: '#/components/schemas/List'
Expand Down
8 changes: 8 additions & 0 deletions api/v1/strict_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ func (srv *StrictServerImpl) GetConsensusBlocksHeight(ctx context.Context, reque
return apiTypes.GetConsensusBlocksHeight200JSONResponse(*block), nil
}

func (srv *StrictServerImpl) GetConsensusRoothashMessages(ctx context.Context, request apiTypes.GetConsensusRoothashMessagesRequestObject) (apiTypes.GetConsensusRoothashMessagesResponseObject, error) {
roothashMessages, err := srv.dbClient.RoothashMessages(ctx, request.Params)
if err != nil {
return nil, err
}
return apiTypes.GetConsensusRoothashMessages200JSONResponse(*roothashMessages), nil
}

func (srv *StrictServerImpl) GetConsensusEntities(ctx context.Context, request apiTypes.GetConsensusEntitiesRequestObject) (apiTypes.GetConsensusEntitiesResponseObject, error) {
entities, err := srv.dbClient.Entities(ctx, request.Params)
if err != nil {
Expand Down
50 changes: 50 additions & 0 deletions storage/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,56 @@ func (c *StorageClient) Events(ctx context.Context, p apiTypes.GetConsensusEvent
return &es, nil
}

func (c *StorageClient) RoothashMessages(ctx context.Context, p apiTypes.GetConsensusRoothashMessagesParams) (*apiTypes.RoothashMessageList, error) {
res, err := c.withTotalCount(
ctx,
queries.RoothashMessages,
p.Runtime,
p.Round,
p.Type,
p.Rel,
p.Limit,
p.Offset,
)
if err != nil {
return nil, wrapError(err)
}
defer res.rows.Close()

ms := RoothashMessageList{
RoothashMessages: []RoothashMessage{},
TotalCount: res.totalCount,
IsTotalCountClipped: res.isTotalCountClipped,
}

for res.rows.Next() {
var m RoothashMessage
var resultCBOR *[]byte
if err := res.rows.Scan(
&m.Runtime,
&m.Round,
&m.Index,
&m.Type,
&m.Body,
&m.ErrorModule,
&m.ErrorCode,
&resultCBOR,
); err != nil {
return nil, wrapError(err)
}
if resultCBOR != nil && m.Type != nil {
result, err := extractMessageResult(*resultCBOR, *m.Type)
if err != nil {
return nil, wrapError(err)
}
m.Result = &result
}
ms.RoothashMessages = append(ms.RoothashMessages, m)
}

return &ms, nil
}

// Entities returns a list of registered entities.
func (c *StorageClient) Entities(ctx context.Context, p apiTypes.GetConsensusEntitiesParams) (*EntityList, error) {
res, err := c.withTotalCount(
Expand Down
62 changes: 62 additions & 0 deletions storage/client/messages.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package client

import (
"fmt"

"github.com/oasisprotocol/oasis-core/go/common/cbor"

apiTypes "github.com/oasisprotocol/nexus/api/v1/types"
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"
)

func extractMessageResult(resultRaw cbor.RawMessage, messageType apiTypes.RoothashMessageType) (interface{}, error) {
switch messageType {
case "staking.transfer":
var result staking.TransferResult
if err := cbor.Unmarshal(resultRaw, &result); err != nil {
return nil, fmt.Errorf("CBOR unmarshal: %w", err)
}
return result, nil
case "staking.withdraw":
var result staking.WithdrawResult
if err := cbor.Unmarshal(resultRaw, &result); err != nil {
return nil, fmt.Errorf("CBOR unmarshal: %w", err)
}
return result, nil
case "staking.add_escrow":
var result staking.AddEscrowResult
if err := cbor.Unmarshal(resultRaw, &result); err != nil {
return nil, fmt.Errorf("CBOR unmarshal: %w", err)
}
return result, nil
case "staking.reclaim_escrow":
var result staking.ReclaimEscrowResult
if err := cbor.Unmarshal(resultRaw, &result); err != nil {
return nil, fmt.Errorf("CBOR unmarshal: %w", err)
}
return result, nil
case "registry.update_runtime":
var result registry.Runtime
if err := cbor.Unmarshal(resultRaw, &result); err != nil {
return nil, fmt.Errorf("CBOR unmarshal: %w", err)
}
return result, nil
case "governance.cast_vote":
// result is always nil, but still unmarshal so we can detect changes
var result *struct{}
if err := cbor.Unmarshal(resultRaw, &result); err != nil {
return nil, fmt.Errorf("CBOR unmarshal: %w", err)
}
return result, nil
case "governance.submit_proposal":
var result governance.Proposal
if err := cbor.Unmarshal(resultRaw, &result); err != nil {
return nil, fmt.Errorf("CBOR unmarshal: %w", err)
}
return result, nil
default:
return nil, fmt.Errorf("unhandled message type %s", messageType)
}
}
20 changes: 20 additions & 0 deletions storage/client/queries/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,26 @@ const (
LIMIT $6::bigint
OFFSET $7::bigint`

RoothashMessages = `
SELECT
runtime,
round,
message_index,
type,
body,
error_module,
error_code,
result
FROM chain.roothash_messages
WHERE
($1::runtime IS NULL OR runtime = $1) AND
($2::bigint IS NULL OR round = $2) AND
($3::text IS NULL OR type = $3) AND
($4::oasis_addr IS NULL OR related_accounts @> ARRAY[$4])
ORDER BY round DESC, message_index DESC
LIMIT $5
OFFSET $6`

Entities = `
SELECT id, address
FROM chain.entities
Expand Down
4 changes: 4 additions & 0 deletions storage/client/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ type EventList = api.ConsensusEventList
// Event is a consensus event.
type Event = api.ConsensusEvent

type RoothashMessageList = api.RoothashMessageList

type RoothashMessage = api.RoothashMessage

// EntityList is the storage response for ListEntities.
type EntityList = api.EntityList

Expand Down
Loading

0 comments on commit 7b6d93f

Please sign in to comment.