[#135] container: allow to register nice names in NNS

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2021-09-29 13:00:32 +03:00 committed by fyrchik
parent dd583e0d1c
commit e4e306c63b
2 changed files with 74 additions and 1 deletions

View file

@ -1,7 +1,8 @@
name: "NeoFS Container" name: "NeoFS Container"
safemethods: ["get", "owner", "list", "eACL", "getContainerSize", "listContainerSizes", "version"] safemethods: ["get", "owner", "list", "eACL", "getContainerSize", "listContainerSizes", "version"]
permissions: permissions:
- methods: ["update", "addKey", "transferX"] - methods: ["update", "addKey", "transferX",
"addRoot", "register", "addRecord"]
events: events:
- name: containerPut - name: containerPut
parameters: parameters:

View file

@ -46,6 +46,8 @@ const (
neofsIDContractKey = "identityScriptHash" neofsIDContractKey = "identityScriptHash"
balanceContractKey = "balanceScriptHash" balanceContractKey = "balanceScriptHash"
netmapContractKey = "netmapScriptHash" netmapContractKey = "netmapScriptHash"
nnsContractKey = "nnsScriptHash"
nnsRootKey = "nnsRoot"
notaryDisabledKey = "notary" notaryDisabledKey = "notary"
containerFeeKey = "ContainerFee" containerFeeKey = "ContainerFee"
@ -61,6 +63,10 @@ var (
eACLPrefix = []byte("eACL") eACLPrefix = []byte("eACL")
) )
// OnNEP11Payment is needed for registration with contract as owner to work.
func OnNEP11Payment(a interop.Hash160, b int, c []byte, d interface{}) {
}
func _deploy(data interface{}, isUpdate bool) { func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext() ctx := storage.GetContext()
@ -73,6 +79,8 @@ func _deploy(data interface{}, isUpdate bool) {
addrNetmap := args[1].(interop.Hash160) addrNetmap := args[1].(interop.Hash160)
addrBalance := args[2].(interop.Hash160) addrBalance := args[2].(interop.Hash160)
addrID := args[3].(interop.Hash160) addrID := args[3].(interop.Hash160)
addrNNS := args[4].(interop.Hash160)
nnsRoot := args[5].(string)
if len(addrNetmap) != 20 || len(addrBalance) != 20 || len(addrID) != 20 { if len(addrNetmap) != 20 || len(addrBalance) != 20 || len(addrID) != 20 {
panic("incorrect length of contract script hash") panic("incorrect length of contract script hash")
@ -81,6 +89,8 @@ func _deploy(data interface{}, isUpdate bool) {
storage.Put(ctx, netmapContractKey, addrNetmap) storage.Put(ctx, netmapContractKey, addrNetmap)
storage.Put(ctx, balanceContractKey, addrBalance) storage.Put(ctx, balanceContractKey, addrBalance)
storage.Put(ctx, neofsIDContractKey, addrID) storage.Put(ctx, neofsIDContractKey, addrID)
storage.Put(ctx, nnsContractKey, addrNNS)
storage.Put(ctx, nnsRootKey, nnsRoot)
// initialize the way to collect signatures // initialize the way to collect signatures
storage.Put(ctx, notaryDisabledKey, notaryDisabled) storage.Put(ctx, notaryDisabledKey, notaryDisabled)
@ -89,6 +99,9 @@ func _deploy(data interface{}, isUpdate bool) {
runtime.Log("container contract notary disabled") runtime.Log("container contract notary disabled")
} }
// add NNS root for container alias domains
contract.Call(addrNNS, "addRoot", contract.All, nnsRoot)
runtime.Log("container contract initialized") runtime.Log("container contract initialized")
} }
@ -112,6 +125,14 @@ func Update(script []byte, manifest []byte, data interface{}) {
// Token is optional and should be stable marshaled SessionToken structure from // Token is optional and should be stable marshaled SessionToken structure from
// API. // API.
func Put(container []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) { func Put(container []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) {
PutNamed(container, signature, publicKey, token, "", "")
}
// PutNamed is similar to put but also sets a TXT record in nns contract.
// Note that zone must exist.
func PutNamed(container []byte, signature interop.Signature,
publicKey interop.PublicKey, token []byte,
name, zone string) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool) notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
@ -125,6 +146,20 @@ func Put(container []byte, signature interop.Signature, publicKey interop.Public
token: token, token: token,
} }
var (
needRegister bool
nnsContractAddr interop.Hash160
domain string
)
if name != "" {
if zone == "" {
zone = storage.Get(ctx, nnsRootKey).(string)
}
nnsContractAddr = storage.Get(ctx, nnsContractKey).(interop.Hash160)
domain = name + "." + zone
needRegister = checkNiceNameAvailable(nnsContractAddr, domain)
}
var ( // for invocation collection without notary var ( // for invocation collection without notary
alphabet = common.AlphabetNodes() alphabet = common.AlphabetNodes()
nodeKey []byte nodeKey []byte
@ -182,6 +217,25 @@ func Put(container []byte, signature interop.Signature, publicKey interop.Public
addContainer(ctx, containerID, ownerID, cnr) addContainer(ctx, containerID, ownerID, cnr)
if name != "" {
if needRegister {
const (
defaultRefresh = 3600 // 1 hour
defaultRetry = 600 // 10 min
defaultExpire = 604800 // 1 week
defaultTTL = 3600 // 1 hour
)
res := contract.Call(nnsContractAddr, "register", contract.All,
domain, runtime.GetExecutingScriptHash(), "ops@nspcc.ru",
defaultRefresh, defaultRetry, defaultExpire, defaultTTL).(bool)
if !res {
panic("can't register the domain " + domain)
}
}
contract.Call(nnsContractAddr, "addRecord", contract.All,
domain, 16 /* TXT */, std.Base58Encode(containerID))
}
if len(token) == 0 { // if container created directly without session if len(token) == 0 { // if container created directly without session
contract.Call(neofsIDContractAddr, "addKey", contract.All, ownerID, [][]byte{publicKey}) contract.Call(neofsIDContractAddr, "addKey", contract.All, ownerID, [][]byte{publicKey})
} }
@ -189,6 +243,24 @@ func Put(container []byte, signature interop.Signature, publicKey interop.Public
runtime.Log("put: added new container") runtime.Log("put: added new container")
} }
// checkNiceNameAvailable checks if 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 {
isAvail := contract.Call(nnsContractAddr, "isAvailable",
contract.ReadStates|contract.AllowCall, domain).(bool)
if isAvail {
return true
}
res := contract.Call(nnsContractAddr, "getRecords",
contract.ReadStates|contract.AllowCall, domain, 16 /* TXT */)
if res != nil {
panic("name is already taken")
}
return false
}
// Delete method removes container from contract storage if it was // Delete method removes container from contract storage if it was
// invoked by Alphabet nodes of the Inner Ring. Otherwise it produces // invoked by Alphabet nodes of the Inner Ring. Otherwise it produces
// containerDelete notification. // containerDelete notification.