Skip to content

Commit

Permalink
Merge branch 'develop' into feature/BCF-2393-multichain-telemetry-sup…
Browse files Browse the repository at this point in the history
…port
  • Loading branch information
george-dorin authored Sep 27, 2023
2 parents 760b4b5 + 05efa5d commit f85236a
Show file tree
Hide file tree
Showing 40 changed files with 343 additions and 208 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.5.0
2.6.0
62 changes: 35 additions & 27 deletions contracts/src/v0.8/dev/vrf/VRFV2PlusWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,11 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
error LinkAlreadySet();
error FailedToTransferLink();

LinkTokenInterface public s_link;
AggregatorV3Interface public s_linkNativeFeed;
ExtendedVRFCoordinatorV2PlusInterface public immutable COORDINATOR;
// s_keyHash is the key hash to use when requesting randomness. Fees are paid based on current gas
// fees, so this should be set to the highest gas lane on the network.
bytes32 s_keyHash;

uint256 public immutable SUBSCRIPTION_ID;
/// @dev this is the size of a VRF v2 fulfillment's calldata abi-encoded in bytes.
/// @dev proofSize = 13 words = 13 * 256 = 3328 bits
/// @dev commitmentSize = 5 words = 5 * 256 = 1280 bits
/// @dev dataSize = proofSize + commitmentSize = 4608 bits
/// @dev selector = 32 bits
/// @dev total data size = 4608 bits + 32 bits = 4640 bits = 580 bytes
uint32 public s_fulfillmentTxSizeBytes = 580;

// 5k is plenty for an EXTCODESIZE call (2600) + warm CALL (100)
// and some arithmetic operations.
Expand All @@ -41,16 +35,6 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
// should only be relied on within the same transaction the request was made.
uint256 public override lastRequestId;

// Configuration fetched from VRFCoordinatorV2

// s_configured tracks whether this contract has been configured. If not configured, randomness
// requests cannot be made.
bool public s_configured;

// s_disabled disables the contract when true. When disabled, new VRF requests cannot be made
// but existing ones can still be fulfilled.
bool public s_disabled;

// s_fallbackWeiPerUnitLink is the backup LINK exchange rate used when the LINK/NATIVE feed is
// stale.
int256 private s_fallbackWeiPerUnitLink;
Expand All @@ -67,33 +51,57 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
// charges.
uint32 private s_fulfillmentFlatFeeNativePPM;

LinkTokenInterface public s_link;

// Other configuration

// s_wrapperGasOverhead reflects the gas overhead of the wrapper's fulfillRandomWords
// function. The cost for this gas is passed to the user.
uint32 private s_wrapperGasOverhead;

// Configuration fetched from VRFCoordinatorV2

/// @dev this is the size of a VRF v2 fulfillment's calldata abi-encoded in bytes.
/// @dev proofSize = 13 words = 13 * 256 = 3328 bits
/// @dev commitmentSize = 5 words = 5 * 256 = 1280 bits
/// @dev dataSize = proofSize + commitmentSize = 4608 bits
/// @dev selector = 32 bits
/// @dev total data size = 4608 bits + 32 bits = 4640 bits = 580 bytes
uint32 public s_fulfillmentTxSizeBytes = 580;

// s_coordinatorGasOverhead reflects the gas overhead of the coordinator's fulfillRandomWords
// function. The cost for this gas is billed to the subscription, and must therefor be included
// in the pricing for wrapped requests. This includes the gas costs of proof verification and
// payment calculation in the coordinator.
uint32 private s_coordinatorGasOverhead;

AggregatorV3Interface public s_linkNativeFeed;

// s_configured tracks whether this contract has been configured. If not configured, randomness
// requests cannot be made.
bool public s_configured;

// s_disabled disables the contract when true. When disabled, new VRF requests cannot be made
// but existing ones can still be fulfilled.
bool public s_disabled;

// s_wrapperPremiumPercentage is the premium ratio in percentage. For example, a value of 0
// indicates no premium. A value of 15 indicates a 15 percent premium.
uint8 private s_wrapperPremiumPercentage;

// s_keyHash is the key hash to use when requesting randomness. Fees are paid based on current gas
// fees, so this should be set to the highest gas lane on the network.
bytes32 s_keyHash;

// s_maxNumWords is the max number of words that can be requested in a single wrapped VRF request.
uint8 s_maxNumWords;

ExtendedVRFCoordinatorV2PlusInterface public immutable COORDINATOR;

struct Callback {
address callbackAddress;
uint32 callbackGasLimit;
uint256 requestGasPrice;
// Reducing requestGasPrice from uint256 to uint64 slots Callback struct
// into a single word, thus saving an entire SSTORE and leading to 21K
// gas cost saving. 18 ETH would be the max gas price we can process.
// GasPrice is unlikely to be more than 14 ETH on most chains
uint64 requestGasPrice;
}
mapping(uint256 => Callback) /* requestID */ /* callback */ public s_callbacks;

Expand Down Expand Up @@ -352,7 +360,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
s_callbacks[requestId] = Callback({
callbackAddress: _sender,
callbackGasLimit: callbackGasLimit,
requestGasPrice: tx.gasprice
requestGasPrice: uint64(tx.gasprice)
});
lastRequestId = requestId;
}
Expand All @@ -378,7 +386,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
s_callbacks[requestId] = Callback({
callbackAddress: msg.sender,
callbackGasLimit: _callbackGasLimit,
requestGasPrice: tx.gasprice
requestGasPrice: uint64(tx.gasprice)
});

return requestId;
Expand Down
6 changes: 1 addition & 5 deletions core/chains/evm/config/toml/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -804,9 +804,5 @@ func (n *Node) SetFrom(f *Node) {
}

func ChainIDInt64(cid relay.ChainID) (int64, error) {
i, err := strconv.Atoi(cid)
if err != nil {
return int64(0), err
}
return int64(i), nil
return strconv.ParseInt(cid, 10, 64)
}
2 changes: 1 addition & 1 deletion core/chains/evm/logpoller/log_poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -945,7 +945,7 @@ func (lp *logPoller) LogsWithSigs(start, end int64, eventSigs []common.Hash, add
}

func (lp *logPoller) LogsCreatedAfter(eventSig common.Hash, address common.Address, after time.Time, confs int, qopts ...pg.QOpt) ([]Log, error) {
return lp.orm.SelectLogsCreatedAfter(eventSig[:], address, after, confs, qopts...)
return lp.orm.SelectLogsCreatedAfter(address, eventSig, after, confs, qopts...)
}

// IndexedLogs finds all the logs that have a topic value in topicValues at index topicIndex.
Expand Down
69 changes: 50 additions & 19 deletions core/chains/evm/logpoller/log_poller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,44 +39,75 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/utils"
)

func logRuntime(t *testing.T, start time.Time) {
func logRuntime(t testing.TB, start time.Time) {
t.Log("runtime", time.Since(start))
}

func TestPopulateLoadedDB(t *testing.T) {
t.Skip("Only for local load testing and query analysis")
lggr := logger.TestLogger(t)
_, db := heavyweight.FullTestDBV2(t, "logs_scale", nil)
chainID := big.NewInt(137)

o := logpoller.NewORM(big.NewInt(137), db, lggr, pgtest.NewQConfig(true))
func populateDatabase(t testing.TB, o *logpoller.ORM, chainID *big.Int) (common.Hash, common.Address, common.Address) {
event1 := EmitterABI.Events["Log1"].ID
address1 := common.HexToAddress("0x2ab9a2Dc53736b361b72d900CdF9F78F9406fbbb")
address2 := common.HexToAddress("0x6E225058950f237371261C985Db6bDe26df2200E")
startDate := time.Date(2010, 1, 1, 12, 12, 12, 0, time.UTC)

// We start at 1 just so block number > 0
for j := 1; j < 1000; j++ {
for j := 1; j < 100; j++ {
var logs []logpoller.Log
// Max we can insert per batch
for i := 0; i < 1000; i++ {
addr := address1
if (i+(1000*j))%2 == 0 {
addr = address2
}
blockNumber := int64(i + (1000 * j))
blockTimestamp := startDate.Add(time.Duration(j*1000) * time.Hour)

logs = append(logs, logpoller.Log{
EvmChainId: utils.NewBig(chainID),
LogIndex: 1,
BlockHash: common.HexToHash(fmt.Sprintf("0x%d", i+(1000*j))),
BlockNumber: int64(i + (1000 * j)),
EventSig: event1,
Topics: [][]byte{event1[:], logpoller.EvmWord(uint64(i + 1000*j)).Bytes()},
Address: addr,
TxHash: common.HexToHash("0x1234"),
Data: logpoller.EvmWord(uint64(i + 1000*j)).Bytes(),
EvmChainId: utils.NewBig(chainID),
LogIndex: 1,
BlockHash: common.HexToHash(fmt.Sprintf("0x%d", i+(1000*j))),
BlockNumber: blockNumber,
BlockTimestamp: blockTimestamp,
EventSig: event1,
Topics: [][]byte{event1[:], logpoller.EvmWord(uint64(i + 1000*j)).Bytes()},
Address: addr,
TxHash: utils.RandomAddress().Hash(),
Data: logpoller.EvmWord(uint64(i + 1000*j)).Bytes(),
CreatedAt: blockTimestamp,
})

}
require.NoError(t, o.InsertLogs(logs))
require.NoError(t, o.InsertBlock(utils.RandomAddress().Hash(), int64((j+1)*1000-1), startDate.Add(time.Duration(j*1000)*time.Hour)))
}

return event1, address1, address2
}

func BenchmarkSelectLogsCreatedAfter(b *testing.B) {
chainId := big.NewInt(137)
_, db := heavyweight.FullTestDBV2(b, "logs_scale", nil)
o := logpoller.NewORM(chainId, db, logger.TestLogger(b), pgtest.NewQConfig(false))
event, address, _ := populateDatabase(b, o, chainId)

// Setting searchDate to pick around 5k logs
searchDate := time.Date(2020, 1, 1, 12, 12, 12, 0, time.UTC)

b.ResetTimer()

for i := 0; i < b.N; i++ {
logs, err := o.SelectLogsCreatedAfter(address, event, searchDate, 500)
require.NotZero(b, len(logs))
require.NoError(b, err)
}
}

func TestPopulateLoadedDB(t *testing.T) {
t.Skip("Only for local load testing and query analysis")
_, db := heavyweight.FullTestDBV2(t, "logs_scale", nil)
chainID := big.NewInt(137)

o := logpoller.NewORM(big.NewInt(137), db, logger.TestLogger(t), pgtest.NewQConfig(true))
event1, address1, address2 := populateDatabase(t, o, chainID)

func() {
defer logRuntime(t, time.Now())
_, err1 := o.SelectLogsByBlockRangeFilter(750000, 800000, address1, event1)
Expand Down
55 changes: 43 additions & 12 deletions core/chains/evm/logpoller/orm.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (o *ORM) SelectLatestLogEventSigWithConfs(eventSig common.Hash, address com
WHERE evm_chain_id = $1
AND event_sig = $2
AND address = $3
AND (block_number + $4) <= (SELECT COALESCE(block_number, 0) FROM evm.log_poller_blocks WHERE evm_chain_id = $1 ORDER BY block_number DESC LIMIT 1)
AND block_number <= (SELECT COALESCE(block_number, 0) FROM evm.log_poller_blocks WHERE evm_chain_id = $1 ORDER BY block_number DESC LIMIT 1) - $4
ORDER BY (block_number, log_index) DESC LIMIT 1`, utils.NewBig(o.chainID), eventSig, address, confs); err != nil {
return nil, err
}
Expand Down Expand Up @@ -231,17 +231,22 @@ func (o *ORM) SelectLogsByBlockRangeFilter(start, end int64, address common.Addr
}

// SelectLogsCreatedAfter finds logs created after some timestamp.
func (o *ORM) SelectLogsCreatedAfter(eventSig []byte, address common.Address, after time.Time, confs int, qopts ...pg.QOpt) ([]Log, error) {
func (o *ORM) SelectLogsCreatedAfter(address common.Address, eventSig common.Hash, after time.Time, confs int, qopts ...pg.QOpt) ([]Log, error) {
minBlock, maxBlock, err := o.blocksRangeAfterTimestamp(after, confs, qopts...)
if err != nil {
return nil, err
}

var logs []Log
q := o.q.WithOpts(qopts...)
err := q.Select(&logs, `
err = q.Select(&logs, `
SELECT * FROM evm.logs
WHERE evm_chain_id = $1
AND address = $2
AND event_sig = $3
AND created_at > $4
AND (block_number + $5) <= (SELECT COALESCE(block_number, 0) FROM evm.log_poller_blocks WHERE evm_chain_id = $1 ORDER BY block_number DESC LIMIT 1)
ORDER BY created_at ASC`, utils.NewBig(o.chainID), address, eventSig, after, confs)
AND block_number > $4
AND block_number <= $5
ORDER BY (block_number, log_index)`, utils.NewBig(o.chainID), address, eventSig, minBlock, maxBlock)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -498,18 +503,24 @@ func validateTopicIndex(index int) error {
}

func (o *ORM) SelectIndexedLogsCreatedAfter(address common.Address, eventSig common.Hash, topicIndex int, topicValues []common.Hash, after time.Time, confs int, qopts ...pg.QOpt) ([]Log, error) {
q := o.q.WithOpts(qopts...)
minBlock, maxBlock, err := o.blocksRangeAfterTimestamp(after, confs, qopts...)
if err != nil {
return nil, err
}

var logs []Log
q := o.q.WithOpts(qopts...)
topicValuesBytes := concatBytes(topicValues)
// Add 1 since postgresql arrays are 1-indexed.
err := q.Select(&logs, `
err = q.Select(&logs, `
SELECT * FROM evm.logs
WHERE evm.logs.evm_chain_id = $1
AND address = $2 AND event_sig = $3
AND address = $2
AND event_sig = $3
AND topics[$4] = ANY($5)
AND created_at > $6
AND block_number <= (SELECT COALESCE(block_number, 0) FROM evm.log_poller_blocks WHERE evm_chain_id = $1 ORDER BY block_number DESC LIMIT 1) - $7
ORDER BY created_at ASC`, utils.NewBig(o.chainID), address, eventSig.Bytes(), topicIndex+1, topicValuesBytes, after, confs)
AND block_number > $6
AND block_number <= $7
ORDER BY (block_number, log_index)`, utils.NewBig(o.chainID), address, eventSig.Bytes(), topicIndex+1, topicValuesBytes, minBlock, maxBlock)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -569,7 +580,27 @@ func (o *ORM) SelectIndexedLogsWithSigsExcluding(sigA, sigB common.Hash, topicIn
return nil, err
}
return logs, nil
}

func (o *ORM) blocksRangeAfterTimestamp(after time.Time, confs int, qopts ...pg.QOpt) (int64, int64, error) {
type blockRange struct {
MinBlockNumber int64 `db:"min_block"`
MaxBlockNumber int64 `db:"max_block"`
}

var br blockRange
q := o.q.WithOpts(qopts...)
err := q.Get(&br, `
SELECT
coalesce(min(block_number), 0) as min_block,
coalesce(max(block_number), 0) as max_block
FROM evm.log_poller_blocks
WHERE evm_chain_id = $1
AND block_timestamp > $2`, utils.NewBig(o.chainID), after)
if err != nil {
return 0, 0, err
}
return br.MinBlockNumber, br.MaxBlockNumber - int64(confs), nil
}

type bytesProducer interface {
Expand Down
Loading

0 comments on commit f85236a

Please sign in to comment.