Skip to content

Commit

Permalink
core: fix nspcc-dev#2845 system fee refund attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
ZhangTao1596 committed Jan 31, 2023
1 parent b56dff2 commit 69b25d2
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 10 deletions.
13 changes: 13 additions & 0 deletions pkg/core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -2248,6 +2248,11 @@ func (bc *Blockchain) FeePerByte() int64 {
return bc.contracts.Policy.GetFeePerByteInternal(bc.dao)
}

// GasRefundFee returns extra fee for system fee refundable transaction
func (bc *Blockchain) SystemFeeRefundCost() int64 {
return bc.contracts.Policy.GetSystemFeeRefundCostInternal(bc.dao)
}

// GetMemPool returns the memory pool of the blockchain.
func (bc *Blockchain) GetMemPool() *mempool.Pool {
return bc.memPool
Expand Down Expand Up @@ -2367,6 +2372,9 @@ func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.
needNetworkFee += (int64(na.NKeys) + 1) * bc.contracts.Notary.GetNotaryServiceFeePerKey(bc.dao)
}
}
if len(t.GetAttributes(transaction.RefundableSystemFeeT)) > 0 {
needNetworkFee += bc.SystemFeeRefundCost()
}
netFee := t.NetworkFee - needNetworkFee
if netFee < 0 {
return fmt.Errorf("%w: net fee is %v, need %v", ErrTxSmallNetworkFee, t.NetworkFee, needNetworkFee)
Expand Down Expand Up @@ -2480,6 +2488,11 @@ func (bc *Blockchain) verifyTxAttributes(d *dao.Simple, tx *transaction.Transact
if !tx.HasSigner(bc.contracts.Notary.Hash) {
return fmt.Errorf("%w: NotaryAssisted attribute was found, but transaction is not signed by the Notary native contract", ErrInvalidAttribute)
}
case transaction.RefundableSystemFeeT:
state := bc.GetContractState(tx.Sender())
if state != nil {
return fmt.Errorf("%w: RefundableSystemFee attribute was found, but transaction sender is contract", ErrInvalidAttribute)
}
default:
if !bc.config.ReservedAttributes && attrType >= transaction.ReservedLowerBound && attrType <= transaction.ReservedUpperBound {
return fmt.Errorf("%w: attribute of reserved type was found, but ReservedAttributes are disabled", ErrInvalidAttribute)
Expand Down
20 changes: 20 additions & 0 deletions pkg/core/native/native_gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,18 @@ func (g *GAS) OnPersist(ic *interop.Context) error {

// PostPersist implements the Contract interface.
func (g *GAS) PostPersist(ic *interop.Context) error {
for _, tx := range ic.Block.Transactions {
attrs := tx.GetAttributes(transaction.RefundableSystemFeeT)
if len(attrs) != 0 {
consumed, err := getTxCosumed(ic.DAO, tx.Hash())
if err != nil {
return err
}
if consumed < tx.SystemFee {
g.mint(ic, tx.Sender(), big.NewInt(tx.SystemFee-consumed), false)
}
}
}
return nil
}

Expand All @@ -149,3 +161,11 @@ func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, error) {
}
return hash.Hash160(s), nil
}

func getTxCosumed(dao *dao.Simple, hash util.Uint256) (int64, error) {
_, _, aer, err := dao.GetTxExecResult(hash)
if err != nil {
return 0, err
}
return aer.GasConsumed, nil
}
41 changes: 37 additions & 4 deletions pkg/core/native/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ import (
const (
policyContractID = -7

defaultExecFeeFactor = interop.DefaultBaseExecFee
defaultFeePerByte = 1000
defaultMaxVerificationGas = 1_50000000
defaultExecFeeFactor = interop.DefaultBaseExecFee
defaultFeePerByte = 1000
defaultMaxVerificationGas = 1_50000000
defaultSystemFeeRefundCost = 0_10000000
// DefaultStoragePrice is the price to pay for 1 byte of storage.
DefaultStoragePrice = 100000

Expand All @@ -33,7 +34,8 @@ const (
maxFeePerByte = 100_000_000
// maxStoragePrice is the maximum allowed price for a byte of storage.
maxStoragePrice = 10000000

// maxSystemFeeRefundCost is the maximun allowed extra fee for gas refundable transaction
maxSystemFeeRefundCost = 1_00000000
// blockedAccountPrefix is a prefix used to store blocked account.
blockedAccountPrefix = 15
)
Expand All @@ -46,6 +48,8 @@ var (
feePerByteKey = []byte{10}
// storagePriceKey is a key used to store storage price.
storagePriceKey = []byte{19}
// systemFeeRefundCostKey is a key usesd to store gas refund fee
systemFeeRefundCostKey = []byte{20}
)

// Policy represents Policy native contract.
Expand All @@ -59,6 +63,7 @@ type PolicyCache struct {
feePerByte int64
maxVerificationGas int64
storagePrice uint32
systemFeeRefundCost int64
blockedAccounts []util.Uint160
}

Expand Down Expand Up @@ -127,6 +132,11 @@ func newPolicy() *Policy {
md = newMethodAndPrice(p.unblockAccount, 1<<15, callflag.States)
p.AddMethod(md, desc)

desc = newDescriptor("setSystemFeeRefundCost", smartcontract.VoidType,
manifest.NewParameter("value", smartcontract.IntegerType))
md = newMethodAndPrice(p.setSystemFeeRefundCost, 1<<15, callflag.States)
p.AddMethod(md, desc)

return p
}

Expand All @@ -140,12 +150,14 @@ func (p *Policy) Initialize(ic *interop.Context) error {
setIntWithKey(p.ID, ic.DAO, feePerByteKey, defaultFeePerByte)
setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, defaultExecFeeFactor)
setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice)
setIntWithKey(p.ID, ic.DAO, systemFeeRefundCostKey, defaultSystemFeeRefundCost)

cache := &PolicyCache{
execFeeFactor: defaultExecFeeFactor,
feePerByte: defaultFeePerByte,
maxVerificationGas: defaultMaxVerificationGas,
storagePrice: DefaultStoragePrice,
systemFeeRefundCost: defaultSystemFeeRefundCost,
blockedAccounts: make([]util.Uint160, 0),
}
ic.DAO.SetCache(p.ID, cache)
Expand All @@ -168,6 +180,7 @@ func (p *Policy) fillCacheFromDAO(cache *PolicyCache, d *dao.Simple) error {
cache.feePerByte = getIntWithKey(p.ID, d, feePerByteKey)
cache.maxVerificationGas = defaultMaxVerificationGas
cache.storagePrice = uint32(getIntWithKey(p.ID, d, storagePriceKey))
cache.systemFeeRefundCost = getIntWithKey(p.ID, d, systemFeeRefundCostKey)

cache.blockedAccounts = make([]util.Uint160, 0)
var fErr error
Expand Down Expand Up @@ -354,6 +367,26 @@ func (p *Policy) unblockAccount(ic *interop.Context, args []stackitem.Item) stac
return stackitem.NewBool(true)
}

func (p *Policy) GetSystemFeeRefundCostInternal(d *dao.Simple) int64 {
cache := d.GetROCache(p.ID).(*PolicyCache)
return cache.systemFeeRefundCost
}

// setSystemFeeRefundCost is a Policy contract method that set extra network fee for gas refundable transaction.
func (p *Policy) setSystemFeeRefundCost(ic *interop.Context, args []stackitem.Item) stackitem.Item {
value := toBigInt(args[0]).Int64()
if value < 0 || value > maxSystemFeeRefundCost {
panic(fmt.Errorf("SystemFeeRefundCost shouldn't be negative or greater than %d", maxSystemFeeRefundCost))
}
if !p.NEO.checkCommittee(ic) {
panic("invalid committee signature")
}
setIntWithKey(p.ID, ic.DAO, systemFeeRefundCostKey, value)
cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache)
cache.systemFeeRefundCost = value
return stackitem.Null{}
}

// CheckPolicy checks whether a transaction conforms to the current policy restrictions,
// like not being signed by a blocked account or not exceeding the block-level system
// fee limit.
Expand Down
7 changes: 6 additions & 1 deletion pkg/core/transaction/attribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ func (attr *Attribute) DecodeBinary(br *io.BinReader) {
attr.Value = new(Conflicts)
case NotaryAssistedT:
attr.Value = new(NotaryAssisted)
case RefundableSystemFeeT:
attr.Value = new(RefundableSystemFee)
default:
if t >= ReservedLowerBound && t <= ReservedUpperBound {
attr.Value = new(Reserved)
Expand All @@ -57,7 +59,7 @@ func (attr *Attribute) EncodeBinary(bw *io.BinWriter) {
bw.WriteB(byte(attr.Type))
switch t := attr.Type; t {
case HighPriority:
case OracleResponseT, NotValidBeforeT, ConflictsT, NotaryAssistedT:
case OracleResponseT, NotValidBeforeT, ConflictsT, NotaryAssistedT, RefundableSystemFeeT:
attr.Value.EncodeBinary(bw)
default:
if t >= ReservedLowerBound && t <= ReservedUpperBound {
Expand Down Expand Up @@ -102,6 +104,9 @@ func (attr *Attribute) UnmarshalJSON(data []byte) error {
case NotaryAssistedT.String():
attr.Type = NotaryAssistedT
attr.Value = new(NotaryAssisted)
case RefundableSystemFeeT.String():
attr.Type = RefundableSystemFeeT
attr.Value = new(RefundableSystemFee)
default:
return errors.New("wrong Type")
}
Expand Down
11 changes: 6 additions & 5 deletions pkg/core/transaction/attrtype.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ const (

// List of valid attribute types.
const (
HighPriority AttrType = 1
OracleResponseT AttrType = 0x11 // OracleResponse
NotValidBeforeT AttrType = 0x20 // NotValidBefore
ConflictsT AttrType = 0x21 // Conflicts
NotaryAssistedT AttrType = 0x22 // NotaryAssisted
HighPriority AttrType = 1
OracleResponseT AttrType = 0x11 // OracleResponse
NotValidBeforeT AttrType = 0x20 // NotValidBefore
ConflictsT AttrType = 0x21 // Conflicts
NotaryAssistedT AttrType = 0x22 // NotaryAssisted
RefundableSystemFeeT AttrType = 0x30 // RefundableSystemFee
)

func (a AttrType) allowMultiple() bool {
Expand Down
20 changes: 20 additions & 0 deletions pkg/core/transaction/refundablesystemfee.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package transaction

import (
"github.com/nspcc-dev/neo-go/pkg/io"
)

// Conflicts represents attribute for refund gas transaction.
type RefundableSystemFee struct {
}

// DecodeBinary implements the io.Serializable interface.
func (c *RefundableSystemFee) DecodeBinary(br *io.BinReader) {
}

// EncodeBinary implements the io.Serializable interface.
func (c *RefundableSystemFee) EncodeBinary(w *io.BinWriter) {
}

func (c *RefundableSystemFee) toJSONMap(m map[string]interface{}) {
}

0 comments on commit 69b25d2

Please sign in to comment.