Skip to content

Commit

Permalink
Merge pull request #1724 from nspcc-dev/rpc/getnative
Browse files Browse the repository at this point in the history
Implement `getnativecontracts` RPC
  • Loading branch information
roman-khimov authored Feb 11, 2021
2 parents 29b1581 + 4f1bea0 commit 2ee755e
Show file tree
Hide file tree
Showing 28 changed files with 320 additions and 220 deletions.
5 changes: 5 additions & 0 deletions internal/fakechain/fakechain.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ func (*FakeChain) IsExtensibleAllowed(uint160 util.Uint160) bool {
return true
}

// GetNatives implements blockchainer.Blockchainer interface.
func (*FakeChain) GetNatives() []state.NativeContract {
panic("TODO")
}

// GetNotaryDepositExpiration implements Blockchainer interface.
func (chain *FakeChain) GetNotaryDepositExpiration(acc util.Uint160) uint32 {
if chain.NotaryDepositExpiration != 0 {
Expand Down
16 changes: 10 additions & 6 deletions pkg/compiler/interop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,17 +166,21 @@ func TestAppCall(t *testing.T) {
innerNef, err := nef.NewFile(inner)
require.NoError(t, err)
return &state.Contract{
Hash: ih,
NEF: *innerNef,
Manifest: *m,
ContractBase: state.ContractBase{
Hash: ih,
NEF: *innerNef,
Manifest: *m,
},
}, nil
} else if h.Equals(barH) {
barNef, err := nef.NewFile(barCtr)
require.NoError(t, err)
return &state.Contract{
Hash: barH,
NEF: *barNef,
Manifest: *mBar,
ContractBase: state.ContractBase{
Hash: barH,
NEF: *barNef,
Manifest: *mBar,
},
}, nil
}
return nil, errors.New("not found")
Expand Down
15 changes: 12 additions & 3 deletions pkg/core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ func (bc *Blockchain) processNEP17Transfer(cache *dao.Cached, h util.Uint256, b
var id int32
nativeContract := bc.contracts.ByHash(sc)
if nativeContract != nil {
id = nativeContract.Metadata().ContractID
id = nativeContract.Metadata().ID
} else {
assetContract, err := bc.contracts.Management.GetContract(cache, sc)
if err != nil {
Expand Down Expand Up @@ -1018,7 +1018,7 @@ func (bc *Blockchain) GetUtilityTokenBalance(acc util.Uint160) *big.Int {
if err != nil {
return big.NewInt(0)
}
balance := bs.Trackers[bc.contracts.GAS.ContractID].Balance
balance := bs.Trackers[bc.contracts.GAS.ID].Balance
return &balance
}

Expand All @@ -1029,7 +1029,7 @@ func (bc *Blockchain) GetGoverningTokenBalance(acc util.Uint160) (*big.Int, uint
if err != nil {
return big.NewInt(0), 0
}
neo := bs.Trackers[bc.contracts.NEO.ContractID]
neo := bs.Trackers[bc.contracts.NEO.ID]
return &neo.Balance, neo.LastUpdatedBlock
}

Expand Down Expand Up @@ -1243,6 +1243,15 @@ func (bc *Blockchain) GetNativeContractScriptHash(name string) (util.Uint160, er
return util.Uint160{}, errors.New("Unknown native contract")
}

// GetNatives returns list of native contracts.
func (bc *Blockchain) GetNatives() []state.NativeContract {
res := make([]state.NativeContract, 0, len(bc.contracts.Contracts))
for _, c := range bc.contracts.Contracts {
res = append(res, c.Metadata().NativeContract)
}
return res
}

// GetConfig returns the config stored in the blockchain.
func (bc *Blockchain) GetConfig() config.ProtocolConfiguration {
return bc.config
Expand Down
1 change: 1 addition & 0 deletions pkg/core/blockchainer/blockchainer.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type Blockchainer interface {
GetAppExecResults(util.Uint256, trigger.Type) ([]state.AppExecResult, error)
GetNotaryDepositExpiration(acc util.Uint160) uint32
GetNativeContractScriptHash(string) (util.Uint160, error)
GetNatives() []state.NativeContract
GetNextBlockValidators() ([]*keys.PublicKey, error)
GetNEP17Balances(util.Uint160) *state.NEP17Balances
GetNotaryContractScriptHash() util.Uint160
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ func checkFAULTState(t *testing.T, result *state.AppExecResult) {
}

func checkBalanceOf(t *testing.T, chain *Blockchain, addr util.Uint160, expected int) {
balance := chain.GetNEP17Balances(addr).Trackers[chain.contracts.GAS.ContractID]
balance := chain.GetNEP17Balances(addr).Trackers[chain.contracts.GAS.ID]
require.Equal(t, int64(expected), balance.Balance.Int64())
}

Expand Down
16 changes: 7 additions & 9 deletions pkg/core/interop/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,9 @@ type Contract interface {

// ContractMD represents native contract instance.
type ContractMD struct {
Manifest manifest.Manifest
Name string
ContractID int32
NEF nef.File
Hash util.Uint160
Methods map[MethodAndArgCount]MethodAndPrice
state.NativeContract
Name string
Methods map[MethodAndArgCount]MethodAndPrice
}

// MethodAndArgCount represents method's signature.
Expand All @@ -116,11 +113,12 @@ type MethodAndArgCount struct {
// NewContractMD returns Contract with the specified list of methods.
func NewContractMD(name string, id int32) *ContractMD {
c := &ContractMD{
Name: name,
ContractID: id,
Methods: make(map[MethodAndArgCount]MethodAndPrice),
Name: name,
Methods: make(map[MethodAndArgCount]MethodAndPrice),
}

c.ID = id

// NEF is now stored in contract state and affects state dump.
// Therefore values are taken from C# node.
c.NEF.Header.Compiler = "neo-core-v3.0"
Expand Down
10 changes: 6 additions & 4 deletions pkg/core/interop_neo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,12 @@ func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.C
ne, err := nef.NewFile(script)
require.NoError(t, err)
contractState := &state.Contract{
NEF: *ne,
Hash: hash.Hash160(script),
Manifest: *m,
ID: 123,
ContractBase: state.ContractBase{
NEF: *ne,
Hash: hash.Hash160(script),
Manifest: *m,
ID: 123,
},
}

chain := newTestChain(t)
Expand Down
36 changes: 22 additions & 14 deletions pkg/core/interop_system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ func TestContractIsStandard(t *testing.T) {
pub := priv.PublicKey()
ne, err := nef.NewFile(pub.GetVerificationScript())
require.NoError(t, err)
err = chain.contracts.Management.PutContractState(ic.DAO, &state.Contract{ID: 42, Hash: pub.GetScriptHash(), NEF: *ne})
err = chain.contracts.Management.PutContractState(ic.DAO,
&state.Contract{ContractBase: state.ContractBase{ID: 42, Hash: pub.GetScriptHash(), NEF: *ne}})
require.NoError(t, err)

v.Estack().PushVal(pub.GetScriptHash().BytesBE())
Expand All @@ -79,7 +80,8 @@ func TestContractIsStandard(t *testing.T) {
script := []byte{byte(opcode.PUSHT)}
ne, err := nef.NewFile(script)
require.NoError(t, err)
require.NoError(t, chain.contracts.Management.PutContractState(ic.DAO, &state.Contract{ID: 24, Hash: hash.Hash160(script), NEF: *ne}))
require.NoError(t, chain.contracts.Management.PutContractState(ic.DAO,
&state.Contract{ContractBase: state.ContractBase{ID: 24, Hash: hash.Hash160(script), NEF: *ne}}))

v.Estack().PushVal(crypto.Hash160(script).BytesBE())
require.NoError(t, contractIsStandard(ic))
Expand Down Expand Up @@ -540,9 +542,11 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
m.Permissions[1].Methods.Add("method")

cs := &state.Contract{
Hash: h,
Manifest: *m,
ID: 42,
ContractBase: state.ContractBase{
Hash: h,
Manifest: *m,
ID: 42,
},
}
ne, err := nef.NewFile(script)
if err != nil {
Expand Down Expand Up @@ -582,10 +586,12 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
}

return cs, &state.Contract{
NEF: *ne,
Hash: hash.Hash160(currScript),
Manifest: *m,
ID: 123,
ContractBase: state.ContractBase{
NEF: *ne,
Hash: hash.Hash160(currScript),
Manifest: *m,
ID: 123,
},
}
}

Expand Down Expand Up @@ -906,11 +912,13 @@ func TestRuntimeCheckWitness(t *testing.T) {
ne, err := nef.NewFile(contractScript)
require.NoError(t, err)
contractState := &state.Contract{
ID: 15,
Hash: contractScriptHash,
NEF: *ne,
Manifest: manifest.Manifest{
Groups: []manifest.Group{{PublicKey: pk.PublicKey(), Signature: make([]byte, keys.SignatureLen)}},
ContractBase: state.ContractBase{
ID: 15,
Hash: contractScriptHash,
NEF: *ne,
Manifest: manifest.Manifest{
Groups: []manifest.Group{{PublicKey: pk.PublicKey(), Signature: make([]byte, keys.SignatureLen)}},
},
},
}
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, contractState))
Expand Down
6 changes: 3 additions & 3 deletions pkg/core/native/designate.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ func (s *Designate) GetDesignatedByRole(d dao.DAO, r Role, index uint32) (keys.P
return val.nodes.Copy(), val.height, nil
}
}
kvs, err := d.GetStorageItemsWithPrefix(s.ContractID, []byte{byte(r)})
kvs, err := d.GetStorageItemsWithPrefix(s.ID, []byte{byte(r)})
if err != nil {
return nil, 0, err
}
Expand Down Expand Up @@ -309,14 +309,14 @@ func (s *Designate) DesignateAsRole(ic *interop.Context, r Role, pubs keys.Publi
key[0] = byte(r)
binary.BigEndian.PutUint32(key[1:], ic.Block.Index+1)

si := ic.DAO.GetStorageItem(s.ContractID, key)
si := ic.DAO.GetStorageItem(s.ID, key)
if si != nil {
return ErrAlreadyDesignated
}
sort.Sort(pubs)
s.rolesChangedFlag.Store(true)
si = &state.StorageItem{Value: NodeList(pubs).Bytes()}
return ic.DAO.PutStorageItem(s.ContractID, key, si)
return ic.DAO.PutStorageItem(s.ID, key, si)
}

func (s *Designate) getRole(item stackitem.Item) (Role, bool) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/native/interop.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func Call(ic *interop.Context) error {
id := int32(ic.VM.Estack().Pop().BigInt().Int64())
var c interop.Contract
for _, ctr := range ic.Natives {
if ctr.Metadata().ContractID == id {
if ctr.Metadata().ID == id {
c = ctr
break
}
Expand Down
37 changes: 18 additions & 19 deletions pkg/core/native/management.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func (m *Management) GetContract(d dao.DAO, hash util.Uint160) (*state.Contract,
func (m *Management) getContractFromDAO(d dao.DAO, hash util.Uint160) (*state.Contract, error) {
contract := new(state.Contract)
key := makeContractKey(hash)
err := getSerializableFromDAO(m.ContractID, d, key, contract)
err := getSerializableFromDAO(m.ID, d, key, contract)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -263,7 +263,7 @@ func (m *Management) markUpdated(h util.Uint160) {
func (m *Management) Deploy(d dao.DAO, sender util.Uint160, neff *nef.File, manif *manifest.Manifest) (*state.Contract, error) {
h := state.CreateContractHash(sender, neff.Checksum, manif.Name)
key := makeContractKey(h)
si := d.GetStorageItem(m.ContractID, key)
si := d.GetStorageItem(m.ID, key)
if si != nil {
return nil, errors.New("contract already exists")
}
Expand All @@ -280,10 +280,12 @@ func (m *Management) Deploy(d dao.DAO, sender util.Uint160, neff *nef.File, mani
return nil, err
}
newcontract := &state.Contract{
ID: id,
Hash: h,
NEF: *neff,
Manifest: *manif,
ContractBase: state.ContractBase{
ID: id,
Hash: h,
NEF: *neff,
Manifest: *manif,
},
}
err = m.PutContractState(d, newcontract)
if err != nil {
Expand Down Expand Up @@ -371,7 +373,7 @@ func (m *Management) Destroy(d dao.DAO, hash util.Uint160) error {
return err
}
key := makeContractKey(hash)
err = d.DeleteStorageItem(m.ContractID, key)
err = d.DeleteStorageItem(m.ID, key)
if err != nil {
return err
}
Expand Down Expand Up @@ -399,7 +401,7 @@ func (m *Management) getMinimumDeploymentFee(ic *interop.Context, args []stackit

// GetMinimumDeploymentFee returns the minimum required fee for contract deploy.
func (m *Management) GetMinimumDeploymentFee(dao dao.DAO) int64 {
return getIntWithKey(m.ContractID, dao, keyMinimumDeploymentFee)
return getIntWithKey(m.ID, dao, keyMinimumDeploymentFee)
}

func (m *Management) setMinimumDeploymentFee(ic *interop.Context, args []stackitem.Item) stackitem.Item {
Expand All @@ -410,7 +412,7 @@ func (m *Management) setMinimumDeploymentFee(ic *interop.Context, args []stackit
if !m.NEO.checkCommittee(ic) {
panic("invalid committee signature")
}
err := setIntWithKey(m.ContractID, ic.DAO, keyMinimumDeploymentFee, int64(value))
err := setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, int64(value))
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -451,10 +453,7 @@ func (m *Management) OnPersist(ic *interop.Context) error {
md := native.Metadata()

cs := &state.Contract{
ID: md.ContractID,
Hash: md.Hash,
NEF: md.NEF,
Manifest: md.Manifest,
ContractBase: md.ContractBase,
}
err := m.PutContractState(ic.DAO, cs)
if err != nil {
Expand All @@ -479,7 +478,7 @@ func (m *Management) InitializeCache(d dao.DAO) error {
defer m.mtx.Unlock()

var initErr error
d.Seek(m.ContractID, []byte{prefixContract}, func(_, v []byte) {
d.Seek(m.ID, []byte{prefixContract}, func(_, v []byte) {
var r = io.NewBinReaderFromBuf(v)
var si state.StorageItem
si.DecodeBinary(r)
Expand Down Expand Up @@ -521,16 +520,16 @@ func (m *Management) PostPersist(ic *interop.Context) error {

// Initialize implements Contract interface.
func (m *Management) Initialize(ic *interop.Context) error {
if err := setIntWithKey(m.ContractID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee); err != nil {
if err := setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee); err != nil {
return err
}
return setIntWithKey(m.ContractID, ic.DAO, keyNextAvailableID, 1)
return setIntWithKey(m.ID, ic.DAO, keyNextAvailableID, 1)
}

// PutContractState saves given contract state into given DAO.
func (m *Management) PutContractState(d dao.DAO, cs *state.Contract) error {
key := makeContractKey(cs.Hash)
if err := putSerializableToDAO(m.ContractID, d, key, cs); err != nil {
if err := putSerializableToDAO(m.ID, d, key, cs); err != nil {
return err
}
m.markUpdated(cs.Hash)
Expand All @@ -541,7 +540,7 @@ func (m *Management) PutContractState(d dao.DAO, cs *state.Contract) error {
}

func (m *Management) getNextContractID(d dao.DAO) (int32, error) {
si := d.GetStorageItem(m.ContractID, keyNextAvailableID)
si := d.GetStorageItem(m.ID, keyNextAvailableID)
if si == nil {
return 0, errors.New("nextAvailableID is not initialized")

Expand All @@ -550,7 +549,7 @@ func (m *Management) getNextContractID(d dao.DAO) (int32, error) {
ret := int32(id.Int64())
id.Add(id, intOne)
si.Value = bigint.ToPreallocatedBytes(id, si.Value)
return ret, d.PutStorageItem(m.ContractID, keyNextAvailableID, si)
return ret, d.PutStorageItem(m.ID, keyNextAvailableID, si)
}

func (m *Management) emitNotification(ic *interop.Context, name string, hash util.Uint160) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/native/management_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func TestManagement_Initialize(t *testing.T) {
t.Run("invalid contract state", func(t *testing.T) {
d := dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
mgmt := newManagement()
require.NoError(t, d.PutStorageItem(mgmt.ContractID, []byte{prefixContract}, &state.StorageItem{Value: []byte{0xFF}}))
require.NoError(t, d.PutStorageItem(mgmt.ID, []byte{prefixContract}, &state.StorageItem{Value: []byte{0xFF}}))
require.Error(t, mgmt.InitializeCache(d))
})
}
Loading

0 comments on commit 2ee755e

Please sign in to comment.