Skip to content

Commit

Permalink
*: make address params optional, take them from NNS
Browse files Browse the repository at this point in the history
They're still there to override/keep compatibility, but they're not mandatory
now. Closes #325.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
  • Loading branch information
roman-khimov committed Nov 23, 2023
1 parent 238986e commit 41e483f
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 109 deletions.
7 changes: 5 additions & 2 deletions alphabet/alphabet_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,11 @@ func _deploy(data any, isUpdate bool) {
panic("non-notary mode is not supported anymore")
}

if len(args.addrNetmap) != interop.Hash160Len || len(args.addrProxy) != interop.Hash160Len {
panic("incorrect length of contract script hash")
if len(args.addrNetmap) != interop.Hash160Len {
args.addrNetmap = common.ResolveFSContract("netmap")
}
if len(args.addrProxy) != interop.Hash160Len {
args.addrProxy = common.ResolveFSContract("proxy")
}

storage.Put(ctx, netmapKey, args.addrNetmap)
Expand Down
47 changes: 47 additions & 0 deletions common/nns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package common

import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
)

// NNSID is the ID of the NNS contract in NeoFS networks. It's always deployed
// first.
const NNSID = 1

// ContractTLD is the default domain used by NeoFS contracts.
const ContractTLD = "neofs"

// InferNNSHash returns NNS contract hash by its well-known ID or panics if
// it can't be resolved.
func InferNNSHash() interop.Hash160 {
var nns = management.GetContractByID(1)
if nns == nil {
panic("no NNS contract")
}
return nns.Hash
}

// ResolveFSContract returns contract hash by name as registered in NNS or
// panics if it can't be resolved.
func ResolveFSContract(name string) interop.Hash160 {
var nns = InferNNSHash()
res := contract.Call(nns, "resolve", contract.ReadOnly, name+"."+ContractTLD, 16 /*TXT*/)
strs := res.([]string)
for _, str := range strs {
if len(str) == interop.Hash160Len*2 {
var h = make([]byte, interop.Hash160Len)
for i := 0; i < interop.Hash160Len; i++ {
ii := (interop.Hash160Len - i - 1) * 2
h[i] = byte(std.Atoi(str[ii:ii+2], 16))
}
return h
} else {
return address.ToHash160(str)
}
}
panic("failed to resolve contract")
}
15 changes: 11 additions & 4 deletions container/container_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,17 @@ func _deploy(data any, isUpdate bool) {
panic("non-notary mode is not supported anymore")
}

if len(args.addrNetmap) != interop.Hash160Len ||
len(args.addrBalance) != interop.Hash160Len ||
len(args.addrID) != interop.Hash160Len {
panic("incorrect length of contract script hash")
if len(args.addrNNS) != interop.Hash160Len {
args.addrNNS = common.InferNNSHash()
}
if len(args.addrNetmap) != interop.Hash160Len {
args.addrNetmap = common.ResolveFSContract("netmap")
}
if len(args.addrBalance) != interop.Hash160Len {
args.addrBalance = common.ResolveFSContract("balance")
}
if len(args.addrID) != interop.Hash160Len {
args.addrID = common.ResolveFSContract("neofsid")
}

storage.Put(ctx, netmapContractKey, args.addrNetmap)
Expand Down
102 changes: 60 additions & 42 deletions tests/alphabet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (

const alphabetPath = "../alphabet"

func deployAlphabetContract(t *testing.T, e *neotest.Executor, addrNetmap, addrProxy util.Uint160, name string, index, total int64) util.Uint160 {
func deployAlphabetContract(t *testing.T, e *neotest.Executor, addrNetmap, addrProxy *util.Uint160, name string, index, total int64) util.Uint160 {
c := neotest.CompileFile(t, e.CommitteeHash, alphabetPath, path.Join(alphabetPath, "config.yml"))

args := make([]any, 6)
Expand All @@ -32,23 +32,27 @@ func deployAlphabetContract(t *testing.T, e *neotest.Executor, addrNetmap, addrP
return c.Hash
}

func newAlphabetInvoker(t *testing.T) (*neotest.Executor, *neotest.ContractInvoker) {
func newAlphabetInvoker(t *testing.T, autohashes bool) (*neotest.Executor, *neotest.ContractInvoker) {
e := newExecutor(t)

ctrNNS := neotest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
ctrNetmap := neotest.CompileFile(t, e.CommitteeHash, netmapPath, path.Join(netmapPath, "config.yml"))
ctrBalance := neotest.CompileFile(t, e.CommitteeHash, balancePath, path.Join(balancePath, "config.yml"))
ctrContainer := neotest.CompileFile(t, e.CommitteeHash, containerPath, path.Join(containerPath, "config.yml"))
ctrProxy := neotest.CompileFile(t, e.CommitteeHash, proxyPath, path.Join(proxyPath, "config.yml"))

e.DeployContract(t, ctrNNS, nil)
nnsHash := deployDefaultNNS(t, e)
deployNetmapContract(t, e, ctrBalance.Hash, ctrContainer.Hash,
container.RegistrationFeeKey, int64(containerFee),
container.AliasFeeKey, int64(containerAliasFee))
deployBalanceContract(t, e, ctrNetmap.Hash, ctrContainer.Hash)
deployContainerContract(t, e, ctrNetmap.Hash, ctrBalance.Hash, ctrNNS.Hash)
deployContainerContract(t, e, &ctrNetmap.Hash, &ctrBalance.Hash, &nnsHash)
deployProxyContract(t, e)
hash := deployAlphabetContract(t, e, ctrNetmap.Hash, ctrProxy.Hash, "Az", 0, 1)

var addrNetmap, addrProxy *util.Uint160
if !autohashes {
addrNetmap, addrProxy = &ctrNetmap.Hash, &ctrProxy.Hash
}
hash := deployAlphabetContract(t, e, addrNetmap, addrProxy, "Az", 0, 1)

alphabet := getAlphabetAcc(t, e)

Expand All @@ -58,64 +62,78 @@ func newAlphabetInvoker(t *testing.T) (*neotest.Executor, *neotest.ContractInvok
}

func TestEmit(t *testing.T) {
_, c := newAlphabetInvoker(t)
for autohashes, name := range map[bool]string{
false: "standard deploy",
true: "deploy with no hashes",
} {
t.Run(name, func(t *testing.T) {
_, c := newAlphabetInvoker(t, autohashes)

const method = "emit"
const method = "emit"

alphabet := getAlphabetAcc(t, c.Executor)
alphabet := getAlphabetAcc(t, c.Executor)

cCommittee := c.WithSigners(neotest.NewSingleSigner(alphabet))
cCommittee.InvokeFail(t, "no gas to emit", method)
cCommittee := c.WithSigners(neotest.NewSingleSigner(alphabet))
cCommittee.InvokeFail(t, "no gas to emit", method)

transferNeoToContract(t, c)
transferNeoToContract(t, c)

cCommittee.Invoke(t, stackitem.Null{}, method)
cCommittee.Invoke(t, stackitem.Null{}, method)

notAlphabet := c.NewAccount(t)
cNotAlphabet := c.WithSigners(notAlphabet)
notAlphabet := c.NewAccount(t)
cNotAlphabet := c.WithSigners(notAlphabet)

cNotAlphabet.InvokeFail(t, "invalid invoker", method)
cNotAlphabet.InvokeFail(t, "invalid invoker", method)
})
}
}

func TestVote(t *testing.T) {
e, c := newAlphabetInvoker(t)
for autohashes, name := range map[bool]string{
false: "standard deploy",
true: "deploy with no hashes",
} {
t.Run(name, func(t *testing.T) {
e, c := newAlphabetInvoker(t, autohashes)

const method = "vote"
const method = "vote"

newAlphabet := c.NewAccount(t)
newAlphabetPub, ok := vm.ParseSignatureContract(newAlphabet.Script())
require.True(t, ok)
cNewAlphabet := c.WithSigners(newAlphabet)
newAlphabet := c.NewAccount(t)
newAlphabetPub, ok := vm.ParseSignatureContract(newAlphabet.Script())
require.True(t, ok)
cNewAlphabet := c.WithSigners(newAlphabet)

cNewAlphabet.InvokeFail(t, common.ErrAlphabetWitnessFailed, method, int64(0), []any{newAlphabetPub})
c.InvokeFail(t, "invalid epoch", method, int64(1), []any{newAlphabetPub})
cNewAlphabet.InvokeFail(t, common.ErrAlphabetWitnessFailed, method, int64(0), []any{newAlphabetPub})
c.InvokeFail(t, "invalid epoch", method, int64(1), []any{newAlphabetPub})

setAlphabetRole(t, e, newAlphabetPub)
transferNeoToContract(t, c)
setAlphabetRole(t, e, newAlphabetPub)
transferNeoToContract(t, c)

neoSH := e.NativeHash(t, nativenames.Neo)
neoInvoker := c.CommitteeInvoker(neoSH)
neoSH := e.NativeHash(t, nativenames.Neo)
neoInvoker := c.CommitteeInvoker(neoSH)

gasSH := e.NativeHash(t, nativenames.Gas)
gasInvoker := e.CommitteeInvoker(gasSH)
gasSH := e.NativeHash(t, nativenames.Gas)
gasInvoker := e.CommitteeInvoker(gasSH)

res, err := gasInvoker.TestInvoke(t, "balanceOf", gasInvoker.Committee.ScriptHash())
require.NoError(t, err)
res, err := gasInvoker.TestInvoke(t, "balanceOf", gasInvoker.Committee.ScriptHash())
require.NoError(t, err)

// transfer some GAS to the new alphabet node
gasInvoker.Invoke(t, stackitem.NewBool(true), "transfer", gasInvoker.Committee.ScriptHash(), newAlphabet.ScriptHash(), res.Top().BigInt().Int64()/2, nil)
// transfer some GAS to the new alphabet node
gasInvoker.Invoke(t, stackitem.NewBool(true), "transfer", gasInvoker.Committee.ScriptHash(), newAlphabet.ScriptHash(), res.Top().BigInt().Int64()/2, nil)

newInvoker := neoInvoker.WithSigners(newAlphabet)
newInvoker := neoInvoker.WithSigners(newAlphabet)

newInvoker.Invoke(t, stackitem.NewBool(true), "registerCandidate", newAlphabetPub)
c.Invoke(t, stackitem.Null{}, method, int64(0), []any{newAlphabetPub})
newInvoker.Invoke(t, stackitem.NewBool(true), "registerCandidate", newAlphabetPub)
c.Invoke(t, stackitem.Null{}, method, int64(0), []any{newAlphabetPub})

// wait one block util
// a new committee is accepted
c.AddNewBlock(t)
// wait one block util
// a new committee is accepted
c.AddNewBlock(t)

cNewAlphabet.Invoke(t, stackitem.Null{}, "emit")
c.InvokeFail(t, "invalid invoker", "emit")
cNewAlphabet.Invoke(t, stackitem.Null{}, "emit")
c.InvokeFail(t, "invalid invoker", "emit")
})
}
}

func transferNeoToContract(t *testing.T, invoker *neotest.ContractInvoker) {
Expand Down
1 change: 1 addition & 0 deletions tests/balance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func deployBalanceContract(t *testing.T, e *neotest.Executor, addrNetmap, addrCo
args[2] = addrContainer

e.DeployContract(t, c, args)
regContractNNS(t, e, "balance", c.Hash)
return c.Hash
}

Expand Down
Loading

0 comments on commit 41e483f

Please sign in to comment.