Skip to content

Replication response size cap#440

Open
jadalsmail wants to merge 18 commits into
mainfrom
replication-response-size-cap
Open

Replication response size cap#440
jadalsmail wants to merge 18 commits into
mainfrom
replication-response-size-cap

Conversation

@jadalsmail

Copy link
Copy Markdown
Collaborator

This PR introduces a size cap for the replication response messages so that avalanchego clients doesn't reject messages above the size limit.
Also added unit tests to test for the size limit.

Comment thread common/msg.go Outdated
size := quoromRoundSizeSlack

if q.VerifiedBlock != nil {
blockBytes, err := q.VerifiedBlock.Bytes()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Instead of Bytes() we can just have an instance method Size for all these sub-message types (Notarization, EmptyNotarization, etc.) and we can just call the Size() and it will recursively compute the size.

We don't want to serialize the block to bytes just to know the size of the block.

Comment thread common/msg.go Outdated
// it is tens of bytes in practivce, 512 is a generous bound
const quoromRoundSizeSlack = 512

func (q *VerifiedQuorumRound) EstimateSize() (int, error) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't think we should have this method return an error. Ideally we would be able to estimate the size of a message without an error.

If we don't marshal the fields to bytes we can remove the error returned.

Comment thread external.go Outdated
}
return rawBlock.MarshalCanoto(), nil
}
func (p *ParsedBlock) Size() int {

@yacovm yacovm Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

type ParsedBlock struct {
	metadata.StateMachineBlock
	msm *metadata.StateMachine
}

Shouldn't the Size() just be a method of StateMachineBlock ?

If StateMachineBlock implements Size() then since ParsedBlock embeds StateMachineBlock, we get it implicitly.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

But here Size() doesn't measure a stateMachineBlock, it measure the RawBlock encoding of one, which is defined in the root package, not in msm (where stateMachineBlock is).
We can't move the Size() method to the msm package, because it doesn't import canotoTag_RawBlock__InnerBlockBytes and canotoTag_RawBlock__Metadata, and msm can't import them because the root already imports msm. (see https://github.com/ava-labs/Simplex/blob/main/external.go#L10)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

but the raw block is just an msm reference which isn't serialized, and the block.

So the Size() of the block should be an instance method of the block, in my opinion.

We can't move the Size() method to the msm package, because it doesn't import canotoTag_RawBlock__InnerBlockBytes and canotoTag_RawBlock__Metadata,

I don't understand.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

ok, in order to do that I just moved the Bytes() and the Size() methods to msm/block.go and they are a method of StateMachineBlock, also moved the RawBlock type there as well. And updated the RawBlock calls to be metadata.RawBlock.

What do you think? Does this work?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't really care where the RawBlock lives, that's fine moving it there.

However, I'm not entirely convinced that using the canoto size cache is the right way to go, it seems to me very ad-hoc and overly dependent on canoto.

I think maybe a better solution would be to pre-compute the size of the StateMachineMetadata by marshaling it once the RawBlock is instantiated, and then Size() can just return the cached version.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This would mean that Size can return to be in RawBlock as initially.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants