Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encode each attestation by version when sending over the wire #351

Merged
merged 2 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 40 additions & 11 deletions pkg/network/protocols/core/protocol.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package core

import (
"encoding/binary"
"encoding/json"

"github.com/libp2p/go-libp2p/core/peer"
Expand All @@ -14,6 +15,7 @@ import (
"github.com/iotaledger/hive.go/runtime/options"
"github.com/iotaledger/hive.go/runtime/syncutils"
"github.com/iotaledger/hive.go/runtime/workerpool"
"github.com/iotaledger/hive.go/serializer/v2/marshalutil"
"github.com/iotaledger/hive.go/serializer/v2/serix"
"github.com/iotaledger/iota-core/pkg/model"
"github.com/iotaledger/iota-core/pkg/network"
Expand Down Expand Up @@ -73,16 +75,16 @@ func (p *Protocol) SendSlotCommitment(cm *model.Commitment, to ...peer.ID) {
}

func (p *Protocol) SendAttestations(cm *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], to ...peer.ID) {
var iotagoAPI iotago.API
if len(attestations) > 0 {
// TODO: there are multiple attestations potentially spanning multiple epochs/versions, we need to use the correct API for each one
iotagoAPI = lo.PanicOnErr(p.apiProvider.APIForVersion(attestations[0].ProtocolVersion))
} else {
iotagoAPI = p.apiProvider.APIForSlot(cm.Index()) // we need an api to serialize empty slices as well
encodedAttestations := marshalutil.New()
encodedAttestations.WriteUint32(uint32(len(attestations)))
for _, att := range attestations {
iotagoAPI := lo.PanicOnErr(p.apiProvider.APIForVersion(att.ProtocolVersion))
encodedAttestations.WriteBytes(lo.PanicOnErr(iotagoAPI.Encode(att)))
Comment on lines +78 to +82
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of marshalutil we could use stream.WriteCollection

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a bytes buffer does not implement WriteSeeker, so could not use it here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok then let's implement something like this https://stackoverflow.com/a/73679110 in the context of #284 so that we're able to use a bytes buffer with the stream package and replace this later

}

p.network.Send(&nwmodels.Packet{Body: &nwmodels.Packet_Attestations{Attestations: &nwmodels.Attestations{
Commitment: cm.Data(),
Attestations: lo.PanicOnErr(iotagoAPI.Encode(attestations)),
Attestations: encodedAttestations.Bytes(),
MerkleProof: lo.PanicOnErr(json.Marshal(merkleProof)),
}}}, to...)
}
Expand Down Expand Up @@ -200,10 +202,37 @@ func (p *Protocol) onAttestations(commitmentBytes []byte, attestationsBytes []by
return
}

var attestations []*iotago.Attestation
// TODO: there could be multiple versions of attestations in the same packet
if _, err := lo.PanicOnErr(p.apiProvider.APIForVersion(iotago.Version(commitmentBytes[0]))).Decode(attestationsBytes, &attestations, serix.WithValidation()); err != nil {
p.Events.Error.Trigger(ierrors.Wrap(err, "failed to deserialize attestations"), id)
if len(attestationsBytes) < 4 {
p.Events.Error.Trigger(ierrors.Errorf("failed to deserialize attestations, invalid attestation count"), id)

return
}

attestationCount := binary.LittleEndian.Uint32(attestationsBytes[0:4])
Comment on lines +205 to +211
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking for the constant, using binary.LittleEndian package and having a manual readOffset seems to be a bit cumbersome. I think this would be much nicer with the stream package, here specifically ReadCollection

readOffset := 4
attestations := make([]*iotago.Attestation, attestationCount)
for i := uint32(0); i < attestationCount; i++ {
version := iotago.Version(attestationsBytes[readOffset])
apiForVersion, err := p.apiProvider.APIForVersion(version)
if err != nil {
p.Events.Error.Trigger(ierrors.Wrapf(err, "failed to deserialize attestations for commitment %s", cm.ID()), id)

return
}

attestation := new(iotago.Attestation)
consumed, err := apiForVersion.Decode(attestationsBytes[readOffset:], attestation, serix.WithValidation())
if err != nil {
p.Events.Error.Trigger(ierrors.Wrap(err, "failed to deserialize attestations"), id)

return
}

readOffset += consumed
attestations[i] = attestation
}
if readOffset != len(attestationsBytes) {
p.Events.Error.Trigger(ierrors.Errorf("failed to deserialize attestations: %d bytes remaining", len(attestationsBytes)-readOffset), id)

return
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/protocol/protocol_fork.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ func (p *Protocol) processFork(fork *chainmanager.Fork) (anchorBlockIDs iotago.B

result, err := p.requestAttestation(ctx, fork.ForkedChain.Commitment(i).ID(), fork.Source, ch)
if err != nil {
return nil, false, true, ierrors.Wrapf(err, "failed to verify commitment %s", result.commitment.ID())
return nil, false, true, ierrors.Wrapf(err, "failed to verify commitment %s", fork.ForkedChain.Commitment(i).ID())
}

// Count how many consecutive slots are heavier/lighter than the main chain.
Expand Down
Loading