Skip to content

Commit 69b25d2

Browse files
author
ZhangTao1596
committed
core: fix nspcc-dev#2845 system fee refund attribute
1 parent b56dff2 commit 69b25d2

File tree

6 files changed

+102
-10
lines changed

6 files changed

+102
-10
lines changed

pkg/core/blockchain.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2248,6 +2248,11 @@ func (bc *Blockchain) FeePerByte() int64 {
22482248
return bc.contracts.Policy.GetFeePerByteInternal(bc.dao)
22492249
}
22502250

2251+
// GasRefundFee returns extra fee for system fee refundable transaction
2252+
func (bc *Blockchain) SystemFeeRefundCost() int64 {
2253+
return bc.contracts.Policy.GetSystemFeeRefundCostInternal(bc.dao)
2254+
}
2255+
22512256
// GetMemPool returns the memory pool of the blockchain.
22522257
func (bc *Blockchain) GetMemPool() *mempool.Pool {
22532258
return bc.memPool
@@ -2367,6 +2372,9 @@ func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.
23672372
needNetworkFee += (int64(na.NKeys) + 1) * bc.contracts.Notary.GetNotaryServiceFeePerKey(bc.dao)
23682373
}
23692374
}
2375+
if len(t.GetAttributes(transaction.RefundableSystemFeeT)) > 0 {
2376+
needNetworkFee += bc.SystemFeeRefundCost()
2377+
}
23702378
netFee := t.NetworkFee - needNetworkFee
23712379
if netFee < 0 {
23722380
return fmt.Errorf("%w: net fee is %v, need %v", ErrTxSmallNetworkFee, t.NetworkFee, needNetworkFee)
@@ -2480,6 +2488,11 @@ func (bc *Blockchain) verifyTxAttributes(d *dao.Simple, tx *transaction.Transact
24802488
if !tx.HasSigner(bc.contracts.Notary.Hash) {
24812489
return fmt.Errorf("%w: NotaryAssisted attribute was found, but transaction is not signed by the Notary native contract", ErrInvalidAttribute)
24822490
}
2491+
case transaction.RefundableSystemFeeT:
2492+
state := bc.GetContractState(tx.Sender())
2493+
if state != nil {
2494+
return fmt.Errorf("%w: RefundableSystemFee attribute was found, but transaction sender is contract", ErrInvalidAttribute)
2495+
}
24832496
default:
24842497
if !bc.config.ReservedAttributes && attrType >= transaction.ReservedLowerBound && attrType <= transaction.ReservedUpperBound {
24852498
return fmt.Errorf("%w: attribute of reserved type was found, but ReservedAttributes are disabled", ErrInvalidAttribute)

pkg/core/native/native_gas.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,18 @@ func (g *GAS) OnPersist(ic *interop.Context) error {
129129

130130
// PostPersist implements the Contract interface.
131131
func (g *GAS) PostPersist(ic *interop.Context) error {
132+
for _, tx := range ic.Block.Transactions {
133+
attrs := tx.GetAttributes(transaction.RefundableSystemFeeT)
134+
if len(attrs) != 0 {
135+
consumed, err := getTxCosumed(ic.DAO, tx.Hash())
136+
if err != nil {
137+
return err
138+
}
139+
if consumed < tx.SystemFee {
140+
g.mint(ic, tx.Sender(), big.NewInt(tx.SystemFee-consumed), false)
141+
}
142+
}
143+
}
132144
return nil
133145
}
134146

@@ -149,3 +161,11 @@ func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, error) {
149161
}
150162
return hash.Hash160(s), nil
151163
}
164+
165+
func getTxCosumed(dao *dao.Simple, hash util.Uint256) (int64, error) {
166+
_, _, aer, err := dao.GetTxExecResult(hash)
167+
if err != nil {
168+
return 0, err
169+
}
170+
return aer.GasConsumed, nil
171+
}

pkg/core/native/policy.go

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ import (
2121
const (
2222
policyContractID = -7
2323

24-
defaultExecFeeFactor = interop.DefaultBaseExecFee
25-
defaultFeePerByte = 1000
26-
defaultMaxVerificationGas = 1_50000000
24+
defaultExecFeeFactor = interop.DefaultBaseExecFee
25+
defaultFeePerByte = 1000
26+
defaultMaxVerificationGas = 1_50000000
27+
defaultSystemFeeRefundCost = 0_10000000
2728
// DefaultStoragePrice is the price to pay for 1 byte of storage.
2829
DefaultStoragePrice = 100000
2930

@@ -33,7 +34,8 @@ const (
3334
maxFeePerByte = 100_000_000
3435
// maxStoragePrice is the maximum allowed price for a byte of storage.
3536
maxStoragePrice = 10000000
36-
37+
// maxSystemFeeRefundCost is the maximun allowed extra fee for gas refundable transaction
38+
maxSystemFeeRefundCost = 1_00000000
3739
// blockedAccountPrefix is a prefix used to store blocked account.
3840
blockedAccountPrefix = 15
3941
)
@@ -46,6 +48,8 @@ var (
4648
feePerByteKey = []byte{10}
4749
// storagePriceKey is a key used to store storage price.
4850
storagePriceKey = []byte{19}
51+
// systemFeeRefundCostKey is a key usesd to store gas refund fee
52+
systemFeeRefundCostKey = []byte{20}
4953
)
5054

5155
// Policy represents Policy native contract.
@@ -59,6 +63,7 @@ type PolicyCache struct {
5963
feePerByte int64
6064
maxVerificationGas int64
6165
storagePrice uint32
66+
systemFeeRefundCost int64
6267
blockedAccounts []util.Uint160
6368
}
6469

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

135+
desc = newDescriptor("setSystemFeeRefundCost", smartcontract.VoidType,
136+
manifest.NewParameter("value", smartcontract.IntegerType))
137+
md = newMethodAndPrice(p.setSystemFeeRefundCost, 1<<15, callflag.States)
138+
p.AddMethod(md, desc)
139+
130140
return p
131141
}
132142

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

144155
cache := &PolicyCache{
145156
execFeeFactor: defaultExecFeeFactor,
146157
feePerByte: defaultFeePerByte,
147158
maxVerificationGas: defaultMaxVerificationGas,
148159
storagePrice: DefaultStoragePrice,
160+
systemFeeRefundCost: defaultSystemFeeRefundCost,
149161
blockedAccounts: make([]util.Uint160, 0),
150162
}
151163
ic.DAO.SetCache(p.ID, cache)
@@ -168,6 +180,7 @@ func (p *Policy) fillCacheFromDAO(cache *PolicyCache, d *dao.Simple) error {
168180
cache.feePerByte = getIntWithKey(p.ID, d, feePerByteKey)
169181
cache.maxVerificationGas = defaultMaxVerificationGas
170182
cache.storagePrice = uint32(getIntWithKey(p.ID, d, storagePriceKey))
183+
cache.systemFeeRefundCost = getIntWithKey(p.ID, d, systemFeeRefundCostKey)
171184

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

370+
func (p *Policy) GetSystemFeeRefundCostInternal(d *dao.Simple) int64 {
371+
cache := d.GetROCache(p.ID).(*PolicyCache)
372+
return cache.systemFeeRefundCost
373+
}
374+
375+
// setSystemFeeRefundCost is a Policy contract method that set extra network fee for gas refundable transaction.
376+
func (p *Policy) setSystemFeeRefundCost(ic *interop.Context, args []stackitem.Item) stackitem.Item {
377+
value := toBigInt(args[0]).Int64()
378+
if value < 0 || value > maxSystemFeeRefundCost {
379+
panic(fmt.Errorf("SystemFeeRefundCost shouldn't be negative or greater than %d", maxSystemFeeRefundCost))
380+
}
381+
if !p.NEO.checkCommittee(ic) {
382+
panic("invalid committee signature")
383+
}
384+
setIntWithKey(p.ID, ic.DAO, systemFeeRefundCostKey, value)
385+
cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache)
386+
cache.systemFeeRefundCost = value
387+
return stackitem.Null{}
388+
}
389+
357390
// CheckPolicy checks whether a transaction conforms to the current policy restrictions,
358391
// like not being signed by a blocked account or not exceeding the block-level system
359392
// fee limit.

pkg/core/transaction/attribute.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ func (attr *Attribute) DecodeBinary(br *io.BinReader) {
4141
attr.Value = new(Conflicts)
4242
case NotaryAssistedT:
4343
attr.Value = new(NotaryAssisted)
44+
case RefundableSystemFeeT:
45+
attr.Value = new(RefundableSystemFee)
4446
default:
4547
if t >= ReservedLowerBound && t <= ReservedUpperBound {
4648
attr.Value = new(Reserved)
@@ -57,7 +59,7 @@ func (attr *Attribute) EncodeBinary(bw *io.BinWriter) {
5759
bw.WriteB(byte(attr.Type))
5860
switch t := attr.Type; t {
5961
case HighPriority:
60-
case OracleResponseT, NotValidBeforeT, ConflictsT, NotaryAssistedT:
62+
case OracleResponseT, NotValidBeforeT, ConflictsT, NotaryAssistedT, RefundableSystemFeeT:
6163
attr.Value.EncodeBinary(bw)
6264
default:
6365
if t >= ReservedLowerBound && t <= ReservedUpperBound {
@@ -102,6 +104,9 @@ func (attr *Attribute) UnmarshalJSON(data []byte) error {
102104
case NotaryAssistedT.String():
103105
attr.Type = NotaryAssistedT
104106
attr.Value = new(NotaryAssisted)
107+
case RefundableSystemFeeT.String():
108+
attr.Type = RefundableSystemFeeT
109+
attr.Value = new(RefundableSystemFee)
105110
default:
106111
return errors.New("wrong Type")
107112
}

pkg/core/transaction/attrtype.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ const (
1414

1515
// List of valid attribute types.
1616
const (
17-
HighPriority AttrType = 1
18-
OracleResponseT AttrType = 0x11 // OracleResponse
19-
NotValidBeforeT AttrType = 0x20 // NotValidBefore
20-
ConflictsT AttrType = 0x21 // Conflicts
21-
NotaryAssistedT AttrType = 0x22 // NotaryAssisted
17+
HighPriority AttrType = 1
18+
OracleResponseT AttrType = 0x11 // OracleResponse
19+
NotValidBeforeT AttrType = 0x20 // NotValidBefore
20+
ConflictsT AttrType = 0x21 // Conflicts
21+
NotaryAssistedT AttrType = 0x22 // NotaryAssisted
22+
RefundableSystemFeeT AttrType = 0x30 // RefundableSystemFee
2223
)
2324

2425
func (a AttrType) allowMultiple() bool {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package transaction
2+
3+
import (
4+
"github.com/nspcc-dev/neo-go/pkg/io"
5+
)
6+
7+
// Conflicts represents attribute for refund gas transaction.
8+
type RefundableSystemFee struct {
9+
}
10+
11+
// DecodeBinary implements the io.Serializable interface.
12+
func (c *RefundableSystemFee) DecodeBinary(br *io.BinReader) {
13+
}
14+
15+
// EncodeBinary implements the io.Serializable interface.
16+
func (c *RefundableSystemFee) EncodeBinary(w *io.BinWriter) {
17+
}
18+
19+
func (c *RefundableSystemFee) toJSONMap(m map[string]interface{}) {
20+
}

0 commit comments

Comments
 (0)