mirror of https://github.com/poanetwork/gecko.git
145 lines
3.4 KiB
Go
145 lines
3.4 KiB
Go
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package state
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"sort"
|
|
|
|
"github.com/ava-labs/gecko/ids"
|
|
"github.com/ava-labs/gecko/snow/consensus/snowstorm"
|
|
"github.com/ava-labs/gecko/snow/engine/avalanche"
|
|
"github.com/ava-labs/gecko/utils"
|
|
"github.com/ava-labs/gecko/utils/hashing"
|
|
"github.com/ava-labs/gecko/utils/wrappers"
|
|
)
|
|
|
|
// maxSize is the maximum allowed vertex size. It is necessary to deter DoS.
|
|
const maxSize = 1 << 20
|
|
|
|
var (
|
|
errBadCodec = errors.New("invalid codec")
|
|
errExtraSpace = errors.New("trailing buffer space")
|
|
errInvalidParents = errors.New("vertex contains non-sorted or duplicated parentIDs")
|
|
errInvalidTxs = errors.New("vertex contains non-sorted or duplicated transactions")
|
|
)
|
|
|
|
type vertex struct {
|
|
id ids.ID
|
|
|
|
chainID ids.ID
|
|
height uint64
|
|
|
|
parentIDs []ids.ID
|
|
txs []snowstorm.Tx
|
|
|
|
bytes []byte
|
|
}
|
|
|
|
func (vtx *vertex) ID() ids.ID { return vtx.id }
|
|
func (vtx *vertex) Bytes() []byte { return vtx.bytes }
|
|
|
|
func (vtx *vertex) Verify() error {
|
|
switch {
|
|
case !ids.IsSortedAndUniqueIDs(vtx.parentIDs):
|
|
return errInvalidParents
|
|
case !isSortedAndUniqueTxs(vtx.txs):
|
|
return errInvalidTxs
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Vertex:
|
|
* Codec | 04 Bytes
|
|
* Chain | 32 Bytes
|
|
* Height | 08 Bytes
|
|
* NumParents | 04 Bytes
|
|
* Repeated (NumParents):
|
|
* ParentID | 32 bytes
|
|
* NumTxs | 04 Bytes
|
|
* Repeated (NumTxs):
|
|
* TxSize | 04 bytes
|
|
* Tx | ?? bytes
|
|
*/
|
|
|
|
// Marshal creates the byte representation of the vertex
|
|
func (vtx *vertex) Marshal() ([]byte, error) {
|
|
p := wrappers.Packer{MaxSize: maxSize}
|
|
|
|
p.PackInt(uint32(CustomID))
|
|
p.PackFixedBytes(vtx.chainID.Bytes())
|
|
p.PackLong(vtx.height)
|
|
|
|
p.PackInt(uint32(len(vtx.parentIDs)))
|
|
for _, parentID := range vtx.parentIDs {
|
|
p.PackFixedBytes(parentID.Bytes())
|
|
}
|
|
|
|
p.PackInt(uint32(len(vtx.txs)))
|
|
for _, tx := range vtx.txs {
|
|
p.PackBytes(tx.Bytes())
|
|
}
|
|
return p.Bytes, p.Err
|
|
}
|
|
|
|
// Unmarshal attempts to set the contents of this vertex to the value encoded in
|
|
// the stream of bytes.
|
|
func (vtx *vertex) Unmarshal(b []byte, vm avalanche.DAGVM) error {
|
|
p := wrappers.Packer{Bytes: b}
|
|
|
|
if codecID := ID(p.UnpackInt()); codecID != CustomID {
|
|
p.Add(errBadCodec)
|
|
}
|
|
|
|
chainID, _ := ids.ToID(p.UnpackFixedBytes(hashing.HashLen))
|
|
height := p.UnpackLong()
|
|
|
|
parentIDs := []ids.ID(nil)
|
|
for i := p.UnpackInt(); i > 0 && !p.Errored(); i-- {
|
|
parentID, _ := ids.ToID(p.UnpackFixedBytes(hashing.HashLen))
|
|
parentIDs = append(parentIDs, parentID)
|
|
}
|
|
|
|
txs := []snowstorm.Tx(nil)
|
|
for i := p.UnpackInt(); i > 0 && !p.Errored(); i-- {
|
|
tx, err := vm.ParseTx(p.UnpackBytes())
|
|
p.Add(err)
|
|
txs = append(txs, tx)
|
|
}
|
|
|
|
if p.Offset != len(b) {
|
|
p.Add(errExtraSpace)
|
|
}
|
|
|
|
if p.Errored() {
|
|
return p.Err
|
|
}
|
|
|
|
*vtx = vertex{
|
|
id: ids.NewID(hashing.ComputeHash256Array(b)),
|
|
parentIDs: parentIDs,
|
|
chainID: chainID,
|
|
height: height,
|
|
txs: txs,
|
|
bytes: b,
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type sortTxsData []snowstorm.Tx
|
|
|
|
func (txs sortTxsData) Less(i, j int) bool {
|
|
return bytes.Compare(txs[i].ID().Bytes(), txs[j].ID().Bytes()) == -1
|
|
}
|
|
func (txs sortTxsData) Len() int { return len(txs) }
|
|
func (txs sortTxsData) Swap(i, j int) { txs[j], txs[i] = txs[i], txs[j] }
|
|
|
|
func sortTxs(txs []snowstorm.Tx) { sort.Sort(sortTxsData(txs)) }
|
|
func isSortedAndUniqueTxs(txs []snowstorm.Tx) bool {
|
|
return utils.IsSortedAndUnique(sortTxsData(txs))
|
|
}
|