network/payload: add Extensible payload
This commit is contained in:
parent
6bc2512767
commit
59a193c7c7
2 changed files with 197 additions and 0 deletions
126
pkg/network/payload/extensible.go
Normal file
126
pkg/network/payload/extensible.go
Normal file
|
@ -0,0 +1,126 @@
|
|||
package payload
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
maxExtensibleCategorySize = 32
|
||||
maxExtensibleDataSize = math.MaxUint16
|
||||
)
|
||||
|
||||
// Extensible represents payload containing arbitrary data.
|
||||
type Extensible struct {
|
||||
// Network represents network magic.
|
||||
Network netmode.Magic
|
||||
// Category is payload type.
|
||||
Category string
|
||||
// ValidBlockStart is starting height for payload to be valid.
|
||||
ValidBlockStart uint32
|
||||
// ValidBlockEnd is height after which payload becomes invalid.
|
||||
ValidBlockEnd uint32
|
||||
// Sender is payload sender or signer.
|
||||
Sender util.Uint160
|
||||
// Data is custom payload data.
|
||||
Data []byte
|
||||
// Witness is payload witness.
|
||||
Witness transaction.Witness
|
||||
|
||||
hash util.Uint256
|
||||
signedHash util.Uint256
|
||||
signedpart []byte
|
||||
}
|
||||
|
||||
var errInvalidPadding = errors.New("invalid padding")
|
||||
|
||||
// NewExtensible creates new extensible payload.
|
||||
func NewExtensible(network netmode.Magic) *Extensible {
|
||||
return &Extensible{Network: network}
|
||||
}
|
||||
|
||||
func (e *Extensible) encodeBinaryUnsigned(w *io.BinWriter) {
|
||||
w.WriteString(e.Category)
|
||||
w.WriteU32LE(e.ValidBlockStart)
|
||||
w.WriteU32LE(e.ValidBlockEnd)
|
||||
w.WriteBytes(e.Sender[:])
|
||||
w.WriteVarBytes(e.Data)
|
||||
}
|
||||
|
||||
// EncodeBinary implements io.Serializable.
|
||||
func (e *Extensible) EncodeBinary(w *io.BinWriter) {
|
||||
e.encodeBinaryUnsigned(w)
|
||||
w.WriteB(1)
|
||||
e.Witness.EncodeBinary(w)
|
||||
}
|
||||
|
||||
func (e *Extensible) decodeBinaryUnsigned(r *io.BinReader) {
|
||||
e.Category = r.ReadString(maxExtensibleCategorySize)
|
||||
e.ValidBlockStart = r.ReadU32LE()
|
||||
e.ValidBlockEnd = r.ReadU32LE()
|
||||
r.ReadBytes(e.Sender[:])
|
||||
e.Data = r.ReadVarBytes(maxExtensibleDataSize)
|
||||
}
|
||||
|
||||
// DecodeBinary implements io.Serializable.
|
||||
func (e *Extensible) DecodeBinary(r *io.BinReader) {
|
||||
e.decodeBinaryUnsigned(r)
|
||||
if r.ReadB() != 1 {
|
||||
if r.Err != nil {
|
||||
return
|
||||
}
|
||||
r.Err = errInvalidPadding
|
||||
return
|
||||
}
|
||||
e.Witness.DecodeBinary(r)
|
||||
}
|
||||
|
||||
// GetSignedPart implements crypto.Verifiable.
|
||||
func (e *Extensible) GetSignedPart() []byte {
|
||||
if e.signedpart == nil {
|
||||
e.updateSignedPart()
|
||||
}
|
||||
return e.signedpart
|
||||
}
|
||||
|
||||
// GetSignedHash implements crypto.Verifiable.
|
||||
func (e *Extensible) GetSignedHash() util.Uint256 {
|
||||
if e.signedHash.Equals(util.Uint256{}) {
|
||||
e.createHash()
|
||||
}
|
||||
return e.signedHash
|
||||
}
|
||||
|
||||
// Hash returns payload hash.
|
||||
func (e *Extensible) Hash() util.Uint256 {
|
||||
if e.hash.Equals(util.Uint256{}) {
|
||||
e.createHash()
|
||||
}
|
||||
return e.hash
|
||||
}
|
||||
|
||||
// createHash creates hashes of the payload.
|
||||
func (e *Extensible) createHash() {
|
||||
b := e.GetSignedPart()
|
||||
e.updateHashes(b)
|
||||
}
|
||||
|
||||
// updateHashes updates hashes based on the given buffer which should
|
||||
// be a signable data slice.
|
||||
func (e *Extensible) updateHashes(b []byte) {
|
||||
e.signedHash = hash.Sha256(b)
|
||||
e.hash = hash.Sha256(e.signedHash.BytesBE())
|
||||
}
|
||||
|
||||
// updateSignedPart updates serialized message if needed.
|
||||
func (e *Extensible) updateSignedPart() {
|
||||
w := io.NewBufBinWriter()
|
||||
e.encodeBinaryUnsigned(w.BinWriter)
|
||||
e.signedpart = w.Bytes()
|
||||
}
|
71
pkg/network/payload/extensible_test.go
Normal file
71
pkg/network/payload/extensible_test.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package payload
|
||||
|
||||
import (
|
||||
"errors"
|
||||
gio "io"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestExtensible_Serializable(t *testing.T) {
|
||||
expected := &Extensible{
|
||||
Category: "test",
|
||||
ValidBlockStart: 12,
|
||||
ValidBlockEnd: 1234,
|
||||
Sender: random.Uint160(),
|
||||
Data: random.Bytes(4),
|
||||
Witness: transaction.Witness{
|
||||
InvocationScript: random.Bytes(3),
|
||||
VerificationScript: random.Bytes(3),
|
||||
},
|
||||
}
|
||||
|
||||
testserdes.EncodeDecodeBinary(t, expected, new(Extensible))
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
w := io.NewBufBinWriter()
|
||||
expected.encodeBinaryUnsigned(w.BinWriter)
|
||||
unsigned := w.Bytes()
|
||||
|
||||
t.Run("unexpected EOF", func(t *testing.T) {
|
||||
err := testserdes.DecodeBinary(unsigned, new(Extensible))
|
||||
require.True(t, errors.Is(err, gio.EOF))
|
||||
})
|
||||
t.Run("invalid padding", func(t *testing.T) {
|
||||
err := testserdes.DecodeBinary(append(unsigned, 42), new(Extensible))
|
||||
require.True(t, errors.Is(err, errInvalidPadding))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestExtensible_Hashes(t *testing.T) {
|
||||
getExtensiblePair := func() (*Extensible, *Extensible) {
|
||||
p1 := NewExtensible(netmode.UnitTestNet)
|
||||
p1.Data = []byte{1, 2, 3}
|
||||
p2 := NewExtensible(netmode.UnitTestNet)
|
||||
p2.Data = []byte{3, 2, 1}
|
||||
return p1, p2
|
||||
}
|
||||
|
||||
t.Run("GetSignedPart", func(t *testing.T) {
|
||||
p1, p2 := getExtensiblePair()
|
||||
require.NotEqual(t, p1.GetSignedPart(), p2.GetSignedPart())
|
||||
require.NotEqual(t, p1.GetSignedPart(), p2.GetSignedPart())
|
||||
})
|
||||
t.Run("GetSignedHash", func(t *testing.T) {
|
||||
p1, p2 := getExtensiblePair()
|
||||
require.NotEqual(t, p1.GetSignedHash(), p2.GetSignedHash())
|
||||
require.NotEqual(t, p1.GetSignedHash(), p2.GetSignedHash())
|
||||
})
|
||||
t.Run("Hash", func(t *testing.T) {
|
||||
p1, p2 := getExtensiblePair()
|
||||
require.NotEqual(t, p1.Hash(), p2.Hash())
|
||||
require.NotEqual(t, p1.Hash(), p2.Hash())
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue