Skip to content

Commit f922d4c

Browse files
committed
core: introduce whitelisted fee contracts
Close #4039. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
1 parent 0fd6ffe commit f922d4c

File tree

22 files changed

+931
-63
lines changed

22 files changed

+931
-63
lines changed

cli/vm/cli.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1160,7 +1160,7 @@ func handleRun(c *cli.Context) error {
11601160
breaks := v.Context().BreakPoints() // We ensure that there's a context loaded.
11611161
ic.ReuseVM(v)
11621162
v.GasLimit = gasLimit
1163-
v.LoadNEFMethod(&cs.NEF, &cs.Manifest, util.Uint160{}, cs.Hash, callflag.All, hasRet, offset, initOff, nil)
1163+
v.LoadNEFMethod(&cs.NEF, &cs.Manifest, util.Uint160{}, cs.Hash, callflag.All, hasRet, offset, initOff, nil, false)
11641164
for _, bp := range breaks {
11651165
v.AddBreakPoint(bp)
11661166
}

docs/node-configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ in development and can change in an incompatible way.
582582
| `Cockatrice` | Introduces the ability to update native contracts. Includes a couple of new native smart contract APIs: `keccak256` of native CryptoLib contract and `getCommitteeAddress` of native NeoToken contract. | https://github.com/nspcc-dev/neo-go/pull/3402 <br> https://github.com/neo-project/neo/pull/2942 <br> https://github.com/nspcc-dev/neo-go/pull/3301 <br> https://github.com/neo-project/neo/pull/2925 <br> https://github.com/nspcc-dev/neo-go/pull/3362 <br> https://github.com/neo-project/neo/pull/3154 |
583583
| `Domovoi` | Makes node use executing contract state for the contract call permissions check instead of the state stored in the native Management contract. In C# also makes System.Runtime.GetNotifications interop properly count stack references of notification parameters which prevents users from creating objects that exceed MaxStackSize constraint, but NeoGo has never had this bug, thus proper behaviour is preserved even before HFDomovoi. It results in the fact that some T5 testnet transactions have different ApplicationLogs compared to the C# node, but the node states match. | https://github.com/nspcc-dev/neo-go/pull/3476 <br> https://github.com/neo-project/neo/pull/3290 <br> https://github.com/nspcc-dev/neo-go/pull/3473 <br> https://github.com/neo-project/neo/pull/3290 <br> https://github.com/neo-project/neo/pull/3301 <br> https://github.com/nspcc-dev/neo-go/pull/3485 |
584584
| `Echidna` | Introduces `Designation` event extension with `Old` and `New` roles data to native RoleManagement contract. Adds support for `base64UrlEncode` and `base64UrlDecode` methods to native StdLib contract. Extends the list of required call flags for `registerCandidate`, `unregisterCandidate`and `vote` methods of native NeoToken contract with AllowNotify flag. Enables `onNEP17Payment` method of NEO contract for candidate registration. Introduces constraint for maximum number of execution notifications. Adds support for `recoverSecp256K1` method of native CryptoLib contract. Introduces `setMillisecondsPerBlock` and `getMillisecondsPerBlock` methods of native Policy contract. Introduces support for NotaryAssisted transaction attribute and native Notary contract. | https://github.com/nspcc-dev/neo-go/pull/3554 <br> https://github.com/nspcc-dev/neo-go/pull/3761 <br> https://github.com/nspcc-dev/neo-go/pull/3554 <br> https://github.com/neo-project/neo/pull/3597 <br> https://github.com/nspcc-dev/neo-go/pull/3700 <br> https://github.com/nspcc-dev/neo-go/pull/3640 <br> https://github.com/neo-project/neo/pull/3548 <br> https://github.com/nspcc-dev/neo-go/pull/3863 <br> https://github.com/neo-project/neo/pull/3696 <br> https://github.com/neo-project/neo/pull/3895 <br> https://github.com/nspcc-dev/neo-go/pull/3835 <br> https://github.com/nspcc-dev/neo-go/pull/3854 <br> https://github.com/neo-project/neo/pull/3175 <br> https://github.com/nspcc-dev/neo-go/pull/3478 <br> https://github.com/neo-project/neo/pull/3178 |
585-
| `Faun` | Adds `getBlockedAccounts` method to native Policy contract. Adds `hexEncode` and `hexDecode` methods to native StdLib contract. | https://github.com/nspcc-dev/neo-go/pull/3932 <br> https://github.com/nspcc-dev/neo-go/pull/4004 <br> https://github.com/neo-project/neo/pull/4147 <br> https://github.com/neo-project/neo/pull/4150 |
585+
| `Faun` | Adds `getBlockedAccounts` method to native Policy contract. Adds `hexEncode` and `hexDecode` methods to native StdLib contract. Adds whitelist fee contract management to native Policy contract: `setWhitelistFeeContract`, `removeWhitelistFeeContract`, `getWhitelistFeeContracts` methods and `WhitelistChanged` event. | https://github.com/nspcc-dev/neo-go/pull/3932 <br> https://github.com/nspcc-dev/neo-go/pull/4004 <br> https://github.com/neo-project/neo/pull/4147 <br> https://github.com/neo-project/neo/pull/4150 <br> https://github.com/nspcc-dev/neo-go/pull/4052 <br> https://github.com/neo-project/neo/pull/4201 |
586586

587587
## DB compatibility
588588

pkg/compiler/native_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ func TestNativeHelpersCompile(t *testing.T) {
203203
{"getMillisecondsPerBlock", nil},
204204
{"setMillisecondsPerBlock", []string{"10"}},
205205
{"getBlockedAccounts", nil},
206+
{"setWhitelistFeeContract", []string{u160, `"method"`, "1", "2"}},
207+
{"removeWhitelistFeeContract", []string{u160, `"method"`, "1"}},
208+
{"getWhitelistFeeContracts", nil},
206209
})
207210
runNativeTestCases(t, *cs.ByName(nativenames.Ledger).Metadata(), "ledger", []nativeTestCase{
208211
{"currentHash", nil},

pkg/config/hardfork.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ const (
5050
// https://github.com/neo-project/neo/pull/3895), #3854 (ported from
5151
// https://github.com/neo-project/neo/pull/3175).
5252
HFEchidna // Echidna
53-
// HFFaun represents hard-fork introduced in #3931, #4004 (ported from
54-
// https://github.com/neo-project/neo/pull/4147,
55-
// https://github.com/neo-project/neo/pull/4150).
53+
// HFFaun represents hard-fork introduced in #3931 (ported from
54+
// https://github.com/neo-project/neo/pull/4147), #4004 (ported from
55+
// https://github.com/neo-project/neo/pull/4150), #4052 (ported from
56+
// https://github.com/neo-project/neo/pull/4201).
5657
HFFaun // Faun
5758
// hfLast denotes the end of hardforks enum. Consider adding new hardforks
5859
// before hfLast.

pkg/core/blockchain.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3352,7 +3352,7 @@ func (bc *Blockchain) InitVerificationContext(ic *interop.Context, hash util.Uin
33523352
}
33533353
ic.Invocations[cs.Hash]++
33543354
ic.VM.LoadNEFMethod(&cs.NEF, &cs.Manifest, util.Uint160{}, hash, callflag.ReadOnly,
3355-
true, verifyOffset, initOffset, nil)
3355+
true, verifyOffset, initOffset, nil, false)
33563356
}
33573357
if len(witness.InvocationScript) != 0 {
33583358
err := vm.IsScriptCorrect(witness.InvocationScript, nil)

pkg/core/custom_native_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,14 @@ func (p *policy) GetMaxValidUntilBlockIncrementFromCache(d *dao.Simple) uint32 {
198198
func (p *policy) GetAttributeFeeInternal(d *dao.Simple, attrType transaction.AttrType) int64 {
199199
return 1
200200
}
201-
func (p *policy) CheckPolicy(d *dao.Simple, tx *transaction.Transaction) error { return nil }
202-
func (p *policy) GetFeePerByteInternal(d *dao.Simple) int64 { return 1 }
203-
func (p *policy) BlockAccountInternal(d *dao.Simple, hash util.Uint160) bool { return false }
204-
func (p *policy) IsBlocked(dao *dao.Simple, hash util.Uint160) bool { return false }
201+
func (p *policy) CheckPolicy(d *dao.Simple, tx *transaction.Transaction) error { return nil }
202+
func (p *policy) GetFeePerByteInternal(d *dao.Simple) int64 { return 1 }
203+
func (p *policy) BlockAccountInternal(d *dao.Simple, hash util.Uint160) bool { return false }
204+
func (p *policy) IsBlocked(dao *dao.Simple, hash util.Uint160) bool { return false }
205+
func (p *policy) IsWhitelisted(d *dao.Simple, hash util.Uint160, method string, argCnt int) int64 {
206+
return -1
207+
}
208+
func (p *policy) CleanWhitelist(ic *interop.Context, hash util.Uint160) {}
205209
func (p *policy) GetMaxValidUntilBlockIncrementInternal(ic *interop.Context) uint32 { return 2 }
206210
func (p *policy) getTimePerBlock(ic *interop.Context, args []stackitem.Item) stackitem.Item {
207211
return stackitem.NewBigInteger(big.NewInt(1000))

pkg/core/interop/context.go

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/nspcc-dev/neo-go/pkg/core/block"
1313
"github.com/nspcc-dev/neo-go/pkg/core/dao"
1414
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
15+
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
1516
"github.com/nspcc-dev/neo-go/pkg/core/state"
1617
"github.com/nspcc-dev/neo-go/pkg/core/storage"
1718
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
@@ -49,15 +50,25 @@ type Ledger interface {
4950
NativeManagementID() int32
5051
}
5152

53+
// PolicyChecker is an interface required for checking native Policy constraints.
54+
type PolicyChecker interface {
55+
// IsBlocked returns whether the provided account is blocked by network policy.
56+
IsBlocked(d *dao.Simple, hash util.Uint160) bool
57+
// IsWhitelisted returns whether the specified contract method is whitelisted.
58+
// Non-negative return value defines the execution fee of a whitelisted method call.
59+
IsWhitelisted(d *dao.Simple, hash util.Uint160, method string, argCnt int) int64
60+
}
61+
5262
// Context represents context in which interops are executed.
5363
type Context struct {
54-
Chain Ledger
55-
Container hash.Hashable
56-
Network uint32
57-
Hardforks map[string]uint32
58-
Natives []Contract
59-
Trigger trigger.Type
60-
Block *block.Block
64+
Chain Ledger
65+
Container hash.Hashable
66+
Network uint32
67+
Hardforks map[string]uint32
68+
Natives []Contract
69+
PolicyChecker PolicyChecker
70+
Trigger trigger.Type
71+
Block *block.Block
6172
// IsBlockPersisted denotes whether current Block was persisted by native
6273
// Ledger contract via PostPersist method.
6374
IsBlockPersisted bool
@@ -85,13 +96,22 @@ func NewContext(trigger trigger.Type, bc Ledger, d *dao.Simple, baseExecFee, bas
8596
getContract func(*dao.Simple, util.Uint160) (*state.Contract, error), natives []Contract,
8697
loadTokenFunc func(ic *Context, id int32) error,
8798
block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context {
88-
dao := d.GetPrivate()
89-
cfg := bc.GetConfig()
99+
var (
100+
dao = d.GetPrivate()
101+
cfg = bc.GetConfig()
102+
pch PolicyChecker
103+
)
104+
for _, nc := range natives {
105+
if nc.Metadata().Name == nativenames.Policy {
106+
pch = nc.(PolicyChecker)
107+
}
108+
}
90109
return &Context{
91110
Chain: bc,
92111
Network: uint32(cfg.Magic),
93112
Hardforks: cfg.Hardforks,
94113
Natives: natives,
114+
PolicyChecker: pch,
95115
Trigger: trigger,
96116
Block: block,
97117
Tx: tx,

pkg/core/interop/contract/call.go

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ import (
88
"strings"
99

1010
"github.com/nspcc-dev/neo-go/pkg/config"
11-
"github.com/nspcc-dev/neo-go/pkg/core/dao"
1211
"github.com/nspcc-dev/neo-go/pkg/core/interop"
13-
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
12+
"github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
1413
"github.com/nspcc-dev/neo-go/pkg/core/state"
1514
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
1615
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
@@ -20,10 +19,6 @@ import (
2019
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
2120
)
2221

23-
type policyChecker interface {
24-
IsBlocked(*dao.Simple, util.Uint160) bool
25-
}
26-
2722
// LoadToken calls method specified by the token id.
2823
func LoadToken(ic *interop.Context, id int32) error {
2924
ctx := ic.VM.Context()
@@ -110,13 +105,19 @@ func callInternal(ic *interop.Context, cs *state.Contract, name string, f callfl
110105
// callExFromNative calls a contract with flags using the provided calling hash.
111106
func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contract,
112107
name string, args []stackitem.Item, f callflag.CallFlag, hasReturn bool, isDynamic bool, callFromNative bool) error {
113-
for _, nc := range ic.Natives {
114-
if nc.Metadata().Name == nativenames.Policy {
115-
var pch = nc.(policyChecker)
116-
if pch.IsBlocked(ic.DAO, cs.Hash) {
117-
return fmt.Errorf("contract %s is blocked", cs.Hash.StringLE())
108+
var whitelisted bool
109+
if ic.PolicyChecker != nil {
110+
if ic.PolicyChecker.IsBlocked(ic.DAO, cs.Hash) {
111+
return fmt.Errorf("contract %s is blocked", cs.Hash.StringLE())
112+
}
113+
if ic.IsHardforkEnabled(config.HFFaun) {
114+
fee := ic.PolicyChecker.IsWhitelisted(ic.DAO, cs.Hash, name, len(args))
115+
if fee >= 0 {
116+
if !ic.VM.AddGas(fee) {
117+
return fmt.Errorf("%w during whitelisted contract execution", storage.ErrGasLimitExceeded)
118+
}
119+
whitelisted = true
118120
}
119-
break
120121
}
121122
}
122123
md := cs.Manifest.ABI.GetMethod(name, len(args))
@@ -165,7 +166,7 @@ func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contra
165166
return nil
166167
}
167168
ic.VM.LoadNEFMethod(&cs.NEF, &cs.Manifest, caller, cs.Hash, f,
168-
hasReturn, methodOff, initOff, onUnload)
169+
hasReturn, methodOff, initOff, onUnload, whitelisted)
169170

170171
for e, i := ic.VM.Estack(), len(args)-1; i >= 0; i-- {
171172
e.PushItem(args[i])

pkg/core/interop/runtime/ext_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ func TestNotify(t *testing.T) {
637637
_, _, bc, cs := getDeployedInternal(t)
638638
ic, err := bc.GetTestVM(trigger.Application, nil, nil)
639639
require.NoError(t, err)
640-
ic.VM.LoadNEFMethod(&cs.NEF, &cs.Manifest, caller, cs.Hash, callflag.NoneFlag, true, 0, -1, nil)
640+
ic.VM.LoadNEFMethod(&cs.NEF, &cs.Manifest, caller, cs.Hash, callflag.NoneFlag, true, 0, -1, nil, false)
641641
ic.VM.Estack().PushVal(args)
642642
ic.VM.Estack().PushVal(name)
643643
return ic

pkg/core/native/contract.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@ type (
7272

7373
// Methods required for proper cross-native communication.
7474
BlockAccountInternal(d *dao.Simple, hash util.Uint160) bool
75-
IsBlocked(dao *dao.Simple, hash util.Uint160) bool
7675
GetMaxValidUntilBlockIncrementInternal(ic *interop.Context) uint32
76+
CleanWhitelist(ic *interop.Context, hash util.Uint160)
77+
interop.PolicyChecker
7778
}
7879

7980
// IOracle is an interface required from native OracleContract contract for

0 commit comments

Comments
 (0)