core: reserve attributes range for experimantal purposes
This commit is contained in:
parent
368ff820b3
commit
09b8b8de73
7 changed files with 107 additions and 7 deletions
|
@ -11,6 +11,8 @@ type (
|
|||
MemPoolSize int `yaml:"MemPoolSize"`
|
||||
// P2PSigExtensions enables additional signature-related transaction attributes
|
||||
P2PSigExtensions bool `yaml:"P2PSigExtensions"`
|
||||
// ReservedAttributes allows to have reserved attributes range for experimental or private purposes.
|
||||
ReservedAttributes bool `yaml:"ReservedAttributes"`
|
||||
// SaveStorageBatch enables storage batch saving before every persist.
|
||||
SaveStorageBatch bool `yaml:"SaveStorageBatch"`
|
||||
SecondsPerBlock int `yaml:"SecondsPerBlock"`
|
||||
|
|
|
@ -1258,7 +1258,7 @@ func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.
|
|||
|
||||
func (bc *Blockchain) verifyTxAttributes(tx *transaction.Transaction) error {
|
||||
for i := range tx.Attributes {
|
||||
switch tx.Attributes[i].Type {
|
||||
switch attrType := tx.Attributes[i].Type; attrType {
|
||||
case transaction.HighPriority:
|
||||
h := bc.contracts.NEO.GetCommitteeAddress()
|
||||
for i := range tx.Signers {
|
||||
|
@ -1303,6 +1303,10 @@ func (bc *Blockchain) verifyTxAttributes(tx *transaction.Transaction) error {
|
|||
if height := bc.BlockHeight(); height < nvb.Height {
|
||||
return fmt.Errorf("%w: NotValidBefore = %d, current height = %d", ErrTxNotYetValid, nvb.Height, height)
|
||||
}
|
||||
default:
|
||||
if !bc.config.ReservedAttributes && attrType >= transaction.ReservedLowerBound && attrType <= transaction.ReservedUpperBound {
|
||||
return errors.New("attribute of reserved type was found, but ReservedAttributes are disabled")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -597,6 +597,38 @@ func TestVerifyTx(t *testing.T) {
|
|||
})
|
||||
})
|
||||
})
|
||||
t.Run("Reserved", func(t *testing.T) {
|
||||
getReservedTx := func(attrType transaction.AttrType) *transaction.Transaction {
|
||||
tx := bc.newTestTx(h, testScript)
|
||||
tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: attrType, Value: &transaction.Reserved{Value: []byte{1, 2, 3}}})
|
||||
tx.NetworkFee += 4_000_000 // multisig check
|
||||
tx.Signers = []transaction.Signer{{
|
||||
Account: testchain.CommitteeScriptHash(),
|
||||
Scopes: transaction.None,
|
||||
}}
|
||||
rawScript := testchain.CommitteeVerificationScript()
|
||||
require.NoError(t, err)
|
||||
size := io.GetVarSize(tx)
|
||||
netFee, sizeDelta := fee.Calculate(rawScript)
|
||||
tx.NetworkFee += netFee
|
||||
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
||||
data := tx.GetSignedPart()
|
||||
tx.Scripts = []transaction.Witness{{
|
||||
InvocationScript: testchain.SignCommittee(data),
|
||||
VerificationScript: rawScript,
|
||||
}}
|
||||
return tx
|
||||
}
|
||||
t.Run("Disabled", func(t *testing.T) {
|
||||
tx := getReservedTx(transaction.ReservedLowerBound + 2)
|
||||
require.Error(t, bc.VerifyTx(tx))
|
||||
})
|
||||
t.Run("Enabled", func(t *testing.T) {
|
||||
bc.config.ReservedAttributes = true
|
||||
tx := getReservedTx(transaction.ReservedLowerBound + 2)
|
||||
require.NoError(t, bc.VerifyTx(tx))
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -30,28 +30,36 @@ type attrJSON struct {
|
|||
func (attr *Attribute) DecodeBinary(br *io.BinReader) {
|
||||
attr.Type = AttrType(br.ReadB())
|
||||
|
||||
switch attr.Type {
|
||||
switch t := attr.Type; t {
|
||||
case HighPriority:
|
||||
return
|
||||
case OracleResponseT:
|
||||
attr.Value = new(OracleResponse)
|
||||
attr.Value.DecodeBinary(br)
|
||||
case NotValidBeforeT:
|
||||
attr.Value = new(NotValidBefore)
|
||||
attr.Value.DecodeBinary(br)
|
||||
default:
|
||||
if t >= ReservedLowerBound && t <= ReservedUpperBound {
|
||||
attr.Value = new(Reserved)
|
||||
break
|
||||
}
|
||||
br.Err = fmt.Errorf("failed decoding TX attribute usage: 0x%2x", int(attr.Type))
|
||||
return
|
||||
}
|
||||
attr.Value.DecodeBinary(br)
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (attr *Attribute) EncodeBinary(bw *io.BinWriter) {
|
||||
bw.WriteB(byte(attr.Type))
|
||||
switch attr.Type {
|
||||
switch t := attr.Type; t {
|
||||
case HighPriority:
|
||||
case OracleResponseT, NotValidBeforeT:
|
||||
attr.Value.EncodeBinary(bw)
|
||||
default:
|
||||
if t >= ReservedLowerBound && t <= ReservedUpperBound {
|
||||
attr.Value.EncodeBinary(bw)
|
||||
break
|
||||
}
|
||||
bw.Err = fmt.Errorf("failed encoding TX attribute usage: 0x%2x", attr.Type)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,29 @@ func TestAttribute_EncodeBinary(t *testing.T) {
|
|||
}
|
||||
testserdes.EncodeDecodeBinary(t, attr, new(Attribute))
|
||||
})
|
||||
t.Run("Reserved", func(t *testing.T) {
|
||||
getReservedAttribute := func(t AttrType) *Attribute {
|
||||
return &Attribute{
|
||||
Type: t,
|
||||
Value: &Reserved{
|
||||
Value: []byte{1, 2, 3, 4, 5},
|
||||
},
|
||||
}
|
||||
}
|
||||
t.Run("lower bound", func(t *testing.T) {
|
||||
testserdes.EncodeDecodeBinary(t, getReservedAttribute(ReservedLowerBound+2), new(Attribute))
|
||||
})
|
||||
t.Run("upper bound", func(t *testing.T) {
|
||||
testserdes.EncodeDecodeBinary(t, getReservedAttribute(ReservedUpperBound), new(Attribute))
|
||||
})
|
||||
t.Run("inside bounds", func(t *testing.T) {
|
||||
testserdes.EncodeDecodeBinary(t, getReservedAttribute(ReservedLowerBound+5), new(Attribute))
|
||||
})
|
||||
t.Run("not reserved", func(t *testing.T) {
|
||||
_, err := testserdes.EncodeBinary(getReservedAttribute(ReservedLowerBound - 1))
|
||||
require.Error(t, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestAttribute_MarshalJSON(t *testing.T) {
|
||||
|
|
|
@ -5,11 +5,18 @@ package transaction
|
|||
// AttrType represents the purpose of the attribute.
|
||||
type AttrType uint8
|
||||
|
||||
const (
|
||||
// ReservedLowerBound is the lower bound of reserved attribute types
|
||||
ReservedLowerBound = 0xe0
|
||||
// ReservedUpperBound is the upper bound of reserved attribute types
|
||||
ReservedUpperBound = 0xff
|
||||
)
|
||||
|
||||
// List of valid attribute types.
|
||||
const (
|
||||
HighPriority AttrType = 1
|
||||
OracleResponseT AttrType = 0x11 // OracleResponse
|
||||
NotValidBeforeT AttrType = 0xe0 // NotValidBefore
|
||||
OracleResponseT AttrType = 0x11 // OracleResponse
|
||||
NotValidBeforeT AttrType = ReservedLowerBound // NotValidBefore
|
||||
)
|
||||
|
||||
func (a AttrType) allowMultiple() bool {
|
||||
|
|
24
pkg/core/transaction/reserved.go
Normal file
24
pkg/core/transaction/reserved.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
)
|
||||
|
||||
// Reserved represents an attribute for experimental or private usage.
|
||||
type Reserved struct {
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// DecodeBinary implements io.Serializable interface.
|
||||
func (e *Reserved) DecodeBinary(br *io.BinReader) {
|
||||
e.Value = br.ReadVarBytes()
|
||||
}
|
||||
|
||||
// EncodeBinary implements io.Serializable interface.
|
||||
func (e *Reserved) EncodeBinary(w *io.BinWriter) {
|
||||
w.WriteVarBytes(e.Value)
|
||||
}
|
||||
|
||||
func (e *Reserved) toJSONMap(m map[string]interface{}) {
|
||||
m["value"] = e.Value
|
||||
}
|
Loading…
Reference in a new issue