new VAA format in Golang
This commit is contained in:
parent
ca4e4a3243
commit
2a096790d3
|
@ -4,12 +4,10 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/certusone/wormhole/bridge/third_party/chainlink/ethschnorr"
|
|
||||||
"github.com/certusone/wormhole/bridge/third_party/chainlink/secp256k1"
|
"github.com/certusone/wormhole/bridge/third_party/chainlink/secp256k1"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"go.dedis.ch/kyber/v3"
|
"go.dedis.ch/kyber/v3"
|
||||||
"go.dedis.ch/kyber/v3/util/key"
|
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
@ -23,8 +21,8 @@ type (
|
||||||
Version uint8
|
Version uint8
|
||||||
// GuardianSetIndex is the index of the guardian set that signed this VAA
|
// GuardianSetIndex is the index of the guardian set that signed this VAA
|
||||||
GuardianSetIndex uint32
|
GuardianSetIndex uint32
|
||||||
// Signature is the signature of the guardian set
|
// SignatureData is the signature of the guardian set
|
||||||
Signature *Signature
|
Signatures []*Signature
|
||||||
|
|
||||||
// Timestamp when the VAA was created
|
// Timestamp when the VAA was created
|
||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
|
@ -41,12 +39,12 @@ type (
|
||||||
// chain is < 32bytes the value is zero-padded on the left.
|
// chain is < 32bytes the value is zero-padded on the left.
|
||||||
Address [32]byte
|
Address [32]byte
|
||||||
|
|
||||||
// Signature of a VAA
|
// Signature of a single guardian
|
||||||
Signature struct {
|
Signature struct {
|
||||||
// Sig is the signature field of a Schnorr signature
|
// Index of the validator
|
||||||
Sig [32]byte
|
Index uint8
|
||||||
// Address is the R equivalent in our Schnorr signature schema
|
// Signature data
|
||||||
Address common.Address
|
Signature [65]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssetMeta describes an asset within the Wormhole protocol
|
// AssetMeta describes an asset within the Wormhole protocol
|
||||||
|
@ -105,9 +103,7 @@ func ParseVAA(data []byte) (*VAA, error) {
|
||||||
if len(data) < minVAALength {
|
if len(data) < minVAALength {
|
||||||
return nil, fmt.Errorf("VAA is too short")
|
return nil, fmt.Errorf("VAA is too short")
|
||||||
}
|
}
|
||||||
v := &VAA{
|
v := &VAA{}
|
||||||
Signature: &Signature{},
|
|
||||||
}
|
|
||||||
|
|
||||||
v.Version = data[0]
|
v.Version = data[0]
|
||||||
if v.Version != supportedVAAVersion {
|
if v.Version != supportedVAAVersion {
|
||||||
|
@ -120,11 +116,27 @@ func ParseVAA(data []byte) (*VAA, error) {
|
||||||
return nil, fmt.Errorf("failed to read guardian set index: %w", err)
|
return nil, fmt.Errorf("failed to read guardian set index: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := reader.Read(v.Signature.Sig[:]); err != nil || n != 32 {
|
lenSignatures, er := reader.ReadByte()
|
||||||
return nil, fmt.Errorf("failed to read signature sig field: %w", err)
|
if er != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read signature length")
|
||||||
}
|
}
|
||||||
if n, err := reader.Read(v.Signature.Address[:]); err != nil || n != 20 {
|
|
||||||
return nil, fmt.Errorf("failed to read signature addr field: %w", err)
|
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)
|
||||||
|
@ -133,14 +145,15 @@ func ParseVAA(data []byte) (*VAA, error) {
|
||||||
}
|
}
|
||||||
v.Timestamp = time.Unix(int64(unixSeconds), 0)
|
v.Timestamp = time.Unix(int64(unixSeconds), 0)
|
||||||
|
|
||||||
action := data[61]
|
currentPos := len(data) - reader.Len()
|
||||||
payloadLength := data[62]
|
action := data[currentPos]
|
||||||
|
payloadLength := data[currentPos+1]
|
||||||
|
|
||||||
if len(data[63:]) != int(payloadLength) {
|
if len(data[currentPos+2:]) != int(payloadLength) {
|
||||||
return nil, fmt.Errorf("payload length does not match given payload data size")
|
return nil, fmt.Errorf("payload length does not match given payload data size")
|
||||||
}
|
}
|
||||||
|
|
||||||
payloadReader := bytes.NewReader(data[63:])
|
payloadReader := bytes.NewReader(data[currentPos+2:])
|
||||||
var err error
|
var err error
|
||||||
switch Action(action) {
|
switch Action(action) {
|
||||||
case ActionGuardianSetUpdate:
|
case ActionGuardianSetUpdate:
|
||||||
|
@ -175,46 +188,33 @@ func (v *VAA) SigningMsg() (*big.Int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifySignature verifies the signature of the VAA given a public key
|
// VerifySignature verifies the signature of the VAA given a public key
|
||||||
func (v *VAA) VerifySignature(pubKey kyber.Point) bool {
|
func (v *VAA) VerifySignatures(addresses []common.Address) bool {
|
||||||
if v.Signature == nil {
|
if len(addresses) < len(v.Signatures) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err := v.SigningMsg()
|
h, err := v.SigningMsg()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
sig := ethschnorr.NewSignature()
|
for _, sig := range v.Signatures {
|
||||||
sig.Signature = new(big.Int).SetBytes(v.Signature.Sig[:])
|
if int(sig.Index) >= len(addresses) {
|
||||||
sig.CommitmentPublicAddress = v.Signature.Address
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
err = ethschnorr.Verify(pubKey, msg, sig)
|
pubKey, err := crypto.Ecrecover(h.Bytes(), sig.Signature[:])
|
||||||
return err == nil
|
if err != nil {
|
||||||
}
|
return false
|
||||||
|
}
|
||||||
|
addr := common.BytesToAddress(crypto.Keccak256(pubKey[1:])[12:])
|
||||||
|
|
||||||
// Sign signs the VAA, setting it's signature field
|
if addr != addresses[sig.Index] {
|
||||||
func (v *VAA) Sign(key *key.Pair) error {
|
return false
|
||||||
if v.Signature != nil {
|
}
|
||||||
return fmt.Errorf("VAA has already been signed")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := v.SigningMsg()
|
return true
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get signing message: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := ethschnorr.Sign(key.Private, hash)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to sign: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set fields
|
|
||||||
v.Signature = &Signature{}
|
|
||||||
copy(v.Signature.Sig[:], common.LeftPadBytes(sig.Signature.Bytes(), 32))
|
|
||||||
v.Signature.Address = sig.CommitmentPublicAddress
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize returns the binary representation of the VAA
|
// Serialize returns the binary representation of the VAA
|
||||||
|
@ -223,14 +223,13 @@ func (v *VAA) Serialize() ([]byte, error) {
|
||||||
MustWrite(buf, binary.BigEndian, v.Version)
|
MustWrite(buf, binary.BigEndian, v.Version)
|
||||||
MustWrite(buf, binary.BigEndian, v.GuardianSetIndex)
|
MustWrite(buf, binary.BigEndian, v.GuardianSetIndex)
|
||||||
|
|
||||||
if v.Signature == nil {
|
// Write signatures
|
||||||
return nil, fmt.Errorf("empty signature")
|
MustWrite(buf, binary.BigEndian, uint8(len(v.Signatures)))
|
||||||
|
for _, sig := range v.Signatures {
|
||||||
|
MustWrite(buf, binary.BigEndian, sig.Index)
|
||||||
|
buf.Write(sig.Signature[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write signature
|
|
||||||
buf.Write(v.Signature.Sig[:])
|
|
||||||
buf.Write(v.Signature.Address[:])
|
|
||||||
|
|
||||||
// Write Body
|
// Write Body
|
||||||
body, err := v.serializeBody()
|
body, err := v.serializeBody()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package vaa
|
package vaa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"github.com/certusone/wormhole/bridge/third_party/chainlink/cryptotest"
|
"github.com/certusone/wormhole/bridge/third_party/chainlink/cryptotest"
|
||||||
"github.com/certusone/wormhole/bridge/third_party/chainlink/secp256k1"
|
"github.com/certusone/wormhole/bridge/third_party/chainlink/secp256k1"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -23,9 +26,11 @@ func TestSerializeDeserialize(t *testing.T) {
|
||||||
vaa: &VAA{
|
vaa: &VAA{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
GuardianSetIndex: 9,
|
GuardianSetIndex: 9,
|
||||||
Signature: &Signature{
|
Signatures: []*Signature{
|
||||||
Sig: [32]byte{2, 8},
|
{
|
||||||
Address: common.Address{1, 2, 3, 4},
|
Index: 1,
|
||||||
|
Signature: [65]byte{},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Timestamp: time.Unix(2837, 0),
|
Timestamp: time.Unix(2837, 0),
|
||||||
Payload: &BodyTransfer{
|
Payload: &BodyTransfer{
|
||||||
|
@ -47,9 +52,11 @@ func TestSerializeDeserialize(t *testing.T) {
|
||||||
vaa: &VAA{
|
vaa: &VAA{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
GuardianSetIndex: 9,
|
GuardianSetIndex: 9,
|
||||||
Signature: &Signature{
|
Signatures: []*Signature{
|
||||||
Sig: [32]byte{2, 8},
|
{
|
||||||
Address: common.Address{1, 2, 3, 4},
|
Index: 1,
|
||||||
|
Signature: [65]byte{},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Timestamp: time.Unix(2837, 0),
|
Timestamp: time.Unix(2837, 0),
|
||||||
Payload: &BodyGuardianSetUpdate{
|
Payload: &BodyGuardianSetUpdate{
|
||||||
|
@ -75,8 +82,6 @@ func TestSerializeDeserialize(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVerifySignature(t *testing.T) {
|
func TestVerifySignature(t *testing.T) {
|
||||||
key := secp256k1.Generate(randomStream)
|
|
||||||
|
|
||||||
v := &VAA{
|
v := &VAA{
|
||||||
Version: 8,
|
Version: 8,
|
||||||
GuardianSetIndex: 9,
|
GuardianSetIndex: 9,
|
||||||
|
@ -93,6 +98,23 @@ func TestVerifySignature(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, v.Sign(key))
|
data, err := v.SigningMsg()
|
||||||
require.True(t, v.VerifySignature(key.Public))
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
key, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
sig, err := crypto.Sign(data.Bytes(), key)
|
||||||
|
require.NoError(t, err)
|
||||||
|
sigData := [65]byte{}
|
||||||
|
copy(sigData[:], sig)
|
||||||
|
|
||||||
|
v.Signatures = append(v.Signatures, &Signature{
|
||||||
|
Index: 0,
|
||||||
|
Signature: sigData,
|
||||||
|
})
|
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
require.True(t, v.VerifySignatures([]common.Address{
|
||||||
|
addr,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue