node: updates serialize and deserialize for v2 VAAs#27
node: updates serialize and deserialize for v2 VAAs#27
Conversation
|
I tried running the tss tests (cd to node/pkg/tss then typed |
There was a problem hiding this comment.
Pull request overview
This PR modifies the VAA serialization and deserialization logic to support v2 VAAs (TSSVaaVersion), which use a Schnorr threshold signature scheme instead of multiple guardian signatures. The changes maintain backward compatibility with v1 VAAs while adding error handling for unsupported versions.
Key changes:
- Introduces
SchnorrSigLengthconstant (52 bytes) to define the Schnorr signature size for v2 VAAs - Updates
Unmarshalto handle version-specific signature deserialization (multiple signatures for v1, single Schnorr signature for v2) - Updates
Marshalto handle version-specific signature serialization with validation checks for v2 VAAs
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } else if v.Version == 2 { | ||
| v.Signatures = make([]*Signature, 1) | ||
| 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) | ||
| if n, err := reader.Read(signature[:SchnorrSigLength]); err != nil || n != SchnorrSigLength { | ||
| return nil, fmt.Errorf("failed to read schnorr signature [%d]: %w", i, err) | ||
| } | ||
|
|
||
| v.Signatures[i] = &Signature{ | ||
| Index: index, | ||
| v.Signatures[0] = &Signature{ | ||
| Index: 0, | ||
| Signature: signature, | ||
| } |
There was a problem hiding this comment.
The new v2 VAA deserialization logic lacks test coverage. Consider adding tests that verify:
- Correct deserialization of v2 VAAs with 52-byte Schnorr signatures
- Error handling for invalid signature lengths
- The signature is properly stored in the 65-byte array with zeros in the remaining bytes
- Round-trip marshal/unmarshal for v2 VAAs
| } else if v.Version == 2 { | ||
| if len(v.Signatures) != 1 { | ||
| panic(fmt.Errorf("tried to serialize v2 VAA with %d signatures", len(v.Signatures)).Error()) | ||
| } | ||
|
|
||
| zeroSlice := sig.Signature[SchnorrSigLength:] | ||
| if !bytes.Equal(zeroSlice, make([]byte, len(zeroSlice))) { | ||
| panic(fmt.Errorf("tried to serialize v2 VAA with invalid signature").Error()) | ||
| } | ||
|
|
||
| buf.Write(sig.Signature[:SchnorrSigLength]) |
There was a problem hiding this comment.
The new v2 VAA serialization logic lacks test coverage. Consider adding tests that verify:
- Correct serialization of v2 VAAs with single Schnorr signature
- Panic behavior when serializing v2 VAA with multiple signatures
- Panic behavior when the signature has non-zero bytes beyond SchnorrSigLength
- Round-trip marshal/unmarshal for v2 VAAs
334d040 to
5632003
Compare
5632003 to
e62b369
Compare
| } | ||
| } else if v.Version == TSSVaaVersion { | ||
| if len(v.Signatures) != 1 { | ||
| panic(fmt.Errorf("tried to serialize v2 VAA with %d signatures", len(v.Signatures)).Error()) |
There was a problem hiding this comment.
It is better to return an error rather than panic here (and below, too).
There was a problem hiding this comment.
Do you know what kind of errors we should be returning? I never saw code for this so I don't know the best way to do it.
|
|
||
| sig := v.Signatures[0] | ||
| zeroSlice := sig.Signature[SchnorrSigLength:] | ||
| if !bytes.Equal(zeroSlice, make([]byte, len(zeroSlice))) { |
There was a problem hiding this comment.
I'm not sure I follow this line.
Are you trying to ensure there are no additional bytes in sig.Signature?
If that is the case, wouldn't it be better to check the length of sig.Signature?
There was a problem hiding this comment.
The type definition for SignatureData forces it to be of fixed length 65 though, doesn't it? 🤔
There was a problem hiding this comment.
I'm a bit wary of changing the type definition. Maybe it's not terrible to do it but I'm a bit unsure it's viable.
| // The schnorr signature is constituted by: | ||
| // - r: 20 bytes | ||
| // - s: 32 bytes | ||
| SchnorrSigLength = 52 |
There was a problem hiding this comment.
This is not correct at the moment.
The output of the frost signature is common.SignatureData (which contains both S=32byte and R=33byte).
The processor translates it into a frost.Signature (65 byte), and stores it that way in the VAA (since we didn't export more than one signature verification function).
It is possible to convert the signature to the smart-contract format: frostsig.ToContractSig() returns S, R, and Address(R).
Does the VAA.Marshal() determine how signatures are passed to the smart contract?
There was a problem hiding this comment.
Hey, yes, VAAs are the serialized format that gets submitted to smart contracts for verification.
There was a problem hiding this comment.
I gather that we may be missing a conversion then, right?
This keeps the Go struct for VAAs the same but modifies the serialization and deserialization for v2 VAAs. It also errors out on unknown versions.