Files
tendermint/test/loadtime/payload/payload.go
Sergio Mena b42c439776 Fix TX payload for DO testnets (#9540)
* Added print

* Fix unmarshall

* Fix unmarshalling

* Simplified steps to unmarshall

* minor

* Use 'encoding/hex'

* Forget about C, this is Go!

* gosec warning

* Set maximum payload size

* nosec annotation
2022-10-12 19:58:50 +02:00

102 lines
2.8 KiB
Go

package payload
import (
"bytes"
"crypto/rand"
"encoding/hex"
"fmt"
"math"
"google.golang.org/protobuf/proto"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
)
const keyPrefix = "a="
const maxPayloadSize = 4 * 1024 * 1024
// NewBytes generates a new payload and returns the encoded representation of
// the payload as a slice of bytes. NewBytes uses the fields on the Options
// to create the payload.
func NewBytes(p *Payload) ([]byte, error) {
p.Padding = make([]byte, 1)
if p.Time == nil {
p.Time = timestamppb.Now()
}
us, err := CalculateUnpaddedSize(p)
if err != nil {
return nil, err
}
if p.Size > maxPayloadSize {
return nil, fmt.Errorf("configured size %d is too large (>%d)", p.Size, maxPayloadSize)
}
pSize := int(p.Size) // #nosec -- The "if" above makes this cast safe
if pSize < us {
return nil, fmt.Errorf("configured size %d not large enough to fit unpadded transaction of size %d", pSize, us)
}
// We halve the padding size because we transform the TX to hex
p.Padding = make([]byte, (pSize-us)/2)
_, err = rand.Read(p.Padding)
if err != nil {
return nil, err
}
b, err := proto.Marshal(p)
if err != nil {
return nil, err
}
h := []byte(hex.EncodeToString(b))
// prepend a single key so that the kv store only ever stores a single
// transaction instead of storing all tx and ballooning in size.
return append([]byte(keyPrefix), h...), nil
}
// FromBytes extracts a paylod from the byte representation of the payload.
// FromBytes leaves the padding untouched, returning it to the caller to handle
// or discard per their preference.
func FromBytes(b []byte) (*Payload, error) {
trH := bytes.TrimPrefix(b, []byte(keyPrefix))
if bytes.Equal(b, trH) {
return nil, fmt.Errorf("payload bytes missing key prefix '%s'", keyPrefix)
}
trB, err := hex.DecodeString(string(trH))
if err != nil {
return nil, err
}
p := &Payload{}
err = proto.Unmarshal(trB, p)
if err != nil {
return nil, err
}
return p, nil
}
// MaxUnpaddedSize returns the maximum size that a payload may be if no padding
// is included.
func MaxUnpaddedSize() (int, error) {
p := &Payload{
Time: timestamppb.Now(),
Connections: math.MaxUint64,
Rate: math.MaxUint64,
Size: math.MaxUint64,
Padding: make([]byte, 1),
}
return CalculateUnpaddedSize(p)
}
// CalculateUnpaddedSize calculates the size of the passed in payload for the
// purpose of determining how much padding to add to add to reach the target size.
// CalculateUnpaddedSize returns an error if the payload Padding field is longer than 1.
func CalculateUnpaddedSize(p *Payload) (int, error) {
if len(p.Padding) != 1 {
return 0, fmt.Errorf("expected length of padding to be 1, received %d", len(p.Padding))
}
b, err := proto.Marshal(p)
if err != nil {
return 0, err
}
h := []byte(hex.EncodeToString(b))
return len(h) + len(keyPrefix), nil
}