Skip to content

Commit

Permalink
go/consensus: Add global minimum gas price
Browse files Browse the repository at this point in the history
  • Loading branch information
kostko committed Apr 25, 2024
1 parent 6230ae8 commit 7e5932d
Show file tree
Hide file tree
Showing 19 changed files with 303 additions and 40 deletions.
1 change: 1 addition & 0 deletions .changelog/5657.breaking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go/consensus: Add global minimum gas price
4 changes: 4 additions & 0 deletions go/consensus/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/oasisprotocol/oasis-core/go/common/keyformat"
"github.com/oasisprotocol/oasis-core/go/common/node"
"github.com/oasisprotocol/oasis-core/go/common/pubsub"
"github.com/oasisprotocol/oasis-core/go/common/quantity"
"github.com/oasisprotocol/oasis-core/go/common/service"
"github.com/oasisprotocol/oasis-core/go/common/version"
"github.com/oasisprotocol/oasis-core/go/consensus/api/transaction"
Expand Down Expand Up @@ -129,6 +130,9 @@ type ClientBackend interface {
// EstimateGas calculates the amount of gas required to execute the given transaction.
EstimateGas(ctx context.Context, req *EstimateGasRequest) (transaction.Gas, error)

// MinGasPrice returns the minimum gas price.
MinGasPrice(ctx context.Context) (*quantity.Quantity, error)

// GetBlock returns a consensus block at a specific height.
GetBlock(ctx context.Context, height int64) (*Block, error)

Expand Down
34 changes: 34 additions & 0 deletions go/consensus/api/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
beacon "github.com/oasisprotocol/oasis-core/go/beacon/api"
cmnGrpc "github.com/oasisprotocol/oasis-core/go/common/grpc"
"github.com/oasisprotocol/oasis-core/go/common/pubsub"
"github.com/oasisprotocol/oasis-core/go/common/quantity"
"github.com/oasisprotocol/oasis-core/go/consensus/api/transaction"
genesis "github.com/oasisprotocol/oasis-core/go/genesis/api"
governance "github.com/oasisprotocol/oasis-core/go/governance/api"
Expand All @@ -32,6 +33,8 @@ var (
methodStateToGenesis = serviceName.NewMethod("StateToGenesis", int64(0))
// methodEstimateGas is the EstimateGas method.
methodEstimateGas = serviceName.NewMethod("EstimateGas", &EstimateGasRequest{})
// methodMinGasPrice is the MinGasPrice method.
methodMinGasPrice = serviceName.NewMethod("MinGasPrice", nil)
// methodGetSignerNonce is a GetSignerNonce method.
methodGetSignerNonce = serviceName.NewMethod("GetSignerNonce", &GetSignerNonceRequest{})
// methodGetBlock is the GetBlock method.
Expand Down Expand Up @@ -93,6 +96,10 @@ var (
MethodName: methodEstimateGas.ShortName(),
Handler: handlerEstimateGas,
},
{
MethodName: methodMinGasPrice.ShortName(),
Handler: handlerMinGasPrice,
},
{
MethodName: methodGetSignerNonce.ShortName(),
Handler: handlerGetSignerNonce,
Expand Down Expand Up @@ -283,6 +290,25 @@ func handlerEstimateGas(
return interceptor(ctx, rq, info, handler)
}

func handlerMinGasPrice(
srv interface{},
ctx context.Context,
_ func(interface{}) error,
interceptor grpc.UnaryServerInterceptor,
) (interface{}, error) {
if interceptor == nil {
return srv.(ClientBackend).MinGasPrice(ctx)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: methodMinGasPrice.FullName(),
}
handler := func(ctx context.Context, _ interface{}) (interface{}, error) {
return srv.(ClientBackend).MinGasPrice(ctx)
}
return interceptor(ctx, nil, info, handler)
}

func handlerGetSignerNonce(
srv interface{},
ctx context.Context,
Expand Down Expand Up @@ -700,6 +726,14 @@ func (c *consensusClient) EstimateGas(ctx context.Context, req *EstimateGasReque
return gas, nil
}

func (c *consensusClient) MinGasPrice(ctx context.Context) (*quantity.Quantity, error) {
var rsp quantity.Quantity
if err := c.conn.Invoke(ctx, methodMinGasPrice.FullName(), nil, &rsp); err != nil {
return nil, err
}
return &rsp, nil
}

func (c *consensusClient) GetSignerNonce(ctx context.Context, req *GetSignerNonceRequest) (uint64, error) {
var nonce uint64
if err := c.conn.Invoke(ctx, methodGetSignerNonce.FullName(), req, &nonce); err != nil {
Expand Down
34 changes: 8 additions & 26 deletions go/consensus/api/submission.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,7 @@ const (
// PriceDiscovery is the consensus fee price discovery interface.
type PriceDiscovery interface {
// GasPrice returns the current consensus gas price.
GasPrice(ctx context.Context) (*quantity.Quantity, error)
}

type staticPriceDiscovery struct {
price quantity.Quantity
}

// NewStaticPriceDiscovery creates a price discovery mechanism which always returns the same static
// price specified at construction time.
func NewStaticPriceDiscovery(price uint64) (PriceDiscovery, error) {
pd := &staticPriceDiscovery{}
if err := pd.price.FromUint64(price); err != nil {
return nil, fmt.Errorf("submission: failed to convert gas price: %w", err)
}
return pd, nil
}

func (pd *staticPriceDiscovery) GasPrice(context.Context) (*quantity.Quantity, error) {
return pd.price.Clone(), nil
}

type noOpPriceDiscovery struct{}

func (pd *noOpPriceDiscovery) GasPrice(context.Context) (*quantity.Quantity, error) {
return nil, transaction.ErrMethodNotSupported
GasPrice() (*quantity.Quantity, error)
}

// SubmissionManager is a transaction submission manager interface.
Expand Down Expand Up @@ -108,7 +84,7 @@ func (m *submissionManager) EstimateGasAndSetFee(ctx context.Context, signer sig

// Fetch current consensus gas price and compute the fee.
var amount *quantity.Quantity
amount, err = m.priceDiscovery.GasPrice(ctx)
amount, err = m.priceDiscovery.GasPrice()
if err != nil {
return fmt.Errorf("failed to determine gas price: %w", err)
}
Expand Down Expand Up @@ -292,6 +268,12 @@ func SignAndSubmitTxWithProof(ctx context.Context, backend Backend, signer signa
return backend.SubmissionManager().SignAndSubmitTxWithProof(ctx, signer, tx)
}

type noOpPriceDiscovery struct{}

func (pd *noOpPriceDiscovery) GasPrice() (*quantity.Quantity, error) {
return nil, transaction.ErrMethodNotSupported
}

// NoOpSubmissionManager implements a submission manager that doesn't support submitting transactions.
type NoOpSubmissionManager struct{}

Expand Down
2 changes: 1 addition & 1 deletion go/consensus/cometbft/abci/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ func (s *applicationState) EpochChanged(ctx *api.Context) (bool, beacon.EpochTim
return true, currentEpoch
}

func (s *applicationState) MinGasPrice() *quantity.Quantity {
func (s *applicationState) LocalMinGasPrice() *quantity.Quantity {
return &s.minGasPrice
}

Expand Down
11 changes: 11 additions & 0 deletions go/consensus/cometbft/abci/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/oasisprotocol/oasis-core/go/common/cbor"
"github.com/oasisprotocol/oasis-core/go/common/crypto/signature"
"github.com/oasisprotocol/oasis-core/go/common/quantity"
consensus "github.com/oasisprotocol/oasis-core/go/consensus/api"
"github.com/oasisprotocol/oasis-core/go/consensus/api/transaction"
"github.com/oasisprotocol/oasis-core/go/consensus/cometbft/api"
Expand Down Expand Up @@ -92,6 +93,16 @@ func (mux *abciMux) processTx(ctx *api.Context, tx *transaction.Transaction, txS
return err
}

// Ensure a minimum gas price.
if params.MinGasPrice > 0 && !ctx.IsSimulation() {
if tx.Fee == nil {
return transaction.ErrGasPriceTooLow
}
if tx.Fee.GasPrice().Cmp(quantity.NewFromUint64(params.MinGasPrice)) < 0 {
return transaction.ErrGasPriceTooLow
}
}

// Route to correct handler.
ctx.Logger().Debug("dispatching",
"app", app.Name(),
Expand Down
6 changes: 3 additions & 3 deletions go/consensus/cometbft/api/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ type ApplicationState interface {
// last block. As a matter of convenience, the current epoch is returned.
EpochChanged(ctx *Context) (bool, beacon.EpochTime)

// MinGasPrice returns the configured minimum gas price.
MinGasPrice() *quantity.Quantity
// LocalMinGasPrice returns the configured local minimum gas price.
LocalMinGasPrice() *quantity.Quantity

// OwnTxSigner returns the transaction signer identity of the local node.
OwnTxSigner() signature.PublicKey
Expand Down Expand Up @@ -171,7 +171,7 @@ func (ms *mockApplicationState) EpochChanged(*Context) (bool, beacon.EpochTime)
return ms.cfg.EpochChanged, ms.cfg.CurrentEpoch
}

func (ms *mockApplicationState) MinGasPrice() *quantity.Quantity {
func (ms *mockApplicationState) LocalMinGasPrice() *quantity.Quantity {
return ms.cfg.MinGasPrice
}

Expand Down
2 changes: 1 addition & 1 deletion go/consensus/cometbft/apps/staking/state/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func AuthenticateAndPayFees(
// configuration, but as long as it is only done in CheckTx, this is ok.
if !ctx.AppState().OwnTxSignerAddress().Equal(addr) {
callerGasPrice := fee.GasPrice()
if fee.Gas > 0 && callerGasPrice.Cmp(ctx.AppState().MinGasPrice()) < 0 {
if fee.Gas > 0 && callerGasPrice.Cmp(ctx.AppState().LocalMinGasPrice()) < 0 {
return transaction.ErrGasPriceTooLow
}
}
Expand Down
2 changes: 1 addition & 1 deletion go/consensus/cometbft/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ func DefaultConfig() Config {
MinGasPrice: 0,
Submission: SubmissionConfig{
GasPrice: 0,
MaxFee: 0,
MaxFee: 10_000_000_000,
},
HaltEpoch: 0,
HaltHeight: 0,
Expand Down
30 changes: 29 additions & 1 deletion go/consensus/cometbft/full/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/oasisprotocol/oasis-core/go/common/logging"
"github.com/oasisprotocol/oasis-core/go/common/node"
"github.com/oasisprotocol/oasis-core/go/common/pubsub"
"github.com/oasisprotocol/oasis-core/go/common/quantity"
cmservice "github.com/oasisprotocol/oasis-core/go/common/service"
"github.com/oasisprotocol/oasis-core/go/common/version"
"github.com/oasisprotocol/oasis-core/go/config"
Expand All @@ -43,6 +44,7 @@ import (
tmroothash "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/roothash"
tmscheduler "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/scheduler"
tmstaking "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/staking"
consensusGenesis "github.com/oasisprotocol/oasis-core/go/consensus/genesis"
genesisAPI "github.com/oasisprotocol/oasis-core/go/genesis/api"
governanceAPI "github.com/oasisprotocol/oasis-core/go/governance/api"
keymanagerAPI "github.com/oasisprotocol/oasis-core/go/keymanager/api"
Expand Down Expand Up @@ -340,6 +342,16 @@ func (n *commonNode) StateToGenesis(ctx context.Context, blockHeight int64) (*ge
return nil, err
}

// Query root consensus parameters.
cs, err := coreState.NewImmutableState(ctx, n.mux.State(), blockHeight)
if err != nil {
return nil, err
}
cp, err := cs.ConsensusParameters(ctx)
if err != nil {
return nil, err
}

// Call StateToGenesis on all backends and merge the results together.
beaconGenesis, err := n.Beacon().StateToGenesis(ctx, blockHeight)
if err != nil {
Expand Down Expand Up @@ -387,7 +399,10 @@ func (n *commonNode) StateToGenesis(ctx context.Context, blockHeight int64) (*ge
Governance: *governanceGenesis,
KeyManager: *keymanagerGenesis,
Scheduler: *schedulerGenesis,
Consensus: genesisDoc.Consensus,
Consensus: consensusGenesis.Genesis{
Backend: api.BackendName,
Parameters: *cp,
},
}, nil
}

Expand Down Expand Up @@ -456,6 +471,19 @@ func (n *commonNode) EstimateGas(_ context.Context, req *consensusAPI.EstimateGa
return n.mux.EstimateGas(req.Signer, req.Transaction)
}

// Implements consensusAPI.Backend.
func (n *commonNode) MinGasPrice(ctx context.Context) (*quantity.Quantity, error) {
cs, err := coreState.NewImmutableState(ctx, n.mux.State(), consensusAPI.HeightLatest)
if err != nil {
return nil, err
}
cp, err := cs.ConsensusParameters(ctx)
if err != nil {
return nil, err
}
return quantity.NewFromUint64(cp.MinGasPrice), nil
}

// Implements consensusAPI.Backend.
func (n *commonNode) Pruner() api.StatePruner {
return n.mux.Pruner()
Expand Down
11 changes: 7 additions & 4 deletions go/consensus/cometbft/full/full.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (
"github.com/oasisprotocol/oasis-core/go/consensus/cometbft/db"
lightAPI "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/light/api"
"github.com/oasisprotocol/oasis-core/go/consensus/metrics"
"github.com/oasisprotocol/oasis-core/go/consensus/pricediscovery"
genesisAPI "github.com/oasisprotocol/oasis-core/go/genesis/api"
cmflags "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/flags"
cmmetrics "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/metrics"
Expand Down Expand Up @@ -943,12 +944,14 @@ func New(

t.Logger.Info("starting a full consensus node")

// Create the submission manager.
pd, err := consensusAPI.NewStaticPriceDiscovery(config.GlobalConfig.Consensus.Submission.GasPrice)
// Create price discovery mechanism and the submission manager.
pd, err := pricediscovery.New(ctx, t, config.GlobalConfig.Consensus.Submission.GasPrice)
if err != nil {
return nil, fmt.Errorf("cometbft: failed to create submission manager: %w", err)
return nil, fmt.Errorf("failed to create price discovery: %w", err)
}
t.submissionMgr = consensusAPI.NewSubmissionManager(t, pd, config.GlobalConfig.Consensus.Submission.MaxFee)
t.submissionMgr = consensusAPI.NewSubmissionManager(t, pd,
config.GlobalConfig.Consensus.Submission.MaxFee,
)

if err := t.lazyInit(); err != nil {
return nil, fmt.Errorf("lazy init: %w", err)
Expand Down
3 changes: 3 additions & 0 deletions go/consensus/genesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ type Parameters struct { // nolint: maligned
MaxBlockGas transaction.Gas `json:"max_block_gas"`
MaxEvidenceSize uint64 `json:"max_evidence_size"`

// MinGasPrice is the minimum gas price.
MinGasPrice uint64 `json:"min_gas_price,omitempty"`

// StateCheckpointInterval is the expected state checkpoint interval (in blocks).
StateCheckpointInterval uint64 `json:"state_checkpoint_interval"`
// StateCheckpointNumKept is the expected minimum number of state checkpoints to keep.
Expand Down
Loading

0 comments on commit 7e5932d

Please sign in to comment.