You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The PrefixedData and PrefixedData8 are both byte slices:
// PrefixedData simply represents a slice of bytes which consists of// a namespace.ID and raw data.// The user has to guarantee that the bytes are valid namespace prefixed data.// Go's type system does not allow enforcing the structure we want:// [namespaceID, rawData ...], especially as this type does not expect any// particular size for the namespace.typePrefixedData []byte// PrefixedData8 like PrefixedData is just a slice of bytes.// It assumes that the slice it represents is at least 8 bytes.// This assumption is not enforced by the type system though.typePrefixedData8 []byte
The definitions are problematic, because (1) casting byte slices may violate the type invariants (as pointed out in the comments) (2) they're mutable (3) PrefixedData has no way to indicate the namespace identifier length. For an example where mutability is a problem, see
because it can only validate that the namespace+data total length is at least as long as the expected namespace length.
Perhaps the definitions of PrefixedData and PrefixedData8 are caused by wanting to support zero-copy and zero-allocation use, where the user generates PrefixedData values by slicing existing byte slices. If so, the behaviour of Push should document that argument values are retained.
Otherwise, I recommend changing the definition to hide the internal structure, and add validating constructors. There are several possibilites:
typePrefixedDatastruct {
// namespace and data can be sliced from the same// underlying []byte allocation.namespace []bytedata []byte
}
funcNewPrefixedData(namespace, data []byte) PrefixedData {
bytes:=make([]byte, len(namespace)+len(data))
d:=PrefixedData{
namespace: bytes[:len(namespace)],
data: bytes[len(namespace)],
}
copy(d.namespace, namespace)
copy(d.data, data)
returnd
}
(I think I've never pushed the other version). I am not sure what the rationale was to make it just byte aliases. Probably because of the way it is consumed (so we do not have to convert back and forth between types maybe). Whoever tackles this should also check in core/app/node to see how this is used proper.
The
PrefixedData
andPrefixedData8
are both byte slices:The definitions are problematic, because (1) casting byte slices may violate the type invariants (as pointed out in the comments) (2) they're mutable (3) PrefixedData has no way to indicate the namespace identifier length. For an example where mutability is a problem, see
nmt/nmt.go
Lines 253 to 257 in 6274243
where it is not clear to the caller that the parameter
namespacedData
is retained by thePush
method.The missing namespace length is problematic for
validateAndExtractNamespace
,nmt/nmt.go
Lines 336 to 340 in 6274243
because it can only validate that the namespace+data total length is at least as long as the expected namespace length.
Perhaps the definitions of
PrefixedData
andPrefixedData8
are caused by wanting to support zero-copy and zero-allocation use, where the user generatesPrefixedData
values by slicing existing byte slices. If so, the behaviour ofPush
should document that argument values are retained.Otherwise, I recommend changing the definition to hide the internal structure, and add validating constructors. There are several possibilites:
or equivalently
@odeke-em
The text was updated successfully, but these errors were encountered: