forked from TrueCloudLab/neoneo-go
Merge pull request #3402 from nspcc-dev/native-upd
native: implement HF-based update
This commit is contained in:
commit
b21db99747
40 changed files with 922 additions and 163 deletions
|
@ -134,6 +134,10 @@ func newTestVMClIWithState(t *testing.T) *executor {
|
||||||
}
|
}
|
||||||
bc, validators, committee, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, store)
|
bc, validators, committee, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, store)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Save config for future usage.
|
||||||
|
protoCfg := bc.GetConfig()
|
||||||
|
|
||||||
go bc.Run()
|
go bc.Run()
|
||||||
e := neotest.NewExecutor(t, bc, validators, committee)
|
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||||
basicchain.InitSimple(t, "../../", e)
|
basicchain.InitSimple(t, "../../", e)
|
||||||
|
@ -145,7 +149,9 @@ func newTestVMClIWithState(t *testing.T) *executor {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB
|
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB
|
||||||
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions = opts
|
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions = opts
|
||||||
cfg.ProtocolConfiguration.StateRootInHeader = true
|
cfg.ProtocolConfiguration.StateRootInHeader = protoCfg.StateRootInHeader
|
||||||
|
cfg.ProtocolConfiguration.P2PStateExchangeExtensions = protoCfg.P2PStateExchangeExtensions
|
||||||
|
cfg.ProtocolConfiguration.Hardforks = protoCfg.Hardforks
|
||||||
return newTestVMCLIWithLogoAndCustomConfig(t, false, &cfg)
|
return newTestVMCLIWithLogoAndCustomConfig(t, false, &cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -332,7 +332,7 @@ protocol-related settings described in the table below.
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| CommitteeHistory | map[uint32]uint32 | none | Number of committee members after the given height, for example `{0: 1, 20: 4}` sets up a chain with one committee member since the genesis and then changes the setting to 4 committee members at the height of 20. `StandbyCommittee` committee setting must have the number of keys equal or exceeding the highest value in this option. Blocks numbers where the change happens must be divisible by the old and by the new values simultaneously. If not set, committee size is derived from the `StandbyCommittee` setting and never changes. |
|
| CommitteeHistory | map[uint32]uint32 | none | Number of committee members after the given height, for example `{0: 1, 20: 4}` sets up a chain with one committee member since the genesis and then changes the setting to 4 committee members at the height of 20. `StandbyCommittee` committee setting must have the number of keys equal or exceeding the highest value in this option. Blocks numbers where the change happens must be divisible by the old and by the new values simultaneously. If not set, committee size is derived from the `StandbyCommittee` setting and never changes. |
|
||||||
| Genesis | [Genesis](#Genesis-Configuration) | none | The set of genesis block settings including NeoGo-specific protocol extensions that should be enabled at the genesis block or during native contracts initialisation. |
|
| Genesis | [Genesis](#Genesis-Configuration) | none | The set of genesis block settings including NeoGo-specific protocol extensions that should be enabled at the genesis block or during native contracts initialisation. |
|
||||||
| Hardforks | `map[string]uint32` | [] | The set of incompatible changes that affect node behaviour starting from the specified height. The default value is an empty set which should be interpreted as "each known hard-fork is applied from the zero blockchain height". The list of valid hard-fork names:<br>• `Aspidochelone` represents hard-fork introduced in [#2469](https://github.com/nspcc-dev/neo-go/pull/2469) (ported from the [reference](https://github.com/neo-project/neo/pull/2712)). It adjusts the prices of `System.Contract.CreateStandardAccount` and `System.Contract.CreateMultisigAccount` interops so that the resulting prices are in accordance with `sha256` method of native `CryptoLib` contract. It also includes [#2519](https://github.com/nspcc-dev/neo-go/pull/2519) (ported from the [reference](https://github.com/neo-project/neo/pull/2749)) that adjusts the price of `System.Runtime.GetRandom` interop and fixes its vulnerability. A special NeoGo-specific change is included as well for ContractManagement's update/deploy call flags behaviour to be compatible with pre-0.99.0 behaviour that was changed because of the [3.2.0 protocol change](https://github.com/neo-project/neo/pull/2653).<br>• `Basilisk` represents hard-fork introduced in [#3056](https://github.com/nspcc-dev/neo-go/pull/3056) (ported from the [reference](https://github.com/neo-project/neo/pull/2881)). It enables strict smart contract script check against a set of JMP instructions and against method boundaries enabled on contract deploy or update. It also includes [#3080](https://github.com/nspcc-dev/neo-go/pull/3080) (ported from the [reference](https://github.com/neo-project/neo/pull/2883)) that increases `stackitem.Integer` JSON parsing precision up to the maximum value supported by the NeoVM. It also includes [#3085](https://github.com/nspcc-dev/neo-go/pull/3085) (ported from the [reference](https://github.com/neo-project/neo/pull/2810)) that enables strict check for notifications emitted by a contract to precisely match the events specified in the contract manifest. |
|
| Hardforks | `map[string]uint32` | [] | The set of incompatible changes that affect node behaviour starting from the specified height. The default value is an empty set which should be interpreted as "each known hard-fork is applied from the zero blockchain height". The list of valid hard-fork names:<br>• `Aspidochelone` represents hard-fork introduced in [#2469](https://github.com/nspcc-dev/neo-go/pull/2469) (ported from the [reference](https://github.com/neo-project/neo/pull/2712)). It adjusts the prices of `System.Contract.CreateStandardAccount` and `System.Contract.CreateMultisigAccount` interops so that the resulting prices are in accordance with `sha256` method of native `CryptoLib` contract. It also includes [#2519](https://github.com/nspcc-dev/neo-go/pull/2519) (ported from the [reference](https://github.com/neo-project/neo/pull/2749)) that adjusts the price of `System.Runtime.GetRandom` interop and fixes its vulnerability. A special NeoGo-specific change is included as well for ContractManagement's update/deploy call flags behaviour to be compatible with pre-0.99.0 behaviour that was changed because of the [3.2.0 protocol change](https://github.com/neo-project/neo/pull/2653).<br>• `Basilisk` represents hard-fork introduced in [#3056](https://github.com/nspcc-dev/neo-go/pull/3056) (ported from the [reference](https://github.com/neo-project/neo/pull/2881)). It enables strict smart contract script check against a set of JMP instructions and against method boundaries enabled on contract deploy or update. It also includes [#3080](https://github.com/nspcc-dev/neo-go/pull/3080) (ported from the [reference](https://github.com/neo-project/neo/pull/2883)) that increases `stackitem.Integer` JSON parsing precision up to the maximum value supported by the NeoVM. It also includes [#3085](https://github.com/nspcc-dev/neo-go/pull/3085) (ported from the [reference](https://github.com/neo-project/neo/pull/2810)) that enables strict check for notifications emitted by a contract to precisely match the events specified in the contract manifest. <br>• `Cockatrice` represents hard-fork introduced in [#3402](https://github.com/nspcc-dev/neo-go/pull/3402) (ported from the [reference](https://github.com/neo-project/neo/pull/2942)). Initially it is introduced along with the ability to update native contracts. This hard-fork also includes a couple of new native smart contract APIs: `keccak256` of native CryptoLib contract introduced in [#3301](https://github.com/nspcc-dev/neo-go/pull/3301) (ported from the [reference](https://github.com/neo-project/neo/pull/2925)) and `getCommitteeAddress` of native NeoToken contract inctroduced in [#3362](https://github.com/nspcc-dev/neo-go/pull/3362) (ported from the [reference](https://github.com/neo-project/neo/pull/3154)). |
|
||||||
| Magic | `uint32` | `0` | Magic number which uniquely identifies Neo network. |
|
| Magic | `uint32` | `0` | Magic number which uniquely identifies Neo network. |
|
||||||
| MaxBlockSize | `uint32` | `262144` | Maximum block size in bytes. |
|
| MaxBlockSize | `uint32` | `262144` | Maximum block size in bytes. |
|
||||||
| MaxBlockSystemFee | `int64` | `900000000000` | Maximum overall transactions system fee per block. |
|
| MaxBlockSystemFee | `int64` | `900000000000` | Maximum overall transactions system fee per block. |
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
@ -610,7 +610,7 @@ func TestCallWithVersion(t *testing.T) {
|
||||||
e.DeployContract(t, ctr, nil)
|
e.DeployContract(t, ctr, nil)
|
||||||
c := e.CommitteeInvoker(ctr.Hash)
|
c := e.CommitteeInvoker(ctr.Hash)
|
||||||
|
|
||||||
policyH := state.CreateNativeContractHash(nativenames.Policy)
|
policyH := nativehashes.Policy
|
||||||
t.Run("good", func(t *testing.T) {
|
t.Run("good", func(t *testing.T) {
|
||||||
c.Invoke(t, e.Chain.GetBaseExecFee(), "callWithVersion", policyH.BytesBE(), 0, "getExecFeeFactor")
|
c.Invoke(t, e.Chain.GetBaseExecFee(), "callWithVersion", policyH.BytesBE(), 0, "getExecFeeFactor")
|
||||||
})
|
})
|
||||||
|
|
|
@ -288,7 +288,7 @@ func runNativeTestCases(t *testing.T, ctr interop.ContractMD, name string, nativ
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMethod(t *testing.T, ctr interop.ContractMD, name string, params []string) interop.MethodAndPrice {
|
func getMethod(t *testing.T, ctr interop.ContractMD, name string, params []string) interop.HFSpecificMethodAndPrice {
|
||||||
paramLen := len(params)
|
paramLen := len(params)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
@ -308,8 +308,10 @@ func getMethod(t *testing.T, ctr interop.ContractMD, name string, params []strin
|
||||||
name = strings.TrimSuffix(name, "WithData")
|
name = strings.TrimSuffix(name, "WithData")
|
||||||
}
|
}
|
||||||
|
|
||||||
md, ok := ctr.GetMethod(name, paramLen)
|
latestHF := config.LatestHardfork()
|
||||||
require.True(t, ok, ctr.Manifest.Name, name, paramLen)
|
cMD := ctr.HFSpecificContractMD(&latestHF)
|
||||||
|
md, ok := cMD.GetMethod(name, paramLen)
|
||||||
|
require.True(t, ok, cMD.Manifest.Name, name, paramLen)
|
||||||
return md
|
return md
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,13 @@ package config
|
||||||
// Hardfork represents the application hard-fork identifier.
|
// Hardfork represents the application hard-fork identifier.
|
||||||
type Hardfork byte
|
type Hardfork byte
|
||||||
|
|
||||||
|
// HFDefault is a default value of Hardfork enum. It's a special constant
|
||||||
|
// aimed to denote the node code enabled by default starting from the
|
||||||
|
// genesis block. HFDefault is not a hard-fork, but this constant can be used for
|
||||||
|
// convenient hard-forks comparison and to refer to the default hard-fork-less
|
||||||
|
// node behaviour.
|
||||||
|
const HFDefault Hardfork = 0 // Default
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// HFAspidochelone represents hard-fork introduced in #2469 (ported from
|
// HFAspidochelone represents hard-fork introduced in #2469 (ported from
|
||||||
// https://github.com/neo-project/neo/pull/2712) and #2519 (ported from
|
// https://github.com/neo-project/neo/pull/2712) and #2519 (ported from
|
||||||
|
@ -15,6 +22,11 @@ const (
|
||||||
// https://github.com/neo-project/neo/pull/2883) and #3085 (ported from
|
// https://github.com/neo-project/neo/pull/2883) and #3085 (ported from
|
||||||
// https://github.com/neo-project/neo/pull/2810).
|
// https://github.com/neo-project/neo/pull/2810).
|
||||||
HFBasilisk // Basilisk
|
HFBasilisk // Basilisk
|
||||||
|
// HFCockatrice represents hard-fork introduced in #3402 (ported from
|
||||||
|
// https://github.com/neo-project/neo/pull/2942), #3301 (ported from
|
||||||
|
// https://github.com/neo-project/neo/pull/2925) and #3362 (ported from
|
||||||
|
// https://github.com/neo-project/neo/pull/3154).
|
||||||
|
HFCockatrice // Cockatrice
|
||||||
// hfLast denotes the end of hardforks enum. Consider adding new hardforks
|
// hfLast denotes the end of hardforks enum. Consider adding new hardforks
|
||||||
// before hfLast.
|
// before hfLast.
|
||||||
hfLast
|
hfLast
|
||||||
|
@ -36,9 +48,38 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cmp returns the result of hardforks comparison. It returns:
|
||||||
|
//
|
||||||
|
// -1 if hf < other
|
||||||
|
// 0 if hf == other
|
||||||
|
// +1 if hf > other
|
||||||
|
func (hf Hardfork) Cmp(other Hardfork) int {
|
||||||
|
switch {
|
||||||
|
case hf == other:
|
||||||
|
return 0
|
||||||
|
case hf < other:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prev returns the previous hardfork for the given one. Calling Prev for the default hardfork is a no-op.
|
||||||
|
func (hf Hardfork) Prev() Hardfork {
|
||||||
|
if hf == HFDefault {
|
||||||
|
panic("unexpected call to Prev for the default hardfork")
|
||||||
|
}
|
||||||
|
return hf >> 1
|
||||||
|
}
|
||||||
|
|
||||||
// IsHardforkValid denotes whether the provided string represents a valid
|
// IsHardforkValid denotes whether the provided string represents a valid
|
||||||
// Hardfork name.
|
// Hardfork name.
|
||||||
func IsHardforkValid(s string) bool {
|
func IsHardforkValid(s string) bool {
|
||||||
_, ok := hardforks[s]
|
_, ok := hardforks[s]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LatestHardfork returns latest known hardfork.
|
||||||
|
func LatestHardfork() Hardfork {
|
||||||
|
return hfLast >> 1
|
||||||
|
}
|
||||||
|
|
|
@ -8,27 +8,31 @@ func _() {
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
// Re-run the stringer command to generate them again.
|
// Re-run the stringer command to generate them again.
|
||||||
var x [1]struct{}
|
var x [1]struct{}
|
||||||
|
_ = x[HFDefault-0]
|
||||||
_ = x[HFAspidochelone-1]
|
_ = x[HFAspidochelone-1]
|
||||||
_ = x[HFBasilisk-2]
|
_ = x[HFBasilisk-2]
|
||||||
_ = x[hfLast-4]
|
_ = x[HFCockatrice-4]
|
||||||
|
_ = x[hfLast-8]
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_Hardfork_name_0 = "AspidocheloneBasilisk"
|
_Hardfork_name_0 = "DefaultAspidocheloneBasilisk"
|
||||||
_Hardfork_name_1 = "hfLast"
|
_Hardfork_name_1 = "Cockatrice"
|
||||||
|
_Hardfork_name_2 = "hfLast"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_Hardfork_index_0 = [...]uint8{0, 13, 21}
|
_Hardfork_index_0 = [...]uint8{0, 7, 20, 28}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i Hardfork) String() string {
|
func (i Hardfork) String() string {
|
||||||
switch {
|
switch {
|
||||||
case 1 <= i && i <= 2:
|
case i <= 2:
|
||||||
i -= 1
|
|
||||||
return _Hardfork_name_0[_Hardfork_index_0[i]:_Hardfork_index_0[i+1]]
|
return _Hardfork_name_0[_Hardfork_index_0[i]:_Hardfork_index_0[i+1]]
|
||||||
case i == 4:
|
case i == 4:
|
||||||
return _Hardfork_name_1
|
return _Hardfork_name_1
|
||||||
|
case i == 8:
|
||||||
|
return _Hardfork_name_2
|
||||||
default:
|
default:
|
||||||
return "Hardfork(" + strconv.FormatInt(int64(i), 10) + ")"
|
return "Hardfork(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
json "github.com/nspcc-dev/go-ordered-json"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/limits"
|
"github.com/nspcc-dev/neo-go/pkg/config/limits"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
|
@ -44,7 +45,7 @@ import (
|
||||||
|
|
||||||
// Tuning parameters.
|
// Tuning parameters.
|
||||||
const (
|
const (
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
|
|
||||||
// DefaultInitialGAS is the default amount of GAS emitted to the standby validators
|
// DefaultInitialGAS is the default amount of GAS emitted to the standby validators
|
||||||
// multisignature account during native GAS contract initialization.
|
// multisignature account during native GAS contract initialization.
|
||||||
|
@ -288,7 +289,7 @@ func NewBlockchain(s storage.Store, cfg config.Blockchain, log *zap.Logger) (*Bl
|
||||||
cfg.Hardforks[hf.String()] = 0
|
cfg.Hardforks[hf.String()] = 0
|
||||||
}
|
}
|
||||||
log.Info("Hardforks are not set, using default value")
|
log.Info("Hardforks are not set, using default value")
|
||||||
} else {
|
} else if len(cfg.Hardforks) != 0 {
|
||||||
// Explicitly set the height of all old omitted hardforks to 0 for proper
|
// Explicitly set the height of all old omitted hardforks to 0 for proper
|
||||||
// IsHardforkEnabled behaviour.
|
// IsHardforkEnabled behaviour.
|
||||||
for _, hf := range config.Hardforks {
|
for _, hf := range config.Hardforks {
|
||||||
|
@ -341,16 +342,36 @@ func (bc *Blockchain) GetDesignatedByRole(r noderoles.Role) (keys.PublicKeys, ui
|
||||||
return res, h, err
|
return res, h, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getCurrentHF returns the latest currently enabled hardfork. In case if no hardforks are enabled, the
|
||||||
|
// default config.Hardfork(0) value is returned.
|
||||||
|
func (bc *Blockchain) getCurrentHF() config.Hardfork {
|
||||||
|
var (
|
||||||
|
height = bc.BlockHeight()
|
||||||
|
current config.Hardfork
|
||||||
|
)
|
||||||
|
// Rely on the fact that hardforks list is continuous.
|
||||||
|
for _, hf := range config.Hardforks {
|
||||||
|
enableHeight, ok := bc.config.Hardforks[hf.String()]
|
||||||
|
if !ok || height < enableHeight {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
current = hf
|
||||||
|
}
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
// SetOracle sets oracle module. It can safely be called on the running blockchain.
|
// SetOracle sets oracle module. It can safely be called on the running blockchain.
|
||||||
// To unregister Oracle service use SetOracle(nil).
|
// To unregister Oracle service use SetOracle(nil).
|
||||||
func (bc *Blockchain) SetOracle(mod native.OracleService) {
|
func (bc *Blockchain) SetOracle(mod native.OracleService) {
|
||||||
orc := bc.contracts.Oracle
|
orc := bc.contracts.Oracle
|
||||||
|
currentHF := bc.getCurrentHF()
|
||||||
if mod != nil {
|
if mod != nil {
|
||||||
md, ok := orc.GetMethod(manifest.MethodVerify, -1)
|
orcMd := orc.HFSpecificContractMD(¤tHF)
|
||||||
|
md, ok := orcMd.GetMethod(manifest.MethodVerify, -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Errorf("%s method not found", manifest.MethodVerify))
|
panic(fmt.Errorf("%s method not found", manifest.MethodVerify))
|
||||||
}
|
}
|
||||||
mod.UpdateNativeContract(orc.NEF.Script, orc.GetOracleResponseScript(),
|
mod.UpdateNativeContract(orcMd.NEF.Script, orc.GetOracleResponseScript(),
|
||||||
orc.Hash, md.MD.Offset)
|
orc.Hash, md.MD.Offset)
|
||||||
keys, _, err := bc.GetDesignatedByRole(noderoles.Oracle)
|
keys, _, err := bc.GetDesignatedByRole(noderoles.Oracle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -487,6 +508,7 @@ func (bc *Blockchain) init() error {
|
||||||
// Check autogenerated native contracts' manifests and NEFs against the stored ones.
|
// Check autogenerated native contracts' manifests and NEFs against the stored ones.
|
||||||
// Need to be done after native Management cache initialization to be able to get
|
// Need to be done after native Management cache initialization to be able to get
|
||||||
// contract state from DAO via high-level bc API.
|
// contract state from DAO via high-level bc API.
|
||||||
|
var current = bc.getCurrentHF()
|
||||||
for _, c := range bc.contracts.Contracts {
|
for _, c := range bc.contracts.Contracts {
|
||||||
md := c.Metadata()
|
md := c.Metadata()
|
||||||
storedCS := bc.GetContractState(md.Hash)
|
storedCS := bc.GetContractState(md.Hash)
|
||||||
|
@ -504,8 +526,9 @@ func (bc *Blockchain) init() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to check native %s state against autogenerated one: %w", md.Name, err)
|
return fmt.Errorf("failed to check native %s state against autogenerated one: %w", md.Name, err)
|
||||||
}
|
}
|
||||||
|
hfMD := md.HFSpecificContractMD(¤t)
|
||||||
autogenCS := &state.Contract{
|
autogenCS := &state.Contract{
|
||||||
ContractBase: md.ContractBase,
|
ContractBase: hfMD.ContractBase,
|
||||||
UpdateCounter: storedCS.UpdateCounter, // it can be restored only from the DB, so use the stored value.
|
UpdateCounter: storedCS.UpdateCounter, // it can be restored only from the DB, so use the stored value.
|
||||||
}
|
}
|
||||||
autogenCSBytes, err := stackitem.SerializeConvertible(autogenCS)
|
autogenCSBytes, err := stackitem.SerializeConvertible(autogenCS)
|
||||||
|
@ -513,8 +536,10 @@ func (bc *Blockchain) init() error {
|
||||||
return fmt.Errorf("failed to check native %s state against autogenerated one: %w", md.Name, err)
|
return fmt.Errorf("failed to check native %s state against autogenerated one: %w", md.Name, err)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(storedCSBytes, autogenCSBytes) {
|
if !bytes.Equal(storedCSBytes, autogenCSBytes) {
|
||||||
return fmt.Errorf("native %s: version mismatch (stored contract state differs from autogenerated one), "+
|
storedJ, _ := json.Marshal(storedCS)
|
||||||
"try to resynchronize the node from the genesis", md.Name)
|
autogenJ, _ := json.Marshal(autogenCS)
|
||||||
|
return fmt.Errorf("native %s: version mismatch for the latest hardfork %s (stored contract state differs from autogenerated one), "+
|
||||||
|
"try to resynchronize the node from the genesis: %s vs %s", md.Name, current, string(storedJ), string(autogenJ))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2269,8 +2294,16 @@ func (bc *Blockchain) GetNativeContractScriptHash(name string) (util.Uint160, er
|
||||||
// GetNatives returns list of native contracts.
|
// GetNatives returns list of native contracts.
|
||||||
func (bc *Blockchain) GetNatives() []state.NativeContract {
|
func (bc *Blockchain) GetNatives() []state.NativeContract {
|
||||||
res := make([]state.NativeContract, 0, len(bc.contracts.Contracts))
|
res := make([]state.NativeContract, 0, len(bc.contracts.Contracts))
|
||||||
|
current := bc.getCurrentHF()
|
||||||
for _, c := range bc.contracts.Contracts {
|
for _, c := range bc.contracts.Contracts {
|
||||||
res = append(res, c.Metadata().NativeContract)
|
activeIn := c.ActiveIn()
|
||||||
|
if !(activeIn == nil || activeIn.Cmp(current) <= 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
md := c.Metadata().HFSpecificContractMD(¤t)
|
||||||
|
res = append(res, state.NativeContract{
|
||||||
|
ContractBase: md.ContractBase,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
|
@ -361,16 +361,24 @@ func TestBlockchain_IsRunning(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewBlockchain_InitHardforks(t *testing.T) {
|
func TestNewBlockchain_InitHardforks(t *testing.T) {
|
||||||
t.Run("empty set", func(t *testing.T) {
|
t.Run("nil set", func(t *testing.T) {
|
||||||
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
||||||
c.ProtocolConfiguration.Hardforks = map[string]uint32{}
|
c.ProtocolConfiguration.Hardforks = nil
|
||||||
require.NoError(t, c.ProtocolConfiguration.Validate())
|
require.NoError(t, c.ProtocolConfiguration.Validate())
|
||||||
})
|
})
|
||||||
require.Equal(t, map[string]uint32{
|
require.Equal(t, map[string]uint32{
|
||||||
config.HFAspidochelone.String(): 0,
|
config.HFAspidochelone.String(): 0,
|
||||||
config.HFBasilisk.String(): 0,
|
config.HFBasilisk.String(): 0,
|
||||||
|
config.HFCockatrice.String(): 0,
|
||||||
}, bc.GetConfig().Hardforks)
|
}, bc.GetConfig().Hardforks)
|
||||||
})
|
})
|
||||||
|
t.Run("empty set", func(t *testing.T) {
|
||||||
|
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
||||||
|
c.ProtocolConfiguration.Hardforks = map[string]uint32{}
|
||||||
|
require.NoError(t, c.ProtocolConfiguration.Validate())
|
||||||
|
})
|
||||||
|
require.Equal(t, map[string]uint32{}, bc.GetConfig().Hardforks)
|
||||||
|
})
|
||||||
t.Run("missing old", func(t *testing.T) {
|
t.Run("missing old", func(t *testing.T) {
|
||||||
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
||||||
c.ProtocolConfiguration.Hardforks = map[string]uint32{config.HFBasilisk.String(): 5}
|
c.ProtocolConfiguration.Hardforks = map[string]uint32{config.HFBasilisk.String(): 5}
|
||||||
|
@ -392,12 +400,13 @@ func TestNewBlockchain_InitHardforks(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("all present", func(t *testing.T) {
|
t.Run("all present", func(t *testing.T) {
|
||||||
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
||||||
c.ProtocolConfiguration.Hardforks = map[string]uint32{config.HFAspidochelone.String(): 5, config.HFBasilisk.String(): 10}
|
c.ProtocolConfiguration.Hardforks = map[string]uint32{config.HFAspidochelone.String(): 5, config.HFBasilisk.String(): 10, config.HFCockatrice.String(): 15}
|
||||||
require.NoError(t, c.ProtocolConfiguration.Validate())
|
require.NoError(t, c.ProtocolConfiguration.Validate())
|
||||||
})
|
})
|
||||||
require.Equal(t, map[string]uint32{
|
require.Equal(t, map[string]uint32{
|
||||||
config.HFAspidochelone.String(): 5,
|
config.HFAspidochelone.String(): 5,
|
||||||
config.HFBasilisk.String(): 10,
|
config.HFBasilisk.String(): 10,
|
||||||
|
config.HFCockatrice.String(): 15,
|
||||||
}, bc.GetConfig().Hardforks)
|
}, bc.GetConfig().Hardforks)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativeprices"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativeprices"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
|
@ -269,7 +270,7 @@ func TestBlockchain_StartFromExistingDB(t *testing.T) {
|
||||||
|
|
||||||
_, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache)
|
_, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native %s: version mismatch (stored contract state differs from autogenerated one)", nativenames.CryptoLib)), err)
|
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native %s: version mismatch for the latest hardfork Cockatrice (stored contract state differs from autogenerated one)", nativenames.CryptoLib)), err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("good", func(t *testing.T) {
|
t.Run("good", func(t *testing.T) {
|
||||||
|
@ -2460,7 +2461,7 @@ func TestBlockchain_GenesisTransactionExtension(t *testing.T) {
|
||||||
emit.Syscall(script.BinWriter, interopnames.SystemRuntimeCheckWitness)
|
emit.Syscall(script.BinWriter, interopnames.SystemRuntimeCheckWitness)
|
||||||
emit.Bytes(script.BinWriter, to.BytesBE())
|
emit.Bytes(script.BinWriter, to.BytesBE())
|
||||||
emit.Syscall(script.BinWriter, interopnames.SystemRuntimeCheckWitness)
|
emit.Syscall(script.BinWriter, interopnames.SystemRuntimeCheckWitness)
|
||||||
emit.AppCall(script.BinWriter, state.CreateNativeContractHash(nativenames.Neo), "transfer", callflag.All, from, to, amount, nil)
|
emit.AppCall(script.BinWriter, nativehashes.Neo, "transfer", callflag.All, from, to, amount, nil)
|
||||||
emit.Opcodes(script.BinWriter, opcode.ASSERT)
|
emit.Opcodes(script.BinWriter, opcode.ASSERT)
|
||||||
|
|
||||||
var sysFee int64 = 1_0000_0000
|
var sysFee int64 = 1_0000_0000
|
||||||
|
@ -2479,3 +2480,17 @@ func TestBlockchain_GenesisTransactionExtension(t *testing.T) {
|
||||||
require.Equal(t, int64(amount), actualNeo.Int64())
|
require.Equal(t, int64(amount), actualNeo.Int64())
|
||||||
require.Equal(t, 0, int(lub))
|
require.Equal(t, 0, int(lub))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestNativenames ensures that nativenames.All contains all expected native contract names
|
||||||
|
// in the right order.
|
||||||
|
func TestNativenames(t *testing.T) {
|
||||||
|
bc, _ := chain.NewSingleWithCustomConfig(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
natives := bc.GetNatives()
|
||||||
|
require.Equal(t, len(natives), len(nativenames.All))
|
||||||
|
for i, cs := range natives {
|
||||||
|
require.Equal(t, cs.Manifest.Name, nativenames.All[i], i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -140,8 +140,14 @@ type Function struct {
|
||||||
// Method is a signature for a native method.
|
// Method is a signature for a native method.
|
||||||
type Method = func(ic *Context, args []stackitem.Item) stackitem.Item
|
type Method = func(ic *Context, args []stackitem.Item) stackitem.Item
|
||||||
|
|
||||||
// MethodAndPrice is a native-contract method descriptor.
|
// MethodAndPrice is a generic hardfork-independent native contract method descriptor.
|
||||||
type MethodAndPrice struct {
|
type MethodAndPrice struct {
|
||||||
|
HFSpecificMethodAndPrice
|
||||||
|
ActiveFrom *config.Hardfork
|
||||||
|
}
|
||||||
|
|
||||||
|
// HFSpecificMethodAndPrice is a hardfork-specific native contract method descriptor.
|
||||||
|
type HFSpecificMethodAndPrice struct {
|
||||||
Func Method
|
Func Method
|
||||||
MD *manifest.Method
|
MD *manifest.Method
|
||||||
CPUFee int64
|
CPUFee int64
|
||||||
|
@ -150,10 +156,23 @@ type MethodAndPrice struct {
|
||||||
RequiredFlags callflag.CallFlag
|
RequiredFlags callflag.CallFlag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Event is a generic hardfork-independent native contract event descriptor.
|
||||||
|
type Event struct {
|
||||||
|
HFSpecificEvent
|
||||||
|
ActiveFrom *config.Hardfork
|
||||||
|
}
|
||||||
|
|
||||||
|
// HFSpecificEvent is a hardfork-specific native contract event descriptor.
|
||||||
|
type HFSpecificEvent struct {
|
||||||
|
MD *manifest.Event
|
||||||
|
}
|
||||||
|
|
||||||
// Contract is an interface for all native contracts.
|
// Contract is an interface for all native contracts.
|
||||||
type Contract interface {
|
type Contract interface {
|
||||||
Initialize(*Context) error
|
// Initialize performs native contract initialization on contract deploy or update.
|
||||||
// ActiveIn returns the hardfork native contract is active from or nil in case
|
// Active hardfork is passed as the second argument.
|
||||||
|
Initialize(*Context, *config.Hardfork, *HFSpecificContractMD) error
|
||||||
|
// ActiveIn returns the hardfork native contract is active starting from or nil in case
|
||||||
// it's always active.
|
// it's always active.
|
||||||
ActiveIn() *config.Hardfork
|
ActiveIn() *config.Hardfork
|
||||||
// InitializeCache aimed to initialize contract's cache when the contract has
|
// InitializeCache aimed to initialize contract's cache when the contract has
|
||||||
|
@ -161,53 +180,174 @@ type Contract interface {
|
||||||
// It should be called each time after node restart iff the contract was
|
// It should be called each time after node restart iff the contract was
|
||||||
// deployed and no Initialize method was called.
|
// deployed and no Initialize method was called.
|
||||||
InitializeCache(blockHeight uint32, d *dao.Simple) error
|
InitializeCache(blockHeight uint32, d *dao.Simple) error
|
||||||
|
// Metadata returns generic native contract metadata.
|
||||||
Metadata() *ContractMD
|
Metadata() *ContractMD
|
||||||
OnPersist(*Context) error
|
OnPersist(*Context) error
|
||||||
PostPersist(*Context) error
|
PostPersist(*Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractMD represents a native contract instance.
|
// ContractMD represents a generic hardfork-independent native contract instance.
|
||||||
type ContractMD struct {
|
type ContractMD struct {
|
||||||
state.NativeContract
|
ID int32
|
||||||
|
Hash util.Uint160
|
||||||
Name string
|
Name string
|
||||||
Methods []MethodAndPrice
|
// methods is a generic set of contract methods with activation hardforks. Any HF-dependent part of included methods
|
||||||
|
// (offsets, in particular) must not be used, there's a mdCache field for that.
|
||||||
|
methods []MethodAndPrice
|
||||||
|
// events is a generic set of contract events with activation hardforks. Any HF-dependent part of events must not be
|
||||||
|
// used, there's a mdCache field for that.
|
||||||
|
events []Event
|
||||||
|
// ActiveHFs is a map of hardforks that contract should react to. Contract update should be called for active
|
||||||
|
// hardforks. Note, that unlike the C# implementation, this map doesn't include contract's activation hardfork.
|
||||||
|
// This map is being initialized on contract creation and used as a read-only, hence, not protected
|
||||||
|
// by mutex.
|
||||||
|
ActiveHFs map[config.Hardfork]struct{}
|
||||||
|
|
||||||
|
// mdCache contains hardfork-specific ready-to-use contract descriptors. This cache is initialized in the native
|
||||||
|
// contracts constructors, and acts as read-only during the whole node lifetime, thus not protected by mutex.
|
||||||
|
mdCache map[config.Hardfork]*HFSpecificContractMD
|
||||||
|
|
||||||
|
// onManifestConstruction is a callback for manifest finalization.
|
||||||
|
onManifestConstruction func(*manifest.Manifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContractMD returns Contract with the specified list of methods.
|
// HFSpecificContractMD is a hardfork-specific native contract descriptor.
|
||||||
func NewContractMD(name string, id int32) *ContractMD {
|
type HFSpecificContractMD struct {
|
||||||
|
state.ContractBase
|
||||||
|
Methods []HFSpecificMethodAndPrice
|
||||||
|
Events []HFSpecificEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContractMD returns Contract with the specified fields set. onManifestConstruction callback every time
|
||||||
|
// after hardfork-specific manifest creation and aimed to finalize the manifest.
|
||||||
|
func NewContractMD(name string, id int32, onManifestConstruction ...func(*manifest.Manifest)) *ContractMD {
|
||||||
c := &ContractMD{Name: name}
|
c := &ContractMD{Name: name}
|
||||||
|
if len(onManifestConstruction) != 0 {
|
||||||
|
c.onManifestConstruction = onManifestConstruction[0]
|
||||||
|
}
|
||||||
|
|
||||||
c.ID = id
|
c.ID = id
|
||||||
|
|
||||||
// NEF is now stored in the contract state and affects state dump.
|
|
||||||
// Therefore, values are taken from C# node.
|
|
||||||
c.NEF.Header.Compiler = "neo-core-v3.0"
|
|
||||||
c.NEF.Header.Magic = nef.Magic
|
|
||||||
c.NEF.Tokens = []nef.MethodToken{} // avoid `nil` result during JSON marshalling
|
|
||||||
c.Hash = state.CreateNativeContractHash(c.Name)
|
c.Hash = state.CreateNativeContractHash(c.Name)
|
||||||
c.Manifest = *manifest.DefaultManifest(name)
|
c.ActiveHFs = make(map[config.Hardfork]struct{})
|
||||||
|
c.mdCache = make(map[config.Hardfork]*HFSpecificContractMD)
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateHash creates a native contract script and updates hash.
|
// HFSpecificContractMD returns hardfork-specific native contract metadata, i.e. with methods, events and script
|
||||||
func (c *ContractMD) UpdateHash() {
|
// corresponding to the specified hardfork. If hardfork is not specified, then default metadata will be returned
|
||||||
|
// (methods, events and script that are always active). Calling this method for hardforks older than the contract
|
||||||
|
// activation hardfork is a no-op.
|
||||||
|
func (c *ContractMD) HFSpecificContractMD(hf *config.Hardfork) *HFSpecificContractMD {
|
||||||
|
var key config.Hardfork
|
||||||
|
if hf != nil {
|
||||||
|
key = *hf
|
||||||
|
}
|
||||||
|
md, ok := c.mdCache[key]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Errorf("native contract descriptor cache is not initialized: contract %s, hardfork %s", c.Hash.StringLE(), key))
|
||||||
|
}
|
||||||
|
if md == nil {
|
||||||
|
panic(fmt.Errorf("native contract descriptor cache is nil: contract %s, hardfork %s", c.Hash.StringLE(), key))
|
||||||
|
}
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildHFSpecificMD generates and caches contract's descriptor for every known hardfork.
|
||||||
|
func (c *ContractMD) BuildHFSpecificMD(activeIn *config.Hardfork) {
|
||||||
|
var start config.Hardfork
|
||||||
|
if activeIn != nil {
|
||||||
|
start = *activeIn
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hf := range append([]config.Hardfork{config.HFDefault}, config.Hardforks...) {
|
||||||
|
switch {
|
||||||
|
case hf.Cmp(start) < 0:
|
||||||
|
continue
|
||||||
|
case hf.Cmp(start) == 0:
|
||||||
|
c.buildHFSpecificMD(hf)
|
||||||
|
default:
|
||||||
|
if _, ok := c.ActiveHFs[hf]; !ok {
|
||||||
|
// Intentionally omit HFSpecificContractMD structure copying since mdCache is read-only.
|
||||||
|
c.mdCache[hf] = c.mdCache[hf.Prev()]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.buildHFSpecificMD(hf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildHFSpecificMD builds hardfork-specific contract descriptor that includes methods and events active starting from
|
||||||
|
// the specified hardfork or older. It also updates cache with the received value.
|
||||||
|
func (c *ContractMD) buildHFSpecificMD(hf config.Hardfork) {
|
||||||
|
var (
|
||||||
|
abiMethods = make([]manifest.Method, 0, len(c.methods))
|
||||||
|
methods = make([]HFSpecificMethodAndPrice, 0, len(c.methods))
|
||||||
|
abiEvents = make([]manifest.Event, 0, len(c.events))
|
||||||
|
events = make([]HFSpecificEvent, 0, len(c.events))
|
||||||
|
)
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
for i := range c.Methods {
|
for i := range c.methods {
|
||||||
offset := w.Len()
|
m := c.methods[i]
|
||||||
c.Methods[i].MD.Offset = offset
|
if !(m.ActiveFrom == nil || (hf != config.HFDefault && (*m.ActiveFrom).Cmp(hf) >= 0)) {
|
||||||
c.Manifest.ABI.Methods[i].Offset = offset
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform method descriptor copy to support independent HF-based offset update.
|
||||||
|
md := *m.MD
|
||||||
|
m.MD = &md
|
||||||
|
m.MD.Offset = w.Len()
|
||||||
|
|
||||||
emit.Int(w.BinWriter, 0)
|
emit.Int(w.BinWriter, 0)
|
||||||
c.Methods[i].SyscallOffset = w.Len()
|
m.SyscallOffset = w.Len()
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemContractCallNative)
|
emit.Syscall(w.BinWriter, interopnames.SystemContractCallNative)
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
emit.Opcodes(w.BinWriter, opcode.RET)
|
||||||
|
|
||||||
|
abiMethods = append(abiMethods, *m.MD)
|
||||||
|
methods = append(methods, m.HFSpecificMethodAndPrice)
|
||||||
}
|
}
|
||||||
if w.Err != nil {
|
if w.Err != nil {
|
||||||
panic(fmt.Errorf("can't create native contract script: %w", w.Err))
|
panic(fmt.Errorf("can't create native contract script: %w", w.Err))
|
||||||
}
|
}
|
||||||
|
for i := range c.events {
|
||||||
|
e := c.events[i]
|
||||||
|
if !(e.ActiveFrom == nil || (hf != config.HFDefault && (*e.ActiveFrom).Cmp(hf) >= 0)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
c.NEF.Script = w.Bytes()
|
abiEvents = append(abiEvents, *e.MD)
|
||||||
c.NEF.Checksum = c.NEF.CalculateChecksum()
|
events = append(events, e.HFSpecificEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEF is now stored in the contract state and affects state dump.
|
||||||
|
// Therefore, values are taken from C# node.
|
||||||
|
nf := nef.File{
|
||||||
|
Header: nef.Header{
|
||||||
|
Magic: nef.Magic,
|
||||||
|
Compiler: "neo-core-v3.0",
|
||||||
|
},
|
||||||
|
Tokens: []nef.MethodToken{}, // avoid `nil` result during JSON marshalling,
|
||||||
|
Script: w.Bytes(),
|
||||||
|
}
|
||||||
|
nf.Checksum = nf.CalculateChecksum()
|
||||||
|
m := manifest.DefaultManifest(c.Name)
|
||||||
|
m.ABI.Methods = abiMethods
|
||||||
|
m.ABI.Events = abiEvents
|
||||||
|
if c.onManifestConstruction != nil {
|
||||||
|
c.onManifestConstruction(m)
|
||||||
|
}
|
||||||
|
md := &HFSpecificContractMD{
|
||||||
|
ContractBase: state.ContractBase{
|
||||||
|
ID: c.ID,
|
||||||
|
Hash: c.Hash,
|
||||||
|
NEF: nf,
|
||||||
|
Manifest: *m,
|
||||||
|
},
|
||||||
|
Methods: methods,
|
||||||
|
Events: events,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mdCache[hf] = md
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddMethod adds a new method to a native contract.
|
// AddMethod adds a new method to a native contract.
|
||||||
|
@ -215,36 +355,35 @@ func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) {
|
||||||
md.MD = desc
|
md.MD = desc
|
||||||
desc.Safe = md.RequiredFlags&(callflag.All^callflag.ReadOnly) == 0
|
desc.Safe = md.RequiredFlags&(callflag.All^callflag.ReadOnly) == 0
|
||||||
|
|
||||||
index := sort.Search(len(c.Manifest.ABI.Methods), func(i int) bool {
|
index := sort.Search(len(c.methods), func(i int) bool {
|
||||||
md := c.Manifest.ABI.Methods[i]
|
md := c.methods[i].MD
|
||||||
if md.Name != desc.Name {
|
if md.Name != desc.Name {
|
||||||
return md.Name >= desc.Name
|
return md.Name >= desc.Name
|
||||||
}
|
}
|
||||||
return len(md.Parameters) > len(desc.Parameters)
|
return len(md.Parameters) > len(desc.Parameters)
|
||||||
})
|
})
|
||||||
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, manifest.Method{})
|
c.methods = append(c.methods, MethodAndPrice{})
|
||||||
copy(c.Manifest.ABI.Methods[index+1:], c.Manifest.ABI.Methods[index:])
|
copy(c.methods[index+1:], c.methods[index:])
|
||||||
c.Manifest.ABI.Methods[index] = *desc
|
c.methods[index] = *md
|
||||||
|
|
||||||
// Cache follows the same order.
|
if md.ActiveFrom != nil {
|
||||||
c.Methods = append(c.Methods, MethodAndPrice{})
|
c.ActiveHFs[*md.ActiveFrom] = struct{}{}
|
||||||
copy(c.Methods[index+1:], c.Methods[index:])
|
}
|
||||||
c.Methods[index] = *md
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMethodByOffset returns method with the provided offset.
|
// GetMethodByOffset returns method with the provided offset.
|
||||||
// Offset is offset of `System.Contract.CallNative` syscall.
|
// Offset is offset of `System.Contract.CallNative` syscall.
|
||||||
func (c *ContractMD) GetMethodByOffset(offset int) (MethodAndPrice, bool) {
|
func (c *HFSpecificContractMD) GetMethodByOffset(offset int) (HFSpecificMethodAndPrice, bool) {
|
||||||
for k := range c.Methods {
|
for k := range c.Methods {
|
||||||
if c.Methods[k].SyscallOffset == offset {
|
if c.Methods[k].SyscallOffset == offset {
|
||||||
return c.Methods[k], true
|
return c.Methods[k], true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MethodAndPrice{}, false
|
return HFSpecificMethodAndPrice{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMethod returns method `name` with the specified number of parameters.
|
// GetMethod returns method `name` with the specified number of parameters.
|
||||||
func (c *ContractMD) GetMethod(name string, paramCount int) (MethodAndPrice, bool) {
|
func (c *HFSpecificContractMD) GetMethod(name string, paramCount int) (HFSpecificMethodAndPrice, bool) {
|
||||||
index := sort.Search(len(c.Methods), func(i int) bool {
|
index := sort.Search(len(c.Methods), func(i int) bool {
|
||||||
md := c.Methods[i]
|
md := c.Methods[i]
|
||||||
res := strings.Compare(name, md.MD.Name)
|
res := strings.Compare(name, md.MD.Name)
|
||||||
|
@ -261,15 +400,16 @@ func (c *ContractMD) GetMethod(name string, paramCount int) (MethodAndPrice, boo
|
||||||
return md, true
|
return md, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MethodAndPrice{}, false
|
return HFSpecificMethodAndPrice{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddEvent adds a new event to the native contract.
|
// AddEvent adds a new event to the native contract.
|
||||||
func (c *ContractMD) AddEvent(name string, ps ...manifest.Parameter) {
|
func (c *ContractMD) AddEvent(md Event) {
|
||||||
c.Manifest.ABI.Events = append(c.Manifest.ABI.Events, manifest.Event{
|
c.events = append(c.events, md)
|
||||||
Name: name,
|
|
||||||
Parameters: ps,
|
if md.ActiveFrom != nil {
|
||||||
})
|
c.ActiveHFs[*md.ActiveFrom] = struct{}{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort sorts interop functions by id.
|
// Sort sorts interop functions by id.
|
||||||
|
|
|
@ -12,12 +12,14 @@ import (
|
||||||
func TestNamesASCII(t *testing.T) {
|
func TestNamesASCII(t *testing.T) {
|
||||||
cfg := config.ProtocolConfiguration{P2PSigExtensions: true}
|
cfg := config.ProtocolConfiguration{P2PSigExtensions: true}
|
||||||
cs := NewContracts(cfg)
|
cs := NewContracts(cfg)
|
||||||
|
latestHF := config.LatestHardfork()
|
||||||
for _, c := range cs.Contracts {
|
for _, c := range cs.Contracts {
|
||||||
require.True(t, isASCII(c.Metadata().Name))
|
require.True(t, isASCII(c.Metadata().Name))
|
||||||
for _, m := range c.Metadata().Methods {
|
hfMD := c.Metadata().HFSpecificContractMD(&latestHF)
|
||||||
|
for _, m := range hfMD.Methods {
|
||||||
require.True(t, isASCII(m.MD.Name))
|
require.True(t, isASCII(m.MD.Name))
|
||||||
}
|
}
|
||||||
for _, e := range c.Metadata().Manifest.ABI.Events {
|
for _, e := range hfMD.Manifest.ABI.Events {
|
||||||
require.True(t, isASCII(e.Name))
|
require.True(t, isASCII(e.Name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,12 @@ import (
|
||||||
func TestNativeGetMethod(t *testing.T) {
|
func TestNativeGetMethod(t *testing.T) {
|
||||||
cfg := config.ProtocolConfiguration{P2PSigExtensions: true}
|
cfg := config.ProtocolConfiguration{P2PSigExtensions: true}
|
||||||
cs := NewContracts(cfg)
|
cs := NewContracts(cfg)
|
||||||
|
latestHF := config.LatestHardfork()
|
||||||
for _, c := range cs.Contracts {
|
for _, c := range cs.Contracts {
|
||||||
|
hfMD := c.Metadata().HFSpecificContractMD(&latestHF)
|
||||||
t.Run(c.Metadata().Name, func(t *testing.T) {
|
t.Run(c.Metadata().Name, func(t *testing.T) {
|
||||||
for _, m := range c.Metadata().Methods {
|
for _, m := range hfMD.Methods {
|
||||||
_, ok := c.Metadata().GetMethod(m.MD.Name, len(m.MD.Parameters))
|
_, ok := hfMD.GetMethod(m.MD.Name, len(m.MD.Parameters))
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -41,7 +41,7 @@ const cryptoContractID = -3
|
||||||
|
|
||||||
func newCrypto() *Crypto {
|
func newCrypto() *Crypto {
|
||||||
c := &Crypto{ContractMD: *interop.NewContractMD(nativenames.CryptoLib, cryptoContractID)}
|
c := &Crypto{ContractMD: *interop.NewContractMD(nativenames.CryptoLib, cryptoContractID)}
|
||||||
defer c.UpdateHash()
|
defer c.BuildHFSpecificMD(c.ActiveIn())
|
||||||
|
|
||||||
desc := newDescriptor("sha256", smartcontract.ByteArrayType,
|
desc := newDescriptor("sha256", smartcontract.ByteArrayType,
|
||||||
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
||||||
|
@ -104,7 +104,7 @@ func newCrypto() *Crypto {
|
||||||
|
|
||||||
desc = newDescriptor("keccak256", smartcontract.ByteArrayType,
|
desc = newDescriptor("keccak256", smartcontract.ByteArrayType,
|
||||||
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
||||||
md = newMethodAndPrice(c.keccak256, 1<<15, callflag.NoneFlag)
|
md = newMethodAndPrice(c.keccak256, 1<<15, callflag.NoneFlag, config.HFCockatrice)
|
||||||
c.AddMethod(md, desc)
|
c.AddMethod(md, desc)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
@ -310,7 +310,7 @@ func (c *Crypto) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize implements the Contract interface.
|
// Initialize implements the Contract interface.
|
||||||
func (c *Crypto) Initialize(ic *interop.Context) error {
|
func (c *Crypto) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,9 +104,10 @@ func (s *Designate) isValidRole(r noderoles.Role) bool {
|
||||||
|
|
||||||
func newDesignate(p2pSigExtensionsEnabled bool, initialNodeRoles map[noderoles.Role]keys.PublicKeys) *Designate {
|
func newDesignate(p2pSigExtensionsEnabled bool, initialNodeRoles map[noderoles.Role]keys.PublicKeys) *Designate {
|
||||||
s := &Designate{ContractMD: *interop.NewContractMD(nativenames.Designation, designateContractID)}
|
s := &Designate{ContractMD: *interop.NewContractMD(nativenames.Designation, designateContractID)}
|
||||||
|
defer s.BuildHFSpecificMD(s.ActiveIn())
|
||||||
|
|
||||||
s.p2pSigExtensionsEnabled = p2pSigExtensionsEnabled
|
s.p2pSigExtensionsEnabled = p2pSigExtensionsEnabled
|
||||||
s.initialNodeRoles = initialNodeRoles
|
s.initialNodeRoles = initialNodeRoles
|
||||||
defer s.UpdateHash()
|
|
||||||
|
|
||||||
desc := newDescriptor("getDesignatedByRole", smartcontract.ArrayType,
|
desc := newDescriptor("getDesignatedByRole", smartcontract.ArrayType,
|
||||||
manifest.NewParameter("role", smartcontract.IntegerType),
|
manifest.NewParameter("role", smartcontract.IntegerType),
|
||||||
|
@ -120,9 +121,11 @@ func newDesignate(p2pSigExtensionsEnabled bool, initialNodeRoles map[noderoles.R
|
||||||
md = newMethodAndPrice(s.designateAsRole, 1<<15, callflag.States|callflag.AllowNotify)
|
md = newMethodAndPrice(s.designateAsRole, 1<<15, callflag.States|callflag.AllowNotify)
|
||||||
s.AddMethod(md, desc)
|
s.AddMethod(md, desc)
|
||||||
|
|
||||||
s.AddEvent(DesignationEventName,
|
eDesc := newEventDescriptor(DesignationEventName,
|
||||||
manifest.NewParameter("Role", smartcontract.IntegerType),
|
manifest.NewParameter("Role", smartcontract.IntegerType),
|
||||||
manifest.NewParameter("BlockIndex", smartcontract.IntegerType))
|
manifest.NewParameter("BlockIndex", smartcontract.IntegerType))
|
||||||
|
eMD := newEvent(eDesc)
|
||||||
|
s.AddEvent(eMD)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -130,7 +133,11 @@ func newDesignate(p2pSigExtensionsEnabled bool, initialNodeRoles map[noderoles.R
|
||||||
// Initialize initializes Designation contract. It is called once at native Management's OnPersist
|
// Initialize initializes Designation contract. It is called once at native Management's OnPersist
|
||||||
// at the genesis block, and we can't properly fill the cache at this point, as there are no roles
|
// at the genesis block, and we can't properly fill the cache at this point, as there are no roles
|
||||||
// data in the storage.
|
// data in the storage.
|
||||||
func (s *Designate) Initialize(ic *interop.Context) error {
|
func (s *Designate) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
|
if hf != s.ActiveIn() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
cache := &DesignationCache{}
|
cache := &DesignationCache{}
|
||||||
ic.DAO.SetCache(s.ID, cache)
|
ic.DAO.SetCache(s.ID, cache)
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ func Call(ic *interop.Context) error {
|
||||||
return fmt.Errorf("native contract %s (version %d) not found", curr.StringLE(), version)
|
return fmt.Errorf("native contract %s (version %d) not found", curr.StringLE(), version)
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
meta = c.Metadata()
|
genericMeta = c.Metadata()
|
||||||
activeIn = c.ActiveIn()
|
activeIn = c.ActiveIn()
|
||||||
)
|
)
|
||||||
if activeIn != nil {
|
if activeIn != nil {
|
||||||
|
@ -40,9 +40,17 @@ func Call(ic *interop.Context) error {
|
||||||
// Persisting block must not be taken into account, native contract can be called
|
// Persisting block must not be taken into account, native contract can be called
|
||||||
// only AFTER its initialization block persist, thus, can't use ic.IsHardforkEnabled.
|
// only AFTER its initialization block persist, thus, can't use ic.IsHardforkEnabled.
|
||||||
if !ok || ic.BlockHeight() < height {
|
if !ok || ic.BlockHeight() < height {
|
||||||
return fmt.Errorf("native contract %s is active after hardfork %s", meta.Name, activeIn.String())
|
return fmt.Errorf("native contract %s is active after hardfork %s", genericMeta.Name, activeIn.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var current config.Hardfork
|
||||||
|
for _, hf := range config.Hardforks {
|
||||||
|
if !ic.IsHardforkEnabled(hf) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
current = hf
|
||||||
|
}
|
||||||
|
meta := genericMeta.HFSpecificContractMD(¤t)
|
||||||
m, ok := meta.GetMethodByOffset(ic.VM.Context().IP())
|
m, ok := meta.GetMethodByOffset(ic.VM.Context().IP())
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("method not found")
|
return fmt.Errorf("method not found")
|
||||||
|
|
|
@ -31,7 +31,7 @@ func newLedger() *Ledger {
|
||||||
var l = &Ledger{
|
var l = &Ledger{
|
||||||
ContractMD: *interop.NewContractMD(nativenames.Ledger, ledgerContractID),
|
ContractMD: *interop.NewContractMD(nativenames.Ledger, ledgerContractID),
|
||||||
}
|
}
|
||||||
defer l.UpdateHash()
|
defer l.BuildHFSpecificMD(l.ActiveIn())
|
||||||
|
|
||||||
desc := newDescriptor("currentHash", smartcontract.Hash256Type)
|
desc := newDescriptor("currentHash", smartcontract.Hash256Type)
|
||||||
md := newMethodAndPrice(l.currentHash, 1<<15, callflag.ReadStates)
|
md := newMethodAndPrice(l.currentHash, 1<<15, callflag.ReadStates)
|
||||||
|
@ -81,7 +81,7 @@ func (l *Ledger) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize implements the Contract interface.
|
// Initialize implements the Contract interface.
|
||||||
func (l *Ledger) Initialize(ic *interop.Context) error {
|
func (l *Ledger) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ func newManagement() *Management {
|
||||||
var m = &Management{
|
var m = &Management{
|
||||||
ContractMD: *interop.NewContractMD(nativenames.Management, ManagementContractID),
|
ContractMD: *interop.NewContractMD(nativenames.Management, ManagementContractID),
|
||||||
}
|
}
|
||||||
defer m.UpdateHash()
|
defer m.BuildHFSpecificMD(m.ActiveIn())
|
||||||
|
|
||||||
desc := newDescriptor("getContract", smartcontract.ArrayType,
|
desc := newDescriptor("getContract", smartcontract.ArrayType,
|
||||||
manifest.NewParameter("hash", smartcontract.Hash160Type))
|
manifest.NewParameter("hash", smartcontract.Hash160Type))
|
||||||
|
@ -164,9 +164,17 @@ func newManagement() *Management {
|
||||||
m.AddMethod(md, desc)
|
m.AddMethod(md, desc)
|
||||||
|
|
||||||
hashParam := manifest.NewParameter("Hash", smartcontract.Hash160Type)
|
hashParam := manifest.NewParameter("Hash", smartcontract.Hash160Type)
|
||||||
m.AddEvent(contractDeployNotificationName, hashParam)
|
eDesc := newEventDescriptor(contractDeployNotificationName, hashParam)
|
||||||
m.AddEvent(contractUpdateNotificationName, hashParam)
|
eMD := newEvent(eDesc)
|
||||||
m.AddEvent(contractDestroyNotificationName, hashParam)
|
m.AddEvent(eMD)
|
||||||
|
|
||||||
|
eDesc = newEventDescriptor(contractUpdateNotificationName, hashParam)
|
||||||
|
eMD = newEvent(eDesc)
|
||||||
|
m.AddEvent(eMD)
|
||||||
|
|
||||||
|
eDesc = newEventDescriptor(contractDestroyNotificationName, hashParam)
|
||||||
|
eMD = newEvent(eDesc)
|
||||||
|
m.AddEvent(eMD)
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,6 +228,11 @@ func (m *Management) getContractByID(ic *interop.Context, args []stackitem.Item)
|
||||||
// GetContract returns a contract with the given hash from the given DAO.
|
// GetContract returns a contract with the given hash from the given DAO.
|
||||||
func GetContract(d *dao.Simple, hash util.Uint160) (*state.Contract, error) {
|
func GetContract(d *dao.Simple, hash util.Uint160) (*state.Contract, error) {
|
||||||
cache := d.GetROCache(ManagementContractID).(*ManagementCache)
|
cache := d.GetROCache(ManagementContractID).(*ManagementCache)
|
||||||
|
return getContract(cache, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getContract returns a contract with the given hash from provided RO or RW cache.
|
||||||
|
func getContract(cache *ManagementCache, hash util.Uint160) (*state.Contract, error) {
|
||||||
cs, ok := cache.contracts[hash]
|
cs, ok := cache.contracts[hash]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, storage.ErrKeyNotFound
|
return nil, storage.ErrKeyNotFound
|
||||||
|
@ -583,27 +596,77 @@ func updateContractCache(cache *ManagementCache, cs *state.Contract) {
|
||||||
func (m *Management) OnPersist(ic *interop.Context) error {
|
func (m *Management) OnPersist(ic *interop.Context) error {
|
||||||
var cache *ManagementCache
|
var cache *ManagementCache
|
||||||
for _, native := range ic.Natives {
|
for _, native := range ic.Natives {
|
||||||
activeIn := native.ActiveIn()
|
var (
|
||||||
if !(activeIn == nil && ic.Block.Index == 0 ||
|
activeIn = native.ActiveIn()
|
||||||
activeIn != nil && ic.IsHardforkActivation(*activeIn)) {
|
isDeploy bool
|
||||||
|
isUpdate bool
|
||||||
|
latestHF config.Hardfork
|
||||||
|
)
|
||||||
|
activeHFs := native.Metadata().ActiveHFs
|
||||||
|
isDeploy = activeIn == nil && ic.Block.Index == 0 ||
|
||||||
|
activeIn != nil && ic.IsHardforkActivation(*activeIn)
|
||||||
|
if !isDeploy {
|
||||||
|
for _, hf := range config.Hardforks {
|
||||||
|
if _, ok := activeHFs[hf]; ok && ic.IsHardforkActivation(hf) {
|
||||||
|
isUpdate = true
|
||||||
|
activation := hf // avoid loop variable pointer exporting.
|
||||||
|
activeIn = &activation // reuse ActiveIn variable for the initialization hardfork.
|
||||||
|
// Break immediately since native Initialize should be called only for the first hardfork in a raw
|
||||||
|
// (if there are multiple hardforks with the same enabling height).
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Search for the latest active hardfork to properly construct manifest.
|
||||||
|
for _, hf := range config.Hardforks {
|
||||||
|
if _, ok := activeHFs[hf]; ok && ic.IsHardforkActivation(hf) {
|
||||||
|
latestHF = hf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !(isDeploy || isUpdate) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
md := native.Metadata()
|
md := native.Metadata()
|
||||||
cs := &state.Contract{
|
hfSpecificMD := md.HFSpecificContractMD(&latestHF)
|
||||||
ContractBase: md.ContractBase,
|
base := hfSpecificMD.ContractBase
|
||||||
|
var cs *state.Contract
|
||||||
|
switch {
|
||||||
|
case isDeploy:
|
||||||
|
cs = &state.Contract{
|
||||||
|
ContractBase: base,
|
||||||
}
|
}
|
||||||
if err := native.Initialize(ic); err != nil {
|
case isUpdate:
|
||||||
return fmt.Errorf("initializing %s native contract: %w", md.Name, err)
|
if cache == nil {
|
||||||
|
cache = ic.DAO.GetRWCache(m.ID).(*ManagementCache)
|
||||||
|
}
|
||||||
|
oldcontract, err := getContract(cache, md.Hash)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to retrieve native %s from cache: %w", md.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
contract := *oldcontract // Make a copy, don't ruin cached contract and cache.
|
||||||
|
contract.NEF = base.NEF
|
||||||
|
contract.Manifest = base.Manifest
|
||||||
|
contract.UpdateCounter++
|
||||||
|
cs = &contract
|
||||||
}
|
}
|
||||||
err := putContractState(ic.DAO, cs, false) // Perform cache update manually.
|
err := putContractState(ic.DAO, cs, false) // Perform cache update manually.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to put contract state: %w", err)
|
||||||
|
}
|
||||||
|
if err := native.Initialize(ic, activeIn, hfSpecificMD); err != nil {
|
||||||
|
return fmt.Errorf("initializing %s native contract at HF %d: %w", md.Name, activeIn, err)
|
||||||
}
|
}
|
||||||
if cache == nil {
|
if cache == nil {
|
||||||
cache = ic.DAO.GetRWCache(m.ID).(*ManagementCache)
|
cache = ic.DAO.GetRWCache(m.ID).(*ManagementCache)
|
||||||
}
|
}
|
||||||
updateContractCache(cache, cs)
|
updateContractCache(cache, cs)
|
||||||
|
|
||||||
|
ntfName := contractDeployNotificationName
|
||||||
|
if isUpdate {
|
||||||
|
ntfName = contractUpdateNotificationName
|
||||||
|
}
|
||||||
|
m.emitNotification(ic, ntfName, cs.Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -666,7 +729,11 @@ func (m *Management) GetNEP17Contracts(d *dao.Simple) []util.Uint160 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize implements the Contract interface.
|
// Initialize implements the Contract interface.
|
||||||
func (m *Management) Initialize(ic *interop.Context) error {
|
func (m *Management) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
|
if hf != m.ActiveIn() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee)
|
setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee)
|
||||||
setIntWithKey(m.ID, ic.DAO, keyNextAvailableID, 1)
|
setIntWithKey(m.ID, ic.DAO, keyNextAvailableID, 1)
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,9 @@ func TestDeployGetUpdateDestroyContract(t *testing.T) {
|
||||||
mgmt.Policy = newPolicy(false)
|
mgmt.Policy = newPolicy(false)
|
||||||
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
||||||
ic := &interop.Context{DAO: d}
|
ic := &interop.Context{DAO: d}
|
||||||
err := mgmt.Initialize(ic)
|
err := mgmt.Initialize(ic, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}))
|
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}, nil, nil))
|
||||||
script := []byte{byte(opcode.RET)}
|
script := []byte{byte(opcode.RET)}
|
||||||
sender := util.Uint160{1, 2, 3}
|
sender := util.Uint160{1, 2, 3}
|
||||||
ne, err := nef.NewFile(script)
|
ne, err := nef.NewFile(script)
|
||||||
|
@ -97,9 +97,9 @@ func TestManagement_GetNEP17Contracts(t *testing.T) {
|
||||||
mgmt := newManagement()
|
mgmt := newManagement()
|
||||||
mgmt.Policy = newPolicy(false)
|
mgmt.Policy = newPolicy(false)
|
||||||
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
||||||
err := mgmt.Initialize(&interop.Context{DAO: d})
|
err := mgmt.Initialize(&interop.Context{DAO: d}, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}))
|
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}, nil, nil))
|
||||||
err = mgmt.InitializeCache(0, d)
|
err = mgmt.InitializeCache(0, d)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ func newGAS(init int64, p2pSigExtensionsEnabled bool) *GAS {
|
||||||
initialSupply: init,
|
initialSupply: init,
|
||||||
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
|
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
|
||||||
}
|
}
|
||||||
defer g.UpdateHash()
|
defer g.BuildHFSpecificMD(g.ActiveIn())
|
||||||
|
|
||||||
nep17 := newNEP17Native(nativenames.Gas, gasContractID)
|
nep17 := newNEP17Native(nativenames.Gas, gasContractID)
|
||||||
nep17.symbol = "GAS"
|
nep17.symbol = "GAS"
|
||||||
|
@ -83,7 +83,11 @@ func (g *GAS) balanceFromBytes(si *state.StorageItem) (*big.Int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes a GAS contract.
|
// Initialize initializes a GAS contract.
|
||||||
func (g *GAS) Initialize(ic *interop.Context) error {
|
func (g *GAS) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
|
if hf != g.ActiveIn() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := g.nep17TokenNative.Initialize(ic); err != nil {
|
if err := g.nep17TokenNative.Initialize(ic); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@ func makeValidatorKey(key *keys.PublicKey) []byte {
|
||||||
// newNEO returns NEO native contract.
|
// newNEO returns NEO native contract.
|
||||||
func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
||||||
n := &NEO{}
|
n := &NEO{}
|
||||||
defer n.UpdateHash()
|
defer n.BuildHFSpecificMD(n.ActiveIn())
|
||||||
|
|
||||||
nep17 := newNEP17Native(nativenames.Neo, neoContractID)
|
nep17 := newNEP17Native(nativenames.Neo, neoContractID)
|
||||||
nep17.symbol = "NEO"
|
nep17.symbol = "NEO"
|
||||||
|
@ -233,7 +233,7 @@ func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
||||||
n.AddMethod(md, desc)
|
n.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("getCommitteeAddress", smartcontract.Hash160Type)
|
desc = newDescriptor("getCommitteeAddress", smartcontract.Hash160Type)
|
||||||
md = newMethodAndPrice(n.getCommitteeAddress, 1<<16, callflag.ReadStates)
|
md = newMethodAndPrice(n.getCommitteeAddress, 1<<16, callflag.ReadStates, config.HFCockatrice)
|
||||||
n.AddMethod(md, desc)
|
n.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("getNextBlockValidators", smartcontract.ArrayType)
|
desc = newDescriptor("getNextBlockValidators", smartcontract.ArrayType)
|
||||||
|
@ -258,27 +258,39 @@ func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
||||||
md = newMethodAndPrice(n.setRegisterPrice, 1<<15, callflag.States)
|
md = newMethodAndPrice(n.setRegisterPrice, 1<<15, callflag.States)
|
||||||
n.AddMethod(md, desc)
|
n.AddMethod(md, desc)
|
||||||
|
|
||||||
n.AddEvent("CandidateStateChanged",
|
eDesc := newEventDescriptor("CandidateStateChanged",
|
||||||
manifest.NewParameter("pubkey", smartcontract.PublicKeyType),
|
manifest.NewParameter("pubkey", smartcontract.PublicKeyType),
|
||||||
manifest.NewParameter("registered", smartcontract.BoolType),
|
manifest.NewParameter("registered", smartcontract.BoolType),
|
||||||
manifest.NewParameter("votes", smartcontract.IntegerType),
|
manifest.NewParameter("votes", smartcontract.IntegerType),
|
||||||
)
|
)
|
||||||
n.AddEvent("Vote",
|
eMD := newEvent(eDesc)
|
||||||
|
n.AddEvent(eMD)
|
||||||
|
|
||||||
|
eDesc = newEventDescriptor("Vote",
|
||||||
manifest.NewParameter("account", smartcontract.Hash160Type),
|
manifest.NewParameter("account", smartcontract.Hash160Type),
|
||||||
manifest.NewParameter("from", smartcontract.PublicKeyType),
|
manifest.NewParameter("from", smartcontract.PublicKeyType),
|
||||||
manifest.NewParameter("to", smartcontract.PublicKeyType),
|
manifest.NewParameter("to", smartcontract.PublicKeyType),
|
||||||
manifest.NewParameter("amount", smartcontract.IntegerType),
|
manifest.NewParameter("amount", smartcontract.IntegerType),
|
||||||
)
|
)
|
||||||
n.AddEvent("CommitteeChanged",
|
eMD = newEvent(eDesc)
|
||||||
|
n.AddEvent(eMD)
|
||||||
|
|
||||||
|
eDesc = newEventDescriptor("CommitteeChanged",
|
||||||
manifest.NewParameter("old", smartcontract.ArrayType),
|
manifest.NewParameter("old", smartcontract.ArrayType),
|
||||||
manifest.NewParameter("new", smartcontract.ArrayType),
|
manifest.NewParameter("new", smartcontract.ArrayType),
|
||||||
)
|
)
|
||||||
|
eMD = newEvent(eDesc)
|
||||||
|
n.AddEvent(eMD)
|
||||||
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes a NEO contract.
|
// Initialize initializes a NEO contract.
|
||||||
func (n *NEO) Initialize(ic *interop.Context) error {
|
func (n *NEO) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
|
if hf != n.ActiveIn() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := n.nep17TokenNative.Initialize(ic); err != nil {
|
if err := n.nep17TokenNative.Initialize(ic); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
||||||
|
@ -45,8 +46,9 @@ func (c *nep17TokenNative) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNEP17Native(name string, id int32) *nep17TokenNative {
|
func newNEP17Native(name string, id int32) *nep17TokenNative {
|
||||||
n := &nep17TokenNative{ContractMD: *interop.NewContractMD(name, id)}
|
n := &nep17TokenNative{ContractMD: *interop.NewContractMD(name, id, func(m *manifest.Manifest) {
|
||||||
n.Manifest.SupportedStandards = []string{manifest.NEP17StandardName}
|
m.SupportedStandards = []string{manifest.NEP17StandardName}
|
||||||
|
})}
|
||||||
|
|
||||||
desc := newDescriptor("symbol", smartcontract.StringType)
|
desc := newDescriptor("symbol", smartcontract.StringType)
|
||||||
md := newMethodAndPrice(n.Symbol, 0, callflag.NoneFlag)
|
md := newMethodAndPrice(n.Symbol, 0, callflag.NoneFlag)
|
||||||
|
@ -77,7 +79,9 @@ func newNEP17Native(name string, id int32) *nep17TokenNative {
|
||||||
md.StorageFee = 50
|
md.StorageFee = 50
|
||||||
n.AddMethod(md, desc)
|
n.AddMethod(md, desc)
|
||||||
|
|
||||||
n.AddEvent("Transfer", transferParams...)
|
eDesc := newEventDescriptor("Transfer", transferParams...)
|
||||||
|
eMD := newEvent(eDesc)
|
||||||
|
n.AddEvent(eMD)
|
||||||
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
@ -319,12 +323,40 @@ func newDescriptor(name string, ret smartcontract.ParamType, ps ...manifest.Para
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMethodAndPrice(f interop.Method, cpuFee int64, flags callflag.CallFlag) *interop.MethodAndPrice {
|
func newMethodAndPrice(f interop.Method, cpuFee int64, flags callflag.CallFlag, activeFrom ...config.Hardfork) *interop.MethodAndPrice {
|
||||||
return &interop.MethodAndPrice{
|
md := &interop.MethodAndPrice{
|
||||||
|
HFSpecificMethodAndPrice: interop.HFSpecificMethodAndPrice{
|
||||||
Func: f,
|
Func: f,
|
||||||
CPUFee: cpuFee,
|
CPUFee: cpuFee,
|
||||||
RequiredFlags: flags,
|
RequiredFlags: flags,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
if len(activeFrom) != 0 {
|
||||||
|
md.ActiveFrom = &activeFrom[0]
|
||||||
|
}
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEventDescriptor(name string, ps ...manifest.Parameter) *manifest.Event {
|
||||||
|
if len(ps) == 0 {
|
||||||
|
ps = []manifest.Parameter{}
|
||||||
|
}
|
||||||
|
return &manifest.Event{
|
||||||
|
Name: name,
|
||||||
|
Parameters: ps,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEvent(desc *manifest.Event, activeFrom ...config.Hardfork) interop.Event {
|
||||||
|
md := interop.Event{
|
||||||
|
HFSpecificEvent: interop.HFSpecificEvent{
|
||||||
|
MD: desc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if len(activeFrom) != 0 {
|
||||||
|
md.ActiveFrom = &activeFrom[0]
|
||||||
|
}
|
||||||
|
return md
|
||||||
}
|
}
|
||||||
|
|
||||||
func toBigInt(s stackitem.Item) *big.Int {
|
func toBigInt(s stackitem.Item) *big.Int {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
@ -19,7 +20,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func newNativeClient(t *testing.T, name string) *neotest.ContractInvoker {
|
func newNativeClient(t *testing.T, name string) *neotest.ContractInvoker {
|
||||||
bc, acc := chain.NewSingle(t)
|
return newCustomNativeClient(t, name, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCustomNativeClient(t *testing.T, name string, f func(cfg *config.Blockchain)) *neotest.ContractInvoker {
|
||||||
|
bc, acc := chain.NewSingleWithCustomConfig(t, f)
|
||||||
e := neotest.NewExecutor(t, bc, acc, acc)
|
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||||
|
|
||||||
return e.CommitteeInvoker(e.NativeHash(t, name))
|
return e.CommitteeInvoker(e.NativeHash(t, name))
|
||||||
|
|
|
@ -6,9 +6,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
ojson "github.com/nspcc-dev/go-ordered-json"
|
||||||
"github.com/nspcc-dev/neo-go/internal/contracts"
|
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
|
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
|
@ -30,10 +34,47 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// defaultCSS holds serialized native contract states built for genesis block (with UpdateCounter 0)
|
||||||
|
// under assumption that all hardforks are disabled.
|
||||||
|
defaultCSS = map[string]string{
|
||||||
|
nativenames.Management: `{"id":-1,"hash":"0xfffdc93764dbaddd97c48f252a53ea4643faa3fd","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"ContractManagement","abi":{"methods":[{"name":"deploy","offset":0,"parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Array","safe":false},{"name":"deploy","offset":7,"parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Array","safe":false},{"name":"destroy","offset":14,"parameters":[],"returntype":"Void","safe":false},{"name":"getContract","offset":21,"parameters":[{"name":"hash","type":"Hash160"}],"returntype":"Array","safe":true},{"name":"getContractById","offset":28,"parameters":[{"name":"id","type":"Integer"}],"returntype":"Array","safe":true},{"name":"getContractHashes","offset":35,"parameters":[],"returntype":"InteropInterface","safe":true},{"name":"getMinimumDeploymentFee","offset":42,"parameters":[],"returntype":"Integer","safe":true},{"name":"hasMethod","offset":49,"parameters":[{"name":"hash","type":"Hash160"},{"name":"method","type":"String"},{"name":"pcount","type":"Integer"}],"returntype":"Boolean","safe":true},{"name":"setMinimumDeploymentFee","offset":56,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"update","offset":63,"parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Void","safe":false},{"name":"update","offset":70,"parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false}],"events":[{"name":"Deploy","parameters":[{"name":"Hash","type":"Hash160"}]},{"name":"Update","parameters":[{"name":"Hash","type":"Hash160"}]},{"name":"Destroy","parameters":[{"name":"Hash","type":"Hash160"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.StdLib: `{"id":-2,"hash":"0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":1991619121},"manifest":{"name":"StdLib","abi":{"methods":[{"name":"atoi","offset":0,"parameters":[{"name":"value","type":"String"}],"returntype":"Integer","safe":true},{"name":"atoi","offset":7,"parameters":[{"name":"value","type":"String"},{"name":"base","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"base58CheckDecode","offset":14,"parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","safe":true},{"name":"base58CheckEncode","offset":21,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"base58Decode","offset":28,"parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","safe":true},{"name":"base58Encode","offset":35,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"base64Decode","offset":42,"parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","safe":true},{"name":"base64Encode","offset":49,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"deserialize","offset":56,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"Any","safe":true},{"name":"itoa","offset":63,"parameters":[{"name":"value","type":"Integer"}],"returntype":"String","safe":true},{"name":"itoa","offset":70,"parameters":[{"name":"value","type":"Integer"},{"name":"base","type":"Integer"}],"returntype":"String","safe":true},{"name":"jsonDeserialize","offset":77,"parameters":[{"name":"json","type":"ByteArray"}],"returntype":"Any","safe":true},{"name":"jsonSerialize","offset":84,"parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","safe":true},{"name":"memoryCompare","offset":91,"parameters":[{"name":"str1","type":"ByteArray"},{"name":"str2","type":"ByteArray"}],"returntype":"Integer","safe":true},{"name":"memorySearch","offset":98,"parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"}],"returntype":"Integer","safe":true},{"name":"memorySearch","offset":105,"parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"memorySearch","offset":112,"parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"},{"name":"backward","type":"Boolean"}],"returntype":"Integer","safe":true},{"name":"serialize","offset":119,"parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","safe":true},{"name":"strLen","offset":126,"parameters":[{"name":"str","type":"String"}],"returntype":"Integer","safe":true},{"name":"stringSplit","offset":133,"parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"}],"returntype":"Array","safe":true},{"name":"stringSplit","offset":140,"parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"},{"name":"removeEmptyEntries","type":"Boolean"}],"returntype":"Array","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.CryptoLib: `{"id":-3,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQA==","checksum":2135988409},"manifest":{"name":"CryptoLib","abi":{"methods":[{"name":"bls12381Add","offset":0,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Deserialize","offset":7,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Equal","offset":14,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","safe":true},{"name":"bls12381Mul","offset":21,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Pairing","offset":28,"parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Serialize","offset":35,"parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","safe":true},{"name":"murmur32","offset":42,"parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","safe":true},{"name":"ripemd160","offset":49,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"sha256","offset":56,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"verifyWithECDsa","offset":63,"parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curve","type":"Integer"}],"returntype":"Boolean","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Ledger: `{"id":-4,"hash":"0xda65b600f7124ce6c79950c1772a36403104f2be","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1110259869},"manifest":{"name":"LedgerContract","abi":{"methods":[{"name":"currentHash","offset":0,"parameters":[],"returntype":"Hash256","safe":true},{"name":"currentIndex","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"getBlock","offset":14,"parameters":[{"name":"indexOrHash","type":"ByteArray"}],"returntype":"Array","safe":true},{"name":"getTransaction","offset":21,"parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Array","safe":true},{"name":"getTransactionFromBlock","offset":28,"parameters":[{"name":"blockIndexOrHash","type":"ByteArray"},{"name":"txIndex","type":"Integer"}],"returntype":"Array","safe":true},{"name":"getTransactionHeight","offset":35,"parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Integer","safe":true},{"name":"getTransactionSigners","offset":42,"parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Array","safe":true},{"name":"getTransactionVMState","offset":49,"parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Integer","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Neo: `{"id":-5,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQA==","checksum":65467259},"manifest":{"name":"NeoToken","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"decimals","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"getAccountState","offset":14,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","safe":true},{"name":"getAllCandidates","offset":21,"parameters":[],"returntype":"InteropInterface","safe":true},{"name":"getCandidateVote","offset":28,"parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","safe":true},{"name":"getCandidates","offset":35,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommittee","offset":42,"parameters":[],"returntype":"Array","safe":true},{"name":"getGasPerBlock","offset":49,"parameters":[],"returntype":"Integer","safe":true},{"name":"getNextBlockValidators","offset":56,"parameters":[],"returntype":"Array","safe":true},{"name":"getRegisterPrice","offset":63,"parameters":[],"returntype":"Integer","safe":true},{"name":"registerCandidate","offset":70,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"setGasPerBlock","offset":77,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setRegisterPrice","offset":84,"parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","safe":false},{"name":"symbol","offset":91,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":98,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":105,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false},{"name":"unclaimedGas","offset":112,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"unregisterCandidate","offset":119,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"vote","offset":126,"parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-17"],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Gas: `{"id":-6,"hash":"0xd2a4cff31913016155e38e474a2c06d08be276cf","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2663858513},"manifest":{"name":"GasToken","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"decimals","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"symbol","offset":14,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":21,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":28,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-17"],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Policy: `{"id":-7,"hash":"0xcc5e4edd9f5f8dba8bb65734541df7a1c081c67b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"PolicyContract","abi":{"methods":[{"name":"blockAccount","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":false},{"name":"getAttributeFee","offset":7,"parameters":[{"name":"attributeType","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"getExecFeeFactor","offset":14,"parameters":[],"returntype":"Integer","safe":true},{"name":"getFeePerByte","offset":21,"parameters":[],"returntype":"Integer","safe":true},{"name":"getStoragePrice","offset":28,"parameters":[],"returntype":"Integer","safe":true},{"name":"isBlocked","offset":35,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":true},{"name":"setAttributeFee","offset":42,"parameters":[{"name":"attributeType","type":"Integer"},{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setExecFeeFactor","offset":49,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setFeePerByte","offset":56,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setStoragePrice","offset":63,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"unblockAccount","offset":70,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Designation: `{"id":-8,"hash":"0x49cf4e5378ffcd4dec034fd98a174c5491e395e2","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0A=","checksum":983638438},"manifest":{"name":"RoleManagement","abi":{"methods":[{"name":"designateAsRole","offset":0,"parameters":[{"name":"role","type":"Integer"},{"name":"nodes","type":"Array"}],"returntype":"Void","safe":false},{"name":"getDesignatedByRole","offset":7,"parameters":[{"name":"role","type":"Integer"},{"name":"index","type":"Integer"}],"returntype":"Array","safe":true}],"events":[{"name":"Designation","parameters":[{"name":"Role","type":"Integer"},{"name":"BlockIndex","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Oracle: `{"id":-9,"hash":"0xfe924b7cfe89ddd271abaf7210a80a7e11178758","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2663858513},"manifest":{"name":"OracleContract","abi":{"methods":[{"name":"finish","offset":0,"parameters":[],"returntype":"Void","safe":false},{"name":"getPrice","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"request","offset":14,"parameters":[{"name":"url","type":"String"},{"name":"filter","type":"String"},{"name":"callback","type":"String"},{"name":"userData","type":"Any"},{"name":"gasForResponse","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setPrice","offset":21,"parameters":[{"name":"price","type":"Integer"}],"returntype":"Void","safe":false},{"name":"verify","offset":28,"parameters":[],"returntype":"Boolean","safe":true}],"events":[{"name":"OracleRequest","parameters":[{"name":"Id","type":"Integer"},{"name":"RequestContract","type":"Hash160"},{"name":"Url","type":"String"},{"name":"Filter","type":"String"}]},{"name":"OracleResponse","parameters":[{"name":"Id","type":"Integer"},{"name":"OriginalTx","type":"Hash256"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Notary: `{"id":-10,"hash":"0xc1e14f19c3e60d0b9244d06dd7ba9b113135ec3b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1110259869},"manifest":{"name":"Notary","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"expirationOf","offset":7,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"getMaxNotValidBeforeDelta","offset":14,"parameters":[],"returntype":"Integer","safe":true},{"name":"lockDepositUntil","offset":21,"parameters":[{"name":"address","type":"Hash160"},{"name":"till","type":"Integer"}],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":28,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"setMaxNotValidBeforeDelta","offset":35,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"verify","offset":42,"parameters":[{"name":"signature","type":"Signature"}],"returntype":"Boolean","safe":true},{"name":"withdraw","offset":49,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"}],"returntype":"Boolean","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
}
|
||||||
|
// cockatriceCSS holds serialized native contract states built for genesis block (with UpdateCounter 0)
|
||||||
|
// under assumption that hardforks from Aspidochelone to Cockatrice (included) are enabled.
|
||||||
|
cockatriceCSS = map[string]string{
|
||||||
|
nativenames.CryptoLib: `{"id":-3,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"CryptoLib","abi":{"methods":[{"name":"bls12381Add","offset":0,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Deserialize","offset":7,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Equal","offset":14,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","safe":true},{"name":"bls12381Mul","offset":21,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Pairing","offset":28,"parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Serialize","offset":35,"parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","safe":true},{"name":"keccak256","offset":42,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"murmur32","offset":49,"parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","safe":true},{"name":"ripemd160","offset":56,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"sha256","offset":63,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"verifyWithECDsa","offset":70,"parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curve","type":"Integer"}],"returntype":"Boolean","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Neo: `{"id":-5,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1325686241},"manifest":{"name":"NeoToken","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"decimals","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"getAccountState","offset":14,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","safe":true},{"name":"getAllCandidates","offset":21,"parameters":[],"returntype":"InteropInterface","safe":true},{"name":"getCandidateVote","offset":28,"parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","safe":true},{"name":"getCandidates","offset":35,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommittee","offset":42,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommitteeAddress","offset":49,"parameters":[],"returntype":"Hash160","safe":true},{"name":"getGasPerBlock","offset":56,"parameters":[],"returntype":"Integer","safe":true},{"name":"getNextBlockValidators","offset":63,"parameters":[],"returntype":"Array","safe":true},{"name":"getRegisterPrice","offset":70,"parameters":[],"returntype":"Integer","safe":true},{"name":"registerCandidate","offset":77,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"setGasPerBlock","offset":84,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setRegisterPrice","offset":91,"parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","safe":false},{"name":"symbol","offset":98,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":105,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":112,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false},{"name":"unclaimedGas","offset":119,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"unregisterCandidate","offset":126,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"vote","offset":133,"parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-17"],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for k, v := range defaultCSS {
|
||||||
|
if _, ok := cockatriceCSS[k]; !ok {
|
||||||
|
cockatriceCSS[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newManagementClient(t *testing.T) *neotest.ContractInvoker {
|
func newManagementClient(t *testing.T) *neotest.ContractInvoker {
|
||||||
return newNativeClient(t, nativenames.Management)
|
return newNativeClient(t, nativenames.Management)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newCustomManagementClient returns native Management invoker backed with chain with
|
||||||
|
// specified custom configuration.
|
||||||
|
func newCustomManagementClient(t *testing.T, f func(cfg *config.Blockchain)) *neotest.ContractInvoker {
|
||||||
|
return newCustomNativeClient(t, nativenames.Management, f)
|
||||||
|
}
|
||||||
|
|
||||||
func TestManagement_MinimumDeploymentFee(t *testing.T) {
|
func TestManagement_MinimumDeploymentFee(t *testing.T) {
|
||||||
testGetSet(t, newManagementClient(t), "MinimumDeploymentFee", 10_00000000, 0, 0)
|
testGetSet(t, newManagementClient(t), "MinimumDeploymentFee", 10_00000000, 0, 0)
|
||||||
}
|
}
|
||||||
|
@ -43,6 +84,253 @@ func TestManagement_MinimumDeploymentFeeCache(t *testing.T) {
|
||||||
testGetSetCache(t, c, "MinimumDeploymentFee", 10_00000000)
|
testGetSetCache(t, c, "MinimumDeploymentFee", 10_00000000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestManagement_GenesisNativeState(t *testing.T) {
|
||||||
|
// check ensures that contract state stored in native Management matches the expected one.
|
||||||
|
check := func(t *testing.T, c *neotest.ContractInvoker, expected map[string]string) {
|
||||||
|
for _, name := range nativenames.All {
|
||||||
|
h := state.CreateNativeContractHash(name)
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
si := stack[0]
|
||||||
|
var cs = &state.Contract{}
|
||||||
|
require.NoError(t, cs.FromStackItem(si), name)
|
||||||
|
jBytes, err := ojson.Marshal(cs)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expected[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name))
|
||||||
|
}, "getContract", h.BytesBE())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("disabled hardforks", func(t *testing.T) {
|
||||||
|
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
check(t, mgmt, defaultCSS)
|
||||||
|
})
|
||||||
|
t.Run("remotely enabled hardforks", func(t *testing.T) {
|
||||||
|
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 100500,
|
||||||
|
config.HFBasilisk.String(): 100500,
|
||||||
|
config.HFCockatrice.String(): 100500,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
check(t, mgmt, defaultCSS)
|
||||||
|
})
|
||||||
|
t.Run("Aspidochelone enabled", func(t *testing.T) {
|
||||||
|
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
check(t, mgmt, defaultCSS)
|
||||||
|
})
|
||||||
|
t.Run("Basilisk enabled", func(t *testing.T) {
|
||||||
|
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
config.HFBasilisk.String(): 0,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
check(t, mgmt, defaultCSS)
|
||||||
|
})
|
||||||
|
t.Run("Cockatrice enabled", func(t *testing.T) {
|
||||||
|
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
config.HFBasilisk.String(): 0,
|
||||||
|
config.HFCockatrice.String(): 0,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
check(t, mgmt, cockatriceCSS)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManagement_NativeDeployUpdateNotifications(t *testing.T) {
|
||||||
|
const cockatriceHeight = 3
|
||||||
|
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
config.HFBasilisk.String(): 0,
|
||||||
|
config.HFCockatrice.String(): cockatriceHeight,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
e := mgmt.Executor
|
||||||
|
|
||||||
|
// Check Deploy notifications.
|
||||||
|
aer, err := mgmt.Chain.GetAppExecResults(e.GetBlockByIndex(t, 0).Hash(), trigger.OnPersist)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(aer))
|
||||||
|
var expected []state.NotificationEvent
|
||||||
|
for _, name := range nativenames.All {
|
||||||
|
switch name {
|
||||||
|
case nativenames.Gas:
|
||||||
|
expected = append(expected, state.NotificationEvent{
|
||||||
|
ScriptHash: nativehashes.Gas,
|
||||||
|
Name: "Transfer",
|
||||||
|
Item: stackitem.NewArray([]stackitem.Item{
|
||||||
|
stackitem.Null{},
|
||||||
|
stackitem.Make(mgmt.Validator.ScriptHash()),
|
||||||
|
stackitem.Make(mgmt.Chain.GetConfig().InitialGASSupply),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
case nativenames.Neo:
|
||||||
|
expected = append(expected, state.NotificationEvent{
|
||||||
|
ScriptHash: nativehashes.Neo,
|
||||||
|
Name: "Transfer",
|
||||||
|
Item: stackitem.NewArray([]stackitem.Item{
|
||||||
|
stackitem.Null{},
|
||||||
|
stackitem.Make(mgmt.Validator.ScriptHash()),
|
||||||
|
stackitem.Make(native.NEOTotalSupply),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
expected = append(expected, state.NotificationEvent{
|
||||||
|
ScriptHash: nativehashes.Management,
|
||||||
|
Name: "Deploy",
|
||||||
|
Item: stackitem.NewArray([]stackitem.Item{
|
||||||
|
stackitem.Make(state.CreateNativeContractHash(name)),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, aer[0].Events)
|
||||||
|
|
||||||
|
// Generate some blocks and check Update notifications.
|
||||||
|
cockatriceBlock := mgmt.GenerateNewBlocks(t, cockatriceHeight)[cockatriceHeight-1]
|
||||||
|
aer, err = mgmt.Chain.GetAppExecResults(cockatriceBlock.Hash(), trigger.OnPersist)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(aer))
|
||||||
|
expected = expected[:0]
|
||||||
|
for _, name := range []string{nativenames.CryptoLib, nativenames.Neo} {
|
||||||
|
expected = append(expected, state.NotificationEvent{
|
||||||
|
ScriptHash: nativehashes.Management,
|
||||||
|
Name: "Update",
|
||||||
|
Item: stackitem.NewArray([]stackitem.Item{
|
||||||
|
stackitem.Make(state.CreateNativeContractHash(name)),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, aer[0].Events)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManagement_NativeUpdate(t *testing.T) {
|
||||||
|
const cockatriceHeight = 3
|
||||||
|
c := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
config.HFBasilisk.String(): 0,
|
||||||
|
config.HFCockatrice.String(): cockatriceHeight,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add some blocks up to the Cockatrice enabling height and check the default natives state.
|
||||||
|
for i := 0; i < cockatriceHeight-1; i++ {
|
||||||
|
c.AddNewBlock(t)
|
||||||
|
for _, name := range nativenames.All {
|
||||||
|
h := state.CreateNativeContractHash(name)
|
||||||
|
cs := c.Chain.GetContractState(h)
|
||||||
|
require.NotNil(t, cs, name)
|
||||||
|
jBytes, err := ojson.Marshal(cs)
|
||||||
|
require.NoError(t, err, name)
|
||||||
|
require.Equal(t, defaultCSS[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Cockatrice block and check the updated native state.
|
||||||
|
c.AddNewBlock(t)
|
||||||
|
for _, name := range nativenames.All {
|
||||||
|
h := state.CreateNativeContractHash(name)
|
||||||
|
cs := c.Chain.GetContractState(h)
|
||||||
|
require.NotNil(t, cs, name)
|
||||||
|
if name == nativenames.Neo || name == nativenames.CryptoLib {
|
||||||
|
// A tiny hack to reuse cockatriceCSS map in the check below.
|
||||||
|
require.Equal(t, uint16(1), cs.UpdateCounter, name)
|
||||||
|
cs.UpdateCounter--
|
||||||
|
}
|
||||||
|
jBytes, err := ojson.Marshal(cs)
|
||||||
|
require.NoError(t, err, name)
|
||||||
|
require.Equal(t, cockatriceCSS[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManagement_NativeUpdate_Call(t *testing.T) {
|
||||||
|
const (
|
||||||
|
cockatriceHeight = 3
|
||||||
|
method = "getCommitteeAddress"
|
||||||
|
)
|
||||||
|
c := newCustomNativeClient(t, nativenames.Neo, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
config.HFBasilisk.String(): 0,
|
||||||
|
config.HFCockatrice.String(): cockatriceHeight,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Invoke Cockatrice-dependant method before Cockatrice should fail.
|
||||||
|
for i := 0; i < cockatriceHeight-1; i++ {
|
||||||
|
c.InvokeFail(t, "at instruction 45 (SYSCALL): System.Contract.Call failed: method not found: getCommitteeAddress/0", method)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoke Cockatrice-dependant method at Cockatrice should be OK.
|
||||||
|
tx := c.NewUnsignedTx(t, c.Hash, method)
|
||||||
|
c.SignTx(t, tx, 1_0000_0000, c.Signers...)
|
||||||
|
c.AddNewBlock(t, tx)
|
||||||
|
c.CheckHalt(t, tx.Hash(), stackitem.Make(c.CommitteeHash))
|
||||||
|
|
||||||
|
// Invoke Cockatrice-dependant method after Cockatrice should be OK.
|
||||||
|
c.Invoke(t, c.CommitteeHash, method)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlockchain_GetNatives ensures that Blockchain's GetNatives API works as expected with
|
||||||
|
// different block heights depending on hardfork settings. This test is located here since it
|
||||||
|
// depends on defaultCSS and cockatriceCSS.
|
||||||
|
func TestBlockchain_GetNatives(t *testing.T) {
|
||||||
|
const cockatriceHeight = 3
|
||||||
|
bc, acc := chain.NewSingleWithCustomConfig(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
config.HFBasilisk.String(): 0,
|
||||||
|
config.HFCockatrice.String(): cockatriceHeight,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||||
|
|
||||||
|
// Check genesis-based native contract states.
|
||||||
|
natives := bc.GetNatives()
|
||||||
|
require.Equal(t, len(nativenames.All), len(natives))
|
||||||
|
for _, cs := range natives {
|
||||||
|
csFull := state.Contract{
|
||||||
|
ContractBase: cs.ContractBase,
|
||||||
|
UpdateCounter: 0,
|
||||||
|
}
|
||||||
|
jBytes, err := ojson.Marshal(csFull)
|
||||||
|
require.NoError(t, err, cs.Manifest.Name)
|
||||||
|
require.Equal(t, defaultCSS[cs.Manifest.Name], string(jBytes), fmt.Errorf("contract %s state mismatch", cs.Manifest.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check native state after update.
|
||||||
|
e.GenerateNewBlocks(t, cockatriceHeight)
|
||||||
|
natives = bc.GetNatives()
|
||||||
|
require.Equal(t, len(nativenames.All), len(natives))
|
||||||
|
for _, cs := range natives {
|
||||||
|
csFull := state.Contract{
|
||||||
|
ContractBase: cs.ContractBase,
|
||||||
|
UpdateCounter: 0, // Since we're comparing only state.NativeContract part, set the update counter to 0 to match the cockatriceCSS.
|
||||||
|
}
|
||||||
|
jBytes, err := ojson.Marshal(csFull)
|
||||||
|
require.NoError(t, err, cs.Manifest.Name)
|
||||||
|
require.Equal(t, cockatriceCSS[cs.Manifest.Name], string(jBytes), fmt.Errorf("contract %s state mismatch", cs.Manifest.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestManagement_ContractCache(t *testing.T) {
|
func TestManagement_ContractCache(t *testing.T) {
|
||||||
c := newManagementClient(t)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
managementInvoker := c.WithSigners(c.Committee)
|
||||||
|
|
34
pkg/core/native/nativehashes/hashes.go
Normal file
34
pkg/core/native/nativehashes/hashes.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package nativehashes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hashes of all native contracts.
|
||||||
|
var (
|
||||||
|
Management util.Uint160
|
||||||
|
Ledger util.Uint160
|
||||||
|
Neo util.Uint160
|
||||||
|
Gas util.Uint160
|
||||||
|
Policy util.Uint160
|
||||||
|
Oracle util.Uint160
|
||||||
|
Designation util.Uint160
|
||||||
|
Notary util.Uint160
|
||||||
|
CryptoLib util.Uint160
|
||||||
|
StdLib util.Uint160
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Management = state.CreateNativeContractHash(nativenames.Management)
|
||||||
|
Ledger = state.CreateNativeContractHash(nativenames.Ledger)
|
||||||
|
Neo = state.CreateNativeContractHash(nativenames.Neo)
|
||||||
|
Gas = state.CreateNativeContractHash(nativenames.Gas)
|
||||||
|
Policy = state.CreateNativeContractHash(nativenames.Policy)
|
||||||
|
Oracle = state.CreateNativeContractHash(nativenames.Oracle)
|
||||||
|
Designation = state.CreateNativeContractHash(nativenames.Designation)
|
||||||
|
Notary = state.CreateNativeContractHash(nativenames.Notary)
|
||||||
|
CryptoLib = state.CreateNativeContractHash(nativenames.CryptoLib)
|
||||||
|
StdLib = state.CreateNativeContractHash(nativenames.StdLib)
|
||||||
|
}
|
|
@ -14,6 +14,20 @@ const (
|
||||||
StdLib = "StdLib"
|
StdLib = "StdLib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// All contains the list of all native contract names ordered by the contract ID.
|
||||||
|
var All = []string{
|
||||||
|
Management,
|
||||||
|
StdLib,
|
||||||
|
CryptoLib,
|
||||||
|
Ledger,
|
||||||
|
Neo,
|
||||||
|
Gas,
|
||||||
|
Policy,
|
||||||
|
Designation,
|
||||||
|
Oracle,
|
||||||
|
Notary,
|
||||||
|
}
|
||||||
|
|
||||||
// IsValid checks if the name is a valid native contract's name.
|
// IsValid checks if the name is a valid native contract's name.
|
||||||
func IsValid(name string) bool {
|
func IsValid(name string) bool {
|
||||||
return name == Management ||
|
return name == Management ||
|
||||||
|
|
|
@ -73,7 +73,7 @@ func copyNotaryCache(src, dst *NotaryCache) {
|
||||||
// newNotary returns Notary native contract.
|
// newNotary returns Notary native contract.
|
||||||
func newNotary() *Notary {
|
func newNotary() *Notary {
|
||||||
n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID)}
|
n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID)}
|
||||||
defer n.UpdateHash()
|
defer n.BuildHFSpecificMD(n.ActiveIn())
|
||||||
|
|
||||||
desc := newDescriptor("onNEP17Payment", smartcontract.VoidType,
|
desc := newDescriptor("onNEP17Payment", smartcontract.VoidType,
|
||||||
manifest.NewParameter("from", smartcontract.Hash160Type),
|
manifest.NewParameter("from", smartcontract.Hash160Type),
|
||||||
|
@ -127,7 +127,11 @@ func (n *Notary) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes Notary native contract and implements the Contract interface.
|
// Initialize initializes Notary native contract and implements the Contract interface.
|
||||||
func (n *Notary) Initialize(ic *interop.Context) error {
|
func (n *Notary) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
|
if hf != n.ActiveIn() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
|
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
|
||||||
|
|
||||||
cache := &NotaryCache{
|
cache := &NotaryCache{
|
||||||
|
|
|
@ -118,7 +118,7 @@ func newOracle() *Oracle {
|
||||||
ContractMD: *interop.NewContractMD(nativenames.Oracle, oracleContractID),
|
ContractMD: *interop.NewContractMD(nativenames.Oracle, oracleContractID),
|
||||||
newRequests: make(map[uint64]*state.OracleRequest),
|
newRequests: make(map[uint64]*state.OracleRequest),
|
||||||
}
|
}
|
||||||
defer o.UpdateHash()
|
defer o.BuildHFSpecificMD(o.ActiveIn())
|
||||||
|
|
||||||
o.oracleScript = CreateOracleResponseScript(o.Hash)
|
o.oracleScript = CreateOracleResponseScript(o.Hash)
|
||||||
|
|
||||||
|
@ -139,12 +139,17 @@ func newOracle() *Oracle {
|
||||||
md = newMethodAndPrice(o.verify, 1<<15, callflag.NoneFlag)
|
md = newMethodAndPrice(o.verify, 1<<15, callflag.NoneFlag)
|
||||||
o.AddMethod(md, desc)
|
o.AddMethod(md, desc)
|
||||||
|
|
||||||
o.AddEvent("OracleRequest", manifest.NewParameter("Id", smartcontract.IntegerType),
|
eDesc := newEventDescriptor("OracleRequest", manifest.NewParameter("Id", smartcontract.IntegerType),
|
||||||
manifest.NewParameter("RequestContract", smartcontract.Hash160Type),
|
manifest.NewParameter("RequestContract", smartcontract.Hash160Type),
|
||||||
manifest.NewParameter("Url", smartcontract.StringType),
|
manifest.NewParameter("Url", smartcontract.StringType),
|
||||||
manifest.NewParameter("Filter", smartcontract.StringType))
|
manifest.NewParameter("Filter", smartcontract.StringType))
|
||||||
o.AddEvent("OracleResponse", manifest.NewParameter("Id", smartcontract.IntegerType),
|
eMD := newEvent(eDesc)
|
||||||
|
o.AddEvent(eMD)
|
||||||
|
|
||||||
|
eDesc = newEventDescriptor("OracleResponse", manifest.NewParameter("Id", smartcontract.IntegerType),
|
||||||
manifest.NewParameter("OriginalTx", smartcontract.Hash256Type))
|
manifest.NewParameter("OriginalTx", smartcontract.Hash256Type))
|
||||||
|
eMD = newEvent(eDesc)
|
||||||
|
o.AddEvent(eMD)
|
||||||
|
|
||||||
desc = newDescriptor("getPrice", smartcontract.IntegerType)
|
desc = newDescriptor("getPrice", smartcontract.IntegerType)
|
||||||
md = newMethodAndPrice(o.getPrice, 1<<15, callflag.ReadStates)
|
md = newMethodAndPrice(o.getPrice, 1<<15, callflag.ReadStates)
|
||||||
|
@ -241,7 +246,9 @@ func (o *Oracle) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes an Oracle contract.
|
// Initialize initializes an Oracle contract.
|
||||||
func (o *Oracle) Initialize(ic *interop.Context) error {
|
func (o *Oracle) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
|
switch hf {
|
||||||
|
case o.ActiveIn():
|
||||||
setIntWithKey(o.ID, ic.DAO, prefixRequestID, 0)
|
setIntWithKey(o.ID, ic.DAO, prefixRequestID, 0)
|
||||||
setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, DefaultOracleRequestPrice)
|
setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, DefaultOracleRequestPrice)
|
||||||
|
|
||||||
|
@ -249,6 +256,18 @@ func (o *Oracle) Initialize(ic *interop.Context) error {
|
||||||
requestPrice: int64(DefaultOracleRequestPrice),
|
requestPrice: int64(DefaultOracleRequestPrice),
|
||||||
}
|
}
|
||||||
ic.DAO.SetCache(o.ID, cache)
|
ic.DAO.SetCache(o.ID, cache)
|
||||||
|
default:
|
||||||
|
orc, _ := o.Module.Load().(*OracleService)
|
||||||
|
if orc != nil && *orc != nil {
|
||||||
|
md, ok := newMD.GetMethod(manifest.MethodVerify, -1)
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Errorf("%s method not found", manifest.MethodVerify))
|
||||||
|
}
|
||||||
|
(*orc).UpdateNativeContract(newMD.NEF.Script, o.GetOracleResponseScript(),
|
||||||
|
o.Hash, md.MD.Offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ func newPolicy(p2pSigExtensionsEnabled bool) *Policy {
|
||||||
ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID),
|
ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID),
|
||||||
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
|
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
|
||||||
}
|
}
|
||||||
defer p.UpdateHash()
|
defer p.BuildHFSpecificMD(p.ActiveIn())
|
||||||
|
|
||||||
desc := newDescriptor("getFeePerByte", smartcontract.IntegerType)
|
desc := newDescriptor("getFeePerByte", smartcontract.IntegerType)
|
||||||
md := newMethodAndPrice(p.getFeePerByte, 1<<15, callflag.ReadStates)
|
md := newMethodAndPrice(p.getFeePerByte, 1<<15, callflag.ReadStates)
|
||||||
|
@ -169,7 +169,11 @@ func (p *Policy) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes Policy native contract and implements the Contract interface.
|
// Initialize initializes Policy native contract and implements the Contract interface.
|
||||||
func (p *Policy) Initialize(ic *interop.Context) error {
|
func (p *Policy) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
|
if hf != p.ActiveIn() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
setIntWithKey(p.ID, ic.DAO, feePerByteKey, defaultFeePerByte)
|
setIntWithKey(p.ID, ic.DAO, feePerByteKey, defaultFeePerByte)
|
||||||
setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, defaultExecFeeFactor)
|
setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, defaultExecFeeFactor)
|
||||||
setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice)
|
setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice)
|
||||||
|
|
|
@ -46,7 +46,7 @@ var (
|
||||||
|
|
||||||
func newStd() *Std {
|
func newStd() *Std {
|
||||||
s := &Std{ContractMD: *interop.NewContractMD(nativenames.StdLib, stdContractID)}
|
s := &Std{ContractMD: *interop.NewContractMD(nativenames.StdLib, stdContractID)}
|
||||||
defer s.UpdateHash()
|
defer s.BuildHFSpecificMD(s.ActiveIn())
|
||||||
|
|
||||||
desc := newDescriptor("serialize", smartcontract.ByteArrayType,
|
desc := newDescriptor("serialize", smartcontract.ByteArrayType,
|
||||||
manifest.NewParameter("item", smartcontract.AnyType))
|
manifest.NewParameter("item", smartcontract.AnyType))
|
||||||
|
@ -439,7 +439,7 @@ func (s *Std) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize implements the Contract interface.
|
// Initialize implements the Contract interface.
|
||||||
func (s *Std) Initialize(ic *interop.Context) error {
|
func (s *Std) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,12 @@ package for more details on NEP-17 interface.
|
||||||
package gas
|
package gas
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Hash stores the hash of the native GAS contract.
|
// Hash stores the hash of the native GAS contract.
|
||||||
var Hash = state.CreateNativeContractHash(nativenames.Gas)
|
var Hash = nativehashes.Gas
|
||||||
|
|
||||||
// NewReader creates a NEP-17 reader for the GAS contract.
|
// NewReader creates a NEP-17 reader for the GAS contract.
|
||||||
func NewReader(invoker nep17.Invoker) *nep17.TokenReader {
|
func NewReader(invoker nep17.Invoker) *nep17.TokenReader {
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||||
|
@ -75,7 +75,7 @@ type HashesIterator struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash stores the hash of the native ContractManagement contract.
|
// Hash stores the hash of the native ContractManagement contract.
|
||||||
var Hash = state.CreateNativeContractHash(nativenames.Management)
|
var Hash = nativehashes.Management
|
||||||
|
|
||||||
// Event is the event emitted on contract deployment/update/destroy.
|
// Event is the event emitted on contract deployment/update/destroy.
|
||||||
// Even though these events are different they all have the same field inside.
|
// Even though these events are different they all have the same field inside.
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -97,7 +97,7 @@ type ValidatorIterator struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash stores the hash of the native NEOToken contract.
|
// Hash stores the hash of the native NEOToken contract.
|
||||||
var Hash = state.CreateNativeContractHash(nativenames.Neo)
|
var Hash = nativehashes.Neo
|
||||||
|
|
||||||
// NewReader creates an instance of ContractReader to get data from the NEO
|
// NewReader creates an instance of ContractReader to get data from the NEO
|
||||||
// contract.
|
// contract.
|
||||||
|
|
|
@ -13,8 +13,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||||
|
@ -76,7 +75,7 @@ type OnNEP17PaymentData struct {
|
||||||
var _ = stackitem.Convertible(&OnNEP17PaymentData{})
|
var _ = stackitem.Convertible(&OnNEP17PaymentData{})
|
||||||
|
|
||||||
// Hash stores the hash of the native Notary contract.
|
// Hash stores the hash of the native Notary contract.
|
||||||
var Hash = state.CreateNativeContractHash(nativenames.Notary)
|
var Hash = nativehashes.Notary
|
||||||
|
|
||||||
// NewReader creates an instance of ContractReader to get data from the Notary
|
// NewReader creates an instance of ContractReader to get data from the Notary
|
||||||
// contract.
|
// contract.
|
||||||
|
|
|
@ -9,8 +9,7 @@ package oracle
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||||
|
@ -32,7 +31,7 @@ type Actor interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash stores the hash of the native OracleContract contract.
|
// Hash stores the hash of the native OracleContract contract.
|
||||||
var Hash = state.CreateNativeContractHash(nativenames.Oracle)
|
var Hash = nativehashes.Oracle
|
||||||
|
|
||||||
const priceSetter = "setPrice"
|
const priceSetter = "setPrice"
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,7 @@ various methods to perform PolicyContract state-changing calls.
|
||||||
package policy
|
package policy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||||
|
@ -34,7 +33,7 @@ type Actor interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash stores the hash of the native PolicyContract contract.
|
// Hash stores the hash of the native PolicyContract contract.
|
||||||
var Hash = state.CreateNativeContractHash(nativenames.Policy)
|
var Hash = nativehashes.Policy
|
||||||
|
|
||||||
const (
|
const (
|
||||||
execFeeSetter = "setExecFeeFactor"
|
execFeeSetter = "setExecFeeFactor"
|
||||||
|
|
|
@ -7,9 +7,8 @@ various methods to perform the only RoleManagement state-changing call.
|
||||||
package rolemgmt
|
package rolemgmt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||||
|
@ -32,7 +31,7 @@ type Actor interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash stores the hash of the native RoleManagement contract.
|
// Hash stores the hash of the native RoleManagement contract.
|
||||||
var Hash = state.CreateNativeContractHash(nativenames.Designation)
|
var Hash = nativehashes.Designation
|
||||||
|
|
||||||
const designateMethod = "designateAsRole"
|
const designateMethod = "designateAsRole"
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,9 @@ type (
|
||||||
Oracle struct {
|
Oracle struct {
|
||||||
Config
|
Config
|
||||||
|
|
||||||
// This fields are readonly thus not protected by mutex.
|
// Native Oracle contract related information that may be updated on Oracle contract
|
||||||
oracleHash util.Uint160
|
// update.
|
||||||
|
oracleInfoLock sync.RWMutex
|
||||||
oracleResponse []byte
|
oracleResponse []byte
|
||||||
oracleScript []byte
|
oracleScript []byte
|
||||||
verifyOffset int
|
verifyOffset int
|
||||||
|
@ -277,10 +278,11 @@ drain:
|
||||||
|
|
||||||
// UpdateNativeContract updates native oracle contract info for tx verification.
|
// UpdateNativeContract updates native oracle contract info for tx verification.
|
||||||
func (o *Oracle) UpdateNativeContract(script, resp []byte, h util.Uint160, verifyOffset int) {
|
func (o *Oracle) UpdateNativeContract(script, resp []byte, h util.Uint160, verifyOffset int) {
|
||||||
|
o.oracleInfoLock.Lock()
|
||||||
|
defer o.oracleInfoLock.Unlock()
|
||||||
|
|
||||||
o.oracleScript = bytes.Clone(script)
|
o.oracleScript = bytes.Clone(script)
|
||||||
o.oracleResponse = bytes.Clone(resp)
|
o.oracleResponse = bytes.Clone(resp)
|
||||||
|
|
||||||
o.oracleHash = h
|
|
||||||
o.verifyOffset = verifyOffset
|
o.verifyOffset = verifyOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -102,7 +103,12 @@ func checkUTF8(v []byte) ([]byte, error) {
|
||||||
|
|
||||||
// CreateResponseTx creates an unsigned oracle response transaction.
|
// CreateResponseTx creates an unsigned oracle response transaction.
|
||||||
func (o *Oracle) CreateResponseTx(gasForResponse int64, vub uint32, resp *transaction.OracleResponse) (*transaction.Transaction, error) {
|
func (o *Oracle) CreateResponseTx(gasForResponse int64, vub uint32, resp *transaction.OracleResponse) (*transaction.Transaction, error) {
|
||||||
tx := transaction.New(o.oracleResponse, 0)
|
var respScript []byte
|
||||||
|
o.oracleInfoLock.RLock()
|
||||||
|
respScript = o.oracleResponse
|
||||||
|
o.oracleInfoLock.RUnlock()
|
||||||
|
|
||||||
|
tx := transaction.New(respScript, 0)
|
||||||
tx.Nonce = uint32(resp.ID)
|
tx.Nonce = uint32(resp.ID)
|
||||||
tx.ValidUntilBlock = vub
|
tx.ValidUntilBlock = vub
|
||||||
tx.Attributes = []transaction.Attribute{{
|
tx.Attributes = []transaction.Attribute{{
|
||||||
|
@ -113,7 +119,7 @@ func (o *Oracle) CreateResponseTx(gasForResponse int64, vub uint32, resp *transa
|
||||||
oracleSignContract := o.getOracleSignContract()
|
oracleSignContract := o.getOracleSignContract()
|
||||||
tx.Signers = []transaction.Signer{
|
tx.Signers = []transaction.Signer{
|
||||||
{
|
{
|
||||||
Account: o.oracleHash,
|
Account: nativehashes.Oracle,
|
||||||
Scopes: transaction.None,
|
Scopes: transaction.None,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -166,8 +172,11 @@ func (o *Oracle) testVerify(tx *transaction.Transaction) (int64, bool, error) {
|
||||||
return 0, false, fmt.Errorf("failed to create test VM: %w", err)
|
return 0, false, fmt.Errorf("failed to create test VM: %w", err)
|
||||||
}
|
}
|
||||||
ic.VM.GasLimit = o.Chain.GetMaxVerificationGAS()
|
ic.VM.GasLimit = o.Chain.GetMaxVerificationGAS()
|
||||||
ic.VM.LoadScriptWithHash(o.oracleScript, o.oracleHash, callflag.ReadOnly)
|
|
||||||
|
o.oracleInfoLock.RLock()
|
||||||
|
ic.VM.LoadScriptWithHash(o.oracleScript, nativehashes.Oracle, callflag.ReadOnly)
|
||||||
ic.VM.Context().Jump(o.verifyOffset)
|
ic.VM.Context().Jump(o.verifyOffset)
|
||||||
|
o.oracleInfoLock.RUnlock()
|
||||||
|
|
||||||
ok := isVerifyOk(ic)
|
ok := isVerifyOk(ic)
|
||||||
return ic.VM.GasConsumed(), ok, nil
|
return ic.VM.GasConsumed(), ok, nil
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
@ -825,7 +826,7 @@ func TestCalculateNetworkFee(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Set fee per Conflicts attribute.
|
// Set fee per Conflicts attribute.
|
||||||
script, err := smartcontract.CreateCallScript(state.CreateNativeContractHash(nativenames.Policy), "setAttributeFee", byte(transaction.ConflictsT), conflictsFee)
|
script, err := smartcontract.CreateCallScript(nativehashes.Policy, "setAttributeFee", byte(transaction.ConflictsT), conflictsFee)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
txSetFee := transaction.New(script, 1_0000_0000)
|
txSetFee := transaction.New(script, 1_0000_0000)
|
||||||
txSetFee.ValidUntilBlock = chain.BlockHeight() + 1
|
txSetFee.ValidUntilBlock = chain.BlockHeight() + 1
|
||||||
|
|
|
@ -89,7 +89,7 @@ const (
|
||||||
faultedTxHashLE = "82279bfe9bada282ca0f8cb8e0bb124b921af36f00c69a518320322c6f4fef60"
|
faultedTxHashLE = "82279bfe9bada282ca0f8cb8e0bb124b921af36f00c69a518320322c6f4fef60"
|
||||||
faultedTxBlock uint32 = 23
|
faultedTxBlock uint32 = 23
|
||||||
invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA"
|
invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA"
|
||||||
block20StateRootLE = "637aac452ef781dee7ac5e898a1edf4d3c5b6420288ea5232dad620f39d2152a"
|
block20StateRootLE = "397c69adbc0201d59623fa913bfff4a2da25c792c484d1d278c061709f2c21cf"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
Loading…
Reference in a new issue