parent
e9a8e957f8
commit
368ff820b3
8 changed files with 109 additions and 1 deletions
|
@ -9,6 +9,8 @@ type (
|
|||
ProtocolConfiguration struct {
|
||||
Magic netmode.Magic `yaml:"Magic"`
|
||||
MemPoolSize int `yaml:"MemPoolSize"`
|
||||
// P2PSigExtensions enables additional signature-related transaction attributes
|
||||
P2PSigExtensions bool `yaml:"P2PSigExtensions"`
|
||||
// SaveStorageBatch enables storage batch saving before every persist.
|
||||
SaveStorageBatch bool `yaml:"SaveStorageBatch"`
|
||||
SecondsPerBlock int `yaml:"SecondsPerBlock"`
|
||||
|
|
|
@ -1196,6 +1196,7 @@ func (bc *Blockchain) verifyHeader(currHeader, prevHeader *block.Header) error {
|
|||
|
||||
// Various errors that could be returned upon verification.
|
||||
var (
|
||||
ErrTxNotYetValid = errors.New("transaction is not yet valid")
|
||||
ErrTxExpired = errors.New("transaction has expired")
|
||||
ErrInsufficientFunds = errors.New("insufficient funds")
|
||||
ErrTxSmallNetworkFee = errors.New("too small network fee")
|
||||
|
@ -1294,6 +1295,14 @@ func (bc *Blockchain) verifyTxAttributes(tx *transaction.Transaction) error {
|
|||
if uint64(tx.NetworkFee+tx.SystemFee) < req.GasForResponse {
|
||||
return fmt.Errorf("%w: oracle tx has insufficient gas", ErrInvalidAttribute)
|
||||
}
|
||||
case transaction.NotValidBeforeT:
|
||||
if !bc.config.P2PSigExtensions {
|
||||
return errors.New("NotValidBefore attribute was found, but P2PSigExtensions are disabled")
|
||||
}
|
||||
nvb := tx.Attributes[i].Value.(*transaction.NotValidBefore)
|
||||
if height := bc.BlockHeight(); height < nvb.Height {
|
||||
return fmt.Errorf("%w: NotValidBefore = %d, current height = %d", ErrTxNotYetValid, nvb.Height, height)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -559,6 +559,44 @@ func TestVerifyTx(t *testing.T) {
|
|||
checkErr(t, ErrInvalidAttribute, tx)
|
||||
})
|
||||
})
|
||||
t.Run("NotValidBefore", func(t *testing.T) {
|
||||
getNVBTx := func(height uint32) *transaction.Transaction {
|
||||
tx := bc.newTestTx(h, testScript)
|
||||
tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: transaction.NotValidBeforeT, Value: &transaction.NotValidBefore{Height: height}})
|
||||
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 := getNVBTx(bc.blockHeight + 1)
|
||||
require.Error(t, bc.VerifyTx(tx))
|
||||
})
|
||||
t.Run("Enabled", func(t *testing.T) {
|
||||
bc.config.P2PSigExtensions = true
|
||||
t.Run("NotYetValid", func(t *testing.T) {
|
||||
tx := getNVBTx(bc.blockHeight + 1)
|
||||
require.True(t, errors.Is(bc.VerifyTx(tx), ErrTxNotYetValid))
|
||||
})
|
||||
t.Run("positive", func(t *testing.T) {
|
||||
tx := getNVBTx(bc.blockHeight)
|
||||
require.NoError(t, bc.VerifyTx(tx))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,9 @@ func (attr *Attribute) DecodeBinary(br *io.BinReader) {
|
|||
case OracleResponseT:
|
||||
attr.Value = new(OracleResponse)
|
||||
attr.Value.DecodeBinary(br)
|
||||
case NotValidBeforeT:
|
||||
attr.Value = new(NotValidBefore)
|
||||
attr.Value.DecodeBinary(br)
|
||||
default:
|
||||
br.Err = fmt.Errorf("failed decoding TX attribute usage: 0x%2x", int(attr.Type))
|
||||
return
|
||||
|
@ -46,7 +49,7 @@ func (attr *Attribute) EncodeBinary(bw *io.BinWriter) {
|
|||
bw.WriteB(byte(attr.Type))
|
||||
switch attr.Type {
|
||||
case HighPriority:
|
||||
case OracleResponseT:
|
||||
case OracleResponseT, NotValidBeforeT:
|
||||
attr.Value.EncodeBinary(bw)
|
||||
default:
|
||||
bw.Err = fmt.Errorf("failed encoding TX attribute usage: 0x%2x", attr.Type)
|
||||
|
@ -78,6 +81,10 @@ func (attr *Attribute) UnmarshalJSON(data []byte) error {
|
|||
// value, we can unmarshal the same data. The overhead is minimal.
|
||||
attr.Value = new(OracleResponse)
|
||||
return json.Unmarshal(data, attr.Value)
|
||||
case "NotValidBefore":
|
||||
attr.Type = NotValidBeforeT
|
||||
attr.Value = new(NotValidBefore)
|
||||
return json.Unmarshal(data, attr.Value)
|
||||
default:
|
||||
return errors.New("wrong Type")
|
||||
|
||||
|
|
|
@ -27,6 +27,15 @@ func TestAttribute_EncodeBinary(t *testing.T) {
|
|||
}
|
||||
testserdes.EncodeDecodeBinary(t, attr, new(Attribute))
|
||||
})
|
||||
t.Run("NotValidBefore", func(t *testing.T) {
|
||||
attr := &Attribute{
|
||||
Type: NotValidBeforeT,
|
||||
Value: &NotValidBefore{
|
||||
Height: 123,
|
||||
},
|
||||
}
|
||||
testserdes.EncodeDecodeBinary(t, attr, new(Attribute))
|
||||
})
|
||||
}
|
||||
|
||||
func TestAttribute_MarshalJSON(t *testing.T) {
|
||||
|
@ -63,4 +72,13 @@ func TestAttribute_MarshalJSON(t *testing.T) {
|
|||
require.Equal(t, attr, actual)
|
||||
testserdes.EncodeDecodeBinary(t, attr, new(Attribute))
|
||||
})
|
||||
t.Run("NotValidBefore", func(t *testing.T) {
|
||||
attr := &Attribute{
|
||||
Type: NotValidBeforeT,
|
||||
Value: &NotValidBefore{
|
||||
Height: 123,
|
||||
},
|
||||
}
|
||||
testserdes.MarshalUnmarshalJSON(t, attr, new(Attribute))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ type AttrType uint8
|
|||
const (
|
||||
HighPriority AttrType = 1
|
||||
OracleResponseT AttrType = 0x11 // OracleResponse
|
||||
NotValidBeforeT AttrType = 0xe0 // NotValidBefore
|
||||
)
|
||||
|
||||
func (a AttrType) allowMultiple() bool {
|
||||
|
|
|
@ -10,11 +10,13 @@ func _() {
|
|||
var x [1]struct{}
|
||||
_ = x[HighPriority-1]
|
||||
_ = x[OracleResponseT-17]
|
||||
_ = x[NotValidBeforeT-224]
|
||||
}
|
||||
|
||||
const (
|
||||
_AttrType_name_0 = "HighPriority"
|
||||
_AttrType_name_1 = "OracleResponse"
|
||||
_AttrType_name_2 = "NotValidBefore"
|
||||
)
|
||||
|
||||
func (i AttrType) String() string {
|
||||
|
@ -23,6 +25,8 @@ func (i AttrType) String() string {
|
|||
return _AttrType_name_0
|
||||
case i == 17:
|
||||
return _AttrType_name_1
|
||||
case i == 224:
|
||||
return _AttrType_name_2
|
||||
default:
|
||||
return "AttrType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
|
|
29
pkg/core/transaction/not_valid_before.go
Normal file
29
pkg/core/transaction/not_valid_before.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
)
|
||||
|
||||
// NotValidBefore represents attribute with the height transaction is not valid before.
|
||||
type NotValidBefore struct {
|
||||
Height uint32 `json:"height"`
|
||||
}
|
||||
|
||||
// DecodeBinary implements io.Serializable interface.
|
||||
func (n *NotValidBefore) DecodeBinary(br *io.BinReader) {
|
||||
bytes := br.ReadVarBytes(4)
|
||||
n.Height = binary.LittleEndian.Uint32(bytes)
|
||||
}
|
||||
|
||||
// EncodeBinary implements io.Serializable interface.
|
||||
func (n *NotValidBefore) EncodeBinary(w *io.BinWriter) {
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, n.Height)
|
||||
w.WriteVarBytes(bytes)
|
||||
}
|
||||
|
||||
func (n *NotValidBefore) toJSONMap(m map[string]interface{}) {
|
||||
m["height"] = n.Height
|
||||
}
|
Loading…
Reference in a new issue