Skip to content

Proposervm API #4029

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions vms/proposervm/block/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,16 @@ type SignedBlock interface {
}

type statelessUnsignedBlock struct {
ParentID ids.ID `serialize:"true"`
Timestamp int64 `serialize:"true"`
PChainHeight uint64 `serialize:"true"`
Certificate []byte `serialize:"true"`
Block []byte `serialize:"true"`
ParentID ids.ID `serialize:"true" json:"parentID"`
Timestamp int64 `serialize:"true" json:"timestamp"`
PChainHeight uint64 `serialize:"true" json:"pChainHeight"`
Certificate []byte `serialize:"true" json:"certificate"`
Block []byte `serialize:"true" json:"block"`
}

type statelessBlock struct {
StatelessBlock statelessUnsignedBlock `serialize:"true"`
Signature []byte `serialize:"true"`
StatelessBlock statelessUnsignedBlock `serialize:"true" json:"statelessBlock"`
Signature []byte `serialize:"true" json:"signature"`

id ids.ID
timestamp time.Time
Expand Down
52 changes: 52 additions & 0 deletions vms/proposervm/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package proposervm

import (
"context"
"fmt"

"github.com/ava-labs/avalanchego/api"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/formatting"
"github.com/ava-labs/avalanchego/utils/rpc"
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add unit tests for this file?


var _ Client = (*client)(nil)

type Client interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

who/what is consuming this interface? Usually in Golang, interfaces are declared where they're used, not where they're implemented.

// GetProposedHeight returns the current height of this node's proposer VM.
GetProposedHeight(ctx context.Context, options ...rpc.Option) (uint64, error)
// GetProposerBlockWrapper returns the ProposerVM block wrapper
GetProposerBlockWrapper(ctx context.Context, proposerID ids.ID, options ...rpc.Option) ([]byte, error)
}

type client struct {
requester rpc.EndpointRequester
}

// NewClient returns a Client for interacting with the ProposerVM API.
// The provided blockchainName should be the blockchainID or an alias (e.g. "P" for the P-Chain).
func NewClient(uri string, blockchainName string) Client {
return &client{
requester: rpc.NewEndpointRequester(uri + fmt.Sprintf("/ext/bc/%s/proposervm", blockchainName)),
}
}

func (c *client) GetProposedHeight(ctx context.Context, options ...rpc.Option) (uint64, error) {
res := &api.GetHeightResponse{}
err := c.requester.SendRequest(ctx, "proposervm.getProposedHeight", struct{}{}, res, options...)
return uint64(res.Height), err
}

func (c *client) GetProposerBlockWrapper(ctx context.Context, proposerID ids.ID, options ...rpc.Option) ([]byte, error) {
res := &api.FormattedBlock{}
if err := c.requester.SendRequest(ctx, "proposervm.getProposerBlockWrapper", &GetProposerBlockArgs{
ProposerID: proposerID,
Encoding: formatting.Hex,
}, res, options...); err != nil {
return nil, err
}
return formatting.Decode(res.Encoding, res.Block)
}
70 changes: 70 additions & 0 deletions vms/proposervm/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add tests to the code in this file?

// See the file LICENSE for licensing terms.
Comment on lines +1 to +2
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lint job did not like including 2025 in the copyright date, nor did I find any instances of 2025 in the codebase, so I assume it is alright to add this file with a terminal date of 2024.


package proposervm

import (
"encoding/json"
"fmt"
"net/http"

"go.uber.org/zap"

"github.com/ava-labs/avalanchego/api"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/formatting"

avajson "github.com/ava-labs/avalanchego/utils/json"
)

type ProposerAPI struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason for having the proposerVM as a field instead of just adding the functions to the proposerVM VM?

If we wish to isolate the VM object then it makes sense to add two functions or (an) interface(s) with:

  1. getLastAcceptedHeight
  2. GetBlock

and then it'll be easier to test.

vm *VM
}

func (p *ProposerAPI) GetProposedHeight(_ *http.Request, _ *struct{}, reply *api.GetHeightResponse) error {
p.vm.ctx.Log.Debug("API called",
zap.String("service", "proposervm"),
zap.String("method", "getProposedHeight"),
)
p.vm.ctx.Lock.Lock()
defer p.vm.ctx.Lock.Unlock()

reply.Height = avajson.Uint64(p.vm.lastAcceptedHeight)
return nil
}

// GetProposerBlockArgs is the parameters supplied to the GetProposerBlockWrapper API
type GetProposerBlockArgs struct {
ProposerID ids.ID `json:"proposerID"`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this type rather than using the existing api.GetBlockArgs to eliminate confusion over what exactly this parameter is. It is the "Proposer ID" listed in the subnets explorer, e.g. https://subnets.avax.network/c-chain/block/64357262, not the block hash/ID as seen by the inner VM.

That said, I'm not sure what the best way to actually source the "Proposer ID" in the general case, since it's not exposed by the innerVM. Similarly, an endpoint to map Block hash -> Proposer ID seems like it would break encapsulation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The word Proposer sounds like the actual proposer node. But if that's the common usage, I think it's fine. Maybe ProposerBlockID etc can make it cleaner. I think the actual word for that is OuterBlockID but sounds even more complicated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The explorer uses Proposer ID/Proposer Node ID to disambiguate, but yes I agree the naming is awkward. Happy to go with whatever the consensus here is.

Encoding formatting.Encoding `json:"encoding"`
}

func (p *ProposerAPI) GetProposerBlockWrapper(r *http.Request, args *GetProposerBlockArgs, reply *api.GetBlockResponse) error {
p.vm.ctx.Log.Debug("API called",
zap.String("service", "proposervm"),
zap.String("method", "getProposerBlockWrapper"),
zap.String("proposerID", args.ProposerID.String()),
zap.String("encoding", args.Encoding.String()),
)
p.vm.ctx.Lock.Lock()
defer p.vm.ctx.Lock.Unlock()

block, err := p.vm.GetBlock(r.Context(), args.ProposerID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

am I reading this wrong and we're actually sending back the entire proposerVM block which also contains the inner VM block? If so, is it possible to only distill the proposerVM bytes?

if err != nil {
return err
}
reply.Encoding = args.Encoding

var result any
if args.Encoding == formatting.JSON {
result = block
} else {
result, err = formatting.Encode(args.Encoding, block.Bytes())
if err != nil {
return fmt.Errorf("couldn't encode block %s as %s: %w", args.ProposerID, args.Encoding, err)
}
}

reply.Block, err = json.Marshal(result)
return err
}
138 changes: 138 additions & 0 deletions vms/proposervm/service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
The ProposerVM API allows clients to fetch information about a Snowman++ chain's ProposerVM.

## Endpoint

```
/ext/bc/{blockchainID}/proposervm
```

## Format

This API uses the `json 2.0` RPC format.

## Methods

### `proposervm.getProposedHeight`

Returns this node's current proposer VM height.

**Signature:**

```
proposervm.getProposedHeight() ->
{
height: int,
}
```

**Example Call:**

```sh
curl -X POST --data '{
"jsonrpc": "2.0",
"method": "proposervm.getProposedHeight",
"params": {},
"id": 1
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/P/proposervm
```

**Example Response:**

```json
{
"jsonrpc": "2.0",
"result": {
"height": "56"
},
"id": 1
}
```

### `proposervm.getProposerBlockWrapper`

Get a block's ProposerVM wrapper by its proposer ID.

**Signature:**

```
proposervm.getProposerBlockWrapper({
proposerID: string
encoding: string // optional
}) -> {
block: string,
encoding: string
}
```

**Request:**

- `proposerID` is the proposer ID. It should be in cb58 format.
- `encoding` is the encoding format to use. Can be either `hex` or `json`. Defaults to `hex`.

#### Hex Example

**Example Call:**

```sh
curl -X POST --data '{
"jsonrpc": "2.0",
"method": "proposervm.getProposerBlockWrapper",
"params": {
"proposerID": "owJxcaDMaehbqoib8FRP7MuPdfGpSdXqD4hjkBtW4vcCJzr2Y",
"encoding": "hex"
},
"id": 1
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/C/proposervm
```

**Example Response:**

```json
{
"jsonrpc":"2.0",
"result":{
"block":"0x000000000000018a9f604b26237c49e54667b80e24433512774dd6bb8cd99032e927ae141dcd0000000068597a6d00000000000000000000053b308205373082031f020900baf3b5c5c6d0d14a300d06092a864886f70d01010b0500307f310b3009060355040613025553310b300906035504080c024e59310f300d06035504070c064974686163613110300e060355040a0c074176616c616273310e300c060355040b0c054765636b6f310c300a06035504030c036176613122302006092a864886f70d01090116137374657068656e406176616c6162732e6f72673020170d3139303730323136313231395a180f33303139303731303136313231395a303a310b3009060355040613025553310b300906035504080c024e593110300e060355040a0c074176616c616273310c300a06035504030c0361766130820222300d06092a864886f70d01010105000382020f003082020a0282020100dd4e847ad276ba36e47d892014332ccf5c934c59541b2f24f9fe2642889dc107861630185fc6925626259770cfb39c382926ec8211e8790e9e9963715eee4e8786de85985a438f09e3a5099d904294834d06f8494f4e9fd4b26b0f2fe240b303ea0595a93014b776c5d036e9cc32d30696b7f94b497a5d7e32ee473f193c5882f79667d26d47f9b628d7abfb08e0047a89e2b3ea9c7077d2c0d83d983dd42cf5024f016f6c36235d3ccd056028ae9e22a34a5c927fe298b3499754e3ddb4c0cc580699df0c07219f17a2f54fefdd06d3ede2942367c6afe84e59321653cd3e55e381b7c6ba3f8b12f40421e7fe86f2ac2602a3b7610f5dc4df368aefc7dfe37e1866bf334af9ac45abcf022e161c0abf241ca4f4419b6c909c19e12fd1b990dea7e0e04cd0ab2067709894765283879670d801b7558216c41ceef7d415a267afc40fde917857fe01a24ef04cee4737403511811562e5f9dcd1a7aca55ef8583dfb130e8173d691436a76fa48f596d5dd9d12e7cebaa85dffbc166b8600ab29ce44fef31ec4145ef0a8b8c0aa8e355862f561947111ef1448a473eed5acf8f461a1bacd52ddfae38024df357ca38f628e17e2c66b3323d89773a31ad3217d5e0abc268b12bd5db8e925a8ee9c0af190f220252d5cb7549c96e462740f42b28808abe1535d0092a8d598221a2bc92d6ba54df8d489f08df68d75e408aa2718aac30203010001300d06092a864886f70d01010b050003820201009039dc03ede448c41bdb40751cfa6d1347311ad7b0427dc9356365b03774d95c319290f544d2eba303c48bdea8bcfc24cb191a88f54de045925ffad24f045fcfd36d4f126c9a1534b886dc5c0f14f1b99bf68c2a3df97854132bd15a2b539a8cb57acab79a4a2d5501de4e642fc02b1024085cb5503f851749c2630fb3507c43139423a06a48e0476bd0ac5c8cd5385bfc16a13dd4f640da7b37d2c29062bae76c7f7d6a97c5e568252d4a323f0c5fd9edc000287b6de11d938e62bef2b08b9264f27afce4a0c760f96a92bd1b77d12197cc6995a34174c3de46e7ea043973b6370b277c289a7aedf7981031e4a82c763750db084636e3e37cc89aeeeaf6ffc30fda69429bb6e9a44346604d80b640f463395e76d94458b75712f2a9ab06712cda1f0cf973bbce04e90e765a9c43321246f583c623751e400bfa1922402b8c72180e44080cc333e8a1af2b7d6b4b229397329c2e5466fff2b18d2c43e8be808af73e9cf9667ece4468e3edd342438c63ae9786deeb021972de57abf93ca0e9ae635d3f7658a0bf317058032d9940da32db4e2434a7af707ed1d9c4a4c484ddde7e4469c372e4c02d08d224ec9c1e2c287dacbacb501b7e8b89f8e2c3fe2d585f5877a69e003c0a81789fa790d29a748b16537a7d23e2e3741b635ff89786325aa839b2d674839d1ffa5b0e9a571a865a28a0ddefe6c0ef81657b9521efed5edc00000373f90370f90298a057b43873f59d6e0ff493909e8373502eaa828f0eb4d7dbe999b855f9b895d88ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940100000000000000000000000000000000000000a0685dcd731e76c2e6a503df5759ab11b324f03c8f26090637ad4dbb26ad5522fba04efbaa764431eba396b9edba52f594a6660473432761931c98b49ef00467b87ea0d95b673818fa493deec414e01e610d97ee287c9421c8eff4102b1647c1a184e4b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010c83e4e1c082a4108468597a6db8560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421843b9aca0080808080a00000000000000000000000000000000000000000000000000000000000000000f8d0f866108534630b8a00825208948db97c7cece249c2b98bdc0226cc4c2a57bf52fc0180820a95a07ef0c6d54c839648ea6cfe191c37efc5ba9d9115695e061870b601c1ce647e75a03e066459cb0853a8d67f7b777753515b2ce627553a4d9b51b06ad6bdf3102b8ef866118534630b8a00825208948db97c7cece249c2b98bdc0226cc4c2a57bf52fc0180820a96a03a1c18eea77d4f08a29e482442ca2f2beb5bc8568e2768470ebdb6ae05382424a06e205ef2e0273c481f2e28a60e504575dba0714608601880d87906a98de41bb3c0808000000200bfcc7483e2c539c2d925a1b4eed65e2cbfae39e284be82d77ee86c078d1a1d4f54d8c46c6695d6619a589137eabcd4a8e93c94394c4d83458b122598b9d69f49f0c9e61b746ad46d91710c3475c97595d17c3ef1aa8c7e846fd8a231bdb68687a208ee752cd64ecf86288bec610688708e34d54ecb15ea4178e1dab8a74109ae1514240a08cddae9dc8ec6db17cfeffc2d0814e908e57676700c6fa3a4bbe1c84e537fd7d2f28491bf9ce3c5e57308027c198a3547ad9a23d365359b0c709c260abf6a04cdc59bcb4a65006c66c9cc2becae49e9275a2f3517260762a7b4c646121583590a25de74a924a66a43d2b1a6f09adfb664e552fc0b1b260594df57795b334c125627e112606d784790f6916dcb610f1e10a424f0ef8244cde7d2f0631e025b28d54f8a24003f3792b420a1eeb62a8a21a27e9eb7e3278b7e14dfc784e736ce2fad220ea9c2d97e98b451fd3a212b6b863a779d55a80ead284e49e0556792663e38316e7a31ac6b1915e96a1dd50924c48ffbb7d46df363baa3c8184457411577aa793cb3a9adef73301ecec8f7561f60a6187f13ed34d2e1756a7959fd5b10a4f753d07dd90022de2a2ea401e2516cd917a2a48c030e2cc0b2dfaa5dec82af18f6772462a5a58a9d128772b45f782c8a1854564158ed75531cc02d2050170de95147a8eb2b8364fb83d74722bee68e1654cf314d629599e3795617ff75c569bd",
"encoding":"hex"
},
"id":1
}
```

#### JSON Example

**Example Call:**

```sh
curl -X POST --data '{
"jsonrpc": "2.0",
"method": "proposervm.getProposerBlockWrapper",
"params": {
"proposerID": "owJxcaDMaehbqoib8FRP7MuPdfGpSdXqD4hjkBtW4vcCJzr2Y",
"encoding": "json"
},
"id": 1
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/C/proposervm
```

**Example Response:**

```json
{
"jsonrpc":"2.0",
"result":{
"block":{
"SignedBlock":{
"block":{
"parentID":"gNms4aTjhR3vLDsTLhNtuWdv6S6xpA6vLjPB53uWSYRxxi3b",
"timestamp":1750694509,
"pChainHeight":0,
"certificate":"MIIFNzCCAx8CCQC687XFxtDRSjANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJVUzELMAkGA1UECAwCTlkxDzANBgNVBAcMBkl0aGFjYTEQMA4GA1UECgwHQXZhbGFiczEOMAwGA1UECwwFR2Vja28xDDAKBgNVBAMMA2F2YTEiMCAGCSqGSIb3DQEJARYTc3RlcGhlbkBhdmFsYWJzLm9yZzAgFw0xOTA3MDIxNjEyMTlaGA8zMDE5MDcxMDE2MTIxOVowOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQKDAdBdmFsYWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDdToR60na6NuR9iSAUMyzPXJNMWVQbLyT5/iZCiJ3BB4YWMBhfxpJWJiWXcM+znDgpJuyCEeh5Dp6ZY3Fe7k6Hht6FmFpDjwnjpQmdkEKUg00G+ElPTp/UsmsPL+JAswPqBZWpMBS3dsXQNunMMtMGlrf5S0l6XX4y7kc/GTxYgveWZ9JtR/m2KNer+wjgBHqJ4rPqnHB30sDYPZg91Cz1Ak8Bb2w2I108zQVgKK6eIqNKXJJ/4pizSZdU4920wMxYBpnfDAchnxei9U/v3QbT7eKUI2fGr+hOWTIWU80+VeOBt8a6P4sS9AQh5/6G8qwmAqO3YQ9dxN82iu/H3+N+GGa/M0r5rEWrzwIuFhwKvyQcpPRBm2yQnBnhL9G5kN6n4OBM0KsgZ3CYlHZSg4eWcNgBt1WCFsQc7vfUFaJnr8QP3pF4V/4Bok7wTO5HN0A1EYEVYuX53NGnrKVe+Fg9+xMOgXPWkUNqdvpI9ZbV3Z0S5866qF3/vBZrhgCrKc5E/vMexBRe8Ki4wKqONVhi9WGUcRHvFEikc+7VrPj0YaG6zVLd+uOAJN81fKOPYo4X4sZrMyPYl3OjGtMhfV4KvCaLEr1duOklqO6cCvGQ8iAlLVy3VJyW5GJ0D0KyiAir4VNdAJKo1ZgiGivJLWulTfjUifCN9o115AiqJxiqwwIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQCQOdwD7eRIxBvbQHUc+m0TRzEa17BCfck1Y2WwN3TZXDGSkPVE0uujA8SL3qi8/CTLGRqI9U3gRZJf+tJPBF/P021PEmyaFTS4htxcDxTxuZv2jCo9+XhUEyvRWitTmoy1esq3mkotVQHeTmQvwCsQJAhctVA/hRdJwmMPs1B8QxOUI6BqSOBHa9CsXIzVOFv8FqE91PZA2ns30sKQYrrnbH99apfF5WglLUoyPwxf2e3AACh7beEdk45ivvKwi5Jk8nr85KDHYPlqkr0bd9Ehl8xplaNBdMPeRufqBDlztjcLJ3womnrt95gQMeSoLHY3UNsIRjbj43zImu7q9v/DD9ppQpu26aRDRmBNgLZA9GM5XnbZRFi3VxLyqasGcSzaHwz5c7vOBOkOdlqcQzISRvWDxiN1HkAL+hkiQCuMchgORAgMwzPooa8rfWtLIpOXMpwuVGb/8rGNLEPovoCK9z6c+WZ+zkRo4+3TQkOMY66Xht7rAhly3ler+Tyg6a5jXT92WKC/MXBYAy2ZQNoy204kNKevcH7R2cSkxITd3n5EacNy5MAtCNIk7JweLCh9rLrLUBt+i4n44sP+LVhfWHemngA8CoF4n6eQ0pp0ixZTen0j4uN0G2Nf+JeGMlqoObLWdIOdH/pbDppXGoZaKKDd7+bA74Fle5Uh7+1e3A==",
"block":"+QNw+QKYoFe0OHP1nW4P9JOQnoNzUC6qgo8OtNfb6Zm4Vfm4ldiMoB3MTejex116q4W1Z7bM1BrTEkUblIp0E/ChQv1A1JNHlAEAAAAAAAAAAAAAAAAAAAAAAAAAoGhdzXMedsLmpQPfV1mrEbMk8DyPJgkGN61NuyatVSL7oE77qnZEMeujlrntulL1lKZmBHNDJ2GTHJi0nvAEZ7h+oNlbZzgY+kk97sQU4B5hDZfuKHyUIcjv9BArFkfBoYTkuQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMg+ThwIKkEIRoWXptuFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAKBW6B8XG8xVpv+DReaSwPhuW0jgG5lsrcABYi+142O0IYQ7msoAgICAgKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPjQ+GYQhTRjC4oAglIIlI25fHzs4knCuYvcAibMTCpXv1L8AYCCCpWgfvDG1UyDlkjqbP4ZHDfvxbqdkRVpXgYYcLYBwc5kfnWgPgZkWcsIU6jWf3t3d1NRWyzmJ1U6TZtRsGrWvfMQK474ZhGFNGMLigCCUgiUjbl8fOziScK5i9wCJsxMKle/UvwBgIIKlqA6HBjup31PCKKeSCRCyi8r61vIVo4naEcOvbauBTgkJKBuIF7y4Cc8SB8uKKYOUEV126BxRghgGIDYeQapjeQbs8CAgA=="},
"signature":"v8x0g+LFOcLZJaG07tZeLL+uOeKEvoLXfuhsB40aHU9U2MRsZpXWYZpYkTfqvNSo6TyUOUxNg0WLEiWYudafSfDJ5ht0atRtkXEMNHXJdZXRfD7xqox+hG/YojG9toaHogjudSzWTs+GKIvsYQaIcI401U7LFepBeOHauKdBCa4VFCQKCM3a6dyOxtsXz+/8LQgU6QjldnZwDG+jpLvhyE5Tf9fS8oSRv5zjxeVzCAJ8GYo1R62aI9NlNZsMcJwmCr9qBM3Fm8tKZQBsZsnMK+yuSeknWi81FyYHYqe0xkYSFYNZCiXedKkkpmpD0rGm8JrftmTlUvwLGyYFlN9XeVszTBJWJ+ESYG14R5D2kW3LYQ8eEKQk8O+CRM3n0vBjHgJbKNVPiiQAPzeStCCh7rYqiiGifp634yeLfhTfx4TnNs4vrSIOqcLZfpi0Uf06IStrhjp3nVWoDq0oTkngVWeSZj44MW56MaxrGRXpah3VCSTEj/u31G3zY7qjyBhEV0EVd6p5PLOpre9zMB7OyPdWH2CmGH8T7TTS4XVqeVn9WxCk91PQfdkAIt4qLqQB4lFs2ReipIwDDizAst+qXeyCrxj2dyRipaWKnRKHcrRfeCyKGFRWQVjtdVMcwC0gUBcN6VFHqOsrg2T7g9dHIr7mjhZUzzFNYpWZ43lWF/8="
}
},
"encoding":"json"
},
"id":1
}
```
22 changes: 22 additions & 0 deletions vms/proposervm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import (
"context"
"errors"
"fmt"
"net/http"
"time"

"github.com/gorilla/rpc/v2"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"

Expand All @@ -24,6 +26,7 @@ import (
"github.com/ava-labs/avalanchego/snow/engine/common"
"github.com/ava-labs/avalanchego/snow/engine/snowman/block"
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/utils/json"
"github.com/ava-labs/avalanchego/utils/math"
"github.com/ava-labs/avalanchego/utils/timer/mockable"
"github.com/ava-labs/avalanchego/utils/units"
Expand Down Expand Up @@ -251,6 +254,25 @@ func (vm *VM) Shutdown(ctx context.Context) error {
return vm.ChainVM.Shutdown(ctx)
}

// overriddes ChainVM.CreateHandlers to expose the proposervm API path
func (vm *VM) CreateHandlers(ctx context.Context) (map[string]http.Handler, error) {
handlers, err := vm.ChainVM.CreateHandlers(ctx)
if err != nil {
return nil, fmt.Errorf("failed to create inner VM handlers: %w", err)
}

server := rpc.NewServer()
server.RegisterCodec(json.NewCodec(), "application/json")
server.RegisterCodec(json.NewCodec(), "application/json;charset=UTF-8")
err = server.RegisterService(&ProposerAPI{vm}, "proposervm")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we have these two also similar to the platformVM?

	server.RegisterInterceptFunc(vm.metrics.InterceptRequest)
	server.RegisterAfterFunc(vm.metrics.AfterRequest)

Any thoughts about this?

if err != nil {
return nil, fmt.Errorf("failed to register proposervm service: %w", err)
}
handlers["/proposervm"] = server

return handlers, nil
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about a unit test that invokes this API?

func (vm *VM) SetState(ctx context.Context, newState snow.State) error {
if err := vm.ChainVM.SetState(ctx, newState); err != nil {
return err
Expand Down