68 lines
1.9 KiB
Go
68 lines
1.9 KiB
Go
package runtime
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
|
|
"cosmossdk.io/core/branch"
|
|
storetypes "cosmossdk.io/store/types"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
)
|
|
|
|
var _ branch.Service = BranchService{}
|
|
|
|
type BranchService struct{}
|
|
|
|
func (b BranchService) Execute(ctx context.Context, f func(ctx context.Context) error) error {
|
|
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
|
branchedCtx, commit := sdkCtx.CacheContext()
|
|
err := f(branchedCtx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
commit()
|
|
return nil
|
|
}
|
|
|
|
func (b BranchService) ExecuteWithGasLimit(ctx context.Context, gasLimit uint64, f func(ctx context.Context) error) (gasUsed uint64, err error) {
|
|
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
|
branchedCtx, commit := sdkCtx.CacheContext()
|
|
// create a new gas meter
|
|
limitedGasMeter := storetypes.NewGasMeter(gasLimit)
|
|
// apply gas meter with limit to branched context
|
|
branchedCtx = branchedCtx.WithGasMeter(limitedGasMeter)
|
|
err = catchOutOfGas(branchedCtx, f)
|
|
// even before checking the error, we want to get the gas used
|
|
// and apply it to the original context.
|
|
gasUsed = limitedGasMeter.GasConsumed()
|
|
sdkCtx.GasMeter().ConsumeGas(gasUsed, "branch")
|
|
// in case of errors, do not commit the branched context
|
|
// return gas used and the error
|
|
if err != nil {
|
|
return gasUsed, err
|
|
}
|
|
// if no error, commit the branched context
|
|
// and return gas used and no error
|
|
commit()
|
|
return gasUsed, nil
|
|
}
|
|
|
|
// catchOutOfGas is a helper function to catch out of gas panics and return them as errors.
|
|
func catchOutOfGas(ctx sdk.Context, f func(ctx context.Context) error) (err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
// we immediately check if it's an out of error gas.
|
|
// if it is not we panic again to propagate it up.
|
|
if _, ok := r.(storetypes.ErrorOutOfGas); !ok {
|
|
_, _ = fmt.Fprintf(os.Stderr, "recovered: %#v", r) // log to stderr
|
|
panic(r)
|
|
}
|
|
err = sdkerrors.ErrOutOfGas
|
|
}
|
|
}()
|
|
return f(ctx)
|
|
}
|