update VAA parsing and structure

Change-Id: Ifd712050645b1b3fad0f8bf5b2227c0fdf89df84
This commit is contained in:
Hendrik Hofstadt 2021-04-15 11:36:29 +02:00
parent 14c8606a8e
commit 844bd3d817
2 changed files with 35 additions and 235 deletions

View File

@ -49,8 +49,8 @@ func (p *Processor) handleLockup(ctx context.Context, k *common.MessagePublicati
)
lockupsObservedTotal.With(prometheus.Labels{
"source_chain": k.SourceChain.String(),
"target_chain": k.TargetChain.String()}).Add(1)
"emitter_chain": k.EmitterChain.String(),
}).Add(1)
// All nodes will create the exact same VAA and sign its digest.
// Consensus is established on this digest.
@ -60,19 +60,10 @@ func (p *Processor) handleLockup(ctx context.Context, k *common.MessagePublicati
GuardianSetIndex: p.gs.Index,
Signatures: nil,
Timestamp: k.Timestamp,
Payload: &vaa.BodyTransfer{
Nonce: k.Nonce,
SourceChain: k.SourceChain,
TargetChain: k.TargetChain,
SourceAddress: k.SourceAddress,
TargetAddress: k.TargetAddress,
Asset: &vaa.AssetMeta{
Chain: k.TokenChain,
Address: k.TokenAddress,
Decimals: k.TokenDecimals,
},
Amount: k.Amount,
},
Nonce: k.Nonce,
EmitterChain: k.EmitterChain,
EmitterAddress: k.EmitterAddress,
Payload: k.Payload,
}
// Generate digest of the unsigned VAA.
@ -87,16 +78,14 @@ func (p *Processor) handleLockup(ctx context.Context, k *common.MessagePublicati
panic(err)
}
p.logger.Info("observed and signed confirmed lockup",
zap.Stringer("source_chain", k.SourceChain),
zap.Stringer("target_chain", k.TargetChain),
p.logger.Info("observed and signed confirmed message publication",
zap.Stringer("source_chain", k.EmitterChain),
zap.Stringer("txhash", k.TxHash),
zap.String("digest", hex.EncodeToString(digest.Bytes())),
zap.String("signature", hex.EncodeToString(s)))
lockupsSignedTotal.With(prometheus.Labels{
"source_chain": k.SourceChain.String(),
"target_chain": k.TargetChain.String()}).Add(1)
"emitter_chain": k.EmitterChain.String()}).Add(1)
p.broadcastSignature(v, s)
}

View File

@ -7,7 +7,6 @@ import (
"encoding/hex"
"fmt"
"io"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
@ -26,8 +25,14 @@ type (
// Timestamp when the VAA was created
Timestamp time.Time
// Payload of the VAA. This describes the action to be performed
Payload vaaBody
// Nonce of the VAA
Nonce uint32
// EmitterChain the VAA was emitted on
EmitterChain ChainID
// EmitterAddress of the contract that emitted the Message
EmitterAddress Address
// Payload of the message
Payload []byte
}
// ChainID of a Wormhole chain
@ -46,52 +51,6 @@ type (
// Signature data
Signature [65]byte // TODO: hex marshaller
}
// AssetMeta describes an asset within the Wormhole protocol
AssetMeta struct {
// Chain is the ID of the chain the original version of the asset exists on
Chain ChainID
// Address is the address of the token contract/mint/equivalent.
Address Address
// Decimals is the number of decimals the token has
Decimals uint8
}
vaaBody interface {
getActionID() Action
serialize() ([]byte, error)
}
BodyTransfer struct {
// Nonce is a user given unique nonce for this transfer
Nonce uint32
// SourceChain is the id of the chain the transfer was initiated from
SourceChain ChainID
// TargetChain is the id of the chain the transfer is directed to
TargetChain ChainID
// TargetAddress is the address of the sender on SourceChain
SourceAddress Address
// TargetAddress is the address of the recipient on TargetChain
TargetAddress Address
// Asset is the asset to be transferred
Asset *AssetMeta
// Amount is the amount of tokens to be transferred
Amount *big.Int
}
BodyGuardianSetUpdate struct {
// Key is the new guardian set key
Keys []common.Address
// NewIndex is the index of the new guardian set
NewIndex uint32
}
BodyContractUpgrade struct {
// ChainID is the chain on which the contract should be upgraded
ChainID uint8
// NewContract is the address of the account containing the new contract.
NewContract Address
}
)
func (a Address) String() string {
@ -112,10 +71,6 @@ func (c ChainID) String() string {
}
const (
ActionGuardianSetUpdate Action = 0x01
ActionContractUpgrade Action = 0x02
ActionTransfer Action = 0x10
// ChainIDSolana is the ChainID of Solana
ChainIDSolana = 1
// ChainIDEthereum is the ChainID of Ethereum
@ -174,30 +129,25 @@ func Unmarshal(data []byte) (*VAA, error) {
}
v.Timestamp = time.Unix(int64(unixSeconds), 0)
var (
action uint8
)
if err := binary.Read(reader, binary.BigEndian, &action); err != nil {
return nil, fmt.Errorf("failed to read action: %w", err)
if err := binary.Read(reader, binary.BigEndian, &v.Nonce); err != nil {
return nil, fmt.Errorf("failed to read nonce: %w", err)
}
currentPos := len(data) - reader.Len()
if err := binary.Read(reader, binary.BigEndian, &v.EmitterChain); err != nil {
return nil, fmt.Errorf("failed to read emitter chain: %w", err)
}
payloadReader := bytes.NewReader(data[currentPos:])
var err error
switch Action(action) {
case ActionGuardianSetUpdate:
v.Payload, err = parseBodyGuardianSetUpdate(payloadReader)
case ActionTransfer:
v.Payload, err = parseBodyTransfer(payloadReader)
case ActionContractUpgrade:
v.Payload, err = parseBodyContractUpgrade(payloadReader)
default:
return nil, fmt.Errorf("unknown action: %d", action)
emitterAddress := Address{}
if n, err := reader.Read(emitterAddress[:]); err != nil || n != 32 {
return nil, fmt.Errorf("failed to read emitter address [%d]: %w", n, err)
}
if err != nil {
return nil, fmt.Errorf("failed to parse payload: %w", err)
payload := make([]byte, 1000)
n, err := reader.Read(payload)
if err != nil || n == 0 {
return nil, fmt.Errorf("failed to read payload [%d]: %w", n, err)
}
v.Payload = payload[:n]
return v, nil
}
@ -275,14 +225,10 @@ func (v *VAA) Marshal() ([]byte, error) {
func (v *VAA) serializeBody() ([]byte, error) {
buf := new(bytes.Buffer)
MustWrite(buf, binary.BigEndian, uint32(v.Timestamp.Unix()))
MustWrite(buf, binary.BigEndian, v.Payload.getActionID())
payloadData, err := v.Payload.serialize()
if err != nil {
return nil, fmt.Errorf("failed to serialize payload: %w", err)
}
buf.Write(payloadData)
MustWrite(buf, binary.BigEndian, v.Nonce)
MustWrite(buf, binary.BigEndian, v.EmitterChain)
buf.Write(v.EmitterAddress[:])
buf.Write(v.Payload)
return buf.Bytes(), nil
}
@ -305,141 +251,6 @@ func (v *VAA) AddSignature(key *ecdsa.PrivateKey, index uint8) {
})
}
func parseBodyTransfer(r io.Reader) (*BodyTransfer, error) {
b := &BodyTransfer{}
if err := binary.Read(r, binary.BigEndian, &b.Nonce); err != nil {
return nil, fmt.Errorf("failed to read nonce: %w", err)
}
if err := binary.Read(r, binary.BigEndian, &b.SourceChain); err != nil {
return nil, fmt.Errorf("failed to read source chain: %w", err)
}
if err := binary.Read(r, binary.BigEndian, &b.TargetChain); err != nil {
return nil, fmt.Errorf("failed to read target chain: %w", err)
}
if n, err := r.Read(b.SourceAddress[:]); err != nil || n != 32 {
return nil, fmt.Errorf("failed to read source address: %w", err)
}
if n, err := r.Read(b.TargetAddress[:]); err != nil || n != 32 {
return nil, fmt.Errorf("failed to read target address: %w", err)
}
b.Asset = &AssetMeta{}
if err := binary.Read(r, binary.BigEndian, &b.Asset.Chain); err != nil {
return nil, fmt.Errorf("failed to read asset chain: %w", err)
}
if n, err := r.Read(b.Asset.Address[:]); err != nil || n != 32 {
return nil, fmt.Errorf("failed to read asset address: %w", err)
}
if err := binary.Read(r, binary.BigEndian, &b.Asset.Decimals); err != nil {
return nil, fmt.Errorf("failed to read asset decimals: %w", err)
}
var amountBytes [32]byte
if n, err := r.Read(amountBytes[:]); err != nil || n != 32 {
return nil, fmt.Errorf("failed to read amount: %w", err)
}
b.Amount = new(big.Int).SetBytes(amountBytes[:])
return b, nil
}
func (v *BodyTransfer) getActionID() Action {
return ActionTransfer
}
func (v *BodyTransfer) serialize() ([]byte, error) {
buf := new(bytes.Buffer)
MustWrite(buf, binary.BigEndian, v.Nonce)
MustWrite(buf, binary.BigEndian, v.SourceChain)
MustWrite(buf, binary.BigEndian, v.TargetChain)
buf.Write(v.SourceAddress[:])
buf.Write(v.TargetAddress[:])
if v.Asset == nil {
return nil, fmt.Errorf("asset is empty")
}
MustWrite(buf, binary.BigEndian, v.Asset.Chain)
buf.Write(v.Asset.Address[:])
MustWrite(buf, binary.BigEndian, v.Asset.Decimals)
if v.Amount == nil {
return nil, fmt.Errorf("amount is empty")
}
buf.Write(common.LeftPadBytes(v.Amount.Bytes(), 32))
return buf.Bytes(), nil
}
func parseBodyGuardianSetUpdate(r io.Reader) (*BodyGuardianSetUpdate, error) {
b := &BodyGuardianSetUpdate{}
if err := binary.Read(r, binary.BigEndian, &b.NewIndex); err != nil {
return nil, fmt.Errorf("failed to read new index: %w", err)
}
keyLen := uint8(0)
if err := binary.Read(r, binary.BigEndian, &keyLen); err != nil {
return nil, fmt.Errorf("failed to read guardianset key len: %w", err)
}
for i := 0; i < int(keyLen); i++ {
key := common.Address{}
if n, err := r.Read(key[:]); err != nil || n != 20 {
return nil, fmt.Errorf("failed to read guardianset key [%d]: %w", i, err)
}
b.Keys = append(b.Keys, key)
}
return b, nil
}
func (v *BodyGuardianSetUpdate) getActionID() Action {
return ActionGuardianSetUpdate
}
func (v *BodyGuardianSetUpdate) serialize() ([]byte, error) {
buf := new(bytes.Buffer)
MustWrite(buf, binary.BigEndian, v.NewIndex)
MustWrite(buf, binary.BigEndian, uint8(len(v.Keys)))
for _, key := range v.Keys {
buf.Write(key.Bytes())
}
return buf.Bytes(), nil
}
func parseBodyContractUpgrade(r io.Reader) (*BodyContractUpgrade, error) {
b := &BodyContractUpgrade{}
if err := binary.Read(r, binary.BigEndian, &b.ChainID); err != nil {
return nil, fmt.Errorf("failed to read chain id: %w", err)
}
if n, err := r.Read(b.NewContract[:]); err != nil || n != 32 {
return nil, fmt.Errorf("failed to read new contract address: %w", err)
}
return b, nil
}
func (v *BodyContractUpgrade) getActionID() Action {
return ActionContractUpgrade
}
func (v *BodyContractUpgrade) serialize() ([]byte, error) {
buf := new(bytes.Buffer)
MustWrite(buf, binary.BigEndian, v.ChainID)
buf.Write(v.NewContract[:])
return buf.Bytes(), nil
}
// MustWrite calls binary.Write and panics on errors
func MustWrite(w io.Writer, order binary.ByteOrder, data interface{}) {
if err := binary.Write(w, order, data); err != nil {