native: drop Neo.Native.Deploy, move contract init to management contract
The contract is almost a stub at the moment, though it does deploy other contracts.
This commit is contained in:
parent
090bee8624
commit
ad3547783d
15 changed files with 123 additions and 99 deletions
BIN
cli/testdata/chain50x2.acc
vendored
BIN
cli/testdata/chain50x2.acc
vendored
Binary file not shown.
|
@ -600,18 +600,16 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
|
|||
}
|
||||
writeBuf.Reset()
|
||||
|
||||
if block.Index > 0 {
|
||||
aer, err := bc.runPersist(bc.contracts.GetPersistScript(), block, cache, trigger.OnPersist)
|
||||
if err != nil {
|
||||
return fmt.Errorf("onPersist failed: %w", err)
|
||||
}
|
||||
appExecResults = append(appExecResults, aer)
|
||||
err = cache.PutAppExecResult(aer, writeBuf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to store onPersist exec result: %w", err)
|
||||
}
|
||||
writeBuf.Reset()
|
||||
aer, err := bc.runPersist(bc.contracts.GetPersistScript(), block, cache, trigger.OnPersist)
|
||||
if err != nil {
|
||||
return fmt.Errorf("onPersist failed: %w", err)
|
||||
}
|
||||
appExecResults = append(appExecResults, aer)
|
||||
err = cache.PutAppExecResult(aer, writeBuf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to store onPersist exec result: %w", err)
|
||||
}
|
||||
writeBuf.Reset()
|
||||
|
||||
for _, tx := range block.Transactions {
|
||||
if err := cache.StoreAsTransaction(tx, block.Index, writeBuf); err != nil {
|
||||
|
@ -673,7 +671,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
|
|||
}
|
||||
}
|
||||
|
||||
aer, err := bc.runPersist(bc.contracts.GetPostPersistScript(), block, cache, trigger.PostPersist)
|
||||
aer, err = bc.runPersist(bc.contracts.GetPostPersistScript(), block, cache, trigger.PostPersist)
|
||||
if err != nil {
|
||||
return fmt.Errorf("postPersist failed: %w", err)
|
||||
}
|
||||
|
@ -1677,7 +1675,7 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160,
|
|||
v.LoadScriptWithHash(cs.Script, hash, smartcontract.ReadStates|smartcontract.AllowCall)
|
||||
v.Jump(v.Context(), md.Offset)
|
||||
|
||||
if cs.ID < 0 {
|
||||
if cs.ID <= 0 {
|
||||
w := io.NewBufBinWriter()
|
||||
emit.Opcodes(w.BinWriter, opcode.DEPTH, opcode.PACK)
|
||||
emit.String(w.BinWriter, manifest.MethodVerify)
|
||||
|
|
|
@ -713,11 +713,18 @@ func TestVerifyTx(t *testing.T) {
|
|||
require.True(t, errors.Is(bc.VerifyTx(tx), ErrHasConflicts))
|
||||
})
|
||||
t.Run("attribute on-chain conflict", func(t *testing.T) {
|
||||
b, err := bc.GetBlock(bc.GetHeaderHash(0))
|
||||
require.NoError(t, err)
|
||||
conflictsHash := b.Transactions[0].Hash()
|
||||
tx := getConflictsTx(conflictsHash)
|
||||
require.Error(t, bc.VerifyTx(tx))
|
||||
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.ValidUntilBlock = 4242
|
||||
tx.Signers = []transaction.Signer{{
|
||||
Account: testchain.MultisigScriptHash(),
|
||||
Scopes: transaction.None,
|
||||
}}
|
||||
require.NoError(t, testchain.SignTx(bc, tx))
|
||||
b := bc.newBlock(tx)
|
||||
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
txConflict := getConflictsTx(tx.Hash())
|
||||
require.Error(t, bc.VerifyTx(txConflict))
|
||||
})
|
||||
t.Run("positive", func(t *testing.T) {
|
||||
tx := getConflictsTx(random.Uint256())
|
||||
|
|
|
@ -163,7 +163,7 @@ func (dao *Simple) DeleteContractState(hash util.Uint160) error {
|
|||
|
||||
// GetAndUpdateNextContractID returns id for the next contract and increases stored ID.
|
||||
func (dao *Simple) GetAndUpdateNextContractID() (int32, error) {
|
||||
var id int32
|
||||
var id = int32(1)
|
||||
key := storage.SYSContractID.Bytes()
|
||||
data, err := dao.Store.Get(key)
|
||||
if err == nil {
|
||||
|
|
|
@ -73,13 +73,13 @@ func TestSimple_GetAndUpdateNextContractID(t *testing.T) {
|
|||
dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
|
||||
id, err := dao.GetAndUpdateNextContractID()
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 0, id)
|
||||
id, err = dao.GetAndUpdateNextContractID()
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 1, id)
|
||||
id, err = dao.GetAndUpdateNextContractID()
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 2, id)
|
||||
id, err = dao.GetAndUpdateNextContractID()
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 3, id)
|
||||
}
|
||||
|
||||
func TestPutGetAppExecResult(t *testing.T) {
|
||||
|
|
|
@ -69,7 +69,6 @@ const (
|
|||
NeoCryptoSHA256 = "Neo.Crypto.SHA256"
|
||||
NeoCryptoRIPEMD160 = "Neo.Crypto.RIPEMD160"
|
||||
NeoNativeCall = "Neo.Native.Call"
|
||||
NeoNativeDeploy = "Neo.Native.Deploy"
|
||||
)
|
||||
|
||||
var names = []string{
|
||||
|
@ -140,5 +139,4 @@ var names = []string{
|
|||
NeoCryptoSHA256,
|
||||
NeoCryptoRIPEMD160,
|
||||
NeoNativeCall,
|
||||
NeoNativeDeploy,
|
||||
}
|
||||
|
|
|
@ -718,7 +718,7 @@ func TestContractCreate(t *testing.T) {
|
|||
|
||||
ic.Tx = transaction.New(netmode.UnitTestNet, []byte{1}, 0)
|
||||
ic.Tx.Signers = append(ic.Tx.Signers, transaction.Signer{Account: sender})
|
||||
cs.ID = 0
|
||||
cs.ID = 1
|
||||
cs.Hash = state.CreateContractHash(sender, cs.Script)
|
||||
|
||||
t.Run("missing NEF", func(t *testing.T) {
|
||||
|
|
|
@ -126,7 +126,6 @@ var neoInterops = []interop.Function{
|
|||
{Name: interopnames.NeoCryptoSHA256, Func: crypto.Sha256, Price: 1000000, ParamCount: 1},
|
||||
{Name: interopnames.NeoCryptoRIPEMD160, Func: crypto.RipeMD160, Price: 1000000, ParamCount: 1},
|
||||
{Name: interopnames.NeoNativeCall, Func: native.Call, Price: 0, RequiredFlags: smartcontract.AllowCall, ParamCount: 1, DisallowCallback: true},
|
||||
{Name: interopnames.NeoNativeDeploy, Func: native.Deploy, Price: 0, RequiredFlags: smartcontract.WriteStates, DisallowCallback: true},
|
||||
}
|
||||
|
||||
// initIDinInteropsSlice initializes IDs from names in one given
|
||||
|
|
|
@ -15,13 +15,14 @@ const reservedContractID = -100
|
|||
|
||||
// Contracts is a set of registered native contracts.
|
||||
type Contracts struct {
|
||||
NEO *NEO
|
||||
GAS *GAS
|
||||
Policy *Policy
|
||||
Oracle *Oracle
|
||||
Designate *Designate
|
||||
Notary *Notary
|
||||
Contracts []interop.Contract
|
||||
Management *Management
|
||||
NEO *NEO
|
||||
GAS *GAS
|
||||
Policy *Policy
|
||||
Oracle *Oracle
|
||||
Designate *Designate
|
||||
Notary *Notary
|
||||
Contracts []interop.Contract
|
||||
// persistScript is vm script which executes "onPersist" method of every native contract.
|
||||
persistScript []byte
|
||||
// postPersistScript is vm script which executes "postPersist" method of every native contract.
|
||||
|
@ -54,6 +55,10 @@ func (cs *Contracts) ByName(name string) interop.Contract {
|
|||
func NewContracts(p2pSigExtensionsEnabled bool) *Contracts {
|
||||
cs := new(Contracts)
|
||||
|
||||
mgmt := newManagement()
|
||||
cs.Management = mgmt
|
||||
cs.Contracts = append(cs.Contracts, mgmt)
|
||||
|
||||
gas := newGAS()
|
||||
neo := newNEO()
|
||||
neo.GAS = gas
|
||||
|
|
|
@ -5,36 +5,10 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
)
|
||||
|
||||
// Deploy deploys native contract.
|
||||
func Deploy(ic *interop.Context) error {
|
||||
if ic.Block == nil || ic.Block.Index != 0 {
|
||||
return errors.New("native contracts can be deployed only at 0 block")
|
||||
}
|
||||
|
||||
for _, native := range ic.Natives {
|
||||
md := native.Metadata()
|
||||
|
||||
cs := &state.Contract{
|
||||
ID: md.ContractID,
|
||||
Hash: md.Hash,
|
||||
Script: md.Script,
|
||||
Manifest: md.Manifest,
|
||||
}
|
||||
if err := ic.DAO.PutContractState(cs); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := native.Initialize(ic); err != nil {
|
||||
return fmt.Errorf("initializing %s native contract: %w", md.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Call calls specified native contract method.
|
||||
func Call(ic *interop.Context) error {
|
||||
name := ic.VM.Estack().Pop().String()
|
||||
|
|
67
pkg/core/native/management.go
Normal file
67
pkg/core/native/management.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
)
|
||||
|
||||
// Management is contract-managing native contract.
|
||||
type Management struct {
|
||||
interop.ContractMD
|
||||
}
|
||||
|
||||
const (
|
||||
managementName = "Neo Contract Management"
|
||||
prefixContract = 8
|
||||
prefixNextAvailableId = 15
|
||||
)
|
||||
|
||||
// newManagement creates new Management native contract.
|
||||
func newManagement() *Management {
|
||||
var m = &Management{ContractMD: *interop.NewContractMD(managementName)}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Metadata implements Contract interface.
|
||||
func (m *Management) Metadata() *interop.ContractMD {
|
||||
return &m.ContractMD
|
||||
}
|
||||
|
||||
// OnPersist implements Contract interface.
|
||||
func (m *Management) OnPersist(ic *interop.Context) error {
|
||||
if ic.Block.Index != 0 { // We're only deploying at 0 at the moment.
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, native := range ic.Natives {
|
||||
md := native.Metadata()
|
||||
|
||||
cs := &state.Contract{
|
||||
ID: md.ContractID,
|
||||
Hash: md.Hash,
|
||||
Script: md.Script,
|
||||
Manifest: md.Manifest,
|
||||
}
|
||||
if err := ic.DAO.PutContractState(cs); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := native.Initialize(ic); err != nil {
|
||||
return fmt.Errorf("initializing %s native contract: %w", md.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PostPersist implements Contract interface.
|
||||
func (m *Management) PostPersist(_ *interop.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Initialize implements Contract interface.
|
||||
func (m *Management) Initialize(_ *interop.Context) error {
|
||||
return nil
|
||||
}
|
|
@ -5,16 +5,12 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||
"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/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
)
|
||||
|
||||
|
@ -56,10 +52,8 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error)
|
|||
}
|
||||
|
||||
b := &block.Block{
|
||||
Base: base,
|
||||
Transactions: []*transaction.Transaction{
|
||||
deployNativeContracts(cfg.Magic),
|
||||
},
|
||||
Base: base,
|
||||
Transactions: []*transaction.Transaction{},
|
||||
ConsensusData: block.ConsensusData{
|
||||
PrimaryIndex: 0,
|
||||
Nonce: 2083236893,
|
||||
|
@ -70,27 +64,6 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error)
|
|||
return b, nil
|
||||
}
|
||||
|
||||
func deployNativeContracts(magic netmode.Magic) *transaction.Transaction {
|
||||
buf := io.NewBufBinWriter()
|
||||
emit.Syscall(buf.BinWriter, interopnames.NeoNativeDeploy)
|
||||
script := buf.Bytes()
|
||||
tx := transaction.New(magic, script, 0)
|
||||
tx.Nonce = 0
|
||||
tx.Signers = []transaction.Signer{
|
||||
{
|
||||
Account: hash.Hash160([]byte{byte(opcode.PUSH1)}),
|
||||
Scopes: transaction.None,
|
||||
},
|
||||
}
|
||||
tx.Scripts = []transaction.Witness{
|
||||
{
|
||||
InvocationScript: []byte{},
|
||||
VerificationScript: []byte{byte(opcode.PUSH1)},
|
||||
},
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
func validatorsFromConfig(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) {
|
||||
vs, err := committeeFromConfig(cfg)
|
||||
if err != nil {
|
||||
|
|
|
@ -17,7 +17,7 @@ func TestGenesisBlockMainNet(t *testing.T) {
|
|||
block, err := createGenesisBlock(cfg.ProtocolConfiguration)
|
||||
require.NoError(t, err)
|
||||
|
||||
expect := "ecaee33262f1bc7c7c28f2b25b54a5d61d50670871f45c0c6fe755a40cbde4a8"
|
||||
expect := "00c6803707b564153d444bfcdf3a13325fc96dda55cc8a740bbd543a1d752fda"
|
||||
assert.Equal(t, expect, block.Hash().StringLE())
|
||||
}
|
||||
|
||||
|
|
|
@ -57,8 +57,8 @@ type rpcTestCase struct {
|
|||
}
|
||||
|
||||
const testContractHash = "743ed26f78e29ecd595535b74a943b1f9ccbc444"
|
||||
const deploymentTxHash = "9ecf1273fe0d8868cc024c8270b569a12edd7ea9d675c88554b937134efb03f8"
|
||||
const genesisBlockHash = "a496577895eb8c227bb866dc44f99f21c0cf06417ca8f2a877cc5d761a50dac0"
|
||||
const deploymentTxHash = "a72dfaebf9543964d74e803723dae6a86196e0915ae9d76b3cc57c3b2e3e8c49"
|
||||
const genesisBlockHash = "0542f4350c6e236d0509bcd98188b0034bfbecc1a0c7fcdb8e4295310d468b70"
|
||||
|
||||
const verifyContractHash = "a2eb22340979804cb10cc1add0b8822c201f4d8a"
|
||||
const verifyContractAVM = "570300412d51083021700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740"
|
||||
|
@ -89,8 +89,9 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
res, ok := acc.(*result.ApplicationLog)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, genesisBlockHash, res.Container.StringLE())
|
||||
assert.Equal(t, 1, len(res.Executions))
|
||||
assert.Equal(t, trigger.PostPersist, res.Executions[0].Trigger) // no onPersist for genesis block
|
||||
assert.Equal(t, 2, len(res.Executions))
|
||||
assert.Equal(t, trigger.OnPersist, res.Executions[0].Trigger)
|
||||
assert.Equal(t, trigger.PostPersist, res.Executions[1].Trigger)
|
||||
assert.Equal(t, vm.HaltState, res.Executions[0].VMState)
|
||||
},
|
||||
},
|
||||
|
@ -103,7 +104,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
require.True(t, ok)
|
||||
assert.Equal(t, genesisBlockHash, res.Container.StringLE())
|
||||
assert.Equal(t, 1, len(res.Executions))
|
||||
assert.Equal(t, trigger.PostPersist, res.Executions[0].Trigger) // no onPersist for genesis block
|
||||
assert.Equal(t, trigger.PostPersist, res.Executions[0].Trigger)
|
||||
assert.Equal(t, vm.HaltState, res.Executions[0].VMState)
|
||||
},
|
||||
},
|
||||
|
@ -115,7 +116,9 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
res, ok := acc.(*result.ApplicationLog)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, genesisBlockHash, res.Container.StringLE())
|
||||
assert.Equal(t, 0, len(res.Executions)) // no onPersist for genesis block
|
||||
assert.Equal(t, 1, len(res.Executions))
|
||||
assert.Equal(t, trigger.OnPersist, res.Executions[0].Trigger)
|
||||
assert.Equal(t, vm.HaltState, res.Executions[0].VMState)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1075,7 +1078,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
|||
})
|
||||
|
||||
t.Run("getrawtransaction", func(t *testing.T) {
|
||||
block, _ := chain.GetBlock(chain.GetHeaderHash(0))
|
||||
block, _ := chain.GetBlock(chain.GetHeaderHash(1))
|
||||
tx := block.Transactions[0]
|
||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s"]}"`, tx.Hash().StringLE())
|
||||
body := doRPCCall(rpc, httpSrv.URL, t)
|
||||
|
@ -1090,7 +1093,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
|||
})
|
||||
|
||||
t.Run("getrawtransaction 2 arguments", func(t *testing.T) {
|
||||
block, _ := chain.GetBlock(chain.GetHeaderHash(0))
|
||||
block, _ := chain.GetBlock(chain.GetHeaderHash(1))
|
||||
tx := block.Transactions[0]
|
||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 0]}"`, tx.Hash().StringLE())
|
||||
body := doRPCCall(rpc, httpSrv.URL, t)
|
||||
|
@ -1105,7 +1108,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
|||
})
|
||||
|
||||
t.Run("getrawtransaction 2 arguments, verbose", func(t *testing.T) {
|
||||
block, _ := chain.GetBlock(chain.GetHeaderHash(0))
|
||||
block, _ := chain.GetBlock(chain.GetHeaderHash(1))
|
||||
TXHash := block.Transactions[0].Hash()
|
||||
_ = block.Transactions[0].Size()
|
||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 1]}"`, TXHash.StringLE())
|
||||
|
@ -1116,7 +1119,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
|||
require.NoErrorf(t, err, "could not parse response: %s", txOut)
|
||||
|
||||
assert.Equal(t, *block.Transactions[0], actual.Transaction)
|
||||
assert.Equal(t, 9, actual.Confirmations)
|
||||
assert.Equal(t, 8, actual.Confirmations)
|
||||
assert.Equal(t, TXHash, actual.Transaction.Hash())
|
||||
})
|
||||
|
||||
|
|
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
Binary file not shown.
Loading…
Reference in a new issue