add BatchVAA structs to Go sdk(#1700)
* go sdk - BatchVAA structs * make VAA body unmarshaling DRY * implement binary encoding interfaces for VAAs * validate observation length before unmarshaling * move shared VerifySignatures logic to new function * make SigningMsg a delegate call * normalize ID of vaa types * add BatchVAA version to signingBody * add Attestation interface with shared VAA methods * add data integrity checks to batchVAA unmarshal
This commit is contained in:
parent
346b68582a
commit
e4096297ae
|
@ -3,6 +3,7 @@ package vaa
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"encoding"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -42,6 +43,27 @@ type (
|
||||||
Payload []byte
|
Payload []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BatchVAA struct {
|
||||||
|
// Version of the VAA schema
|
||||||
|
Version uint8
|
||||||
|
// GuardianSetIndex is the index of the guardian set that signed this VAA
|
||||||
|
GuardianSetIndex uint32
|
||||||
|
// SignatureData is the signature of the guardian set
|
||||||
|
Signatures []*Signature
|
||||||
|
|
||||||
|
// EmitterChain the VAAs were emitted on
|
||||||
|
EmitterChain ChainID
|
||||||
|
|
||||||
|
// The chain-native identifier of the transaction that created the batch VAA.
|
||||||
|
TransactionID common.Hash
|
||||||
|
|
||||||
|
// array of Observation VAA hashes
|
||||||
|
Hashes []common.Hash
|
||||||
|
|
||||||
|
// Observations in the batch
|
||||||
|
Observations []*Observation
|
||||||
|
}
|
||||||
|
|
||||||
// ChainID of a Wormhole chain
|
// ChainID of a Wormhole chain
|
||||||
ChainID uint16
|
ChainID uint16
|
||||||
// Action of a VAA
|
// Action of a VAA
|
||||||
|
@ -61,6 +83,13 @@ type (
|
||||||
|
|
||||||
SignatureData [65]byte
|
SignatureData [65]byte
|
||||||
|
|
||||||
|
Observation struct {
|
||||||
|
// Index of the observation in a Batch array
|
||||||
|
Index uint8
|
||||||
|
// Signed Observation data
|
||||||
|
Observation *VAA
|
||||||
|
}
|
||||||
|
|
||||||
TransferPayloadHdr struct {
|
TransferPayloadHdr struct {
|
||||||
Type uint8
|
Type uint8
|
||||||
Amount *big.Int
|
Amount *big.Int
|
||||||
|
@ -69,6 +98,20 @@ type (
|
||||||
TargetAddress Address
|
TargetAddress Address
|
||||||
TargetChain ChainID
|
TargetChain ChainID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attestation interface contains the methods common to all VAA types
|
||||||
|
Attestation interface {
|
||||||
|
encoding.BinaryMarshaler
|
||||||
|
encoding.BinaryUnmarshaler
|
||||||
|
serializeBody()
|
||||||
|
signingBody() []byte
|
||||||
|
SigningMsg() common.Hash
|
||||||
|
VerifySignatures(addrs []common.Address) bool
|
||||||
|
UniqueID() string
|
||||||
|
HexDigest() string
|
||||||
|
AddSignature(key *ecdsa.PrivateKey, index uint8)
|
||||||
|
GetEmitterChain() ChainID
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -287,60 +330,35 @@ const (
|
||||||
// - sequence (8 bytes)
|
// - sequence (8 bytes)
|
||||||
// - consistency level (1 byte)
|
// - consistency level (1 byte)
|
||||||
// - payload (0 bytes)
|
// - payload (0 bytes)
|
||||||
//
|
// BATCH
|
||||||
// From Above: 1 + 4 + 1 + 0 + 4 + 4 + 2 + 32 + 8 + 1 + 0 // Equals 57
|
// - Length of Observation Hashes (1 byte) <== minimum one
|
||||||
|
// - Observation Hash (32 bytes)
|
||||||
|
// - Length of Observations (1 byte) <== minimum one
|
||||||
|
// - Observation Index (1 byte)
|
||||||
|
// - Observation Length (1 byte)
|
||||||
|
// - Observation, aka BODY, aka Headless (51 bytes)
|
||||||
|
// From Above:
|
||||||
|
// HEADER: 1 + 4 + 1 + 0 = 6
|
||||||
|
// BODY: 4 + 4 + 2 + 32 + 8 + 1 + 0 = 51
|
||||||
|
// BATCH: 1 + 32 + 1 + 1 + 1 + 51 = 88
|
||||||
//
|
//
|
||||||
// More details here: https://docs.wormholenetwork.com/wormhole/vaas
|
// More details here: https://docs.wormholenetwork.com/wormhole/vaas
|
||||||
minVAALength = 57
|
minHeadlessVAALength = 51 // HEADER
|
||||||
|
minVAALength = 57 // HEADER + BODY
|
||||||
|
minBatchVAALength = 94 // HEADER + BATCH
|
||||||
|
|
||||||
SupportedVAAVersion = 0x01
|
SupportedVAAVersion = 0x01
|
||||||
|
BatchVAAVersion = 0x02
|
||||||
|
|
||||||
InternalTruncatedPayloadSafetyLimit = 1000
|
InternalTruncatedPayloadSafetyLimit = 1000
|
||||||
)
|
)
|
||||||
|
|
||||||
// Unmarshal deserializes the binary representation of a VAA
|
// UnmarshalBody deserializes the binary representation of a VAA's "BODY" properties
|
||||||
|
// The BODY fields are common among multiple types of VAA - v1, v2 (BatchVAA), etc
|
||||||
//
|
//
|
||||||
// WARNING: Unmarshall will truncate payloads at 1000 bytes, this is done mainly to avoid denial of service
|
// WARNING: UnmarshallBody will truncate payloads at 1000 bytes, this is done mainly to avoid denial of service
|
||||||
// - If you need to access the full payload, consider parsing VAA from Bytes instead of Unmarshal
|
// - If you need to access the full payload, consider parsing VAA from Bytes instead of Unmarshal
|
||||||
func Unmarshal(data []byte) (*VAA, error) {
|
func UnmarshalBody(data []byte, reader *bytes.Reader, v *VAA) (*VAA, error) {
|
||||||
if len(data) < minVAALength {
|
|
||||||
return nil, fmt.Errorf("VAA is too short")
|
|
||||||
}
|
|
||||||
v := &VAA{}
|
|
||||||
|
|
||||||
v.Version = data[0]
|
|
||||||
if v.Version != SupportedVAAVersion {
|
|
||||||
return nil, fmt.Errorf("unsupported VAA version: %d", v.Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := bytes.NewReader(data[1:])
|
|
||||||
|
|
||||||
if err := binary.Read(reader, binary.BigEndian, &v.GuardianSetIndex); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read guardian set index: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
lenSignatures, er := reader.ReadByte()
|
|
||||||
if er != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read signature length")
|
|
||||||
}
|
|
||||||
|
|
||||||
v.Signatures = make([]*Signature, lenSignatures)
|
|
||||||
for i := 0; i < int(lenSignatures); i++ {
|
|
||||||
index, err := reader.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read validator index [%d]", i)
|
|
||||||
}
|
|
||||||
|
|
||||||
signature := [65]byte{}
|
|
||||||
if n, err := reader.Read(signature[:]); err != nil || n != 65 {
|
|
||||||
return nil, fmt.Errorf("failed to read signature [%d]: %w", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
v.Signatures[i] = &Signature{
|
|
||||||
Index: index,
|
|
||||||
Signature: signature,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unixSeconds := uint32(0)
|
unixSeconds := uint32(0)
|
||||||
if err := binary.Read(reader, binary.BigEndian, &unixSeconds); err != nil {
|
if err := binary.Read(reader, binary.BigEndian, &unixSeconds); err != nil {
|
||||||
return nil, fmt.Errorf("failed to read timestamp: %w", err)
|
return nil, fmt.Errorf("failed to read timestamp: %w", err)
|
||||||
|
@ -385,32 +403,234 @@ func Unmarshal(data []byte) (*VAA, error) {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unmarshal deserializes the binary representation of a VAA
|
||||||
|
func Unmarshal(data []byte) (*VAA, error) {
|
||||||
|
if len(data) < minVAALength {
|
||||||
|
return nil, fmt.Errorf("VAA is too short")
|
||||||
|
}
|
||||||
|
v := &VAA{}
|
||||||
|
|
||||||
|
v.Version = data[0]
|
||||||
|
if v.Version != SupportedVAAVersion {
|
||||||
|
return nil, fmt.Errorf("unsupported VAA version: %d", v.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := bytes.NewReader(data[1:])
|
||||||
|
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &v.GuardianSetIndex); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read guardian set index: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lenSignatures, er := reader.ReadByte()
|
||||||
|
if er != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read signature length")
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Signatures = make([]*Signature, lenSignatures)
|
||||||
|
for i := 0; i < int(lenSignatures); i++ {
|
||||||
|
index, err := reader.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read validator index [%d]", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
signature := [65]byte{}
|
||||||
|
if n, err := reader.Read(signature[:]); err != nil || n != 65 {
|
||||||
|
return nil, fmt.Errorf("failed to read signature [%d]: %w", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Signatures[i] = &Signature{
|
||||||
|
Index: index,
|
||||||
|
Signature: signature,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UnmarshalBody(data, reader, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBatch deserializes the binary representation of a BatchVAA
|
||||||
|
func UnmarshalBatch(data []byte) (*BatchVAA, error) {
|
||||||
|
if len(data) < minBatchVAALength {
|
||||||
|
return nil, fmt.Errorf("BatchVAA.Observation is too short")
|
||||||
|
}
|
||||||
|
v := &BatchVAA{}
|
||||||
|
|
||||||
|
v.Version = data[0]
|
||||||
|
if v.Version != BatchVAAVersion {
|
||||||
|
return nil, fmt.Errorf("unsupported VAA version: %d", v.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := bytes.NewReader(data[1:])
|
||||||
|
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &v.GuardianSetIndex); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read guardian set index: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lenSignatures, er := reader.ReadByte()
|
||||||
|
if er != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read signature length")
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Signatures = make([]*Signature, int(lenSignatures))
|
||||||
|
for i := 0; i < int(lenSignatures); i++ {
|
||||||
|
index, err := reader.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read validator index [%d]", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
signature := [65]byte{}
|
||||||
|
if n, err := reader.Read(signature[:]); err != nil || n != 65 {
|
||||||
|
return nil, fmt.Errorf("failed to read signature [%d]: %w", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Signatures[i] = &Signature{
|
||||||
|
Index: uint8(index),
|
||||||
|
Signature: signature,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lenHashes, err := reader.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read hashes length [%w]", err)
|
||||||
|
}
|
||||||
|
numHashes := int(lenHashes)
|
||||||
|
|
||||||
|
v.Hashes = make([]common.Hash, numHashes)
|
||||||
|
for i := 0; i < int(lenHashes); i++ {
|
||||||
|
hash := [32]byte{}
|
||||||
|
if n, err := reader.Read(hash[:]); err != nil || n != 32 {
|
||||||
|
return nil, fmt.Errorf("failed to read hash [%d]: %w", i, err)
|
||||||
|
}
|
||||||
|
v.Hashes[i] = common.BytesToHash(hash[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
lenObservations, err := reader.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read observations length: %w", err)
|
||||||
|
}
|
||||||
|
numObservations := int(lenObservations)
|
||||||
|
|
||||||
|
if numHashes != numObservations {
|
||||||
|
// should never happen, check anyway
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"failed unmarshaling BatchVAA, observations differs from hashes")
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Observations = make([]*Observation, numObservations)
|
||||||
|
for i := 0; i < int(lenObservations); i++ {
|
||||||
|
index, err := reader.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read Observation index [%d]: %w", i, err)
|
||||||
|
}
|
||||||
|
obsvIndex := uint8(index)
|
||||||
|
|
||||||
|
obsvLength := uint32(0)
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &obsvLength); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read Observation length: %w", err)
|
||||||
|
}
|
||||||
|
numBytes := int(obsvLength)
|
||||||
|
|
||||||
|
// ensure numBytes is within expected bounds before allocating arrays
|
||||||
|
// cannot be negative
|
||||||
|
if numBytes < 0 {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"failed to read Observation index: %v, byte length is negative", i)
|
||||||
|
}
|
||||||
|
// cannot be longer than what is left in the array
|
||||||
|
if numBytes > reader.Len() {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"failed to read Observation index: %v, byte length is erroneous", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
obs := make([]byte, numBytes)
|
||||||
|
if n, err := reader.Read(obs[:]); err != nil || n == 0 {
|
||||||
|
return nil, fmt.Errorf("failed to read Observation bytes [%d]: %w", n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure the observation meets the minimum length of headless VAAs
|
||||||
|
if len(obs) < minHeadlessVAALength {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"BatchVAA.Observation is too short. Index: %v", obsvIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode the observation, which is just the "BODY" fields of a v1 VAA
|
||||||
|
headless, err := UnmarshalBody(data, bytes.NewReader(obs[:]), &VAA{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal Observation VAA. %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for malformed data - verify that the hash of the observation matches what was supplied
|
||||||
|
// the guardian has no interest in or use for observations after the batch has been signed, but still check
|
||||||
|
obsHash := headless.SigningMsg()
|
||||||
|
if obsHash != v.Hashes[obsvIndex] {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"BatchVAA Observation %v does not match supplied hash", obsvIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Observations[i] = &Observation{
|
||||||
|
Index: obsvIndex,
|
||||||
|
Observation: headless,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
// signingBody returns the binary representation of the data that is relevant for signing and verifying the VAA
|
// signingBody returns the binary representation of the data that is relevant for signing and verifying the VAA
|
||||||
func (v *VAA) signingBody() []byte {
|
func (v *VAA) signingBody() []byte {
|
||||||
return v.serializeBody()
|
return v.serializeBody()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SigningMsg returns the hash of the signing body. This is used for signature generation and verification
|
// signingBody returns the binary representation of the data that is relevant for signing and verifying the VAA
|
||||||
func (v *VAA) SigningMsg() common.Hash {
|
func (v *BatchVAA) signingBody() []byte {
|
||||||
// In order to save space in the solana signature verification instruction, we hash twice so we only need to pass in
|
buf := new(bytes.Buffer)
|
||||||
// the first hash (32 bytes) vs the full body data.
|
|
||||||
hash := crypto.Keccak256Hash(crypto.Keccak256Hash(v.signingBody()).Bytes())
|
// add the VAA version
|
||||||
return hash
|
MustWrite(buf, binary.BigEndian, v.Version)
|
||||||
|
|
||||||
|
// create the hash array from the Observations of the BatchVAA
|
||||||
|
hashes := v.ObsvHashArray()
|
||||||
|
|
||||||
|
MustWrite(buf, binary.BigEndian, hashes)
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifySignatures verifies the signature of the VAA given the signer addresses.
|
// SigningMsg returns the hash of the signing body.
|
||||||
// Returns true if the signatures were verified successfully.
|
func SigningMsg(data []byte) common.Hash {
|
||||||
func (v *VAA) VerifySignatures(addresses []common.Address) bool {
|
// In order to save space in the solana signature verification instruction, we hash twice so we only need to pass in
|
||||||
if len(addresses) < len(v.Signatures) {
|
// the first hash (32 bytes) vs the full body data.
|
||||||
return false
|
return crypto.Keccak256Hash(crypto.Keccak256Hash(data).Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// SigningMsg returns the hash of the signing body. This is used for signature generation and verification
|
||||||
|
func (v *VAA) SigningMsg() common.Hash {
|
||||||
|
return SigningMsg(v.signingBody())
|
||||||
|
}
|
||||||
|
|
||||||
|
// SigningMsg returns the hash of the signing body. This is used for signature generation and verification
|
||||||
|
func (v *BatchVAA) SigningMsg() common.Hash {
|
||||||
|
return SigningMsg(v.signingBody())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObsvHashArray creates an array of hashes of Observation.
|
||||||
|
// hashes in the array have the index position of their Observation.Index.
|
||||||
|
func (v *BatchVAA) ObsvHashArray() []common.Hash {
|
||||||
|
hashes := make([]common.Hash, len(v.Observations))
|
||||||
|
for _, msg := range v.Observations {
|
||||||
|
obsIndex := msg.Index
|
||||||
|
hashes[obsIndex] = msg.Observation.SigningMsg()
|
||||||
}
|
}
|
||||||
|
|
||||||
h := v.SigningMsg()
|
return hashes
|
||||||
|
}
|
||||||
|
|
||||||
|
func VerifySignatures(data []byte, signatures []*Signature, addresses []common.Address) bool {
|
||||||
|
|
||||||
last_index := -1
|
last_index := -1
|
||||||
signing_addresses := []common.Address{}
|
signing_addresses := []common.Address{}
|
||||||
|
|
||||||
for _, sig := range v.Signatures {
|
for _, sig := range signatures {
|
||||||
if int(sig.Index) >= len(addresses) {
|
if int(sig.Index) >= len(addresses) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -422,7 +642,7 @@ func (v *VAA) VerifySignatures(addresses []common.Address) bool {
|
||||||
last_index = int(sig.Index)
|
last_index = int(sig.Index)
|
||||||
|
|
||||||
// Get pubKey to determine who signers address
|
// Get pubKey to determine who signers address
|
||||||
pubKey, err := crypto.Ecrecover(h.Bytes(), sig.Signature[:])
|
pubKey, err := crypto.Ecrecover(data, sig.Signature[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -445,6 +665,69 @@ func (v *VAA) VerifySignatures(addresses []common.Address) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VerifySignatures verifies the signature of the VAA given the signer addresses.
|
||||||
|
// Returns true if the signatures were verified successfully.
|
||||||
|
func (v *VAA) VerifySignatures(addresses []common.Address) bool {
|
||||||
|
if len(addresses) < len(v.Signatures) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return VerifySignatures(v.SigningMsg().Bytes(), v.Signatures, addresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifySignatures verifies the signature of the BatchVAA given the signer addresses.
|
||||||
|
// Returns true if the signatures were verified successfully.
|
||||||
|
func (v *BatchVAA) VerifySignatures(addresses []common.Address) bool {
|
||||||
|
if len(addresses) < len(v.Signatures) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return VerifySignatures(v.SigningMsg().Bytes(), v.Signatures, addresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal returns the binary representation of the BatchVAA
|
||||||
|
func (v *BatchVAA) Marshal() ([]byte, error) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
MustWrite(buf, binary.BigEndian, v.Version)
|
||||||
|
MustWrite(buf, binary.BigEndian, v.GuardianSetIndex)
|
||||||
|
|
||||||
|
// Write signatures
|
||||||
|
MustWrite(buf, binary.BigEndian, uint8(len(v.Signatures)))
|
||||||
|
for _, sig := range v.Signatures {
|
||||||
|
MustWrite(buf, binary.BigEndian, sig.Index)
|
||||||
|
buf.Write(sig.Signature[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write Body
|
||||||
|
buf.Write(v.serializeBody())
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serializes the body of the BatchVAA.
|
||||||
|
func (v *BatchVAA) serializeBody() []byte {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
hashes := v.ObsvHashArray()
|
||||||
|
|
||||||
|
MustWrite(buf, binary.BigEndian, uint8(len(hashes)))
|
||||||
|
MustWrite(buf, binary.BigEndian, hashes)
|
||||||
|
|
||||||
|
MustWrite(buf, binary.BigEndian, uint8(len(v.Observations)))
|
||||||
|
for _, obsv := range v.Observations {
|
||||||
|
|
||||||
|
MustWrite(buf, binary.BigEndian, uint8(obsv.Index))
|
||||||
|
|
||||||
|
obsvBytes := obsv.Observation.serializeBody()
|
||||||
|
|
||||||
|
lenBytes := len(obsvBytes)
|
||||||
|
MustWrite(buf, binary.BigEndian, uint32(lenBytes))
|
||||||
|
buf.Write(obsvBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
// Verify is a function on the VAA that takes a complete set of guardian keys as input and attempts certain checks with respect to this guardian.
|
// Verify is a function on the VAA that takes a complete set of guardian keys as input and attempts certain checks with respect to this guardian.
|
||||||
// Verify will return nil if the VAA passes checks. Otherwise, Verify will return an error containing the text of the first check to fail.
|
// Verify will return nil if the VAA passes checks. Otherwise, Verify will return an error containing the text of the first check to fail.
|
||||||
// NOTE: Verify will not work correctly if a subset of the guardian set keys is passed in. The complete guardian set must be passed in.
|
// NOTE: Verify will not work correctly if a subset of the guardian set keys is passed in. The complete guardian set must be passed in.
|
||||||
|
@ -495,16 +778,81 @@ func (v *VAA) Marshal() ([]byte, error) {
|
||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// implement encoding.BinaryMarshaler interface for the VAA struct
|
||||||
|
func (v VAA) MarshalBinary() ([]byte, error) {
|
||||||
|
return v.Marshal()
|
||||||
|
}
|
||||||
|
|
||||||
|
// implement encoding.BinaryUnmarshaler interface for the VAA struct
|
||||||
|
func (v *VAA) UnmarshalBinary(data []byte) error {
|
||||||
|
vaa, err := Unmarshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// derefernce the stuct created by Unmarshal, and assign it to the method's context
|
||||||
|
*v = *vaa
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// implement encoding.BinaryMarshaler interface for BatchVAA struct
|
||||||
|
func (b BatchVAA) MarshalBinary() ([]byte, error) {
|
||||||
|
return b.Marshal()
|
||||||
|
}
|
||||||
|
|
||||||
|
// implement encoding.BinaryUnmarshaler interface for BatchVAA struct
|
||||||
|
func (b *BatchVAA) UnmarshalBinary(data []byte) error {
|
||||||
|
batch, err := UnmarshalBatch(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// derefernce the stuct created by Unmarshal, and assign it to the method's context
|
||||||
|
*b = *batch
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// MessageID returns a human-readable emitter_chain/emitter_address/sequence tuple.
|
// MessageID returns a human-readable emitter_chain/emitter_address/sequence tuple.
|
||||||
func (v *VAA) MessageID() string {
|
func (v *VAA) MessageID() string {
|
||||||
return fmt.Sprintf("%d/%s/%d", v.EmitterChain, v.EmitterAddress, v.Sequence)
|
return fmt.Sprintf("%d/%s/%d", v.EmitterChain, v.EmitterAddress, v.Sequence)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BatchID returns a human-readable emitter_chain/transaction_hex
|
||||||
|
func (v *BatchVAA) BatchID() string {
|
||||||
|
if len(v.Observations) == 0 {
|
||||||
|
// cant have a batch without Observations, but check just be safe
|
||||||
|
panic("Cannot create a BatchID from BatchVAA with no Observations.")
|
||||||
|
}
|
||||||
|
nonce := v.Observations[0].Observation.Nonce
|
||||||
|
return fmt.Sprintf("%d/%s/%d", v.EmitterChain, hex.EncodeToString(v.TransactionID.Bytes()), nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniqueID normalizes the ID of the VAA (any type) for the Attestation interface
|
||||||
|
// UniqueID returns the MessageID that uniquely identifies the Attestation
|
||||||
|
func (v *VAA) UniqueID() string {
|
||||||
|
return v.MessageID()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniqueID returns the BatchID that uniquely identifies the Attestation
|
||||||
|
func (b *BatchVAA) UniqueID() string {
|
||||||
|
return b.BatchID()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransactionID implements the processor.Batch interface for *BatchVAA.
|
||||||
|
func (v *BatchVAA) GetTransactionID() common.Hash {
|
||||||
|
return v.TransactionID
|
||||||
|
}
|
||||||
|
|
||||||
// HexDigest returns the hex-encoded digest.
|
// HexDigest returns the hex-encoded digest.
|
||||||
func (v *VAA) HexDigest() string {
|
func (v *VAA) HexDigest() string {
|
||||||
return hex.EncodeToString(v.SigningMsg().Bytes())
|
return hex.EncodeToString(v.SigningMsg().Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HexDigest returns the hex-encoded digest.
|
||||||
|
func (b *BatchVAA) HexDigest() string {
|
||||||
|
return hex.EncodeToString(b.SigningMsg().Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
SECURITY: Do not change this code! Changing it could result in two different hashes for
|
SECURITY: Do not change this code! Changing it could result in two different hashes for
|
||||||
the same observation. But xDapps rely on the hash of an observation for replay protection.
|
the same observation. But xDapps rely on the hash of an observation for replay protection.
|
||||||
|
@ -536,6 +884,22 @@ func (v *VAA) AddSignature(key *ecdsa.PrivateKey, index uint8) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// creates signature of BatchVAA.Hashes and adds it to BatchVAA.Signatures.
|
||||||
|
func (v *BatchVAA) AddSignature(key *ecdsa.PrivateKey, index uint8) {
|
||||||
|
|
||||||
|
sig, err := crypto.Sign(v.SigningMsg().Bytes(), key)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
sigData := [65]byte{}
|
||||||
|
copy(sigData[:], sig)
|
||||||
|
|
||||||
|
v.Signatures = append(v.Signatures, &Signature{
|
||||||
|
Index: index,
|
||||||
|
Signature: sigData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: This function assumes that the caller has verified that the VAA is from the token bridge.
|
// NOTE: This function assumes that the caller has verified that the VAA is from the token bridge.
|
||||||
func IsTransfer(payload []byte) bool {
|
func IsTransfer(payload []byte) bool {
|
||||||
return (len(payload) > 0) && ((payload[0] == 1) || (payload[0] == 3))
|
return (len(payload) > 0) && ((payload[0] == 1) || (payload[0] == 3))
|
||||||
|
@ -593,6 +957,11 @@ func (v *VAA) GetEmitterChain() ChainID {
|
||||||
return v.EmitterChain
|
return v.EmitterChain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetEmitterChain implements the processor.Batch interface for *BatchVAA.
|
||||||
|
func (v *BatchVAA) GetEmitterChain() ChainID {
|
||||||
|
return v.EmitterChain
|
||||||
|
}
|
||||||
|
|
||||||
// MustWrite calls binary.Write and panics on errors
|
// MustWrite calls binary.Write and panics on errors
|
||||||
func MustWrite(w io.Writer, order binary.ByteOrder, data interface{}) {
|
func MustWrite(w io.Writer, order binary.ByteOrder, data interface{}) {
|
||||||
if err := binary.Write(w, order, data); err != nil {
|
if err := binary.Write(w, order, data); err != nil {
|
||||||
|
@ -636,3 +1005,35 @@ func BytesToAddress(b []byte) (Address, error) {
|
||||||
copy(address[32-len(b):], b)
|
copy(address[32-len(b):], b)
|
||||||
return address, nil
|
return address, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringToHash converts a hex-encoded string into a common.Hash
|
||||||
|
func StringToHash(value string) (common.Hash, error) {
|
||||||
|
var tx common.Hash
|
||||||
|
|
||||||
|
// Make sure we have enough to decode
|
||||||
|
if len(value) < 2 {
|
||||||
|
return tx, fmt.Errorf("value must be at least 1 byte")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim any preceding "0x" to the address
|
||||||
|
value = strings.TrimPrefix(value, "0x")
|
||||||
|
|
||||||
|
res, err := hex.DecodeString(value)
|
||||||
|
if err != nil {
|
||||||
|
return tx, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tx = common.BytesToHash(res)
|
||||||
|
|
||||||
|
return tx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func BytesToHash(b []byte) (common.Hash, error) {
|
||||||
|
var hash common.Hash
|
||||||
|
if len(b) > 32 {
|
||||||
|
return hash, fmt.Errorf("value must be no more than 32 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = common.BytesToHash(b)
|
||||||
|
return hash, nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue