Documentation
¶
Overview ¶
Package bcs implements Binary Canonical Serialization BCS.
The bcs package can be used to serialize and deserialize complex types into a binary canonical format that is non-self describing. Meaning that you will need to know the format ahead of time in order to serialize and deserialize. Check out Serializer for serialization and Deserializer for deserialization.
Index ¶
- func Deserialize(dest Unmarshaler, bytes []byte) error
- func DeserializeOption[T any](des *Deserializer, deserialize func(des *Deserializer, out *T)) *T
- func DeserializeSequence[T any](des *Deserializer) []T
- func DeserializeSequenceWithFunction[T any](des *Deserializer, deserialize func(des *Deserializer, out *T)) []T
- func Serialize(value Marshaler) ([]byte, error)
- func SerializeBool(input bool) ([]byte, error)
- func SerializeBytes(input []byte) ([]byte, error)
- func SerializeOption[T any](ser *Serializer, input *T, serialize func(ser *Serializer, item T))
- func SerializeSequence[AT []T, T any](array AT, ser *Serializer)
- func SerializeSequenceOnly[AT []T, T any](input AT) ([]byte, error)
- func SerializeSequenceWithFunction[AT []T, T any](array AT, ser *Serializer, serialize func(ser *Serializer, item T))
- func SerializeSerialized(input Serialized) ([]byte, error)
- func SerializeSingle(marshal func(ser *Serializer)) ([]byte, error)
- func SerializeU128(input big.Int) ([]byte, error)
- func SerializeU16(input uint16) ([]byte, error)
- func SerializeU256(input big.Int) ([]byte, error)
- func SerializeU32(input uint32) ([]byte, error)
- func SerializeU64(input uint64) ([]byte, error)
- func SerializeU8(input uint8) ([]byte, error)
- func SerializeUleb128(input uint32) ([]byte, error)
- type Deserializer
- func (des *Deserializer) Bool() bool
- func (des *Deserializer) Error() error
- func (des *Deserializer) ReadBytes() []byte
- func (des *Deserializer) ReadFixedBytes(length int) []byte
- func (des *Deserializer) ReadFixedBytesInto(dest []byte)
- func (des *Deserializer) ReadString() string
- func (des *Deserializer) Remaining() int
- func (des *Deserializer) Serialized() *Serialized
- func (des *Deserializer) SetError(err error)
- func (des *Deserializer) Struct(v Unmarshaler)
- func (des *Deserializer) U128() big.Int
- func (des *Deserializer) U16() uint16
- func (des *Deserializer) U256() big.Int
- func (des *Deserializer) U32() uint32
- func (des *Deserializer) U64() uint64
- func (des *Deserializer) U8() uint8
- func (des *Deserializer) Uleb128() uint32
- type Marshaler
- type Serialized
- type Serializer
- func (ser *Serializer) Bool(v bool)
- func (ser *Serializer) Error() error
- func (ser *Serializer) FixedBytes(v []byte)
- func (ser *Serializer) Reset()
- func (ser *Serializer) Serialized(s Serialized)
- func (ser *Serializer) SetError(err error)
- func (ser *Serializer) Struct(v Marshaler)
- func (ser *Serializer) ToBytes() []byte
- func (ser *Serializer) U128(v big.Int)
- func (ser *Serializer) U16(v uint16)
- func (ser *Serializer) U256(v big.Int)
- func (ser *Serializer) U32(v uint32)
- func (ser *Serializer) U64(v uint64)
- func (ser *Serializer) U8(v uint8)
- func (ser *Serializer) Uleb128(val uint32)
- func (ser *Serializer) WriteBytes(v []byte)
- func (ser *Serializer) WriteString(v string)
- type Struct
- type Unmarshaler
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Deserialize ¶
func Deserialize(dest Unmarshaler, bytes []byte) error
Deserialize deserializes a single item from bytes.
This function will error if there are remaining bytes.
func DeserializeOption ¶ added in v1.2.0
func DeserializeOption[T any](des *Deserializer, deserialize func(des *Deserializer, out *T)) *T
DeserializeOption deserializes an optional value
Under the hood, this is represented as a 0 or 1 length array ¶
Here's an example for handling an optional value:
// For a Some(10) value
bytes == []byte{0x01, 0x0A}
des := NewDeserializer(bytes)
output := DeserializeOption(des, nil, func(des *Deserializer, out *uint8) {
out = des.U8()
})
// output == &10
// For a None value
bytes2 == []byte{0x00}
des2 := NewDeserializer(bytes2)
output := DeserializeOption(des2, nil, func(des *Deserializer, out *uint8) {
out = des.U8()
})
// output == nil
func DeserializeSequence ¶
func DeserializeSequence[T any](des *Deserializer) []T
DeserializeSequence deserializes an Unmarshaler implementation array
This lets you deserialize a whole sequence of Unmarshaler, and will fail if any member fails. All sequences are prefixed with an Uleb128 length.
func DeserializeSequenceWithFunction ¶
func DeserializeSequenceWithFunction[T any](des *Deserializer, deserialize func(des *Deserializer, out *T)) []T
DeserializeSequenceWithFunction deserializes any array with the given function
This lets you deserialize a whole sequence of any type, and will fail if any member fails. All sequences are prefixed with an Uleb128 length.
func Serialize ¶
Serialize serializes a single item
type MyStruct struct {
num uint64
}
func (str *MyStruct) MarshalBCS(ser *Serialize) {
ser.U64(num)
}
struct := &MyStruct{ num: 100 }
bytes, _ := Serialize(struct)
func SerializeBytes ¶
SerializeBytes Serializes a single byte array
input := []byte{0x1, 0x2}
bytes, _ := SerializeBytes(input)
func SerializeOption ¶ added in v1.2.0
func SerializeOption[T any](ser *Serializer, input *T, serialize func(ser *Serializer, item T))
SerializeOption serializes an optional value
Under the hood, this is represented as a 0 or 1 length array ¶
Here's an example for handling an optional value:
// For a Some(10) value
input := uint8(10)
ser := &Serializer{}
bytes, _ := SerializeOption(ser, &input, func(ser *Serializer, item uint8) {
ser.U8(item)
})
// bytes == []byte{0x01,0x0A}
// For a None value
ser2 := &Serializer{}
bytes2, _ := SerializeOption(ser2, nil, func(ser *Serializer, item uint8) {
ser.U8(item)
})
// bytes2 == []byte{0x00}
func SerializeSequence ¶
func SerializeSequence[AT []T, T any](array AT, ser *Serializer)
SerializeSequence serializes a sequence of Marshaler implemented types. Prefixed with the length of the sequence.
It works with both array values by reference and by value:
type MyStruct struct {
num uint64
}
func (str *MyStruct) MarshalBCS(ser *Serialize) {
ser.U64(num)
}
myArray := []MyStruct{
MyStruct{num: 0},
MyStruct{num: 1},
MyStruct{num: 2},
}
serializer := &Serializer{}
SerializeSequence(myArray, ser)
bytes := serializer.ToBytes()
func SerializeSequenceOnly ¶
SerializeSequenceOnly serializes a sequence into a single value using SerializeSequence
type MyStruct struct {
num uint64
}
func (str *MyStruct) MarshalBCS(ser *Serialize) {
ser.U64(num)
}
myArray := []MyStruct{
MyStruct{num: 0},
MyStruct{num: 1},
MyStruct{num: 2},
}
bytes, err := SerializeSequenceOnly(myArray)
func SerializeSequenceWithFunction ¶
func SerializeSequenceWithFunction[AT []T, T any](array AT, ser *Serializer, serialize func(ser *Serializer, item T))
SerializeSequenceWithFunction allows custom serialization of a sequence, which can be useful for non-bcs.Struct types
array := []string{"hello", "blockchain"}
SerializeSequenceWithFunction(array, func(ser *Serializer, item string) {
ser.WriteString(item)
}
func SerializeSerialized ¶ added in v1.6.0
func SerializeSerialized(input Serialized) ([]byte, error)
func SerializeSingle ¶
func SerializeSingle(marshal func(ser *Serializer)) ([]byte, error)
SerializeSingle is a convenience function, to not have to create a serializer to serialize one value
Here's an example for handling a nested byte array
input := [][]byte{[]byte{0x1}, []byte{0x2}}
bytes, _ := SerializeSingle(func(ser *Serializer) {
ser.Uleb128(len(input))
for _, list := range input {
ser.WriteBytes(list)
}
})
func SerializeU128 ¶
SerializeU128 Serializes a single uint128
u128 := big.NewInt(1) bytes, _ := SerializeU128(u128)
func SerializeU256 ¶
SerializeU256 Serializes a single uint256
u256 := big.NewInt(1) bytes, _ := SerializeU256(u256)
func SerializeUleb128 ¶ added in v1.6.0
SerializeUleb128 Serializes a single uleb128
Types ¶
type Deserializer ¶
type Deserializer struct {
// contains filtered or unexported fields
}
Deserializer is a type to deserialize a known set of bytes. The reader must know the types, as the format is not self-describing.
Use NewDeserializer to initialize the Deserializer
bytes := []byte{0x01}
deserializer := NewDeserializer(bytes)
num := deserializer.U8()
if deserializer.Error() != nil {
return deserializer.Error()
}
func NewDeserializer ¶
func NewDeserializer(bytes []byte) *Deserializer
NewDeserializer creates a new Deserializer from a byte array.
func (*Deserializer) Bool ¶
func (des *Deserializer) Bool() bool
Bool deserializes a single byte as a bool
func (*Deserializer) Error ¶
func (des *Deserializer) Error() error
Error If there has been any error, return it
func (*Deserializer) ReadBytes ¶
func (des *Deserializer) ReadBytes() []byte
ReadBytes reads bytes prefixed with a length
func (*Deserializer) ReadFixedBytes ¶
func (des *Deserializer) ReadFixedBytes(length int) []byte
ReadFixedBytes reads bytes not-prefixed with a length
func (*Deserializer) ReadFixedBytesInto ¶
func (des *Deserializer) ReadFixedBytesInto(dest []byte)
ReadFixedBytesInto reads bytes not-prefixed with a length into a byte array
func (*Deserializer) ReadString ¶
func (des *Deserializer) ReadString() string
ReadString reads UTF-8 bytes prefixed with a length
func (*Deserializer) Remaining ¶
func (des *Deserializer) Remaining() int
Remaining tells the remaining bytes, which can be useful if there were more bytes than expected
bytes := []byte{0x01, 0x02}
deserializer := NewDeserializer(bytes)
num := deserializer.U8()
deserializer.Remaining == 1
func (*Deserializer) Serialized ¶ added in v1.6.0
func (des *Deserializer) Serialized() *Serialized
DeserializeBytes deserializes a byte array prefixed with a length
func (*Deserializer) SetError ¶
func (des *Deserializer) SetError(err error)
SetError If the data is well-formed but nonsense, UnmarshalBCS() code can set error
func (*Deserializer) Struct ¶
func (des *Deserializer) Struct(v Unmarshaler)
Struct reads an Unmarshaler implementation from bcs bytes
This is used for handling types outside the provided primitives
func (*Deserializer) U128 ¶
func (des *Deserializer) U128() big.Int
U128 deserializes a single unsigned 128-bit integer
func (*Deserializer) U16 ¶
func (des *Deserializer) U16() uint16
U16 deserializes a single unsigned 16-bit integer
func (*Deserializer) U256 ¶
func (des *Deserializer) U256() big.Int
U256 deserializes a single unsigned 256-bit integer
func (*Deserializer) U32 ¶
func (des *Deserializer) U32() uint32
U32 deserializes a single unsigned 32-bit integer
func (*Deserializer) U64 ¶
func (des *Deserializer) U64() uint64
func (*Deserializer) U8 ¶
func (des *Deserializer) U8() uint8
U8 deserializes a single unsigned 8-bit integer
func (*Deserializer) Uleb128 ¶
func (des *Deserializer) Uleb128() uint32
Uleb128 deserializes a 32-bit integer from a variable length Unsigned LEB128
type Marshaler ¶
type Marshaler interface {
// MarshalBCS implements a way to serialize the type into BCS. Note that the error will need to be directly set
// using [Serializer.SetError] on the [Serializer]. If using this function, you will need to use [Serializer.Error]
// to retrieve the error.
MarshalBCS(ser *Serializer)
}
Marshaler is an interface for any type that can be serialized into BCS
It's highly suggested to implement on a pointer to a type, and not the type directly. For example, you could implement a simple Marshaler for a given struct.
type MyStruct struct {
num uint8
boolean bool
}
func (str *MyStruct) MarshalBCS(ser *Serializer) {
ser.U8(str.num)
ser.Bool(str.boolean)
}
Additionally, if there is expected data, you can add errors to serialization. It's suggested to stop serialization after any errors.
type MyStruct struct {
num uint8 // Only allowed to be 0-10
boolean bool
}
func (str *MyStruct) MarshalBCS(ser *Serializer) {
if str.num > 10 {
ser.SetError(fmt.Error("Cannot serialize MyStruct, num is greater than 10: %d", str.num)
return
}
ser.U8(str.num)
ser.Bool(str.boolean)
}
type Serialized ¶ added in v1.6.0
type Serialized struct {
Value []byte
}
Serialized represents a serialized transaction argument
func NewSerialized ¶ added in v1.6.0
func NewSerialized(value []byte) *Serialized
NewSerialized creates a new Serialized instance
func (*Serialized) Serialized ¶ added in v1.6.0
func (s *Serialized) Serialized(serializer *Serializer)
Serialize serializes the Serialized instance
func (*Serialized) SerializedForEntryFunction ¶ added in v1.6.0
func (s *Serialized) SerializedForEntryFunction(serializer *Serializer)
SerializeForEntryFunction serializes the Serialized instance for entry function
func (*Serialized) SerializedForScriptFunction ¶ added in v1.6.0
func (s *Serialized) SerializedForScriptFunction(serializer *Serializer)
SerializeForScriptFunction serializes the Serialized instance for script function
type Serializer ¶
type Serializer struct {
// contains filtered or unexported fields
}
Serializer is a holding type to serialize a set of items into one shared buffer
serializer := &Serializer{}
serializer.U64(uint64(10))
serializedBytes := serializer.ToBytes()
func (*Serializer) Bool ¶
func (ser *Serializer) Bool(v bool)
Bool serialize a bool into a single byte, 0x01 for true and 0x00 for false
func (*Serializer) Error ¶
func (ser *Serializer) Error() error
Error the error if serialization has failed at any point
func (*Serializer) FixedBytes ¶
func (ser *Serializer) FixedBytes(v []byte)
FixedBytes similar to Serializer.WriteBytes, but it forgoes the length header. This is useful if you know the fixed length size of the data, such as AccountAddress
func (*Serializer) Serialized ¶ added in v1.6.0
func (ser *Serializer) Serialized(s Serialized)
Serialized wraps already serialized bytes with BCS serialization It prepends the byte array with its length (encoded as Uleb128) and writes the bytes Primarily used for handling nested serialization structures where bytes are already in BCS format
func (*Serializer) SetError ¶
func (ser *Serializer) SetError(err error)
SetError If the data is well-formed but nonsense, [Marshaler.MarshalBCS] code can set the error.
func (*Serializer) Struct ¶
func (ser *Serializer) Struct(v Marshaler)
Struct uses custom serialization for a Marshaler implementation.
func (*Serializer) ToBytes ¶
func (ser *Serializer) ToBytes() []byte
ToBytes outputs the encoded bytes
func (*Serializer) U128 ¶
func (ser *Serializer) U128(v big.Int)
U128 serialize an unsigned 128-bit integer in little-endian format
func (*Serializer) U16 ¶
func (ser *Serializer) U16(v uint16)
U16 serialize an unsigned 16-bit integer in little-endian format
func (*Serializer) U256 ¶
func (ser *Serializer) U256(v big.Int)
U256 serialize an unsigned 256-bit integer in little-endian format
func (*Serializer) U32 ¶
func (ser *Serializer) U32(v uint32)
U32 serialize an unsigned 32-bit integer in little-endian format
func (*Serializer) U64 ¶
func (ser *Serializer) U64(v uint64)
U64 serialize an unsigned 64-bit integer in little-endian format
func (*Serializer) Uleb128 ¶
func (ser *Serializer) Uleb128(val uint32)
Uleb128 serialize an unsigned 32-bit integer as an Uleb128. This is used specifically for sequence lengths, and enums.
func (*Serializer) WriteBytes ¶
func (ser *Serializer) WriteBytes(v []byte)
WriteBytes serialize an array of bytes with its length first as an Uleb128.
func (*Serializer) WriteString ¶
func (ser *Serializer) WriteString(v string)
WriteString similar to Serializer.WriteBytes using the UTF-8 byte representation of the string
type Struct ¶
type Struct interface {
Marshaler
Unmarshaler
}
Struct is an interface for an on-chain type. It must be able to be both Marshaler and Unmarshaler for BCS
type Unmarshaler ¶
type Unmarshaler interface {
// UnmarshalBCS implements a way to deserialize the type into BCS. Note that the error will need to be directly set
// using [Deserializer.SetError] on the [Deserializer]. If using this function, you will need to use [Deserializer.Error]
// to retrieve the error.
UnmarshalBCS(des *Deserializer)
}
Unmarshaler is an interface for any type that can be deserialized from BCS
It's highly suggested to implement on a pointer to a type, and not the type directly. For example, you could implement a simple Unmarshaler for a given struct. You will need to add any appropriate error handling.
type MyStruct struct {
num uint8
boolean bool
}
func (str *MyStruct) UnmarshalBCS(des *Deserializer) {
str.num = des.U8()
str.boolean = des.Bool()
}
Additionally, if there is expected formatting errors, you can add errors to deserialization. It's suggested to stop serialization after any errors.
type MyStruct struct {
num uint8 // Only allowed to be 0-10
boolean bool
}
func (str *MyStruct) UnmarshalBCS(des *Deserializer) {
str.num = des.U8()
if des.Error() {
// End early, since deserialization failed
return
}
if str.num > 10 {
ser.SetError(fmt.Error("Cannot deserialize MyStruct, num is greater than 10: %d", str.num)
return
}
str.boolean = des.Bool()
}