Skip to content
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

[NONEVM-714] - Add multiple blocks aggregation functionality to Block History Estimator #896

Open
wants to merge 14 commits into
base: develop
Choose a base branch
from
34 changes: 33 additions & 1 deletion pkg/solana/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import (

"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"golang.org/x/sync/singleflight"

"github.com/smartcontractkit/chainlink-common/pkg/logger"

mn "github.com/smartcontractkit/chainlink-solana/pkg/solana/client/multinode"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/config"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/monitor"
Expand All @@ -36,6 +37,8 @@ type Reader interface {
ChainID(ctx context.Context) (mn.StringID, error)
GetFeeForMessage(ctx context.Context, msg string) (uint64, error)
GetLatestBlock(ctx context.Context) (*rpc.GetBlockResult, error)
GetBlocksWithLimit(ctx context.Context, startSlot uint64, limit uint64) (*rpc.BlocksResult, error)
GetBlock(ctx context.Context, slot uint64) (*rpc.GetBlockResult, error)
}

// AccountReader is an interface that allows users to pass either the solana rpc client or the relay client
Expand Down Expand Up @@ -275,3 +278,32 @@ func (c *Client) GetLatestBlock(ctx context.Context) (*rpc.GetBlockResult, error
})
return v.(*rpc.GetBlockResult), err
}

func (c *Client) GetBlock(ctx context.Context, slot uint64) (*rpc.GetBlockResult, error) {
// get block based on slot
done := c.latency("get_block")
defer done()
ctx, cancel := context.WithTimeout(ctx, c.txTimeout)
defer cancel()
v, err, _ := c.requestGroup.Do("GetBlockWithOpts", func() (interface{}, error) {
version := uint64(0) // pull all tx types (legacy + v0)
return c.rpc.GetBlockWithOpts(ctx, slot, &rpc.GetBlockOpts{
Commitment: c.commitment,
MaxSupportedTransactionVersion: &version,
})
})
return v.(*rpc.GetBlockResult), err
}

func (c *Client) GetBlocksWithLimit(ctx context.Context, startSlot uint64, limit uint64) (*rpc.BlocksResult, error) {
done := c.latency("get_blocks_with_limit")
defer done()

ctx, cancel := context.WithTimeout(ctx, c.txTimeout)
defer cancel()

v, err, _ := c.requestGroup.Do("GetBlocksWithLimit", func() (interface{}, error) {
return c.rpc.GetBlocksWithLimit(ctx, startSlot, limit, c.commitment)
})
return v.(*rpc.BlocksResult), err
}
30 changes: 30 additions & 0 deletions pkg/solana/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,36 @@ func TestClient_Reader_Integration(t *testing.T) {
assert.NotEqual(t, solana.Hash{}, block.Blockhash)
assert.NotEqual(t, uint64(0), block.ParentSlot)
assert.NotEqual(t, uint64(0), block.ParentSlot)

// GetBlock
// Test fetching a valid block
block, err = c.GetBlock(ctx, slot0)
assert.NoError(t, err)
assert.NotNil(t, block)
assert.Equal(t, slot0, block.ParentSlot+1)
assert.NotEqual(t, solana.Hash{}, block.Blockhash)

// Test fetching a block with an invalid future slot
futureSlot := slot0 + 1000000
block, err = c.GetBlock(ctx, futureSlot)
assert.Error(t, err)
assert.Nil(t, block)

// GetBlocksWithLimit
// Define the limit of blocks to fetch and calculate the start slot
limit := uint64(10)
startSlot := slot0 - limit + 1

// Fetch blocks with limit
blocksResult, err := c.GetBlocksWithLimit(ctx, startSlot, limit)
assert.NoError(t, err)
assert.NotNil(t, blocksResult)

// Verify that the slots returned are within the expected range
for _, slot := range *blocksResult {
assert.GreaterOrEqual(t, slot, startSlot)
assert.LessOrEqual(t, slot, slot0)
}
}

func TestClient_Reader_ChainID(t *testing.T) {
Expand Down
60 changes: 60 additions & 0 deletions pkg/solana/client/mocks/ReaderWriter.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions pkg/solana/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var defaultConfigSet = Chain{
BlockHistoryPollPeriod: config.MustNewDuration(5 * time.Second),
ComputeUnitLimitDefault: ptr(uint32(200_000)), // set to 0 to disable adding compute unit limit
EstimateComputeUnitLimit: ptr(false), // set to false to disable compute unit limit estimation
BlockHistoryDepth: ptr(uint64(20)), // number of blocks to fetch for multiple blocks fee estimation
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to keep things consistent with EVM where we can, could we rename this config to BlockHistorySize instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also to keep existing behavior of the BHE, we should maybe set this default to 1 to mimic latest block behavior.

}

//go:generate mockery --name Config --output ./mocks/ --case=underscore --filename config.go
Expand All @@ -53,6 +54,7 @@ type Config interface {
ComputeUnitPriceDefault() uint64
FeeBumpPeriod() time.Duration
BlockHistoryPollPeriod() time.Duration
BlockHistoryDepth() uint64
ComputeUnitLimitDefault() uint32
EstimateComputeUnitLimit() bool
}
Expand All @@ -74,6 +76,7 @@ type Chain struct {
ComputeUnitPriceDefault *uint64
FeeBumpPeriod *config.Duration
BlockHistoryPollPeriod *config.Duration
BlockHistoryDepth *uint64
ComputeUnitLimitDefault *uint32
EstimateComputeUnitLimit *bool
}
Expand Down Expand Up @@ -127,6 +130,9 @@ func (c *Chain) SetDefaults() {
if c.BlockHistoryPollPeriod == nil {
c.BlockHistoryPollPeriod = defaultConfigSet.BlockHistoryPollPeriod
}
if c.BlockHistoryDepth == nil {
c.BlockHistoryDepth = defaultConfigSet.BlockHistoryDepth
}
if c.ComputeUnitLimitDefault == nil {
c.ComputeUnitLimitDefault = defaultConfigSet.ComputeUnitLimitDefault
}
Expand Down
18 changes: 18 additions & 0 deletions pkg/solana/config/mocks/config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/solana/config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ func setFromChain(c, f *Chain) {
if f.BlockHistoryPollPeriod != nil {
c.BlockHistoryPollPeriod = f.BlockHistoryPollPeriod
}
if f.BlockHistoryDepth != nil {
c.BlockHistoryDepth = f.BlockHistoryDepth
}
}

func (c *TOMLConfig) ValidateConfig() (err error) {
Expand Down Expand Up @@ -278,6 +281,10 @@ func (c *TOMLConfig) BlockHistoryPollPeriod() time.Duration {
return c.Chain.BlockHistoryPollPeriod.Duration()
}

func (c *TOMLConfig) BlockHistoryDepth() uint64 {
return *c.Chain.BlockHistoryDepth
}

func (c *TOMLConfig) ComputeUnitLimitDefault() uint32 {
return *c.Chain.ComputeUnitLimitDefault
}
Expand Down
Loading
Loading