diff --git a/serv/config/config.yaml b/serv/config/config.yaml index ab32d332..2e654766 100644 --- a/serv/config/config.yaml +++ b/serv/config/config.yaml @@ -27,15 +27,17 @@ blockchain: InitBlockHash: 0x25fc3eee09cdba915458cf3c5a836e06eb9835596426347f2a5b0e70b0a58cf8 MessageAddress: 0xc7441Ac47596D1356fcc70062dA0462FcA98E14e Senders: 67f20dc3b0842117c049b292dd88794b3321c95a1b607e735be88c34327420ba + Validators: 67f20dc3b0842117c049b292dd88794b3321c95a1b607e735be88c34327420ba BlockInterval: 1000 # BusinessContract: 0x91171cf194a4B66Bd459Ada038397c7e890FB9D4 Events: 0x599c34a8d0b3638870afcfe3d7d8125602721889a7535cda986ea656e63fc38c,0x5849ae3f4bc77f0ebd2d6db4ff282f91f2191d3df4493e63176c2ed22fb81852 - ChainId: 421614 RpcUrl: https://arbitrum-sepolia.blockpi.network/v1/rpc/public - InitBlockNumber: 66437500 - InitBlockHash: 0x372ed339d0e8f061696d02afdf50d8b162962694b01e025ecdec290d7b84acec + InitBlockNumber: 68116018 + InitBlockHash: 0xf9e7820aca608ccb6131b56ade99617bca6e96b91cca80b92e026cd26cc91714 MessageAddress: 0x2A82058E46151E337Baba56620133FC39BD5B71F Senders: 0x7990759362da82e88493fd64d058c5e011253ceb45902986590ef4acb0e97706 + Validators: 0x7990759362da82e88493fd64d058c5e011253ceb45902986590ef4acb0e97706 BlockInterval: 250 # BusinessContract: 0x8Ac2C830532d7203a12C4C32C0BE4d3d15917534 Events: 0x599c34a8d0b3638870afcfe3d7d8125602721889a7535cda986ea656e63fc38c,0x5849ae3f4bc77f0ebd2d6db4ff282f91f2191d3df4493e63176c2ed22fb81852 \ No newline at end of file diff --git a/serv/go.mod b/serv/go.mod index 65f8a6e3..1af49a54 100644 --- a/serv/go.mod +++ b/serv/go.mod @@ -47,6 +47,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/google/uuid v1.4.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/holiman/uint256 v1.2.4 // indirect @@ -69,6 +70,7 @@ require ( github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/storyicon/sigverify v1.1.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/supranational/blst v0.3.11 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect diff --git a/serv/go.sum b/serv/go.sum index ff94e0a7..18d3cd16 100644 --- a/serv/go.sum +++ b/serv/go.sum @@ -243,6 +243,8 @@ github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= +github.com/storyicon/sigverify v1.1.0 h1:Fz153Jvloz1P0G3TrG7dHGyAlB3mpjmFeu5IszfJWQ0= +github.com/storyicon/sigverify v1.1.0/go.mod h1:q0qxvhdUsMIBAry3h7/IMW7BebRkiT8496TrQP1XW5s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/serv/internal/config/config.go b/serv/internal/config/config.go index bfbdd1d7..9abeb91e 100644 --- a/serv/internal/config/config.go +++ b/serv/internal/config/config.go @@ -38,6 +38,7 @@ type Blockchain struct { MessageAddress string Events string Senders string + Validators string BlockInterval int64 } diff --git a/serv/internal/enums/message.go b/serv/internal/enums/message.go index f200410c..8a32d94c 100644 --- a/serv/internal/enums/message.go +++ b/serv/internal/enums/message.go @@ -11,7 +11,8 @@ const ( type MessageStatus int64 const ( - MessageStatusPending MessageStatus = iota + MessageStatusValidating MessageStatus = iota + MessageStatusPending MessageStatusBroadcast MessageStatusValid MessageStatusInvalid diff --git a/serv/internal/listener/build.go b/serv/internal/listener/build.go index 3bbea32d..1f1becbe 100644 --- a/serv/internal/listener/build.go +++ b/serv/internal/listener/build.go @@ -7,6 +7,7 @@ import ( "bytes" "context" "encoding/hex" + "encoding/json" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -76,7 +77,13 @@ func (l *Listener) buildMessage(message models.Message) error { toAddress := common.HexToAddress(l.Blockchain.MessageAddress) log.Debugf("toAddress: %v\n", toAddress) - data := msg.Send(message.FromChainId, message.FromId, message.FromSender, message.ToContractAddress, message.ToBytes, nil) + var signatures []string + err = json.Unmarshal([]byte(message.Signatures), &signatures) + if err != nil { + return errors.WithStack(err) + } + + data := msg.Send(message.FromChainId, message.FromId, message.FromSender, message.ToContractAddress, message.ToBytes, signatures) log.Debugf("data: %x\n", data) gasLimit, err := l.RPC.EstimateGas(context.Background(), ethereum.CallMsg{ From: common.HexToAddress(UserAddress), diff --git a/serv/internal/listener/listener.go b/serv/internal/listener/listener.go index 28c133cc..de3f40ee 100644 --- a/serv/internal/listener/listener.go +++ b/serv/internal/listener/listener.go @@ -8,10 +8,11 @@ import ( ) type DataMap struct { - Events []common.Hash - Contracts []common.Address - EventMap map[common.Hash][]Event - SenderMap map[string]string + Events []common.Hash + Contracts []common.Address + EventMap map[common.Hash][]Event + SenderMap map[string]string + ValidatorMap map[string]string } type Listener struct { @@ -36,10 +37,11 @@ func NewListener(db *gorm.DB, cache *config.Cache, blockchain config.Blockchain) SyncedBlockNumber: 0, SyncedBlockHash: common.HexToHash("0x0"), DataMap: DataMap{ - Events: make([]common.Hash, 0), - Contracts: make([]common.Address, 0), - EventMap: make(map[common.Hash][]Event, 0), - SenderMap: make(map[string]string, 0), + Events: make([]common.Hash, 0), + Contracts: make([]common.Address, 0), + EventMap: make(map[common.Hash][]Event, 0), + SenderMap: make(map[string]string, 0), + ValidatorMap: make(map[string]string, 0), }, } } @@ -47,15 +49,16 @@ func NewListener(db *gorm.DB, cache *config.Cache, blockchain config.Blockchain) func (l *Listener) Run() { l.loadAccounts() l.AutoRegister() - go l.syncLatestBlockNumber() - go l.syncBlock() - go l.syncEvent() - go l.syncTask() - go l.checkBlock() - go l.migrateBlock() - go l.migrateEvent() - go l.consume() - go l.confirm() - go l.broadcast() - go l.build() + //go l.syncLatestBlockNumber() + //go l.syncBlock() + //go l.syncEvent() + //go l.syncTask() + //go l.checkBlock() + //go l.migrateBlock() + //go l.migrateEvent() + //go l.consume() + //go l.confirm() + //go l.broadcast() + //go l.build() + //go l.validate() } diff --git a/serv/internal/listener/register.go b/serv/internal/listener/register.go index e40f63c9..ef929584 100644 --- a/serv/internal/listener/register.go +++ b/serv/internal/listener/register.go @@ -34,11 +34,20 @@ func (l *Listener) loadAccounts() { for i, accountKey := range senders { accountAddress, err := l.ToAddress(accountKey) if err != nil { - log.Panicf("WithdrawAccounts[%d] invalid", i) + log.Panicf("senders[%d] invalid", i) continue } l.DataMap.SenderMap[accountAddress] = accountKey } + validators := strings.Split(l.Blockchain.Validators, ",") + for i, accountKey := range validators { + accountAddress, err := l.ToAddress(accountKey) + if err != nil { + log.Panicf("validators[%d] invalid", i) + continue + } + l.DataMap.ValidatorMap[accountAddress] = accountKey + } } func (l *Listener) ToAddress(accountKey string) (string, error) { diff --git a/serv/internal/listener/validate.go b/serv/internal/listener/validate.go new file mode 100644 index 00000000..6fb54e1b --- /dev/null +++ b/serv/internal/listener/validate.go @@ -0,0 +1,82 @@ +package listener + +import ( + "bsquared.network/b2-message-channel-serv/internal/enums" + "bsquared.network/b2-message-channel-serv/internal/models" + "bsquared.network/b2-message-channel-serv/internal/utils/message" + "encoding/json" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + "sync" + "time" +) + +func (l *Listener) validate() { + duration := time.Millisecond * time.Duration(l.Blockchain.BlockInterval) + for { + list, err := l.pendingCallMessage(10) + if err != nil { + log.Errorf("Get pending call message err: %s\n", err) + time.Sleep(duration) + continue + } + if len(list) == 0 { + log.Infof("Get pending call message length is 0\n") + time.Sleep(duration) + continue + } + + var wg sync.WaitGroup + for _, message := range list { + wg.Add(1) + go func(_wg *sync.WaitGroup, message models.Message) { + defer _wg.Done() + err = l.validateMessage(message) + if err != nil { + log.Errorf("Handle err: %v, %v\n", err, message) + } + }(&wg, message) + } + wg.Wait() + } +} + +func (l *Listener) validateMessage(msg models.Message) error { + var signatures []string + for _, validatorKey := range l.DataMap.ValidatorMap { + _key, err := crypto.ToECDSA(common.FromHex(validatorKey)) + if err != nil { + return errors.WithStack(err) + } + signature, err := message.SignMessageSend(l.Blockchain.ChainId, l.Blockchain.MessageAddress, msg.FromChainId, msg.FromId, msg.FromSender, msg.ToChainId, msg.ToContractAddress, msg.ToBytes, _key) + if err != nil { + return errors.WithStack(err) + } + signatures = append(signatures, signature) + } + + _signatures, err := json.Marshal(&signatures) + if err != nil { + return errors.WithStack(err) + } + + err = l.Db.Where("`id`=? AND `status`=?", msg.Id, enums.MessageStatusValidating).Updates(map[string]interface{}{ + "signatures": string(_signatures), + "status": enums.MessageStatusPending, + }).Error + if err != nil { + return errors.WithStack(err) + } + return nil +} + +func (l *Listener) validatingCallMessage(limit int) ([]models.Message, error) { + var list []models.Message + err := l.Db.Where("`to_chain_id`=? AND `type`=? AND `status`=?", l.Blockchain.ChainId, enums.MessageTypeCall, enums.MessageStatusValidating).Limit(limit).Find(&list).Error + if err != nil { + return nil, err + } + return list, nil +} diff --git a/serv/internal/models/messages.go b/serv/internal/models/messages.go index 9e9400f6..43a41b46 100644 --- a/serv/internal/models/messages.go +++ b/serv/internal/models/messages.go @@ -13,6 +13,7 @@ type Message struct { ToChainId int64 `json:"to_chain_id"` ToContractAddress string `json:"to_contract_address"` ToBytes string `json:"to_bytes"` + Signatures string `json:"signatures"` Status enums.MessageStatus `json:"status"` Blockchain } diff --git a/serv/internal/utils/message/message.go b/serv/internal/utils/message/message.go new file mode 100644 index 00000000..bd6820b4 --- /dev/null +++ b/serv/internal/utils/message/message.go @@ -0,0 +1,94 @@ +package message + +import ( + "crypto/ecdsa" + "encoding/json" + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/signer/core/apitypes" + "github.com/pkg/errors" + "github.com/storyicon/sigverify" +) + +const MessageSendTypedData = `{ + "types":{ + "EIP712Domain":[ + { + "name":"name", + "type":"string" + }, + { + "name":"version", + "type":"string" + }, + { + "name":"chainId", + "type":"uint256" + }, + { + "name":"verifyingContract", + "type":"address" + } + ], + "Send":[ + { + "name":"from_chain_id", + "type":"uint256" + }, + { + "name":"from_id", + "type":"uint256" + }, + { + "name":"from_sender", + "type":"address" + }, + { + "name":"to_chain_id", + "type":"uint256" + }, + { + "name":"contract_address", + "type":"address" + }, + { + "name":"data", + "type":"bytes" + } + ] + }, + "domain":{ + "name":"B2MessageBridge", + "version":"1", + "chainId":"%d", + "verifyingContract":"%s" + }, + "primaryType":"Send", + "message":{ + "from_chain_id":"%s", + "from_id":"%s", + "from_sender":"%s", + "to_chain_id":"%s", + "contract_address":"%s", + "data":%s + } +}` + +func SignMessageSend(chainId int64, messageContract string, fromChainId int64, fromId int64, fromSender string, toChainId int64, contractAddress string, data string, key *ecdsa.PrivateKey) (string, error) { + _data := fmt.Sprintf(MessageSendTypedData, chainId, messageContract, fromChainId, fromId, fromSender, toChainId, contractAddress, data) + var typedData apitypes.TypedData + if err := json.Unmarshal([]byte(_data), &typedData); err != nil { + return "", errors.WithStack(err) + } + _, originHash, err := sigverify.HashTypedData(typedData) + fmt.Println("originHash", common.Bytes2Hex(originHash)) + if err != nil { + return "", errors.WithStack(err) + } + sig, err := crypto.Sign(originHash, key) + if err != nil { + return "", errors.WithStack(err) + } + return common.Bytes2Hex(sig), nil +}