-
Notifications
You must be signed in to change notification settings - Fork 471
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
network: Limit message length based on Tag #5388
Conversation
…posals which have a hard limit of config.MaxTxnBytesPerBlock
Co-authored-by: Pavel Zbitskiy <[email protected]>
Changes after applying msgp alises fixes
// proof size for individual message types we have smaller valid bounds. | ||
// Exported because it's used in the stateproof package for ensuring that SigPartProof constant is correct size. | ||
func ProofMaxSizeByElements(n int) (s int) { | ||
s = 1 + 4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am very worried about having such fine-grained calculations done manually.
The tradeoff we need to consider is:
what is the risk of allowing loose bounds versus having such fine-grained calculations done manually.
Still considering this, and making sure the calculations are correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's actually not fully manual. It's a modification of the autogenerated test: here https://github.com/algorand/go-algorand/pull/5388/files#diff-add6a5ef46c2629eb7484bf3113df670d70cfdb6661ca69e236a778a7e1cb4c4R334
and we have a test here https://github.com/algorand/go-algorand/pull/5388/files#diff-181a1af1a38a976d2ddce9a94f778520e2ad787079c3cc99dbd1b7483386aa73R248 that ensures that that auto-generated ProofMaxSize and and ProofMaxSizeByElements output the same value when the allocbound is used as the input
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test is very good and reliable. However, the mapping of the function SingleLeafProofMaxSize
to SingleLeafProof
is not obvious for someone who is not aware of this arrangement.
In this particular case, this function is not needed. It can be implemented by setting a constant bound directly to the Proof field in:
Signature struct {
_struct struct{} `codec:",omitempty,omitemptyarray"`
Signature crypto.FalconSignature `codec:"sig"`
VectorCommitmentIndex uint64 `codec:"idx"`
Proof merklearray.SingleLeafProof `codec:"prf"`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah but we do have a pattern for doing this manual implementation already for basics.MicroAlgos and this solution will continue working if we ever use SingleLeafProof elsewhere. Finding it's maxtotalbytes
bound and using it is more implicit IMO.
This reverts commit e52b870.
Co-authored-by: Shant Karakashian <[email protected]>
// proof size for individual message types we have smaller valid bounds. | ||
// Exported because it's used in the stateproof package for ensuring that SigPartProof constant is correct size. | ||
func ProofMaxSizeByElements(n int) (s int) { | ||
s = 1 + 4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test is very good and reliable. However, the mapping of the function SingleLeafProofMaxSize
to SingleLeafProof
is not obvious for someone who is not aware of this arrangement.
In this particular case, this function is not needed. It can be implemented by setting a constant bound directly to the Proof field in:
Signature struct {
_struct struct{} `codec:",omitempty,omitemptyarray"`
Signature crypto.FalconSignature `codec:"sig"`
VectorCommitmentIndex uint64 `codec:"idx"`
Proof merklearray.SingleLeafProof `codec:"prf"`
// VRFVerifierMaxSize forwards to base implementation since it's expected by the msgp generated MaxSize functions | ||
func VRFVerifierMaxSize() int { | ||
return VrfPubkeyMaxSize() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed? The bound can be set like elsewhere in the struct field where this is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because of type alias. This type is on TODO for removal.
|
||
// MessageOfInterestMaxSize returns the maximum size of a MI message sent over the network | ||
// by encoding all of the tags currenttly in use. | ||
func MessageOfInterestMaxSize() int { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be in the test file, since it is only used from the test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can move it but would have to move it to node_test.go
which seems like an odd place for it and it would break symmetry since all other MaxSize() functions are available in non-test code.
@@ -108,6 +108,12 @@ func (a MicroAlgos) MsgIsZero() bool { | |||
return a.Raw == 0 | |||
} | |||
|
|||
// MicroAlgosMaxSize returns maximum possible msgp encoded size of MicroAlgos in bytes. | |||
// It is expected by msgp generated MaxSize functions | |||
func MicroAlgosMaxSize() (s int) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed? MicroAlgos has one field of type uint64.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same reason as the previous conversations we've had: msgp
when it sees a type defined outside of the current file it's parsing it assumes that it has this function defined. MicroAlgos is an example of a struct that we generate fully manually and don't use msgp for but we still need this method and the ones above for the rest of the msgp generated functions to work since they call them when they are Marshalling/Unmarshalling themselves or checking their own MaxSize
// VRFVerifierMaxSize forwards to base implementation since it's expected by the msgp generated MaxSize functions | ||
func VRFVerifierMaxSize() int { | ||
return VrfPubkeyMaxSize() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because of type alias. This type is on TODO for removal.
Co-authored-by: Pavel Zbitskiy <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed primarily the Network and Tags modules, and looked lightly at the rest.
@@ -100,7 +100,7 @@ func RandomAssetParams() basics.AssetParams { | |||
DefaultFrozen: crypto.RandUint64()%2 == 0, | |||
} | |||
if crypto.RandUint64()%5 != 0 { | |||
ap.UnitName = fmt.Sprintf("un%x", uint32(crypto.RandUint64()%0x7fffffff)) | |||
ap.UnitName = fmt.Sprintf("un%x", uint32(crypto.RandUint64()%0x7fffff)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you did not write the conditional, but do you happen to know why we check if the random int is divisible by 5?
for _, spec := range genDecl.Specs { | ||
if valueSpec, ok := spec.(*ast.ValueSpec); ok { | ||
for _, n := range valueSpec.Names { | ||
if n.Name == "TagLength" { | ||
if strings.HasSuffix(n.Name, "MaxSize") || n.Name == "TagLength" { | ||
continue | ||
} | ||
declaredTags = append(declaredTags, n.Name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if I'm reading this block right, we're trying to iterate over the valueSpec of a given tag, and through excluding MaxSize and TagLength, we expect that the only remaining thing is the declared tag's name? This seems fragile if/when more is added to the spec. Since it's just a test I don't know that it's worth pursuing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it is fragile as you can see since both myself and Shant had to add exclusions here for MaxSize and TagLength respectively. Hopefully we won't be adding a lot more specs to protocol.Tags
Summary
This PR implements specific message length limits based on the protocol Tag of the message being currently read in the
wsPeer
.The new
<TypeName>MaxSize() int
methods are being generated by themsgp
library based on the suppliedallocbound
s and the new struct annotationtotalallocbound
used to limit the total number of bytes encoded in a collection type.New constants are defined and
Tag.MaxSize()
method switching between them is used when resetingLimitedReaderSlurper
. These don't override the global maxAllocation setting so if max message size is larger than the maxAllocation we will still error out with the old limit.TODOs:
Write a method calculating maxSize forWe decided to keepTS
TopicMsgRes
tagsTS
andVB
at existing maximum message size since their theoretical limits weren't meaningfully different.Implement actual enforcement of limits on topic messages (MI, TS, UE) since those are not read usingThis statement was false. They are read using the slurper.LimitedReadSlurper
Test Plan