[#119] nns: Integrate FrostfsID into NNS
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
This commit is contained in:
parent
db79f79f72
commit
b6983ae888
3 changed files with 117 additions and 19 deletions
40
nns/frostfsid.go
Normal file
40
nns/frostfsid.go
Normal 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], "getValueForSubjectKey", contract.ReadOnly, addr, FrostfsIDNNSTLDPermissionKey).(string); res == FrostfsIDTLDRegistrationAllowed {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
|
@ -344,7 +344,7 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str
|
||||||
|
|
||||||
tldBytes := storage.Get(ctx, tldKey)
|
tldBytes := storage.Get(ctx, tldKey)
|
||||||
if countZone == 1 {
|
if countZone == 1 {
|
||||||
checkCommittee()
|
checkCommitteeAndFrostfsID(ctx, owner)
|
||||||
if tldBytes != nil {
|
if tldBytes != nil {
|
||||||
panic("TLD already exists")
|
panic("TLD already exists")
|
||||||
}
|
}
|
||||||
|
@ -381,7 +381,11 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str
|
||||||
if !isValid(owner) {
|
if !isValid(owner) {
|
||||||
panic("invalid owner")
|
panic("invalid owner")
|
||||||
}
|
}
|
||||||
common.CheckOwnerWitness(owner)
|
|
||||||
|
if !checkFrostfsID(ctx, owner) {
|
||||||
|
common.CheckOwnerWitness(owner)
|
||||||
|
}
|
||||||
|
|
||||||
runtime.BurnGas(GetPrice())
|
runtime.BurnGas(GetPrice())
|
||||||
var (
|
var (
|
||||||
tokenKey = getTokenKey([]byte(name))
|
tokenKey = getTokenKey([]byte(name))
|
||||||
|
@ -924,6 +928,14 @@ func isValid(address interop.Hash160) bool {
|
||||||
return address != nil && len(address) == interop.Hash160Len
|
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.
|
// checkCommittee panics if the script container is not signed by the committee.
|
||||||
func checkCommittee() {
|
func checkCommittee() {
|
||||||
committee := neo.GetCommittee()
|
committee := neo.GetCommittee()
|
||||||
|
|
|
@ -12,10 +12,12 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
|
"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/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"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/neotest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,7 +25,7 @@ const nnsPath = "../nns"
|
||||||
|
|
||||||
const msPerYear = 365 * 24 * time.Hour / time.Millisecond
|
const msPerYear = 365 * 24 * time.Hour / time.Millisecond
|
||||||
|
|
||||||
func newNNSInvoker(t *testing.T, addRoot bool) *neotest.ContractInvoker {
|
func newNNSInvoker(t *testing.T, addRoot, needFrostfsID bool) (*neotest.ContractInvoker, *neotest.ContractInvoker) {
|
||||||
e := newExecutor(t)
|
e := newExecutor(t)
|
||||||
ctr := neotest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
|
ctr := neotest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
|
||||||
e.DeployContract(t, ctr, nil)
|
e.DeployContract(t, ctr, nil)
|
||||||
|
@ -36,11 +38,27 @@ func newNNSInvoker(t *testing.T, addRoot bool) *neotest.ContractInvoker {
|
||||||
"com", c.CommitteeHash,
|
"com", c.CommitteeHash,
|
||||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||||
}
|
}
|
||||||
return c
|
|
||||||
|
if !needFrostfsID {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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 c, e.CommitteeInvoker(frostfdID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNNSGeneric(t *testing.T) {
|
func TestNNSGeneric(t *testing.T) {
|
||||||
c := newNNSInvoker(t, false)
|
c, _ := newNNSInvoker(t, false, false)
|
||||||
|
|
||||||
c.Invoke(t, "NNS", "symbol")
|
c.Invoke(t, "NNS", "symbol")
|
||||||
c.Invoke(t, 0, "decimals")
|
c.Invoke(t, 0, "decimals")
|
||||||
|
@ -48,7 +66,7 @@ func TestNNSGeneric(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNNSRegisterTLD(t *testing.T) {
|
func TestNNSRegisterTLD(t *testing.T) {
|
||||||
c := newNNSInvoker(t, false)
|
c, _ := newNNSInvoker(t, false, false)
|
||||||
|
|
||||||
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
||||||
|
|
||||||
|
@ -91,7 +109,7 @@ func TestNNSRegisterTLD(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNNSRegister(t *testing.T) {
|
func TestNNSRegister(t *testing.T) {
|
||||||
c := newNNSInvoker(t, false)
|
c, _ := newNNSInvoker(t, false, false)
|
||||||
|
|
||||||
accTop := c.NewAccount(t)
|
accTop := c.NewAccount(t)
|
||||||
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
||||||
|
@ -243,7 +261,7 @@ func TestNNSRegister(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteDomain(t *testing.T) {
|
func TestDeleteDomain(t *testing.T) {
|
||||||
c := newNNSInvoker(t, false)
|
c, _ := newNNSInvoker(t, false, false)
|
||||||
|
|
||||||
acc1 := c.NewAccount(t)
|
acc1 := c.NewAccount(t)
|
||||||
c1 := c.WithSigners(c.Committee, acc1)
|
c1 := c.WithSigners(c.Committee, acc1)
|
||||||
|
@ -306,7 +324,7 @@ func TestDeleteDomain(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGlobalDomain(t *testing.T) {
|
func TestGlobalDomain(t *testing.T) {
|
||||||
c := newNNSInvoker(t, false)
|
c, _ := newNNSInvoker(t, false, false)
|
||||||
|
|
||||||
accTop := c.NewAccount(t)
|
accTop := c.NewAccount(t)
|
||||||
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
||||||
|
@ -349,7 +367,7 @@ func TestGlobalDomain(t *testing.T) {
|
||||||
c.InvokeFail(t, "global domain is already taken", "isAvailable", "dom.testdomain.com")
|
c.InvokeFail(t, "global domain is already taken", "isAvailable", "dom.testdomain.com")
|
||||||
}
|
}
|
||||||
func TestTLDRecord(t *testing.T) {
|
func TestTLDRecord(t *testing.T) {
|
||||||
c := newNNSInvoker(t, true)
|
c, _ := newNNSInvoker(t, true, false)
|
||||||
c.Invoke(t, stackitem.Null{}, "addRecord",
|
c.Invoke(t, stackitem.Null{}, "addRecord",
|
||||||
"com", int64(nns.A), "1.2.3.4")
|
"com", int64(nns.A), "1.2.3.4")
|
||||||
|
|
||||||
|
@ -358,7 +376,7 @@ func TestTLDRecord(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNNSRegisterMulti(t *testing.T) {
|
func TestNNSRegisterMulti(t *testing.T) {
|
||||||
c := newNNSInvoker(t, true)
|
c, _ := newNNSInvoker(t, true, false)
|
||||||
|
|
||||||
newArgs := func(domain string, account neotest.Signer) []any {
|
newArgs := func(domain string, account neotest.Signer) []any {
|
||||||
return []any{
|
return []any{
|
||||||
|
@ -407,7 +425,7 @@ func TestNNSRegisterMulti(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNNSUpdateSOA(t *testing.T) {
|
func TestNNSUpdateSOA(t *testing.T) {
|
||||||
c := newNNSInvoker(t, true)
|
c, _ := newNNSInvoker(t, true, false)
|
||||||
|
|
||||||
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
||||||
c.Invoke(t, true, "register",
|
c.Invoke(t, true, "register",
|
||||||
|
@ -429,7 +447,7 @@ func TestNNSUpdateSOA(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNNSGetAllRecords(t *testing.T) {
|
func TestNNSGetAllRecords(t *testing.T) {
|
||||||
c := newNNSInvoker(t, true)
|
c, _ := newNNSInvoker(t, true, false)
|
||||||
|
|
||||||
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
||||||
c.Invoke(t, true, "register",
|
c.Invoke(t, true, "register",
|
||||||
|
@ -469,7 +487,7 @@ func TestNNSGetAllRecords(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExpiration(t *testing.T) {
|
func TestExpiration(t *testing.T) {
|
||||||
c := newNNSInvoker(t, true)
|
c, _ := newNNSInvoker(t, true, false)
|
||||||
|
|
||||||
refresh, retry, expire, ttl := int64(101), int64(102), int64(msPerYear/1000*10), int64(104)
|
refresh, retry, expire, ttl := int64(101), int64(102), int64(msPerYear/1000*10), int64(104)
|
||||||
c.Invoke(t, true, "register",
|
c.Invoke(t, true, "register",
|
||||||
|
@ -508,7 +526,7 @@ func TestExpiration(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNNSSetAdmin(t *testing.T) {
|
func TestNNSSetAdmin(t *testing.T) {
|
||||||
c := newNNSInvoker(t, true)
|
c, _ := newNNSInvoker(t, true, false)
|
||||||
|
|
||||||
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
||||||
c.Invoke(t, true, "register",
|
c.Invoke(t, true, "register",
|
||||||
|
@ -527,8 +545,36 @@ func TestNNSSetAdmin(t *testing.T) {
|
||||||
"testdomain.com", int64(nns.TXT), "will be added")
|
"testdomain.com", int64(nns.TXT), "will be added")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNNS_Frostfsid(t *testing.T) {
|
||||||
|
nnsInv, f := newNNSInvoker(t, false, true)
|
||||||
|
|
||||||
|
acc, err := wallet.NewAccount()
|
||||||
|
require.NoError(t, err)
|
||||||
|
acc.PrivateKey().PublicKey().Bytes()
|
||||||
|
|
||||||
|
nnsUserInv := nnsInv.NewInvoker(nnsInv.Hash, newSigner(t, nnsInv, acc))
|
||||||
|
|
||||||
|
nnsUserInv.InvokeFail(t, "not witnessed by committee", "register",
|
||||||
|
"testdomain.com", acc.PublicKey().GetScriptHash(),
|
||||||
|
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||||
|
|
||||||
|
f.Invoke(t, stackitem.Null{}, createSubjectMethod, "", acc.PrivateKey().PublicKey().Bytes())
|
||||||
|
|
||||||
|
f.Invoke(t, stackitem.Null{}, setSubjectKVMethod, acc.PublicKey().GetScriptHash(), nns.FrostfsIDNNSTLDPermissionKey, nns.FrostfsIDTLDRegistrationAllowed)
|
||||||
|
|
||||||
|
nnsUserInv.Invoke(t, true, "register",
|
||||||
|
"testdomain.com", acc.PublicKey().GetScriptHash(),
|
||||||
|
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||||
|
|
||||||
|
f.Invoke(t, stackitem.Null{}, setSubjectKVMethod, acc.PublicKey().GetScriptHash(), nns.FrostfsIDNNSTLDPermissionKey, nns.FrostfsIDTLDRegistrationDisallowed)
|
||||||
|
|
||||||
|
nnsUserInv.InvokeFail(t, "not witnessed by committee", "register",
|
||||||
|
"testdomain.kz", acc.PublicKey().GetScriptHash(),
|
||||||
|
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||||
|
}
|
||||||
|
|
||||||
func TestNNSIsAvailable(t *testing.T) {
|
func TestNNSIsAvailable(t *testing.T) {
|
||||||
c := newNNSInvoker(t, false)
|
c, _ := newNNSInvoker(t, false, false)
|
||||||
|
|
||||||
c.Invoke(t, true, "isAvailable", "com")
|
c.Invoke(t, true, "isAvailable", "com")
|
||||||
|
|
||||||
|
@ -575,7 +621,7 @@ func TestNNSIsAvailable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNNSRenew(t *testing.T) {
|
func TestNNSRenew(t *testing.T) {
|
||||||
c := newNNSInvoker(t, true)
|
c, _ := newNNSInvoker(t, true, false)
|
||||||
|
|
||||||
acc := c.NewAccount(t)
|
acc := c.NewAccount(t)
|
||||||
c1 := c.WithSigners(c.Committee, acc)
|
c1 := c.WithSigners(c.Committee, acc)
|
||||||
|
@ -600,7 +646,7 @@ func TestNNSRenew(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNNSResolve(t *testing.T) {
|
func TestNNSResolve(t *testing.T) {
|
||||||
c := newNNSInvoker(t, true)
|
c, _ := newNNSInvoker(t, true, false)
|
||||||
|
|
||||||
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
||||||
c.Invoke(t, true, "register",
|
c.Invoke(t, true, "register",
|
||||||
|
@ -617,7 +663,7 @@ func TestNNSResolve(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNNSAndProxy(t *testing.T) {
|
func TestNNSAndProxy(t *testing.T) {
|
||||||
c := newNNSInvoker(t, false)
|
c, _ := newNNSInvoker(t, false, false)
|
||||||
proxyHash := deployProxyContract(t, c.Executor)
|
proxyHash := deployProxyContract(t, c.Executor)
|
||||||
proxySigner := neotest.NewContractSigner(proxyHash, func(*transaction.Transaction) []any { return nil })
|
proxySigner := neotest.NewContractSigner(proxyHash, func(*transaction.Transaction) []any { return nil })
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue