[#9999] nns: Add support global domain
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
This commit is contained in:
parent
49e5270f67
commit
8a472c0f57
3 changed files with 147 additions and 0 deletions
|
@ -2,6 +2,7 @@ package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
"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/contract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/convert"
|
"github.com/nspcc-dev/neo-go/pkg/interop/convert"
|
||||||
|
@ -182,6 +183,7 @@ func PutNamed(container []byte, signature interop.Signature,
|
||||||
needRegister bool
|
needRegister bool
|
||||||
nnsContractAddr interop.Hash160
|
nnsContractAddr interop.Hash160
|
||||||
domain string
|
domain string
|
||||||
|
globalDomain string
|
||||||
)
|
)
|
||||||
if name != "" {
|
if name != "" {
|
||||||
if zone == "" {
|
if zone == "" {
|
||||||
|
@ -189,6 +191,12 @@ func PutNamed(container []byte, signature interop.Signature,
|
||||||
}
|
}
|
||||||
nnsContractAddr = storage.Get(ctx, nnsContractKey).(interop.Hash160)
|
nnsContractAddr = storage.Get(ctx, nnsContractKey).(interop.Hash160)
|
||||||
domain = name + "." + zone
|
domain = name + "." + zone
|
||||||
|
|
||||||
|
tldGlobalDomain := getGlobalDomain(nnsContractAddr, zone)
|
||||||
|
if tldGlobalDomain != "" {
|
||||||
|
globalDomain = name + "." + tldGlobalDomain
|
||||||
|
checkGlobalDomainAvailable(nnsContractAddr, globalDomain)
|
||||||
|
}
|
||||||
needRegister = checkNiceNameAvailable(nnsContractAddr, domain)
|
needRegister = checkNiceNameAvailable(nnsContractAddr, domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,10 +251,81 @@ func PutNamed(container []byte, signature interop.Signature,
|
||||||
storage.Put(ctx, key, domain)
|
storage.Put(ctx, key, domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if globalDomain != "" {
|
||||||
|
if res := contract.Call(nnsContractAddr, "register", contract.All,
|
||||||
|
globalDomain, runtime.GetExecutingScriptHash(), "ops@frostfs.info",
|
||||||
|
defaultRefresh, defaultRetry, defaultExpire, defaultTTL).(bool); !res {
|
||||||
|
panic("can't register the global domain")
|
||||||
|
}
|
||||||
|
|
||||||
|
contract.Call(nnsContractAddr, "addRecord", contract.All,
|
||||||
|
domain, int64(nns.CNAME), globalDomain)
|
||||||
|
}
|
||||||
|
|
||||||
runtime.Log("added new container")
|
runtime.Log("added new container")
|
||||||
runtime.Notify("PutSuccess", containerID, publicKey)
|
runtime.Notify("PutSuccess", containerID, publicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// solveCnametgt determines the value of the txt parameter for the Cnametgt record of the specified domain.
|
||||||
|
// If the txt record is missing, the function returns an empty string.
|
||||||
|
func solveCnametgt(nnsContractAddr interop.Hash160, domain string) string {
|
||||||
|
if domain == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
res := contract.Call(nnsContractAddr, "getRecords",
|
||||||
|
contract.ReadStates|contract.AllowCall, domain, nns.TXT)
|
||||||
|
if res == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
names := res.([]string)
|
||||||
|
|
||||||
|
tgt := ""
|
||||||
|
for _, name := range names {
|
||||||
|
fragments := std.StringSplit(name, "=")
|
||||||
|
if len(fragments) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if fragments[0] == nns.Cnametgt {
|
||||||
|
tgt = fragments[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tgt
|
||||||
|
}
|
||||||
|
|
||||||
|
// splitDomain splits domain name into parts.
|
||||||
|
func splitDomain(name string) []string {
|
||||||
|
fragments := std.StringSplit(name, ".")
|
||||||
|
return fragments
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGlobalDomain returns the GlobalDomain for a specific domain.
|
||||||
|
func getGlobalDomain(nnsContractAddr interop.Hash160, zone string) string {
|
||||||
|
subDomains := splitDomain(zone)
|
||||||
|
globalDomain := ""
|
||||||
|
if len(subDomains) > 0 {
|
||||||
|
ns := subDomains[len(subDomains)-1]
|
||||||
|
if ns == "ns" && len(ns) > 1 {
|
||||||
|
ns = subDomains[len(subDomains)-2] + "." + subDomains[len(subDomains)-1]
|
||||||
|
}
|
||||||
|
if ns != "" {
|
||||||
|
globalDomain = solveCnametgt(nnsContractAddr, ns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return globalDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkGlobalDomainAvailable checks if the nice name is available for the container.
|
||||||
|
// It panics if the name is taken.
|
||||||
|
func checkGlobalDomainAvailable(nnsContractAddr interop.Hash160, globalDomain string) {
|
||||||
|
if isAvail := contract.Call(nnsContractAddr, "isAvailable",
|
||||||
|
contract.ReadStates|contract.AllowCall, globalDomain).(bool); !isAvail {
|
||||||
|
panic("global domain is already taken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// checkNiceNameAvailable checks if the nice name is available for the container.
|
// checkNiceNameAvailable checks if the nice name is available for the container.
|
||||||
// It panics if the name is taken. Returned value specifies if new domain registration is needed.
|
// It panics if the name is taken. Returned value specifies if new domain registration is needed.
|
||||||
func checkNiceNameAvailable(nnsContractAddr interop.Hash160, domain string) bool {
|
func checkNiceNameAvailable(nnsContractAddr interop.Hash160, domain string) bool {
|
||||||
|
@ -301,6 +380,11 @@ func Delete(containerID []byte, signature interop.Signature, publicKey interop.P
|
||||||
if res != nil && std.Base58Encode(containerID) == string(res.([]any)[0].(string)) {
|
if res != nil && std.Base58Encode(containerID) == string(res.([]any)[0].(string)) {
|
||||||
contract.Call(nnsContractAddr, "deleteRecords", contract.All, domain, 16 /* TXT */)
|
contract.Call(nnsContractAddr, "deleteRecords", contract.All, domain, 16 /* TXT */)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// res = contract.Call(nnsContractAddr, "getRecords", contract.ReadStates|contract.AllowCall, domain, nns.CNAME)
|
||||||
|
// if res != nil && std.Base58Encode(containerID) == string(res.([]any)[0].(string)) {
|
||||||
|
// contract.Call(nnsContractAddr, "deleteRecords", contract.All, domain, nns.CNAME)
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
removeContainer(ctx, containerID, ownerID)
|
removeContainer(ctx, containerID, ownerID)
|
||||||
runtime.Log("remove container")
|
runtime.Log("remove container")
|
||||||
|
|
|
@ -69,6 +69,10 @@ const (
|
||||||
errInvalidDomainName = "invalid domain name format"
|
errInvalidDomainName = "invalid domain name format"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Cnametgt = "cnametgt"
|
||||||
|
)
|
||||||
|
|
||||||
// RecordState is a type that registered entities are saved to.
|
// RecordState is a type that registered entities are saved to.
|
||||||
type RecordState struct {
|
type RecordState struct {
|
||||||
Name string
|
Name string
|
||||||
|
|
|
@ -165,6 +165,14 @@ func checkContainerList(t *testing.T, c *neotest.ContractInvoker, expected [][]b
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// default SOA record field values
|
||||||
|
defaultRefresh = 3600 // 1 hour
|
||||||
|
defaultRetry = 600 // 10 min
|
||||||
|
defaultExpire = 3600 * 24 * 365 * 10 // 10 years
|
||||||
|
defaultTTL = 3600 // 1 hour
|
||||||
|
)
|
||||||
|
|
||||||
func TestContainerPut(t *testing.T) {
|
func TestContainerPut(t *testing.T) {
|
||||||
c, cBal, _ := newContainerInvoker(t)
|
c, cBal, _ := newContainerInvoker(t)
|
||||||
|
|
||||||
|
@ -233,6 +241,57 @@ func TestContainerPut(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("create global domain", func(t *testing.T) {
|
||||||
|
ctrNNS := neotest.CompileFile(t, c.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
|
||||||
|
nnsHash := ctrNNS.Hash
|
||||||
|
cNNS := c.CommitteeInvoker(nnsHash)
|
||||||
|
cNNS.Invoke(t, true, "register",
|
||||||
|
"animals", c.CommitteeHash,
|
||||||
|
"whateveriwant@world.com", int64(0), int64(0), int64(100_000), int64(0))
|
||||||
|
|
||||||
|
cNNS.Invoke(t, true, "register",
|
||||||
|
"ns", c.CommitteeHash,
|
||||||
|
"whateveriwant@world.com", int64(0), int64(0), int64(100_000), int64(0))
|
||||||
|
|
||||||
|
cNNS.Invoke(t, true, "register",
|
||||||
|
"poland.ns", c.CommitteeHash,
|
||||||
|
"whateveriwant@world.com", int64(0), int64(0), int64(100_000), int64(0))
|
||||||
|
|
||||||
|
cNNS.Invoke(t, true, "register",
|
||||||
|
"sweden.ns", c.CommitteeHash,
|
||||||
|
"whateveriwant@world.com", int64(0), int64(0), int64(100_000), int64(0))
|
||||||
|
|
||||||
|
cNNS.Invoke(t, stackitem.Null{}, "addRecord",
|
||||||
|
"poland.ns", int64(nns.TXT), nns.Cnametgt+"=animals")
|
||||||
|
|
||||||
|
cNNS.Invoke(t, stackitem.Null{}, "addRecord", "poland.ns", int64(nns.TXT), "random-record")
|
||||||
|
|
||||||
|
cNNS.Invoke(t, stackitem.Null{}, "addRecord", "poland.ns", int64(nns.TXT), "ne-qqq=random-record2")
|
||||||
|
cNNS.Invoke(t, stackitem.Null{}, "addRecord", "sweden.ns", int64(nns.TXT), nns.Cnametgt+"=animals")
|
||||||
|
|
||||||
|
balanceMint(t, cBal, acc, (containerFee+containerAliasFee)*5, []byte{})
|
||||||
|
putArgs := []any{cnt.value, cnt.sig, cnt.pub, cnt.token, "bober", "poland.ns"}
|
||||||
|
c3 := c.WithSigners(c.Committee, acc)
|
||||||
|
c3.Invoke(t, stackitem.Null{}, "putNamed", putArgs...)
|
||||||
|
b := c3.TopBlock(t)
|
||||||
|
|
||||||
|
expected := stackitem.NewArray([]stackitem.Item{stackitem.NewBuffer(
|
||||||
|
[]byte(fmt.Sprintf("bober.animals ops@frostfs.info %d %d %d %d %d",
|
||||||
|
b.Timestamp, defaultRefresh, defaultRetry, defaultExpire, defaultTTL)))})
|
||||||
|
cNNS.Invoke(t, expected, "getRecords", "bober.animals", int64(nns.SOA))
|
||||||
|
|
||||||
|
expected = stackitem.NewArray([]stackitem.Item{
|
||||||
|
stackitem.NewBuffer([]byte("bober.animals")),
|
||||||
|
})
|
||||||
|
|
||||||
|
cNNS.Invoke(t, false, "isAvailable", "bober.animals")
|
||||||
|
|
||||||
|
cNNS.Invoke(t, expected, "getRecords", "bober.poland.ns", int64(nns.CNAME))
|
||||||
|
|
||||||
|
putArgs = []any{cnt.value, cnt.sig, cnt.pub, cnt.token, "bober", "sweden.ns"}
|
||||||
|
c3.InvokeFail(t, "global domain is already taken", "putNamed", putArgs...)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("gas costs are the same for all containers in block", func(t *testing.T) {
|
t.Run("gas costs are the same for all containers in block", func(t *testing.T) {
|
||||||
const (
|
const (
|
||||||
containerPerBlock = 512
|
containerPerBlock = 512
|
||||||
|
|
Loading…
Reference in a new issue