Compare commits

..

4 Commits

Author SHA1 Message Date
ChuhanJin
6db2c0a8cb fix(bridge-history-api): crossMsgs can be empty (#646)
Co-authored-by: vincent <419436363@qq.com>
2023-07-14 17:39:37 +08:00
ChuhanJin
0caf0d4052 fix(bridge-history-api): fix claimable api sql issue (#645)
Co-authored-by: vincent <419436363@qq.com>
2023-07-14 15:26:31 +08:00
vyzo
95f2f7da0f fix(bridge): adjust gas fee cap in resumbitTransaction for rising basefee (#625)
Co-authored-by: georgehao <haohongfan@gmail.com>
Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com>
2023-07-13 17:29:17 +08:00
Xi Lin
d2a1459768 fix(contracts): fix dropping message with nonce 0 (#640)
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-07-13 16:48:45 +08:00
7 changed files with 132 additions and 42 deletions

View File

@@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/jmoiron/sqlx"
"github.com/lib/pq"
)
type l2CrossMsgOrm struct {
@@ -143,7 +144,7 @@ func (l *l2CrossMsgOrm) GetL2EarliestNoBlockTimestampHeight() (uint64, error) {
func (l *l2CrossMsgOrm) GetL2CrossMsgByMsgHashList(msgHashList []string) ([]*CrossMsg, error) {
var results []*CrossMsg
rows, err := l.db.Queryx(`SELECT * FROM cross_message WHERE msg_hash in ($1) AND msg_type = $2 AND deleted_at IS NULL;`, msgHashList, Layer2Msg)
rows, err := l.db.Queryx(`SELECT * FROM cross_message WHERE msg_hash = ANY($1) AND msg_type = $2 AND deleted_at IS NULL;`, pq.Array(msgHashList), Layer2Msg)
for rows.Next() {
msg := &CrossMsg{}
if err = rows.StructScan(msg); err != nil {

View File

@@ -122,7 +122,8 @@ func (h *historyBackend) GetClaimableTxsByAddress(address common.Address, offset
msgHashList = append(msgHashList, result.MsgHash)
}
crossMsgs, err := h.db.GetL2CrossMsgByMsgHashList(msgHashList)
if err != nil || len(crossMsgs) == 0 {
// crossMsgs can be empty, because they can be emitted by user directly call contract
if err != nil {
return txHistories, 0, err
}
crossMsgMap := make(map[string]*orm.CrossMsg)

View File

@@ -343,6 +343,20 @@ func (s *Sender) resubmitTransaction(feeData *FeeData, auth *bind.TransactOpts,
if gasTipCap.Cmp(feeData.gasTipCap) < 0 {
gasTipCap = feeData.gasTipCap
}
// adjust for rising basefee
adjBaseFee := big.NewInt(0)
if feeGas := atomic.LoadUint64(&s.baseFeePerGas); feeGas != 0 {
adjBaseFee.SetUint64(feeGas)
}
adjBaseFee = adjBaseFee.Mul(adjBaseFee, escalateMultipleNum)
adjBaseFee = adjBaseFee.Div(adjBaseFee, escalateMultipleDen)
currentGasFeeCap := new(big.Int).Add(gasTipCap, adjBaseFee)
if gasFeeCap.Cmp(currentGasFeeCap) < 0 {
gasFeeCap = currentGasFeeCap
}
// but don't exceed maxGasPrice
if gasFeeCap.Cmp(maxGasPrice) > 0 {
gasFeeCap = maxGasPrice
}

View File

@@ -65,6 +65,7 @@ func TestSender(t *testing.T) {
t.Run("test min gas limit", testMinGasLimit)
t.Run("test resubmit transaction", testResubmitTransaction)
t.Run("test resubmit transaction with rising base fee", testResubmitTransactionWithRisingBaseFee)
t.Run("test check pending transaction", testCheckPendingTransaction)
t.Run("test 1 account sender", func(t *testing.T) { testBatchSender(t, 1) })
@@ -154,6 +155,43 @@ func testResubmitTransaction(t *testing.T) {
}
}
func testResubmitTransactionWithRisingBaseFee(t *testing.T) {
txType := "DynamicFeeTx"
cfgCopy := *cfg.L1Config.RelayerConfig.SenderConfig
cfgCopy.TxType = txType
s, err := NewSender(context.Background(), &cfgCopy, privateKeys)
assert.NoError(t, err)
auth := s.auths.getAccount()
tx := types.NewTransaction(auth.Nonce.Uint64(), common.Address{}, big.NewInt(0), 0, big.NewInt(0), nil)
s.baseFeePerGas = 1000
feeData, err := s.getFeeData(auth, &common.Address{}, big.NewInt(0), nil, 0)
assert.NoError(t, err)
// bump the basefee by 10x
s.baseFeePerGas *= 10
// resubmit and check that the gas fee has been adjusted accordingly
newTx, err := s.resubmitTransaction(feeData, auth, tx)
assert.NoError(t, err)
escalateMultipleNum := new(big.Int).SetUint64(s.config.EscalateMultipleNum)
escalateMultipleDen := new(big.Int).SetUint64(s.config.EscalateMultipleDen)
maxGasPrice := new(big.Int).SetUint64(s.config.MaxGasPrice)
adjBaseFee := new(big.Int)
adjBaseFee.SetUint64(s.baseFeePerGas)
adjBaseFee = adjBaseFee.Mul(adjBaseFee, escalateMultipleNum)
adjBaseFee = adjBaseFee.Div(adjBaseFee, escalateMultipleDen)
expectedGasFeeCap := new(big.Int).Add(feeData.gasTipCap, adjBaseFee)
if expectedGasFeeCap.Cmp(maxGasPrice) > 0 {
expectedGasFeeCap = maxGasPrice
}
assert.Equal(t, expectedGasFeeCap.Int64(), newTx.GasFeeCap().Int64())
s.Stop()
}
func testCheckPendingTransaction(t *testing.T) {
for _, txType := range txTypes {
cfgCopy := *cfg.L1Config.RelayerConfig.SenderConfig

View File

@@ -5,7 +5,7 @@ import (
"runtime/debug"
)
var tag = "v4.0.18"
var tag = "v4.0.21"
var commit = func() string {
if info, ok := debug.ReadBuildInfo(); ok {

View File

@@ -66,10 +66,16 @@ contract L1ScrollMessenger is PausableUpgradeable, ScrollMessengerBase, IL1Scrol
mapping(bytes32 => ReplayState) public replayStates;
/// @notice Mapping from queue index to previous replay queue index.
///
/// @dev If a message `x` was replayed 3 times with index `q1`, `q2` and `q3`, the
/// value of `prevReplayIndex` and `replayStates` will be `replayStates[hash(x)].lastIndex = q3`,
/// `replayStates[hash(x)].times = 3`, `prevReplayIndex[q3] = q2`, `prevReplayIndex[q2] = q1`
/// and `prevReplayIndex[q1] = x`.
/// `replayStates[hash(x)].times = 3`, `prevReplayIndex[q3] = q2`, `prevReplayIndex[q2] = q1`,
/// `prevReplayIndex[q1] = x` and `prevReplayIndex[x]=nil`.
///
/// @dev The index `x` that `prevReplayIndex[x]=nil` is used as the termination of the list.
/// Usually we use `0` to represent `nil`, but we cannot distinguish it with the first message
/// with index zero. So a nonzero offset `1` is added to the value of `prevReplayIndex[x]` to
/// avoid such situation.
mapping(uint256 => uint256) public prevReplayIndex;
/***************
@@ -200,11 +206,13 @@ contract L1ScrollMessenger is PausableUpgradeable, ScrollMessengerBase, IL1Scrol
ReplayState memory _replayState = replayStates[_xDomainCalldataHash];
// update the replayed message chain.
if (_replayState.lastIndex == 0) {
// the message has not been replayed before.
prevReplayIndex[_nextQueueIndex] = _messageNonce;
} else {
prevReplayIndex[_nextQueueIndex] = _replayState.lastIndex;
unchecked {
if (_replayState.lastIndex == 0) {
// the message has not been replayed before.
prevReplayIndex[_nextQueueIndex] = _messageNonce + 1;
} else {
prevReplayIndex[_nextQueueIndex] = _replayState.lastIndex + 1;
}
}
_replayState.lastIndex = uint128(_nextQueueIndex);
@@ -265,6 +273,9 @@ contract L1ScrollMessenger is PausableUpgradeable, ScrollMessengerBase, IL1Scrol
IL1MessageQueue(_messageQueue).dropCrossDomainMessage(_lastIndex);
_lastIndex = prevReplayIndex[_lastIndex];
if (_lastIndex == 0) break;
unchecked {
_lastIndex = _lastIndex - 1;
}
}
isL1MessageDropped[_xDomainCalldataHash] = true;

View File

@@ -144,9 +144,9 @@ contract L1ScrollMessengerTest is L1GatewayTestBase {
(_replayTimes, _lastIndex) = l1Messenger.replayStates(hash);
assertEq(_replayTimes, i + 1);
assertEq(_lastIndex, i + 3);
assertEq(l1Messenger.prevReplayIndex(i + 3), i + 2);
assertEq(l1Messenger.prevReplayIndex(i + 3), i + 2 + 1);
for (uint256 j = 0; j <= i; j++) {
assertEq(l1Messenger.prevReplayIndex(i + 3 - j), i + 2 - j);
assertEq(l1Messenger.prevReplayIndex(i + 3 - j), i + 2 - j + 1);
}
}
}
@@ -239,51 +239,76 @@ contract L1ScrollMessengerTest is L1GatewayTestBase {
l1Messenger.updateMaxReplayTimes(10);
// replay 3 times
for (uint256 i = 0; i < 3; i++) {
l1Messenger.replayMessage(address(this), address(0), 0, 0, new bytes(0), 0, address(0));
}
assertEq(messageQueue.nextCrossDomainMessageIndex(), 4);
// replay 1 time
l1Messenger.replayMessage(address(this), address(0), 0, 0, new bytes(0), 0, address(0));
assertEq(messageQueue.nextCrossDomainMessageIndex(), 2);
// only first 3 are skipped
// skip all 2 messages
hevm.startPrank(address(rollup));
messageQueue.popCrossDomainMessage(0, 4, 0x7);
assertEq(messageQueue.pendingQueueIndex(), 4);
messageQueue.popCrossDomainMessage(0, 2, 0x3);
assertEq(messageQueue.pendingQueueIndex(), 2);
hevm.stopPrank();
// message already dropped or executed, revert
hevm.expectRevert("message already dropped or executed");
l1Messenger.dropMessage(address(this), address(0), 0, 0, new bytes(0));
// send one message with nonce 4 and replay 4 times
l1Messenger.sendMessage(address(0), 0, new bytes(0), 0);
for (uint256 i = 0; i < 4; i++) {
l1Messenger.replayMessage(address(this), address(0), 0, 4, new bytes(0), 0, address(0));
}
assertEq(messageQueue.nextCrossDomainMessageIndex(), 9);
// skip all 5 messages
hevm.startPrank(address(rollup));
messageQueue.popCrossDomainMessage(4, 5, 0x1f);
assertEq(messageQueue.pendingQueueIndex(), 9);
hevm.stopPrank();
for (uint256 i = 4; i < 9; ++i) {
for (uint256 i = 0; i < 2; ++i) {
assertGt(uint256(messageQueue.getCrossDomainMessage(i)), 0);
}
hevm.expectEmit(false, false, false, true);
emit OnDropMessageCalled(new bytes(0));
l1Messenger.dropMessage(address(this), address(0), 0, 4, new bytes(0));
for (uint256 i = 4; i < 9; ++i) {
l1Messenger.dropMessage(address(this), address(0), 0, 0, new bytes(0));
for (uint256 i = 0; i < 2; ++i) {
assertEq(messageQueue.getCrossDomainMessage(i), bytes32(0));
}
// send one message with nonce 2 and replay 3 times
l1Messenger.sendMessage(address(0), 0, new bytes(0), 0);
assertEq(messageQueue.nextCrossDomainMessageIndex(), 3);
for (uint256 i = 0; i < 3; i++) {
l1Messenger.replayMessage(address(this), address(0), 0, 2, new bytes(0), 0, address(0));
}
assertEq(messageQueue.nextCrossDomainMessageIndex(), 6);
// only first 3 are skipped
hevm.startPrank(address(rollup));
messageQueue.popCrossDomainMessage(2, 4, 0x7);
assertEq(messageQueue.pendingQueueIndex(), 6);
hevm.stopPrank();
// message already dropped or executed, revert
hevm.expectRevert("message already dropped or executed");
l1Messenger.dropMessage(address(this), address(0), 0, 2, new bytes(0));
// send one message with nonce 6 and replay 4 times
l1Messenger.sendMessage(address(0), 0, new bytes(0), 0);
for (uint256 i = 0; i < 4; i++) {
l1Messenger.replayMessage(address(this), address(0), 0, 6, new bytes(0), 0, address(0));
}
assertEq(messageQueue.nextCrossDomainMessageIndex(), 11);
// skip all 5 messages
hevm.startPrank(address(rollup));
messageQueue.popCrossDomainMessage(6, 5, 0x1f);
assertEq(messageQueue.pendingQueueIndex(), 11);
hevm.stopPrank();
for (uint256 i = 6; i < 11; ++i) {
assertGt(uint256(messageQueue.getCrossDomainMessage(i)), 0);
}
hevm.expectEmit(false, false, false, true);
emit OnDropMessageCalled(new bytes(0));
l1Messenger.dropMessage(address(this), address(0), 0, 6, new bytes(0));
for (uint256 i = 6; i < 11; ++i) {
assertEq(messageQueue.getCrossDomainMessage(i), bytes32(0));
}
// Message already dropped, revert
hevm.expectRevert("Message already dropped");
l1Messenger.dropMessage(address(this), address(0), 0, 4, new bytes(0));
l1Messenger.dropMessage(address(this), address(0), 0, 0, new bytes(0));
hevm.expectRevert("Message already dropped");
l1Messenger.dropMessage(address(this), address(0), 0, 6, new bytes(0));
// replay dropped message, revert
hevm.expectRevert("Message already dropped");
l1Messenger.replayMessage(address(this), address(0), 0, 4, new bytes(0), 0, address(0));
l1Messenger.replayMessage(address(this), address(0), 0, 0, new bytes(0), 0, address(0));
hevm.expectRevert("Message already dropped");
l1Messenger.replayMessage(address(this), address(0), 0, 6, new bytes(0), 0, address(0));
}
function onDropMessage(bytes memory message) external payable {