[#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 (
|
||||
"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/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/convert"
|
||||
|
@ -182,6 +183,7 @@ func PutNamed(container []byte, signature interop.Signature,
|
|||
needRegister bool
|
||||
nnsContractAddr interop.Hash160
|
||||
domain string
|
||||
globalDomain string
|
||||
)
|
||||
if name != "" {
|
||||
if zone == "" {
|
||||
|
@ -189,6 +191,12 @@ func PutNamed(container []byte, signature interop.Signature,
|
|||
}
|
||||
nnsContractAddr = storage.Get(ctx, nnsContractKey).(interop.Hash160)
|
||||
domain = name + "." + zone
|
||||
|
||||
tldGlobalDomain := getGlobalDomain(nnsContractAddr, zone)
|
||||
if tldGlobalDomain != "" {
|
||||
globalDomain = name + "." + tldGlobalDomain
|
||||
checkGlobalDomainAvailable(nnsContractAddr, globalDomain)
|
||||
}
|
||||
needRegister = checkNiceNameAvailable(nnsContractAddr, domain)
|
||||
}
|
||||
|
||||
|
@ -243,10 +251,81 @@ func PutNamed(container []byte, signature interop.Signature,
|
|||
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.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.
|
||||
// It panics if the name is taken. Returned value specifies if new domain registration is needed.
|
||||
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)) {
|
||||
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)
|
||||
runtime.Log("remove container")
|
||||
|
|
|
@ -69,6 +69,10 @@ const (
|
|||
errInvalidDomainName = "invalid domain name format"
|
||||
)
|
||||
|
||||
const (
|
||||
Cnametgt = "cnametgt"
|
||||
)
|
||||
|
||||
// RecordState is a type that registered entities are saved to.
|
||||
type RecordState struct {
|
||||
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) {
|
||||
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) {
|
||||
const (
|
||||
containerPerBlock = 512
|
||||
|
|
Loading…
Reference in a new issue