good morning!!!!

Skip to content
Commits on Source (5)
package main
import (
"encoding/json"
"fmt"
"os"
"gfx.cafe/open/jrpc/openrpc/generate"
"gfx.cafe/open/jrpc/openrpc/templates"
"gfx.cafe/open/jrpc/openrpc/types"
"github.com/alecthomas/kong"
)
var CLI struct {
Compile CompileCommand `cmd:"" help:"Compile a folder into a single openrpc spec"`
Generate GenerateCommand `cmd:"" help:"Compile a folder into a single openrpc spec"`
Template TemplateCommand `cmd:"" help:"print template to stdout"`
}
type TemplateCommand struct {
}
func (c *TemplateCommand) Run() error {
fmt.Print(templates.TEMPLATE)
return nil
}
type CompileCommand struct {
Methods []string `name:"methods" short:"m" help:"root of method dirs" type:"path"`
Schemas []string `name:"schemas" short:"s" help:"root schema dirs" type:"path"`
Output string `name:"output" short:"o" help:"path to output file"`
}
func (c *CompileCommand) Run() error {
openrpc := types.NewOpenRPCSpec1()
var err error
for _, v := range c.Methods {
err = openrpc.AddMethods(v)
if err != nil {
return err
}
}
for _, v := range c.Schemas {
err = openrpc.AddSchemas(v)
if err != nil {
return err
}
}
jzn, err := json.MarshalIndent(openrpc, "", " ")
if err != nil {
return err
}
err = os.WriteFile(c.Output, jzn, 0644)
if err != nil {
return err
}
return nil
}
type GenerateCommand struct {
Spec string `name:"spec" short:"s" help:"path to jopenrpc spec"`
Output string `name:"output" short:"o" help:"output directory and package"`
Template string `name:"template" short:"t" help:"template to generate with"`
Package string `name:"package" short:"p" default:"api" help:"package name"`
}
func (c *GenerateCommand) Run() error {
if c.Spec == "" {
return fmt.Errorf("spec file is required")
}
openrpc, err := readSpec(c.Spec)
if err != nil {
return err
}
openrpc.Package = c.Package
if err = generate.Generate(openrpc, c.Template, c.Output); err != nil {
return err
}
return nil
}
func readSpec(file string) (out *types.OpenRPC, err error) {
var data []byte
data, err = os.ReadFile(file)
if err != nil {
return
}
out = new(types.OpenRPC)
err = json.Unmarshal(data, out)
return
}
func NewCLI() *kong.Context {
ctx := kong.Parse(&CLI)
return ctx
}
package main
import (
"fmt"
"os"
)
func main() {
ctx := NewCLI()
if err := ctx.Run(); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
package generate
import (
"bytes"
"fmt"
"go/format"
"os"
"path"
"path/filepath"
"text/template"
"gfx.cafe/open/jrpc/openrpc/templates"
"gfx.cafe/open/jrpc/openrpc/types"
"github.com/iancoleman/strcase"
)
var funcs = template.FuncMap{
"list": func(v ...any) []any {
return v
},
"camelCase": func(v string) string {
return strcase.ToCamel(v)
},
"goType": func(v string) string {
switch v {
case "boolean":
return "bool"
case "number":
return "float64"
case "integer":
return "int"
case "string":
return "string"
case "null", "object":
return "struct{}"
default:
panic(fmt.Sprintln("unknown go type:", v))
}
},
"refName": func(v string) string {
return filepath.Base(v)
},
}
func Generate(rpc *types.OpenRPC, ts string, output string) error {
var wr bytes.Buffer
var t *template.Template
var err error
if ts == "default" {
t, err = template.New(path.Base(ts)).Funcs(funcs).Parse(templates.TEMPLATE)
if err != nil {
return err
}
} else {
t, err = template.New(path.Base(ts)).Funcs(funcs).ParseFiles(ts)
if err != nil {
return err
}
}
err = t.Execute(&wr, rpc)
if err != nil {
return err
}
var fmtd []byte
fmtd, err = format.Source(wr.Bytes())
if err != nil {
return err
}
err = os.WriteFile(output, fmtd, 0600)
if err != nil {
return err
}
wr.Reset()
return nil
}
package generate
{{- range (getObjects .Objects) }}
{{ printf "type %s struct {" .Name }}
{{- range (getFields .Fields) }}
{{ maybeFieldComment .Desc }}
{{- if (eq .Name .Type) }}
{{ printf "%s" .Name }}
{{- else }}
{{ printf "%s %s" .Name .Type }}
{{- end }}
{{- end }}
{{ printf "}" }}
{{- end }}
{
"openrpc": "1.0.0",
"info": {
"version": "1.0.0",
"title": "JSON-RPC",
"description": "This is a test OpenRPC spec"
},
"methods": [
{
"name": "service_method",
"description": "returns the method of this service",
"summary": "current service",
"params": [
{
"name": "param1",
"description": "this is a desc",
"schema": {
"title": "param1",
"type": "string",
"pattern": "^0x[a-fA-F\\d]+$"
}
},
{
"name": "param2",
"description": "this is a desc",
"schema": {
"$ref": "#/components/schemas/Param2"
}
},
{
"$ref": "#/components/schemas/Param3"
},
{
"name": "param4",
"description": "this is a desc",
"schema": {
"type": "object",
"title": "param4",
"description": "this is an object",
"properties": {
"foo": {
"description": "this is a foo",
"title": "foo",
"type": "string"
},
"bar": {
"description": "this is a bar",
"title": "bar",
"type": "string"
}
}
}
}
],
"result": {
"name": "data",
"description": "the requested data",
"schema": {
"title": "data",
"type": "string"
}
}
}
],
"components": {
"schemas": {
"Param2": {
"title": "param2",
"type": "string"
},
"Param3": {
"title": "param3",
"type": "string"
}
}
}
}
- name: debug_getRawHeader
summary: Returns an RLP-encoded header.
params:
- name: Block
required: true
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
result:
name: Header RLP
schema:
$ref: '#/components/schemas/bytes'
- name: debug_getRawBlock
summary: Returns an RLP-encoded block.
params:
- name: Block
required: true
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
result:
name: Block RLP
schema:
$ref: '#/components/schemas/bytes'
- name: debug_getRawTransaction
summary: Returns an array of EIP-2718 binary-encoded transactions.
params:
- name: Transaction hash
required: true
schema:
$ref: '#/components/schemas/hash32'
result:
name: EIP-2718 binary-encoded transaction
schema:
$ref: '#/components/schemas/bytes'
- name: debug_getRawReceipts
summary: Returns an array of EIP-2718 binary-encoded receipts.
params:
- name: Block
required: true
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
result:
name: Receipts
schema:
title: Receipt array
type: array
items:
$ref: '#/components/schemas/bytes'
- name: debug_getBadBlocks
summary: Returns an array of recent bad blocks that the client has seen on the network.
params: []
result:
name: Blocks
schema:
title: Bad block array
type: array
items:
$ref: '#/components/schemas/BadBlock'
# Engine JSON-RPC API
The Engine JSON-RPC API is a collection of methods that all execution clients implement.
This interface allows the communication between consensus and execution layers of the two-component post-Merge Ethereum Client.
This API is in *active development* and currently [specified as a markdown document](./specification.md).
A schema will follow once the specification stabilizes.
# Authentication
The `engine` JSON-RPC interface, exposed by execution layer clients and consumed by consensus layer clients, needs to be authenticated. The authentication scheme chosen for this purpose is [JWT](https://jwt.io/).
The type of attacks that this authentication scheme attempts to protect against are the following:
- RPC port exposed towards the internet, allowing attackers to exchange messages with execution layer engine API.
- RPC port exposed towards the browser, allowing malicious webpages to submit messages to the execution layer engine API.
The authentication scheme is _not_ designed to
- Prevent attackers with capability to read ('sniff') network traffic from reading the traffic,
- Prevent attackers with capability to read ('sniff') network traffic from performing replay-attacks of earlier messages.
Authentication is performed as follows:
- For `HTTP` dialogue, each `jsonrpc` request is individually authenticated by supplying `JWT` token in the HTTP header.
- For a WebSocket dialogue, only the initial handshake is authenticated, after which the message dialogue proceeds without further use of JWT.
- Clarification: The websocket handshake starts with the consensus layer client performing a websocket upgrade request. This is a regular http GET request, and the actual
parameters for the WS-handshake are carried in the http headers.
- For `inproc`, a.k.a raw ipc communication, no authentication is required, under the assumption that a process able to access `ipc` channels for the process, which usually means local file access, is already sufficiently permissioned that further authentication requirements do not add security.
## JWT specifications
- The execution layer client **MUST** expose the authenticated Engine API at a port independent from existing JSON-RPC API.
- The default port for the authenticated Engine API is `8551`. The Engine API is exposed under the `engine` namespace.
- The execution layer client **MUST** support at least the following `alg` `HMAC + SHA256` (`HS256`)
- The execution layer client **MUST** reject the `alg` `none`.
The HMAC algorithm implies that several consensus layer clients will be able to use the same key, and from an authentication perspective, be able to impersonate each other. From a deployment perspective, it means that an EL does not need to be provisioned with individual keys for each consensus layer client.
## Key distribution
The execution layer and consensus layer clients **SHOULD** accept a configuration parameter: `jwt-secret`, which designates a file containing the hex-encoded 256 bit secret key to be used for verifying/generating JWT tokens.
If such a parameter is not given, the client **SHOULD** generate such a token, valid for the duration of the execution, and **SHOULD** store the hex-encoded secret as a `jwt.hex` file on the filesystem. This file can then be used to provision the counterpart client.
If such a parameter _is_ given, but the file cannot be read, or does not contain a hex-encoded key of `256` bits, the client **SHOULD** treat this as an error: either abort the startup, or show error and continue without exposing the authenticated port.
## JWT Claims
This specification utilizes the following list of JWT claims:
- Required: `iat` (issued-at) claim. The execution layer client **SHOULD** only accept `iat` timestamps which are within +-60 seconds from the current time.
- Optional: `id` claim. The consensus layer client **MAY** use this to communicate a unique identifier for the individual consensus layer client.
- Optional: `clv` claim. The consensus layer client **MAY** use this to communicate the consensus layer client type/version.
Other claims **MAY** be included in the JWT payload. If the execution layer client sees claims it does not recognize, these **MUST** be ignored.
## Examples
Todo, add some examples of JWT authentication here.
This diff is collapsed.
- name: eth_getBlockByHash
summary: Returns information about a block by hash.
params:
- name: Block hash
required: true
schema:
$ref: '#/components/schemas/hash32'
- name: Hydrated transactions
required: true
schema:
title: hydrated
type: boolean
result:
name: Block information
schema:
$ref: '#/components/schemas/Block'
- name: eth_getBlockByNumber
summary: Returns information about a block by number.
params:
- name: Block
required: true
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
- name: Hydrated transactions
required: true
schema:
title: hydrated
type: boolean
result:
name: Block information
schema:
$ref: '#/components/schemas/Block'
- name: eth_getBlockTransactionCountByHash
summary: Returns the number of transactions in a block from a block matching the given block hash.
params:
- name: Block hash
schema:
$ref: '#/components/schemas/hash32'
result:
name: Transaction count
schema:
$ref: '#/components/schemas/uint'
- name: eth_getBlockTransactionCountByNumber
summary: Returns the number of transactions in a block matching the given block number.
params:
- name: Block
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
result:
name: Transaction count
schema:
$ref: '#/components/schemas/uint'
- name: eth_getUncleCountByBlockHash
summary: Returns the number of uncles in a block from a block matching the given block hash.
params:
- name: Block hash
schema:
$ref: '#/components/schemas/hash32'
result:
name: Uncle count
schema:
$ref: '#/components/schemas/uint'
- name: eth_getUncleCountByBlockNumber
summary: Returns the number of transactions in a block matching the given block number.
params:
- name: Block
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
result:
name: Uncle count
schema:
$ref: '#/components/schemas/uint'
- name: eth_chainId
summary: Returns the chain ID of the current network.
params: []
result:
name: Chain ID
schema:
$ref: '#/components/schemas/uint'
- name: eth_syncing
summary: Returns an object with data about the sync status or false.
params: []
result:
name: Syncing status
schema:
$ref: '#/components/schemas/SyncingStatus'
- name: eth_coinbase
summary: Returns the client coinbase address.
params: []
result:
name: Coinbase address
schema:
$ref: '#/components/schemas/address'
- name: eth_accounts
summary: Returns a list of addresses owned by client.
params: []
result:
name: Accounts
schema:
title: Accounts
type: array
items:
$ref: '#/components/schemas/address'
- name: eth_blockNumber
summary: Returns the number of most recent block.
params: []
result:
name: Block number
schema:
$ref: '#/components/schemas/uint'
- name: eth_call
summary: Executes a new message call immediately without creating a transaction on the block chain.
params:
- name: Transaction
required: true
schema:
$ref: '#/components/schemas/GenericTransaction'
- name: Block
required: false
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
result:
name: Return data
schema:
$ref: '#/components/schemas/bytes'
- name: eth_estimateGas
summary: Generates and returns an estimate of how much gas is necessary to allow the transaction to complete.
params:
- name: Transaction
required: true
schema:
$ref: '#/components/schemas/GenericTransaction'
- name: Block
required: false
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
result:
name: Gas used
schema:
$ref: '#/components/schemas/uint'
- name: eth_createAccessList
summary: Generates an access list for a transaction.
params:
- name: Transaction
required: true
schema:
$ref: '#/components/schemas/GenericTransaction'
- name: Block
required: false
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
result:
name: Gas used
schema:
title: Access list result
type: object
properties:
accessList:
title: "accessList"
$ref: '#/components/schemas/AccessList'
error:
title: "error"
type: string
gasUsed:
title: Gas used
$ref: '#/components/schemas/uint'
- name: eth_gasPrice
summary: Returns the current price per gas in wei.
params: []
result:
name: Gas price
schema:
title: Gas price
$ref: '#/components/schemas/uint'
- name: eth_maxPriorityFeePerGas
summary: Returns the current maxPriorityFeePerGas per gas in wei.
params: []
result:
name: Max priority fee per gas
schema:
title: Max priority fee per gas
$ref: '#/components/schemas/uint'
- name: eth_feeHistory
summary: Transaction fee history
description: Returns transaction base fee per gas and effective priority fee per gas for the requested/supported block range.
params:
- name: blockCount
description: Requested range of blocks. Clients will return less than the requested range if not all blocks are available.
required: true
schema:
$ref: '#/components/schemas/uint'
- name: newestBlock
description: Highest block of the requested range.
required: true
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
- name: rewardPercentiles
description: A monotonically increasing list of percentile values. For each block in the requested range, the transactions will be sorted in ascending order by effective tip per gas and the coresponding effective tip for the percentile will be determined, accounting for gas consumed.
required: true
schema:
title: rewardPercentiles
type: array
items:
title: rewardPercentile
description: Floating point value between 0 and 100.
type: number
result:
name: feeHistoryResult
description: Fee history for the returned block range. This can be a subsection of the requested range if not all blocks are available.
schema:
title: feeHistoryResults
description: Fee history results.
type: object
required:
- oldestBlock
- baseFeePerGas
- gasUsedRatio
properties:
oldestBlock:
title: oldestBlock
description: Lowest number block of returned range.
$ref: '#/components/schemas/uint'
baseFeePerGas:
title: baseFeePerGasArray
description: An array of block base fees per gas. This includes the next block after the newest of the returned range, because this value can be derived from the newest block. Zeroes are returned for pre-EIP-1559 blocks.
type: array
items:
$ref: '#/components/schemas/uint'
reward:
title: rewardArray
description: A two-dimensional array of effective priority fees per gas at the requested block percentiles.
type: array
items:
title: rewardPercentile
description: An array of effective priority fee per gas data points from a single block. All zeroes are returned if the block is empty.
type: array
items:
title: rewardPercentile
description: A given percentile sample of effective priority fees per gas from a single block in ascending order, weighted by gas used. Zeroes are returned if the block is empty.
$ref: '#/components/schemas/uint'
- name: eth_newFilter
summary: Creates a filter object, based on filter options, to notify when the state changes (logs).
params:
- name: Filter
schema:
$ref: '#/components/schemas/Filter'
result:
name: Filter Identifier
schema:
$ref: '#/components/schemas/uint'
- name: eth_newBlockFilter
summary: Creates a filter in the node, to notify when a new block arrives.
params: []
result:
name: Filter Identifier
schema:
$ref: '#/components/schemas/uint'
- name: eth_newPendingTransactionFilter
summary: Creates a filter in the node, to notify when new pending transactions arrive.
params: []
result:
name: Filter Identifier
schema:
$ref: '#/components/schemas/uint'
- name: eth_uninstallFilter
summary: Uninstalls a filter with given id.
params:
- name: Filter Identifier
schema:
$ref: '#/components/schemas/uint'
result:
name: Success
schema:
type: boolean
- name: eth_getFilterChanges
summary: Polling method for a filter, which returns an array of logs which occurred since last poll.
params:
- name: Filter Identifier
schema:
$ref: '#/components/schemas/uint'
result:
name: Log objects
schema:
$ref: '#/components/schemas/FilterResults'
- name: eth_getFilterLogs
summary: Returns an array of all logs matching filter with given id.
params:
- name: Filter Identifier
schema:
$ref: '#/components/schemas/uint'
result:
name: Log objects
schema:
$ref: '#/components/schemas/FilterResults'
- name: eth_getLogs
summary: Returns an array of all logs matching filter with given id.
params:
- name: Filter
schema:
$ref: '#/components/schemas/Filter'
result:
name: Log objects
schema:
$ref: '#/components/schemas/FilterResults'
- name: eth_mining
summary: Returns whether the client is actively mining new blocks.
params: []
result:
name: Mining status
schema:
title: miningStatus
type: boolean
- name: eth_hashrate
summary: Returns the number of hashes per second that the node is mining with.
params: []
result:
name: Mining status
schema:
title: Hashrate
$ref: '#/components/schemas/uint'
- name: eth_getWork
summary: Returns the hash of the current block, the seedHash, and the boundary condition to be met (“target”).
params: []
result:
name: Current work
schema:
type: array
items:
- title: Proof-of-work hash
$ref: '#/components/schemas/bytes32'
- title: seed hash
$ref: '#/components/schemas/bytes32'
- title: difficulty
$ref: '#/components/schemas/bytes32'
- name: eth_submitWork
summary: Used for submitting a proof-of-work solution.
params:
- name: nonce
required: true
schema:
$ref: '#/components/schemas/bytes8'
- name: hash
required: true
schema:
$ref: '#/components/schemas/bytes32'
- name: digest
required: true
schema:
$ref: '#/components/schemas/bytes32'
result:
name: Success
schema:
type: boolean
- name: eth_submitHashrate
summary: Used for submitting mining hashrate.
params:
- name: Hashrate
required: true
schema:
$ref: '#/components/schemas/bytes32'
- name: ID
required: true
schema:
$ref: '#/components/schemas/bytes32'
result:
name: Success
schema:
type: boolean
- name: eth_sign
summary: Returns an EIP-191 signature over the provided data.
params:
- name: Address
required: true
schema:
$ref: '#/components/schemas/address'
- name: Message
required: true
schema:
$ref: '#/components/schemas/bytes'
result:
name: Signature
schema:
$ref: '#/components/schemas/bytes65'
- name: eth_signTransaction
summary: Returns an RLP encoded transaction signed by the specified account.
params:
- name: Transaction
required: true
schema:
$ref: '#/components/schemas/GenericTransaction'
result:
name: Encoded transaction
schema:
$ref: '#/components/schemas/bytes'
- name: eth_getBalance
summary: Returns the balance of the account of given address.
params:
- name: Address
required: true
schema:
$ref: '#/components/schemas/address'
- name: Block
required: false
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
result:
name: Balance
schema:
$ref: '#/components/schemas/uint'
- name: eth_getStorageAt
summary: Returns the value from a storage position at a given address.
params:
- name: Address
required: true
schema:
$ref: '#/components/schemas/address'
- name: Storage slot
required: true
schema:
$ref: '#/components/schemas/uint256'
- name: Block
required: false
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
result:
name: Value
schema:
$ref: '#/components/schemas/bytes'
- name: eth_getTransactionCount
summary: Returns the number of transactions sent from an address.
params:
- name: Address
required: true
schema:
$ref: '#/components/schemas/address'
- name: Block
required: false
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
result:
name: Transaction count
schema:
$ref: '#/components/schemas/uint'
- name: eth_getCode
summary: Returns code at a given address.
params:
- name: Address
required: true
schema:
$ref: '#/components/schemas/address'
- name: Block
required: false
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
result:
name: Bytecode
schema:
$ref: '#/components/schemas/bytes'
- name: eth_getProof
summary: Returns the merkle proof for a given account and optionally some storage keys.
params:
- name: Address
required: true
schema:
$ref: '#/components/schemas/address'
- name: StorageKeys
required: true
schema:
title: Storage keys
type: array
items:
$ref: '#/components/schemas/hash32'
- name: Block
required: true
schema:
$ref: '#/components/schemas/BlockNumberOrTag'
result:
name: Account
schema:
$ref: '#/components/schemas/AccountProof'
- name: eth_sendTransaction
summary: Signs and submits a transaction.
params:
- name: Transaction
required: true
schema:
$ref: '#/components/schemas/GenericTransaction'
result:
name: Transaction hash
schema:
$ref: '#/components/schemas/hash32'
- name: eth_sendRawTransaction
summary: Submits a raw transaction.
params:
- name: Transaction
required: true
schema:
$ref: '#/components/schemas/bytes'
result:
name: Transaction hash
schema:
$ref: '#/components/schemas/hash32'