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:
Roman Khimov 2020-12-08 18:28:00 +03:00
parent 090bee8624
commit ad3547783d
15 changed files with 123 additions and 99 deletions

Binary file not shown.

View file

@ -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)

View file

@ -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())

View file

@ -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 {

View file

@ -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) {

View file

@ -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,
}

View file

@ -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) {

View file

@ -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

View file

@ -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

View file

@ -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()

View 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
}

View file

@ -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 {

View file

@ -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())
}

View file

@ -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())
})

Binary file not shown.