Annotation of the Monitor

This section provides a very well-annotated version of the monitor program of the Hello World BVS. It aims to help developers get familiar with the programming environment. The monitor program simply listens to events on a BVS and prints it to the console.

In pseudo (and Python-like) code, the monitor program acts as the following:

chainio = init_chainio()
indexer = init_indexer(
    starting_block=chainio.latest_block,
    event_filter=["wasm-NewTaskCreated", "wasm-TaskResponded"])

while True:
    event = await indexer.wait_for_new_event()
    print(event)

Here's the annotated version taken from commit c5764b62732516f634c08251258474cb8f4a97db

// The Monitor structure
type Monitor struct {
    bvsContract string       // The address of the BVS contract
    chainIO     io.ChainIO   // ChainIO to enable communication with the BVS
}

// RunMonitor runs the monitor. This is the entery point of the monitor
func RunMonitor() {
    m := NewMonitor()
    m.Run()
}

// NewMonitor creates a new instance of the Monitor struct.
func NewMonitor() *Monitor {
    // Create a new ChainIO instance
    // ChainIO is our main interfaace from a off-chain program to any on-chain contract
    chainIO, err := io.NewChainIO(core.C.Chain.Id, core.C.Chain.Rpc, core.C.Owner.KeyDir, core.C.Owner.Bech32Prefix, elkLogger, metricsIndicators, types.TxManagerParams{
        MaxRetries:             3,
        RetryInterval:          1 * time.Second,
        ConfirmationTimeout:    60 * time.Second,
        GasPriceAdjustmentRate: "1.1",
    })
    if err != nil {
        panic(err)
    }
    // Setup the keyring so we have access to private keys
    client, err := chainIO.SetupKeyring(core.C.Owner.KeyName, core.C.Owner.KeyringBackend)
    if err != nil {
        panic(err)
    }
    
    // Query the BVS directory and find the BVS we are monitoring
    txResp, err := api.NewBVSDirectoryImpl(client, core.C.Chain.BvsDirectory).GetBVSInfo(core.C.Chain.BvsHash)
    if err != nil {
        panic(err)
    }
    
    // Construct and returnthe Monitor structure
    return &Monitor{
        bvsContract: txResp.BVSContract,
        chainIO:     client,
    }
}

// Run runs the event indexer and monitors for new task created and task responded events.
func (m *Monitor) Run() {
    // Create a context (see https://pkg.go.dev/context for detail) that ChainIO and
    // other components will run in.
    ctx := context.Background()
    // Queries the RPC node to reterve the current latest block. The information
    // is later on passed to the EvenIndexer to listen to newly created event
    res, err := m.chainIO.QueryNodeStatus(ctx)
    if err != nil {
        panic(err)
    }
    latestBlock := res.SyncInfo.LatestBlockHeight
    fmt.Println("latestBlock: ", latestBlock)
    // Create the EventIndexer with listening to the BVS for new events
    // of type "wasm-NewTaskCreated" and "wasm-TaskResponded". And with a very 
    // small rate limiter. The last 2 parameters 1, 5 denotes 1 event (in a bucket
    // rate limiter) and 5 events per second.
    evtIndexer := indexer.NewEventIndexer(
        m.chainIO.GetClientCtx(),
        m.bvsContract,
        latestBlock,
        []string{"wasm-NewTaskCreated", "wasm-TaskResponded"},
        1,
        5)
    // start the event indexer
    evtChain, err := evtIndexer.Run(ctx)
    if err != nil {
        panic(err)
    }
    fmt.Println("chain: ", evtChain)
    
    // Now wait for events and print their respective information. Event data
    // is stored in evt.AttrMap while the type can be determined by checking 
    // evt.EventType
    for evt := range evtChain {
        switch evt.EventType {
        // For a NewTaskCreated event, we print the block height, transaction hash
        // the TaskId and it's input value 
        case "wasm-NewTaskCreated":
            blockHeight := evt.BlockHeight
            txnHash := evt.TxHash
            taskId := evt.AttrMap["taskId"]
            taskInput := evt.AttrMap["input"]
            fmt.Printf("[NewTaskCreated] blockHeight: %d, txnHash: %s, taskId: %s, taskInput: %s\n", blockHeight, txnHash, taskId, taskInput)
        // For a TaskResponded event, print block height, transaction hash taskId, result
        // and the operators whom performed the calculations.
        case "wasm-TaskResponded":
            blockHeight := evt.BlockHeight
            txnHash := evt.TxHash
            taskId := evt.AttrMap["taskId"]
            taskResult := evt.AttrMap["result"]
            taskOperators := evt.AttrMap["operators"]
            fmt.Printf("[TaskResponded] blockHeight: %d, txnHash: %s, taskId: %s, taskResult: %s, taskOperators: %s\n", blockHeight, txnHash, taskId, taskResult, taskOperators)
        default:
            // Should not reach here as we covered all event types specsifed to the EventIndexer
            fmt.Printf("Unknown event type. evt: %+v\n", evt)
        }
    }
}

Last updated