neoneo-go/pkg/core/native/neo_types.go
Roman Khimov c3d989ebda stackitem: reusable serialization context
We serialize items a lot and this allows to avoid a number of allocations.
2022-06-02 15:38:39 +03:00

100 lines
2.4 KiB
Go

package native
import (
"crypto/elliptic"
"errors"
"math/big"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// gasIndexPair contains the block index together with the generated gas per block.
// It is used to cache NEO GASRecords.
type gasIndexPair struct {
Index uint32
GASPerBlock big.Int
}
// gasRecord contains the history of gas per block changes. It is used only by NEO cache.
type gasRecord []gasIndexPair
type (
// keyWithVotes is a serialized key with votes balance. It's not deserialized
// because some uses of it imply serialized-only usage and converting to
// PublicKey is quite expensive.
keyWithVotes struct {
Key string
Votes *big.Int
// UnmarshaledKey contains public key if it was unmarshaled.
UnmarshaledKey *keys.PublicKey
}
keysWithVotes []keyWithVotes
)
// PublicKey unmarshals and returns the public key of k.
func (k keyWithVotes) PublicKey() (*keys.PublicKey, error) {
if k.UnmarshaledKey != nil {
return k.UnmarshaledKey, nil
}
return keys.NewPublicKeyFromBytes([]byte(k.Key), elliptic.P256())
}
func (k keysWithVotes) toStackItem() stackitem.Item {
arr := make([]stackitem.Item, len(k))
for i := range k {
arr[i] = stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray([]byte(k[i].Key)),
stackitem.NewBigInteger(k[i].Votes),
})
}
return stackitem.NewArray(arr)
}
func (k *keysWithVotes) fromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
var kvs = make(keysWithVotes, len(arr))
for i := range arr {
s, ok := arr[i].Value().([]stackitem.Item)
if !ok {
return errors.New("element is not a struct")
} else if len(s) < 2 {
return errors.New("invalid length")
}
pub, err := s[0].TryBytes()
if err != nil {
return err
}
vs, err := s[1].TryInteger()
if err != nil {
return err
}
kvs[i].Key = string(pub)
kvs[i].Votes = vs
}
*k = kvs
return nil
}
// Bytes serializes keys with votes slice.
func (k keysWithVotes) Bytes(sc *stackitem.SerializationContext) []byte {
buf, err := sc.Serialize(k.toStackItem(), false)
if err != nil {
panic(err)
}
return buf
}
// DecodeBytes deserializes keys and votes slice.
func (k *keysWithVotes) DecodeBytes(data []byte) error {
it, err := stackitem.Deserialize(data)
if err != nil {
return err
}
return k.fromStackItem(it)
}