[#119] nns: Integrate FrostfsID into NNS

Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
This commit is contained in:
Alexander Chuprov 2024-10-29 17:47:22 +03:00
parent 9f4ed3d9c6
commit a7106d0cc6
Signed by: achuprov
GPG key ID: 2D916FFD803B0EDD
3 changed files with 109 additions and 3 deletions

40
nns/frostfsid.go Normal file
View file

@ -0,0 +1,40 @@
package nns
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/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
const FrostfsIDNNSName = "frostfsid.frostfs"
const (
FrostfsIDNNSTLDPermissionKey = "nns-allow-register-tld"
FrostfsIDTLDRegistrationAllowed = "allow"
FrostfsIDTLDRegistrationDisallowed = "disallow"
)
func checkFrostfsID(ctx storage.Context, addr interop.Hash160) bool {
if len(addr) == 0 {
return false
}
frostfsIDAddress := getRecordsByType(ctx, []byte(tokenIDFromName(FrostfsIDNNSName)), FrostfsIDNNSName, TXT)
if len(frostfsIDAddress) == 0 {
return false
}
decodedBytes := std.Base58Decode([]byte(frostfsIDAddress[1]))
if len(decodedBytes) < 21 || management.GetContract(decodedBytes[1:21]) == nil {
return false
}
if res := contract.Call(decodedBytes[1:21], "getSubjectKV", contract.ReadOnly, addr, FrostfsIDNNSTLDPermissionKey).(string); res == FrostfsIDTLDRegistrationAllowed {
return true
}
return false
}

View file

@ -344,7 +344,7 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str
tldBytes := storage.Get(ctx, tldKey)
if countZone == 1 {
checkCommittee()
checkCommitteeAndFrostfsID(ctx, owner)
if tldBytes != nil {
panic("TLD already exists")
}
@ -924,6 +924,14 @@ func isValid(address interop.Hash160) bool {
return address != nil && len(address) == interop.Hash160Len
}
// checkCommitteeAndFrostfsID panics if the script container is not signed by the committee.
// or if the owner does not have permission in FrostfsID.
func checkCommitteeAndFrostfsID(ctx storage.Context, owner interop.Hash160) {
if !checkFrostfsID(ctx, owner) {
checkCommittee()
}
}
// checkCommittee panics if the script container is not signed by the committee.
func checkCommittee() {
committee := neo.GetCommittee()

View file

@ -12,10 +12,12 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
"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/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
)
@ -23,8 +25,7 @@ const nnsPath = "../nns"
const msPerYear = 365 * 24 * time.Hour / time.Millisecond
func newNNSInvoker(t *testing.T, addRoot bool) *neotest.ContractInvoker {
e := newExecutor(t)
func deployNNS(t *testing.T, e *neotest.Executor, addRoot bool) *neotest.ContractInvoker {
ctr := neotest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
e.DeployContract(t, ctr, nil)
@ -36,9 +37,32 @@ func newNNSInvoker(t *testing.T, addRoot bool) *neotest.ContractInvoker {
"com", c.CommitteeHash,
"myemail@frostfs.info", refresh, retry, expire, ttl)
}
return c
}
func newNNSInvoker(t *testing.T, addRoot bool) *neotest.ContractInvoker {
e := newExecutor(t)
return deployNNS(t, e, addRoot)
}
func newNNSInvokerWithFrostfsID(t *testing.T, addRoot bool) (*neotest.ContractInvoker, *neotest.ContractInvoker) {
e := newExecutor(t)
c := deployNNS(t, e, addRoot)
frostfdID := deployFrostFSIDContract(t, e, e.CommitteeHash)
c.Invoke(t, true, "register",
nns.FrostfsIDNNSName, c.CommitteeHash,
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
c.Invoke(t, stackitem.Null{}, "addRecord",
nns.FrostfsIDNNSName, int64(nns.TXT), frostfdID.StringLE())
c.Invoke(t, stackitem.Null{}, "addRecord",
nns.FrostfsIDNNSName, int64(nns.TXT), address.Uint160ToString(frostfdID))
return e.NewInvoker(c.Hash), e.CommitteeInvoker(frostfdID)
}
func TestNNSGeneric(t *testing.T) {
c := newNNSInvoker(t, false)
@ -527,6 +551,40 @@ func TestNNSSetAdmin(t *testing.T) {
"testdomain.com", int64(nns.TXT), "will be added")
}
func TestNNS_Frostfsid(t *testing.T) {
nnsInv, f := newNNSInvokerWithFrostfsID(t, false)
acc, err := wallet.NewAccount()
require.NoError(t, err)
nnsUserInv := nnsInv.NewInvoker(nnsInv.Hash, newSigner(t, nnsInv, acc))
nnsUserInv.InvokeFail(t, "not witnessed by committee", "register",
"ddd", acc.ScriptHash(),
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
nnsUserInv.InvokeFail(t, "not witnessed by committee", "register",
"testdomain.kz", acc.ScriptHash(),
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
f.Invoke(t, stackitem.Null{}, createSubjectMethod, "", acc.PrivateKey().PublicKey().Bytes())
nnsUserInv.InvokeFail(t, "not witnessed by committee", "register",
"testdomain.kz", acc.ScriptHash(),
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
f.Invoke(t, stackitem.Null{}, setSubjectKVMethod, acc.ScriptHash(), nns.FrostfsIDNNSTLDPermissionKey, nns.FrostfsIDTLDRegistrationAllowed)
nnsUserInv.Invoke(t, true, "register",
"testdomain.kz", acc.ScriptHash(),
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
f.Invoke(t, stackitem.Null{}, setSubjectKVMethod, acc.ScriptHash(), nns.FrostfsIDNNSTLDPermissionKey, nns.FrostfsIDTLDRegistrationDisallowed)
nnsUserInv.InvokeFail(t, "not witnessed by committee", "register",
"testdomain.uz", acc.ScriptHash(),
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
}
func TestNNSIsAvailable(t *testing.T) {
c := newNNSInvoker(t, false)