From 02d7257ff59e3246913b391b70a7652226211bbf Mon Sep 17 00:00:00 2001 From: Hendrik Hofstadt Date: Fri, 18 Jun 2021 18:50:06 +0200 Subject: [PATCH] Implement PostedMessage fetching in the client Change-Id: I8b59cdbfaf37212e187257cf46674086ad96d19d --- bridge/go.mod | 1 + bridge/go.sum | 2 + bridge/pkg/common/chainlock.go | 1 + bridge/pkg/solana/client.go | 81 ++++++------------------------ bridge/pkg/vaa/structs.go | 4 +- solana/bridge/program/src/types.rs | 57 ++++++++++++++++++++- 6 files changed, 78 insertions(+), 68 deletions(-) diff --git a/bridge/go.mod b/bridge/go.mod index 7fb8f72ac..e1cd0c417 100644 --- a/bridge/go.mod +++ b/bridge/go.mod @@ -49,6 +49,7 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/mr-tron/base58 v1.2.0 github.com/multiformats/go-multiaddr v0.3.1 + github.com/near/borsh-go v0.3.0 github.com/olekukonko/tablewriter v0.0.4 // indirect github.com/pborman/uuid v1.2.1 // indirect github.com/peterh/liner v1.2.1 // indirect diff --git a/bridge/go.sum b/bridge/go.sum index 939ab2c1f..792056020 100644 --- a/bridge/go.sum +++ b/bridge/go.sum @@ -1009,6 +1009,8 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/near/borsh-go v0.3.0 h1:+DvG7eApOD3KrHIh7TwZvYzhXUF/OzMTC6aRTUEtW+8= +github.com/near/borsh-go v0.3.0/go.mod h1:NeMochZp7jN/pYFuxLkrZtmLqbADmnp/y1+/dL+AsyQ= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= diff --git a/bridge/pkg/common/chainlock.go b/bridge/pkg/common/chainlock.go index 6cbbcc684..41b0af322 100644 --- a/bridge/pkg/common/chainlock.go +++ b/bridge/pkg/common/chainlock.go @@ -12,6 +12,7 @@ type MessagePublication struct { Timestamp time.Time Nonce uint32 + Sequence uint64 EmitterChain vaa.ChainID EmitterAddress vaa.Address Payload []byte diff --git a/bridge/pkg/solana/client.go b/bridge/pkg/solana/client.go index 6588d8373..55455f774 100644 --- a/bridge/pkg/solana/client.go +++ b/bridge/pkg/solana/client.go @@ -1,10 +1,7 @@ package ethereum import ( - "bytes" "context" - "encoding/binary" - "fmt" "github.com/certusone/wormhole/bridge/pkg/common" "github.com/certusone/wormhole/bridge/pkg/p2p" gossipv1 "github.com/certusone/wormhole/bridge/pkg/proto/gossip/v1" @@ -14,9 +11,9 @@ import ( "github.com/dfuse-io/solana-go/rpc" eth_common "github.com/ethereum/go-ethereum/common" "github.com/mr-tron/base58" + "github.com/near/borsh-go" "github.com/prometheus/client_golang/prometheus" "go.uber.org/zap" - "math/big" "time" ) @@ -115,13 +112,10 @@ func (s *SolanaWatcher) Run(ctx context.Context) error { accounts, err := rpcClient.GetProgramAccounts(rCtx, s.bridge, &rpc.GetProgramAccountsOpts{ Commitment: rpc.CommitmentMax, // TODO: deprecated, use Finalized Filters: []rpc.RPCFilter{ - { - DataSize: 1184, // Search for MessagePublicationAccount accounts - }, { Memcmp: &rpc.RPCFilterMemcmp{ - Offset: 1140, // Offset of VaaTime - Bytes: solana.Base58{0, 0, 0, 0}, // VAA time is 0 when no VAA is present + Offset: 0, // Offset of VaaTime + Bytes: solana.Base58{'m', 's', 'g'}, // Prefix of the posted message accounts }, }, }, @@ -151,7 +145,7 @@ func (s *SolanaWatcher) Run(ctx context.Context) error { } // VAA submitted - if proposal.VaaTime.Unix() != 0 { + if proposal.VaaTime != 0 { solanaAccountSkips.WithLabelValues("is_submitted_vaa").Inc() continue } @@ -160,17 +154,13 @@ func (s *SolanaWatcher) Run(ctx context.Context) error { copy(txHash[:], acc.Pubkey[:]) lock := &common.MessagePublication{ - TxHash: txHash, - Timestamp: proposal.LockupTime, - Nonce: proposal.Nonce, - SourceAddress: proposal.SourceAddress, - TargetAddress: proposal.ForeignAddress, - SourceChain: vaa.ChainIDSolana, - TargetChain: proposal.ToChainID, - TokenChain: proposal.Asset.Chain, - TokenAddress: proposal.Asset.Address, - TokenDecimals: proposal.Asset.Decimals, - Amount: proposal.Amount, + TxHash: txHash, + Timestamp: time.Unix(int64(proposal.SubmissionTime), 0), + Nonce: proposal.Nonce, + Sequence: proposal.Sequence, + EmitterChain: proposal.EmitterChain, + EmitterAddress: proposal.EmitterAddress, + Payload: proposal.Payload, } solanaLockupsConfirmed.Inc() @@ -193,10 +183,11 @@ func (s *SolanaWatcher) Run(ctx context.Context) error { type ( MessagePublicationAccount struct { VaaVersion uint8 - VaaTime time.Time + VaaTime uint32 VaaSignatureAccount vaa.Address - SubmissionTime time.Time + SubmissionTime uint32 Nonce uint32 + Sequence uint64 EmitterChain vaa.ChainID EmitterAddress vaa.Address Payload []byte @@ -205,49 +196,9 @@ type ( func ParseTransferOutProposal(data []byte) (*MessagePublicationAccount, error) { prop := &MessagePublicationAccount{} - r := bytes.NewBuffer(data) - - // Skip initialized bool - r.Next(1) - - if err := binary.Read(r, binary.LittleEndian, &prop.VaaVersion); err != nil { - return nil, fmt.Errorf("failed to read to vaa version: %w", err) + if err := borsh.Deserialize(prop, data); err != nil { + return nil, err } - var vaaTime uint32 - if err := binary.Read(r, binary.LittleEndian, &vaaTime); err != nil { - return nil, fmt.Errorf("failed to read vaa time: %w", err) - } - prop.VaaTime = time.Unix(int64(vaaTime), 0) - - if n, err := r.Read(prop.VaaSignatureAccount[:]); err != nil || n != 32 { - return nil, fmt.Errorf("failed to read signature account: %w", err) - } - - var submissionTime uint32 - if err := binary.Read(r, binary.LittleEndian, &submissionTime); err != nil { - return nil, fmt.Errorf("failed to read lockup time: %w", err) - } - prop.SubmissionTime = time.Unix(int64(submissionTime), 0) - - if err := binary.Read(r, binary.LittleEndian, &prop.Nonce); err != nil { - return nil, fmt.Errorf("failed to read nonce: %w", err) - } - - if err := binary.Read(r, binary.LittleEndian, &prop.EmitterChain); err != nil { - return nil, fmt.Errorf("failed to read emitter chain: %w", err) - } - - if n, err := r.Read(prop.EmitterAddress[:]); err != nil || n != 32 { - return nil, fmt.Errorf("failed to read emitter address: %w", err) - } - - payload := make([]byte, 1000) - n, err := r.Read(payload) - if err != nil || n == 0 { - return nil, fmt.Errorf("failed to read vaa: %w", err) - } - prop.Payload = payload[:n] - return prop, nil } diff --git a/bridge/pkg/vaa/structs.go b/bridge/pkg/vaa/structs.go index 97b7beea5..a1576eb8b 100644 --- a/bridge/pkg/vaa/structs.go +++ b/bridge/pkg/vaa/structs.go @@ -27,6 +27,8 @@ type ( Timestamp time.Time // Nonce of the VAA Nonce uint32 + // Sequence of the VAA + Sequence uint64 // EmitterChain the VAA was emitted on EmitterChain ChainID // EmitterAddress of the contract that emitted the Message @@ -36,7 +38,7 @@ type ( } // ChainID of a Wormhole chain - ChainID uint8 + ChainID uint16 // Action of a VAA Action uint8 diff --git a/solana/bridge/program/src/types.rs b/solana/bridge/program/src/types.rs index 189a86e3a..22e12e0d5 100644 --- a/solana/bridge/program/src/types.rs +++ b/solana/bridge/program/src/types.rs @@ -1,5 +1,9 @@ use crate::{ - api::ForeignAddress, + api::{ + ForeignAddress, + PostMessage, + PostMessageData, + }, vaa::{ DeserializeGovernancePayload, DeserializePayload, @@ -26,6 +30,11 @@ use std::{ io::{ Cursor, Read, + Write, + }, + ops::{ + Deref, + DerefMut, }, str::FromStr, }; @@ -104,8 +113,52 @@ impl Owned for SignatureSet { } } +#[repr(transparent)] +pub struct PostedMessage(pub PostedMessageData); + +impl BorshSerialize for PostedMessage { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + writer.write(&['m' as u8, 's' as u8, 'g' as u8]); + BorshSerialize::serialize(&self.0, writer) + } +} + +impl BorshDeserialize for PostedMessage { + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + Ok(PostedMessage( + ::deserialize(&mut &buf[3..])?, + )) + } +} + +impl Deref for PostedMessage { + type Target = PostedMessageData; + + fn deref(&self) -> &Self::Target { + unsafe { std::mem::transmute(&self.0) } + } +} + +impl DerefMut for PostedMessage { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { std::mem::transmute(&mut self.0) } + } +} + +impl Default for PostedMessage { + fn default() -> Self { + PostedMessage(PostedMessageData::default()) + } +} + +impl Clone for PostedMessage { + fn clone(&self) -> Self { + PostedMessage(self.0.clone()) + } +} + #[derive(Default, BorshSerialize, BorshDeserialize, Clone)] -pub struct PostedMessage { +pub struct PostedMessageData { /// Header of the posted VAA pub vaa_version: u8,