bridge_messages.go 4.84 KB
Newer Older
1 2 3 4 5
package database

import (
	"errors"
	"fmt"
Hamdi Allam's avatar
Hamdi Allam committed
6
	"math/big"
7 8

	"gorm.io/gorm"
Hamdi Allam's avatar
Hamdi Allam committed
9
	"gorm.io/gorm/clause"
10 11

	"github.com/ethereum/go-ethereum/common"
Hamdi Allam's avatar
Hamdi Allam committed
12
	"github.com/ethereum/go-ethereum/log"
13 14 15 16 17 18 19 20 21

	"github.com/google/uuid"
)

/**
 * Types
 */

type BridgeMessage struct {
Hamdi Allam's avatar
Hamdi Allam committed
22
	MessageHash common.Hash `gorm:"primaryKey;serializer:bytes"`
Hamdi Allam's avatar
Hamdi Allam committed
23
	Nonce       *big.Int    `gorm:"serializer:u256"`
24 25 26 27 28

	SentMessageEventGUID    uuid.UUID
	RelayedMessageEventGUID *uuid.UUID

	Tx       Transaction `gorm:"embedded"`
Hamdi Allam's avatar
Hamdi Allam committed
29
	GasLimit *big.Int    `gorm:"serializer:u256"`
30 31 32 33
}

type L1BridgeMessage struct {
	BridgeMessage         `gorm:"embedded"`
Hamdi Allam's avatar
Hamdi Allam committed
34
	TransactionSourceHash common.Hash `gorm:"serializer:bytes"`
35 36 37 38
}

type L2BridgeMessage struct {
	BridgeMessage             `gorm:"embedded"`
Hamdi Allam's avatar
Hamdi Allam committed
39
	TransactionWithdrawalHash common.Hash `gorm:"serializer:bytes"`
40 41 42
}

type BridgeMessagesView interface {
43 44
	L1BridgeMessage(common.Hash) (*L1BridgeMessage, error)
	L1BridgeMessageWithFilter(BridgeMessage) (*L1BridgeMessage, error)
45

46 47
	L2BridgeMessage(common.Hash) (*L2BridgeMessage, error)
	L2BridgeMessageWithFilter(BridgeMessage) (*L2BridgeMessage, error)
48 49 50 51 52
}

type BridgeMessagesDB interface {
	BridgeMessagesView

53
	StoreL1BridgeMessages([]L1BridgeMessage) error
54 55
	MarkRelayedL1BridgeMessage(common.Hash, uuid.UUID) error

56
	StoreL2BridgeMessages([]L2BridgeMessage) error
57 58 59 60 61 62 63 64
	MarkRelayedL2BridgeMessage(common.Hash, uuid.UUID) error
}

/**
 * Implementation
 */

type bridgeMessagesDB struct {
Hamdi Allam's avatar
Hamdi Allam committed
65
	log  log.Logger
66 67 68
	gorm *gorm.DB
}

Hamdi Allam's avatar
Hamdi Allam committed
69
func newBridgeMessagesDB(log log.Logger, db *gorm.DB) BridgeMessagesDB {
Hamdi Allam's avatar
Hamdi Allam committed
70
	return &bridgeMessagesDB{log: log.New("table", "bridge_messages"), gorm: db}
71 72 73 74 75 76
}

/**
 * Arbitrary Messages Sent from L1
 */

77
func (db bridgeMessagesDB) StoreL1BridgeMessages(messages []L1BridgeMessage) error {
Hamdi Allam's avatar
Hamdi Allam committed
78
	deduped := db.gorm.Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "message_hash"}}, DoNothing: true})
Hamdi Allam's avatar
Hamdi Allam committed
79
	result := deduped.Create(&messages)
80
	if result.Error == nil && int(result.RowsAffected) < len(messages) {
Hamdi Allam's avatar
Hamdi Allam committed
81 82 83
		db.log.Warn("ignored L1 bridge message duplicates", "duplicates", len(messages)-int(result.RowsAffected))
	}

84 85 86
	return result.Error
}

87 88
func (db bridgeMessagesDB) L1BridgeMessage(msgHash common.Hash) (*L1BridgeMessage, error) {
	return db.L1BridgeMessageWithFilter(BridgeMessage{MessageHash: msgHash})
89 90
}

91
func (db bridgeMessagesDB) L1BridgeMessageWithFilter(filter BridgeMessage) (*L1BridgeMessage, error) {
92
	var sentMessage L1BridgeMessage
93
	result := db.gorm.Where(&filter).Take(&sentMessage)
94 95 96 97 98 99 100 101 102 103 104
	if result.Error != nil {
		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, result.Error
	}

	return &sentMessage, nil
}

func (db bridgeMessagesDB) MarkRelayedL1BridgeMessage(messageHash common.Hash, relayEvent uuid.UUID) error {
105
	message, err := db.L1BridgeMessage(messageHash)
106 107 108
	if err != nil {
		return err
	} else if message == nil {
Hamdi Allam's avatar
Hamdi Allam committed
109 110 111 112 113 114 115
		return fmt.Errorf("L1BridgeMessage %s not found", messageHash)
	}

	if message.RelayedMessageEventGUID != nil && message.RelayedMessageEventGUID.ID() == relayEvent.ID() {
		return nil
	} else if message.RelayedMessageEventGUID != nil {
		return fmt.Errorf("relayed message %s re-relayed with a different event %d", messageHash, relayEvent)
116 117 118 119 120 121 122
	}

	message.RelayedMessageEventGUID = &relayEvent
	result := db.gorm.Save(message)
	return result.Error
}

123 124 125 126
/**
 * Arbitrary Messages Sent from L2
 */

127
func (db bridgeMessagesDB) StoreL2BridgeMessages(messages []L2BridgeMessage) error {
Hamdi Allam's avatar
Hamdi Allam committed
128
	deduped := db.gorm.Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "message_hash"}}, DoNothing: true})
Hamdi Allam's avatar
Hamdi Allam committed
129
	result := deduped.Create(&messages)
130 131
	if result.Error == nil && int(result.RowsAffected) < len(messages) {
		db.log.Warn("ignored L2 bridge message duplicates", "duplicates", len(messages)-int(result.RowsAffected))
Hamdi Allam's avatar
Hamdi Allam committed
132 133
	}

134 135 136
	return result.Error
}

137 138
func (db bridgeMessagesDB) L2BridgeMessage(msgHash common.Hash) (*L2BridgeMessage, error) {
	return db.L2BridgeMessageWithFilter(BridgeMessage{MessageHash: msgHash})
139 140
}

141
func (db bridgeMessagesDB) L2BridgeMessageWithFilter(filter BridgeMessage) (*L2BridgeMessage, error) {
142
	var sentMessage L2BridgeMessage
143
	result := db.gorm.Where(&filter).Take(&sentMessage)
144 145 146 147 148 149 150 151 152 153 154
	if result.Error != nil {
		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, result.Error
	}

	return &sentMessage, nil
}

func (db bridgeMessagesDB) MarkRelayedL2BridgeMessage(messageHash common.Hash, relayEvent uuid.UUID) error {
155
	message, err := db.L2BridgeMessage(messageHash)
156 157 158
	if err != nil {
		return err
	} else if message == nil {
Hamdi Allam's avatar
Hamdi Allam committed
159 160 161 162 163 164
		return fmt.Errorf("L2BridgeMessage %s not found", messageHash)
	}

	if message.RelayedMessageEventGUID != nil && message.RelayedMessageEventGUID.ID() == relayEvent.ID() {
		return nil
	} else if message.RelayedMessageEventGUID != nil {
Hamdi Allam's avatar
Hamdi Allam committed
165
		return fmt.Errorf("relayed message %s re-relayed with a different event %s", messageHash, relayEvent)
166 167 168 169 170 171
	}

	message.RelayedMessageEventGUID = &relayEvent
	result := db.gorm.Save(message)
	return result.Error
}