[#48] frostfsid: Update contract
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
0ea65ca637
commit
edfab37677
3 changed files with 909 additions and 125 deletions
|
@ -1,4 +1,89 @@
|
||||||
name: "Identity"
|
name: "Identity"
|
||||||
safemethods: ["key", "version"]
|
safemethods: ["version"]
|
||||||
permissions:
|
permissions:
|
||||||
- methods: ["update"]
|
- methods: ["update"]
|
||||||
|
events:
|
||||||
|
- name: CreateSubject
|
||||||
|
parameters:
|
||||||
|
- name: subjectAddress
|
||||||
|
type: Hash160
|
||||||
|
- name: AddSubjectKey
|
||||||
|
parameters:
|
||||||
|
- name: subjectAddress
|
||||||
|
type: Hash160
|
||||||
|
- name: subjectKey
|
||||||
|
type: PublicKey
|
||||||
|
- name: RemoveSubjectKey
|
||||||
|
parameters:
|
||||||
|
- name: subjectAddress
|
||||||
|
type: Hash160
|
||||||
|
- name: subjectKey
|
||||||
|
type: PublicKey
|
||||||
|
- name: SetSubjectName
|
||||||
|
parameters:
|
||||||
|
- name: subjectAddress
|
||||||
|
type: Hash160
|
||||||
|
- name: name
|
||||||
|
type: String
|
||||||
|
- name: SetSubjectKV
|
||||||
|
parameters:
|
||||||
|
- name: subjectAddress
|
||||||
|
type: Hash160
|
||||||
|
- name: key
|
||||||
|
type: String
|
||||||
|
- name: value
|
||||||
|
type: String
|
||||||
|
- name: DeleteSubjectKV
|
||||||
|
parameters:
|
||||||
|
- name: subjectAddress
|
||||||
|
type: Hash160
|
||||||
|
- name: key
|
||||||
|
type: String
|
||||||
|
- name: DeleteSubject
|
||||||
|
parameters:
|
||||||
|
- name: subjectAddress
|
||||||
|
type: Hash160
|
||||||
|
- name: CreateNamespace
|
||||||
|
parameters:
|
||||||
|
- name: namespace
|
||||||
|
type: String
|
||||||
|
- name: AddSubjectToNamespace
|
||||||
|
parameters:
|
||||||
|
- name: subjectAddress
|
||||||
|
type: Hash160
|
||||||
|
- name: namespace
|
||||||
|
type: String
|
||||||
|
- name: RemoveSubjectFromNamespace
|
||||||
|
parameters:
|
||||||
|
- name: subjectAddress
|
||||||
|
type: Hash160
|
||||||
|
- name: namespace
|
||||||
|
type: String
|
||||||
|
- name: CreateGroup
|
||||||
|
parameters:
|
||||||
|
- name: namespace
|
||||||
|
type: String
|
||||||
|
- name: group
|
||||||
|
type: String
|
||||||
|
- name: AddSubjectToGroup
|
||||||
|
parameters:
|
||||||
|
- name: subjectAddress
|
||||||
|
type: Hash160
|
||||||
|
- name: namespace
|
||||||
|
type: String
|
||||||
|
- name: group
|
||||||
|
type: String
|
||||||
|
- name: RemoveSubjectFromGroup
|
||||||
|
parameters:
|
||||||
|
- name: subjectAddress
|
||||||
|
type: Hash160
|
||||||
|
- name: namespace
|
||||||
|
type: String
|
||||||
|
- name: group
|
||||||
|
type: String
|
||||||
|
- name: DeleteGroup
|
||||||
|
parameters:
|
||||||
|
- name: namespace
|
||||||
|
type: String
|
||||||
|
- name: group
|
||||||
|
type: String
|
||||||
|
|
|
@ -1,28 +1,24 @@
|
||||||
|
// Package frostfsid
|
||||||
/*
|
/*
|
||||||
FrostFSID contract is a contract deployed in FrostFS sidechain.
|
FrostFSID contract is a contract deployed in FrostFS sidechain.
|
||||||
|
|
||||||
FrostFSID contract is used to store connection between an OwnerID and its public keys.
|
|
||||||
OwnerID is a 25-byte N3 wallet address that can be produced from a public key.
|
|
||||||
It is one-way conversion. In simple cases, FrostFS verifies ownership by checking
|
|
||||||
signature and relation between a public key and an OwnerID.
|
|
||||||
|
|
||||||
In more complex cases, a user can use public keys unrelated to the OwnerID to maintain
|
|
||||||
secure access to the data. FrostFSID contract stores relation between an OwnerID and
|
|
||||||
arbitrary public keys. Data owner can bind a public key with its account or unbind it
|
|
||||||
by invoking Bind or Unbind methods of FrostFS contract in the mainchain. After that,
|
|
||||||
Alphabet nodes produce multisigned AddKey and RemoveKey invocations of FrostFSID
|
|
||||||
contract.
|
|
||||||
|
|
||||||
# Contract notifications
|
# Contract notifications
|
||||||
|
|
||||||
FrostFSID contract does not produce notifications to process.
|
FrostFSID contract does not produce notifications to process.
|
||||||
|
|
||||||
# Contract storage scheme
|
# Contract storage scheme
|
||||||
|
|
||||||
| Key | Value | Description |
|
| Key | Value | Description |
|
||||||
|-----------------------------|------------|----------------------------------|
|
|---------------------------------------------------------------------------------|--------------------------------|----------------------------------------------------|
|
||||||
| `processingScriptHash` | Hash160 | netmap contract hash |
|
| `o` + [ owner address ] | []byte{1} | contract owners that can invoke write methods |
|
||||||
| `containerScriptHash` | Hash160 | container contract hash |
|
| `s` + [ subject address ] | Serialized Subject structure | subject into |
|
||||||
| `o` + ownerID + publicKey | ByteArray | it flags owner's public key |
|
| `a` + [ pk address ] + [ subject address ] | []byte{1} | link extra public keys for subject |
|
||||||
|
| `n` + [ RIPEMD160 of namespace ] | Serialized Namespace structure | namespace info |
|
||||||
|
| `N` + [ RIPEMD160 of namespace ] + [ subject address ] | []byte{1} | subject that belongs to the namespace |
|
||||||
|
| `l` + [ RIPEMD160 of namespace ] + [ RIPEMD160 of subject name ] | Subject public key | subject name to public key index |
|
||||||
|
| `g` + [ RIPEMD160 of namespace ] + [ RIPEMD160 of group ] | Serialized Group structure | group into |
|
||||||
|
| `G` + [ RIPEMD160 of namespace ] + [ RIPEMD160 of group ] + [ subject address ] | []byte{1} | subject that belongs to the group |
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package frostfsid
|
package frostfsid
|
||||||
|
|
|
@ -3,52 +3,106 @@ package frostfsid
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
||||||
"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/iterator"
|
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/native/crypto"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
|
"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/runtime"
|
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
UserInfo struct {
|
Subject struct {
|
||||||
Keys [][]byte
|
PrimaryKey interop.PublicKey
|
||||||
|
AdditionalKeys []interop.PublicKey
|
||||||
|
Namespace string
|
||||||
|
Name string
|
||||||
|
KV map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
SubjectExtended struct {
|
||||||
|
PrimaryKey interop.PublicKey
|
||||||
|
AdditionalKeys []interop.PublicKey
|
||||||
|
Namespace string
|
||||||
|
Name string
|
||||||
|
KV map[string]string
|
||||||
|
Groups []Group
|
||||||
|
}
|
||||||
|
|
||||||
|
Namespace struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
NamespaceExtended struct {
|
||||||
|
Name string
|
||||||
|
GroupsCount int
|
||||||
|
SubjectsCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
Group struct {
|
||||||
|
Name string
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupExtended struct {
|
||||||
|
Name string
|
||||||
|
Namespace string
|
||||||
|
SubjectsCount int
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ownerSize = 1 + interop.Hash160Len + 4
|
ownerKeysPrefix = 'o'
|
||||||
)
|
subjectKeysPrefix = 's'
|
||||||
|
additionalKeysPrefix = 'a'
|
||||||
const (
|
namespaceKeysPrefix = 'n'
|
||||||
netmapContractKey = "netmapScriptHash"
|
namespaceSubjectsKeysPrefix = 'N'
|
||||||
containerContractKey = "containerScriptHash"
|
namespaceSubjectsNamesPrefix = 'l'
|
||||||
ownerKeysPrefix = 'o'
|
groupKeysPrefix = 'g'
|
||||||
|
groupSubjectsKeysPrefix = 'G'
|
||||||
)
|
)
|
||||||
|
|
||||||
func _deploy(data any, isUpdate bool) {
|
func _deploy(data any, isUpdate bool) {
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
if isUpdate {
|
|
||||||
args := data.([]any)
|
|
||||||
common.CheckVersion(args[len(args)-1].(int))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
args := data.(struct {
|
args := data.(struct {
|
||||||
addrNetmap interop.Hash160
|
owners []interop.Hash160
|
||||||
addrContainer interop.Hash160
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if len(args.addrNetmap) != interop.Hash160Len || len(args.addrContainer) != interop.Hash160Len {
|
for _, owner := range args.owners {
|
||||||
panic("incorrect length of contract script hash")
|
if len(owner) != interop.Hash160Len {
|
||||||
|
panic("incorrect length of owner addresses")
|
||||||
|
}
|
||||||
|
storage.Put(ctx, ownerKey(owner), []byte{1})
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.Put(ctx, netmapContractKey, args.addrNetmap)
|
|
||||||
storage.Put(ctx, containerContractKey, args.addrContainer)
|
|
||||||
|
|
||||||
runtime.Log("frostfsid contract initialized")
|
runtime.Log("frostfsid contract initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AddOwner(addr interop.Hash160) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
if !common.HasUpdateAccess() {
|
||||||
|
panic("not witnessed")
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.Put(ctx, ownerKey(addr), []byte{1})
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteOwner(addr interop.Hash160) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
if !common.HasUpdateAccess() {
|
||||||
|
panic("not witnessed")
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.Delete(ctx, ownerKey(addr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListOwners() iterator.Iterator {
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
return storage.Find(ctx, []byte{ownerKeysPrefix}, storage.KeysOnly|storage.RemovePrefix)
|
||||||
|
}
|
||||||
|
|
||||||
// Update method updates contract source code and manifest. It can be invoked
|
// Update method updates contract source code and manifest. It can be invoked
|
||||||
// only by committee.
|
// only by committee.
|
||||||
func Update(script []byte, manifest []byte, data any) {
|
func Update(script []byte, manifest []byte, data any) {
|
||||||
|
@ -60,96 +114,745 @@ func Update(script []byte, manifest []byte, data any) {
|
||||||
runtime.Log("frostfsid contract updated")
|
runtime.Log("frostfsid contract updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddKey binds a list of the provided public keys to the OwnerID. It can be invoked only by
|
|
||||||
// Alphabet nodes.
|
|
||||||
//
|
|
||||||
// This method panics if the OwnerID is not an ownerSize byte or the public key is not 33 byte long.
|
|
||||||
// If the key is already bound, the method ignores it.
|
|
||||||
func AddKey(owner []byte, keys []interop.PublicKey) {
|
|
||||||
// V2 format
|
|
||||||
if len(owner) != ownerSize {
|
|
||||||
panic("incorrect owner")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range keys {
|
|
||||||
if len(keys[i]) != interop.PublicKeyCompressedLen {
|
|
||||||
panic("incorrect public key")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
|
|
||||||
common.CheckAlphabetWitness()
|
|
||||||
|
|
||||||
ownerKey := append([]byte{ownerKeysPrefix}, owner...)
|
|
||||||
for i := range keys {
|
|
||||||
stKey := append(ownerKey, keys[i]...)
|
|
||||||
storage.Put(ctx, stKey, []byte{1})
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime.Log("key bound to the owner")
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveKey unbinds the provided public keys from the OwnerID. It can be invoked only by
|
|
||||||
// Alphabet nodes.
|
|
||||||
//
|
|
||||||
// This method panics if the OwnerID is not an ownerSize byte or the public key is not 33 byte long.
|
|
||||||
// If the key is already unbound, the method ignores it.
|
|
||||||
func RemoveKey(owner []byte, keys []interop.PublicKey) {
|
|
||||||
// V2 format
|
|
||||||
if len(owner) != ownerSize {
|
|
||||||
panic("incorrect owner")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range keys {
|
|
||||||
if len(keys[i]) != interop.PublicKeyCompressedLen {
|
|
||||||
panic("incorrect public key")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
|
|
||||||
multiaddr := common.AlphabetAddress()
|
|
||||||
if !runtime.CheckWitness(multiaddr) {
|
|
||||||
panic("invocation from non inner ring node")
|
|
||||||
}
|
|
||||||
|
|
||||||
ownerKey := append([]byte{ownerKeysPrefix}, owner...)
|
|
||||||
for i := range keys {
|
|
||||||
stKey := append(ownerKey, keys[i]...)
|
|
||||||
storage.Delete(ctx, stKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key method returns a list of 33-byte public keys bound with the OwnerID.
|
|
||||||
//
|
|
||||||
// This method panics if the owner is not ownerSize byte long.
|
|
||||||
func Key(owner []byte) [][]byte {
|
|
||||||
// V2 format
|
|
||||||
if len(owner) != ownerSize {
|
|
||||||
panic("incorrect owner")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := storage.GetReadOnlyContext()
|
|
||||||
|
|
||||||
ownerKey := append([]byte{ownerKeysPrefix}, owner...)
|
|
||||||
info := getUserInfo(ctx, ownerKey)
|
|
||||||
|
|
||||||
return info.Keys
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version returns the version of the contract.
|
// Version returns the version of the contract.
|
||||||
func Version() int {
|
func Version() int {
|
||||||
return common.Version
|
return common.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserInfo(ctx storage.Context, key any) UserInfo {
|
func CreateSubject(key interop.PublicKey) {
|
||||||
it := storage.Find(ctx, key, storage.KeysOnly|storage.RemovePrefix)
|
ctx := storage.GetContext()
|
||||||
pubs := [][]byte{}
|
checkContractOwner(ctx)
|
||||||
for iterator.Next(it) {
|
|
||||||
pub := iterator.Value(it).([]byte)
|
if len(key) != interop.PublicKeyCompressedLen {
|
||||||
pubs = append(pubs, pub)
|
panic("incorrect public key length")
|
||||||
}
|
}
|
||||||
|
|
||||||
return UserInfo{Keys: pubs}
|
addr := contract.CreateStandardAccount(key)
|
||||||
|
sKey := subjectKeyFromAddr(addr)
|
||||||
|
data := storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data != nil {
|
||||||
|
panic("subject already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
saPrefix := subjectAdditionalPrefix(key)
|
||||||
|
it := storage.Find(ctx, saPrefix, storage.KeysOnly)
|
||||||
|
for iterator.Next(it) {
|
||||||
|
panic("key is occupied")
|
||||||
|
}
|
||||||
|
|
||||||
|
subj := Subject{
|
||||||
|
PrimaryKey: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.Put(ctx, sKey, std.Serialize(subj))
|
||||||
|
runtime.Notify("CreateSubject", interop.Hash160(addr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
checkContractOwner(ctx)
|
||||||
|
|
||||||
|
if len(addr) != interop.Hash160Len {
|
||||||
|
panic("incorrect address length")
|
||||||
|
}
|
||||||
|
if len(key) != interop.PublicKeyCompressedLen {
|
||||||
|
panic("incorrect public key length")
|
||||||
|
}
|
||||||
|
|
||||||
|
saKey := subjectAdditionalKey(key, addr)
|
||||||
|
data := storage.Get(ctx, saKey).([]byte)
|
||||||
|
if data != nil {
|
||||||
|
panic("key already added")
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.Put(ctx, saKey, []byte{1})
|
||||||
|
|
||||||
|
sKey := subjectKeyFromAddr(addr)
|
||||||
|
data = storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("address not found")
|
||||||
|
}
|
||||||
|
subject := std.Deserialize(data).(Subject)
|
||||||
|
subject.AdditionalKeys = append(subject.AdditionalKeys, key)
|
||||||
|
|
||||||
|
storage.Put(ctx, sKey, std.Serialize(subject))
|
||||||
|
runtime.Notify("AddSubjectKey", addr, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
checkContractOwner(ctx)
|
||||||
|
|
||||||
|
if len(addr) != interop.Hash160Len {
|
||||||
|
panic("incorrect address length")
|
||||||
|
}
|
||||||
|
if len(key) != interop.PublicKeyCompressedLen {
|
||||||
|
panic("incorrect public key length")
|
||||||
|
}
|
||||||
|
|
||||||
|
saKey := subjectAdditionalKey(key, addr)
|
||||||
|
data := storage.Get(ctx, saKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("key already removed")
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.Delete(ctx, saKey)
|
||||||
|
|
||||||
|
sKey := subjectKeyFromAddr(addr)
|
||||||
|
data = storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("address not found")
|
||||||
|
}
|
||||||
|
subject := std.Deserialize(data).(Subject)
|
||||||
|
|
||||||
|
var additionalKeys []interop.PublicKey
|
||||||
|
for i := 0; i < len(subject.AdditionalKeys); i++ {
|
||||||
|
if !common.BytesEqual(subject.AdditionalKeys[i], key) {
|
||||||
|
additionalKeys = append(additionalKeys, subject.AdditionalKeys[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subject.AdditionalKeys = additionalKeys
|
||||||
|
|
||||||
|
storage.Put(ctx, sKey, std.Serialize(subject))
|
||||||
|
runtime.Notify("RemoveSubjectKey", addr, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetSubjectName(addr interop.Hash160, name string) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
checkContractOwner(ctx)
|
||||||
|
|
||||||
|
if len(addr) != interop.Hash160Len {
|
||||||
|
panic("incorrect address length")
|
||||||
|
}
|
||||||
|
|
||||||
|
sKey := subjectKeyFromAddr(addr)
|
||||||
|
data := storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("subject not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
subject := std.Deserialize(data).(Subject)
|
||||||
|
oldName := subject.Name
|
||||||
|
subject.Name = name
|
||||||
|
storage.Put(ctx, sKey, std.Serialize(subject))
|
||||||
|
|
||||||
|
updateNamespaceSubjectName(ctx, subject, oldName)
|
||||||
|
|
||||||
|
runtime.Notify("SetSubjectName", addr, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetSubjectKV(addr interop.Hash160, key, val string) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
checkContractOwner(ctx)
|
||||||
|
|
||||||
|
if len(addr) != interop.Hash160Len {
|
||||||
|
panic("incorrect address length")
|
||||||
|
}
|
||||||
|
|
||||||
|
sKey := subjectKeyFromAddr(addr)
|
||||||
|
data := storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("subject not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
subject := std.Deserialize(data).(Subject)
|
||||||
|
if subject.KV == nil {
|
||||||
|
subject.KV = map[string]string{}
|
||||||
|
}
|
||||||
|
subject.KV[key] = val
|
||||||
|
|
||||||
|
storage.Put(ctx, sKey, std.Serialize(subject))
|
||||||
|
runtime.Notify("SetSubjectKV", addr, key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteSubjectKV(addr interop.Hash160, key string) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
checkContractOwner(ctx)
|
||||||
|
|
||||||
|
if len(addr) != interop.Hash160Len {
|
||||||
|
panic("incorrect address length")
|
||||||
|
}
|
||||||
|
|
||||||
|
sKey := subjectKeyFromAddr(addr)
|
||||||
|
data := storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("subject not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
subject := std.Deserialize(data).(Subject)
|
||||||
|
delete(subject.KV, key)
|
||||||
|
|
||||||
|
storage.Put(ctx, sKey, std.Serialize(subject))
|
||||||
|
runtime.Notify("DeleteSubjectKV", addr, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteSubject(addr interop.Hash160) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
checkContractOwner(ctx)
|
||||||
|
|
||||||
|
if len(addr) != interop.Hash160Len {
|
||||||
|
panic("incorrect address length")
|
||||||
|
}
|
||||||
|
|
||||||
|
sKey := subjectKeyFromAddr(addr)
|
||||||
|
data := storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
subj := std.Deserialize(data).(Subject)
|
||||||
|
|
||||||
|
for i := 0; i < len(subj.AdditionalKeys); i++ {
|
||||||
|
storage.Delete(ctx, subjectAdditionalKey(subj.AdditionalKeys[i], addr))
|
||||||
|
}
|
||||||
|
storage.Delete(ctx, sKey)
|
||||||
|
|
||||||
|
if subj.Namespace != "" {
|
||||||
|
removeSubjectFromNamespace(ctx, subj.Namespace, addr)
|
||||||
|
}
|
||||||
|
deleteNamespaceSubjectName(ctx, subj.Namespace, subj.Name)
|
||||||
|
|
||||||
|
runtime.Notify("DeleteSubject", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSubject(addr interop.Hash160) Subject {
|
||||||
|
if len(addr) != interop.Hash160Len {
|
||||||
|
panic("incorrect address length")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
sKey := subjectKeyFromAddr(addr)
|
||||||
|
data := storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("subject not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return std.Deserialize(data).(Subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSubjectExtended(addr interop.Hash160) SubjectExtended {
|
||||||
|
if len(addr) != interop.Hash160Len {
|
||||||
|
panic("incorrect address length")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
sKey := subjectKeyFromAddr(addr)
|
||||||
|
data := storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("subject not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
subj := std.Deserialize(data).(Subject)
|
||||||
|
|
||||||
|
var groups []Group
|
||||||
|
if subj.Namespace != "" {
|
||||||
|
nsHash := ripemd160Hash(subj.Namespace)
|
||||||
|
it := storage.Find(ctx, groupPrefixFromHash(nsHash), storage.KeysOnly|storage.RemovePrefix)
|
||||||
|
for iterator.Next(it) {
|
||||||
|
groupHash := iterator.Value(it).([]byte)
|
||||||
|
data = storage.Get(ctx, groupSubjectKeyFromHashes(nsHash, groupHash, addr)).([]byte)
|
||||||
|
if data != nil {
|
||||||
|
data = storage.Get(ctx, groupKeyFromHashes(nsHash, groupHash)).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("group not found")
|
||||||
|
}
|
||||||
|
groups = append(groups, std.Deserialize(data).(Group))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subjExt := SubjectExtended{
|
||||||
|
PrimaryKey: subj.PrimaryKey,
|
||||||
|
AdditionalKeys: subj.AdditionalKeys,
|
||||||
|
Namespace: subj.Namespace,
|
||||||
|
Name: subj.Name,
|
||||||
|
KV: subj.KV,
|
||||||
|
Groups: groups,
|
||||||
|
}
|
||||||
|
|
||||||
|
return subjExt
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSubjectByKey(key interop.PublicKey) Subject {
|
||||||
|
if len(key) != interop.PublicKeyCompressedLen {
|
||||||
|
panic("incorrect key length")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
|
||||||
|
sKey := subjectKey(key)
|
||||||
|
data := storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data != nil {
|
||||||
|
return std.Deserialize(data).(Subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
saPrefix := subjectAdditionalPrefix(key)
|
||||||
|
it := storage.Find(ctx, saPrefix, storage.KeysOnly|storage.RemovePrefix)
|
||||||
|
for iterator.Next(it) {
|
||||||
|
addr := iterator.Value(it).([]byte)
|
||||||
|
sKey = subjectKeyFromAddr(addr)
|
||||||
|
data = storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data != nil {
|
||||||
|
return std.Deserialize(data).(Subject)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("subject not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSubjectKeyByName(ns, name string) interop.PublicKey {
|
||||||
|
if ns == "" || name == "" {
|
||||||
|
panic("invalid namespace or name")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
|
||||||
|
nsSubjNameKey := namespaceSubjectNameKey(ns, name)
|
||||||
|
subjKey := storage.Get(ctx, nsSubjNameKey).(interop.PublicKey)
|
||||||
|
if subjKey == nil {
|
||||||
|
panic("subject not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return subjKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListSubjects() iterator.Iterator {
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
return storage.Find(ctx, []byte{subjectKeysPrefix}, storage.KeysOnly|storage.RemovePrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateNamespace(ns string) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
checkContractOwner(ctx)
|
||||||
|
|
||||||
|
if ns == "" {
|
||||||
|
panic("invalid namespace name")
|
||||||
|
}
|
||||||
|
|
||||||
|
nsKey := namespaceKey(ns)
|
||||||
|
data := storage.Get(ctx, nsKey).([]byte)
|
||||||
|
if data != nil {
|
||||||
|
panic("namespace already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace := Namespace{
|
||||||
|
Name: ns,
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.Put(ctx, nsKey, std.Serialize(namespace))
|
||||||
|
runtime.Notify("CreateNamespace", ns)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNamespace(ns string) Namespace {
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
nsKey := namespaceKey(ns)
|
||||||
|
data := storage.Get(ctx, nsKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("namespace not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return std.Deserialize(data).(Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNamespaceExtended(ns string) NamespaceExtended {
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
nsKey := namespaceKey(ns)
|
||||||
|
data := storage.Get(ctx, nsKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("namespace not found")
|
||||||
|
}
|
||||||
|
namespace := std.Deserialize(data).(Namespace)
|
||||||
|
|
||||||
|
nsExtended := NamespaceExtended{
|
||||||
|
Name: namespace.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
it := storage.Find(ctx, groupPrefix(ns), storage.KeysOnly)
|
||||||
|
for iterator.Next(it) {
|
||||||
|
nsExtended.GroupsCount += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
it = storage.Find(ctx, namespaceSubjectPrefix(ns), storage.KeysOnly)
|
||||||
|
for iterator.Next(it) {
|
||||||
|
nsExtended.SubjectsCount += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return nsExtended
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListNamespaces() iterator.Iterator {
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
return storage.Find(ctx, []byte{namespaceKeysPrefix}, storage.ValuesOnly|storage.DeserializeValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddSubjectToNamespace(addr interop.Hash160, ns string) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
checkContractOwner(ctx)
|
||||||
|
|
||||||
|
if ns == "" {
|
||||||
|
panic("invalid namespace name")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(addr) != interop.Hash160Len {
|
||||||
|
panic("invalid address length")
|
||||||
|
}
|
||||||
|
|
||||||
|
sKey := subjectKeyFromAddr(addr)
|
||||||
|
data := storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("subject not found")
|
||||||
|
}
|
||||||
|
subject := std.Deserialize(data).(Subject)
|
||||||
|
|
||||||
|
if subject.Namespace == ns {
|
||||||
|
panic("subject already added")
|
||||||
|
}
|
||||||
|
if subject.Namespace != "" {
|
||||||
|
panic("subject cannot be moved to another namespace")
|
||||||
|
}
|
||||||
|
|
||||||
|
subject.Namespace = ns
|
||||||
|
storage.Put(ctx, sKey, std.Serialize(subject))
|
||||||
|
|
||||||
|
data = storage.Get(ctx, namespaceKey(ns)).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("namespace not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
nsSubjKey := namespaceSubjectKey(ns, addr)
|
||||||
|
storage.Put(ctx, nsSubjKey, []byte{1})
|
||||||
|
|
||||||
|
setNamespaceSubjectName(ctx, subject)
|
||||||
|
|
||||||
|
runtime.Notify("AddSubjectToNamespace", addr, ns)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveSubjectFromNamespace(addr interop.Hash160) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
checkContractOwner(ctx)
|
||||||
|
|
||||||
|
if len(addr) != interop.Hash160Len {
|
||||||
|
panic("invalid address length")
|
||||||
|
}
|
||||||
|
|
||||||
|
sKey := subjectKeyFromAddr(addr)
|
||||||
|
data := storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("subject not found")
|
||||||
|
}
|
||||||
|
subject := std.Deserialize(data).(Subject)
|
||||||
|
|
||||||
|
if subject.Namespace == "" {
|
||||||
|
panic("subject does not belong to any namespace")
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSubjectFromNamespace(ctx, subject.Namespace, addr)
|
||||||
|
deleteNamespaceSubjectName(ctx, subject.Namespace, subject.Name)
|
||||||
|
|
||||||
|
subject.Namespace = ""
|
||||||
|
storage.Put(ctx, sKey, std.Serialize(subject))
|
||||||
|
|
||||||
|
runtime.Notify("RemoveSubjectFromNamespace", addr, subject.Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListNamespaceSubjects(ns string) iterator.Iterator {
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
return storage.Find(ctx, namespaceSubjectPrefix(ns), storage.KeysOnly|storage.RemovePrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateGroup(ns, group string) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
checkContractOwner(ctx)
|
||||||
|
|
||||||
|
if ns == "" {
|
||||||
|
panic("invalid namespace name")
|
||||||
|
}
|
||||||
|
if group == "" {
|
||||||
|
panic("invalid group name")
|
||||||
|
}
|
||||||
|
|
||||||
|
nsKey := namespaceKey(ns)
|
||||||
|
data := storage.Get(ctx, nsKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("namespace not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
gr := Group{
|
||||||
|
Name: group,
|
||||||
|
Namespace: ns,
|
||||||
|
}
|
||||||
|
gKey := groupKey(ns, group)
|
||||||
|
data = storage.Get(ctx, gKey).([]byte)
|
||||||
|
if data != nil {
|
||||||
|
panic("group already exists")
|
||||||
|
}
|
||||||
|
storage.Put(ctx, gKey, std.Serialize(gr))
|
||||||
|
|
||||||
|
runtime.Notify("CreateGroup", ns, group)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroup(ns, group string) Group {
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
gKey := groupKey(ns, group)
|
||||||
|
data := storage.Get(ctx, gKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("group not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return std.Deserialize(data).(Group)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroupExtended(ns, group string) GroupExtended {
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
gKey := groupKey(ns, group)
|
||||||
|
data := storage.Get(ctx, gKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("group not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
gr := std.Deserialize(data).(Group)
|
||||||
|
|
||||||
|
grExtended := GroupExtended{
|
||||||
|
Name: gr.Name,
|
||||||
|
Namespace: gr.Namespace,
|
||||||
|
}
|
||||||
|
|
||||||
|
it := storage.Find(ctx, groupSubjectPrefix(ns, group), storage.KeysOnly)
|
||||||
|
for iterator.Next(it) {
|
||||||
|
grExtended.SubjectsCount += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return grExtended
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListGroups(ns string) iterator.Iterator {
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
return storage.Find(ctx, groupPrefix(ns), storage.ValuesOnly|storage.DeserializeValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddSubjectToGroup(addr interop.Hash160, group string) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
checkContractOwner(ctx)
|
||||||
|
|
||||||
|
if len(addr) != interop.Hash160Len {
|
||||||
|
panic("invalid address length")
|
||||||
|
}
|
||||||
|
|
||||||
|
sKey := subjectKeyFromAddr(addr)
|
||||||
|
data := storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("subject not found")
|
||||||
|
}
|
||||||
|
subject := std.Deserialize(data).(Subject)
|
||||||
|
|
||||||
|
if subject.Namespace == "" {
|
||||||
|
panic("subject does not belong to any namespace")
|
||||||
|
}
|
||||||
|
|
||||||
|
nSubjKey := namespaceSubjectKey(subject.Namespace, addr)
|
||||||
|
storage.Put(ctx, nSubjKey, []byte{1})
|
||||||
|
|
||||||
|
gKey := groupKey(subject.Namespace, group)
|
||||||
|
data = storage.Get(ctx, gKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("group not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
gsKey := groupSubjectKey(subject.Namespace, group, addr)
|
||||||
|
storage.Put(ctx, gsKey, []byte{1})
|
||||||
|
|
||||||
|
runtime.Notify("AddSubjectToGroup", addr, subject.Namespace, group)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveSubjectFromGroup(addr interop.Hash160, ns, group string) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
checkContractOwner(ctx)
|
||||||
|
|
||||||
|
if len(addr) != interop.Hash160Len {
|
||||||
|
panic("invalid address length")
|
||||||
|
}
|
||||||
|
|
||||||
|
sKey := subjectKeyFromAddr(addr)
|
||||||
|
data := storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("subject not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
gKey := groupKey(ns, group)
|
||||||
|
data = storage.Get(ctx, gKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("group not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
gsKey := groupSubjectKey(ns, group, addr)
|
||||||
|
storage.Delete(ctx, gsKey)
|
||||||
|
|
||||||
|
runtime.Notify("RemoveSubjectFromGroup", addr, ns, group)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListGroupSubjects(ns, group string) iterator.Iterator {
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
return storage.Find(ctx, groupSubjectPrefix(ns, group), storage.KeysOnly|storage.RemovePrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteGroup(ns, group string) {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
checkContractOwner(ctx)
|
||||||
|
|
||||||
|
gKey := groupKey(ns, group)
|
||||||
|
data := storage.Get(ctx, gKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("group not found")
|
||||||
|
}
|
||||||
|
storage.Delete(ctx, gKey)
|
||||||
|
|
||||||
|
it := storage.Find(ctx, groupSubjectPrefix(ns, group), storage.KeysOnly)
|
||||||
|
for iterator.Next(it) {
|
||||||
|
gsKey := iterator.Value(it).([]byte)
|
||||||
|
storage.Delete(ctx, gsKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.Notify("DeleteGroup", ns, group)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkContractOwner(ctx storage.Context) {
|
||||||
|
it := storage.Find(ctx, []byte{ownerKeysPrefix}, storage.KeysOnly|storage.RemovePrefix)
|
||||||
|
for iterator.Next(it) {
|
||||||
|
owner := iterator.Value(it).([]byte)
|
||||||
|
if runtime.CheckWitness(owner) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("not witnessed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeSubjectFromNamespace(ctx storage.Context, ns string, addr interop.Hash160) {
|
||||||
|
nsSubjKey := namespaceSubjectKey(ns, addr)
|
||||||
|
storage.Delete(ctx, nsSubjKey)
|
||||||
|
|
||||||
|
nsHash := ripemd160Hash(ns)
|
||||||
|
it := storage.Find(ctx, groupPrefixFromHash(nsHash), storage.KeysOnly|storage.RemovePrefix)
|
||||||
|
for iterator.Next(it) {
|
||||||
|
groupHash := iterator.Value(it).([]byte)
|
||||||
|
storage.Delete(ctx, groupSubjectKeyFromHashes(nsHash, groupHash, addr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setNamespaceSubjectName(ctx storage.Context, subj Subject) {
|
||||||
|
if subj.Name == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nsSubjNameKey := namespaceSubjectNameKey(subj.Namespace, subj.Name)
|
||||||
|
subjKey := storage.Get(ctx, nsSubjNameKey).(interop.PublicKey)
|
||||||
|
if subjKey == nil {
|
||||||
|
storage.Put(ctx, nsSubjNameKey, subj.PrimaryKey)
|
||||||
|
} else if !common.BytesEqual(subjKey, subj.PrimaryKey) {
|
||||||
|
panic("subject name is not available in the current namespace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteNamespaceSubjectName(ctx storage.Context, ns, subjName string) {
|
||||||
|
if subjName == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nsSubjNameKey := namespaceSubjectNameKey(ns, subjName)
|
||||||
|
storage.Delete(ctx, nsSubjNameKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNamespaceSubjectName(ctx storage.Context, subj Subject, oldName string) {
|
||||||
|
if subj.Name == oldName {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteNamespaceSubjectName(ctx, subj.Namespace, oldName)
|
||||||
|
setNamespaceSubjectName(ctx, subj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ownerKey(owner interop.Hash160) []byte {
|
||||||
|
return append([]byte{ownerKeysPrefix}, owner...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func subjectKey(key interop.PublicKey) []byte {
|
||||||
|
addr := contract.CreateStandardAccount(key)
|
||||||
|
return subjectKeyFromAddr(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func subjectKeyFromAddr(addr interop.Hash160) []byte {
|
||||||
|
return append([]byte{subjectKeysPrefix}, addr...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func subjectAdditionalKey(additionalKey interop.PublicKey, primeAddr interop.Hash160) []byte {
|
||||||
|
return append(subjectAdditionalPrefix(additionalKey), primeAddr...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func subjectAdditionalPrefix(additionalKey interop.PublicKey) []byte {
|
||||||
|
addr := contract.CreateStandardAccount(additionalKey)
|
||||||
|
return append([]byte{additionalKeysPrefix}, addr...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func namespaceKey(ns string) []byte {
|
||||||
|
return namespaceKeyFromHash(ripemd160Hash(ns))
|
||||||
|
}
|
||||||
|
func namespaceKeyFromHash(ns []byte) []byte {
|
||||||
|
return append([]byte{namespaceKeysPrefix}, ns...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupKey(ns, group string) []byte {
|
||||||
|
prefix := groupPrefix(ns)
|
||||||
|
return append(prefix, ripemd160Hash(group)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupKeyFromHashes(nsHash, groupHash []byte) []byte {
|
||||||
|
prefix := groupPrefixFromHash(nsHash)
|
||||||
|
return append(prefix, groupHash...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupPrefix(ns string) []byte {
|
||||||
|
return groupPrefixFromHash(ripemd160Hash(ns))
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupPrefixFromHash(nsHash []byte) []byte {
|
||||||
|
return append([]byte{groupKeysPrefix}, nsHash...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ripemd160Hash(data string) []byte {
|
||||||
|
return crypto.Ripemd160([]byte(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func namespaceSubjectKey(ns string, addr interop.Hash160) []byte {
|
||||||
|
prefix := namespaceSubjectPrefix(ns)
|
||||||
|
return append(prefix, addr...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func namespaceSubjectPrefix(ns string) []byte {
|
||||||
|
nsHash := ripemd160Hash(ns)
|
||||||
|
return append([]byte{namespaceSubjectsKeysPrefix}, nsHash...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func namespaceSubjectNameKey(ns, subjName string) []byte {
|
||||||
|
nsHash := ripemd160Hash(ns)
|
||||||
|
nameHash := ripemd160Hash(subjName)
|
||||||
|
return append([]byte{namespaceSubjectsNamesPrefix}, append(nsHash, nameHash...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupSubjectKey(ns, group string, addr interop.Hash160) []byte {
|
||||||
|
prefix := groupSubjectPrefix(ns, group)
|
||||||
|
return append(prefix, addr...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupSubjectKeyFromHashes(nsHash, groupHash []byte, addr interop.Hash160) []byte {
|
||||||
|
prefix := groupSubjectPrefixFromHashes(nsHash, groupHash)
|
||||||
|
return append(prefix, addr...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupSubjectPrefix(ns, group string) []byte {
|
||||||
|
nsHash := ripemd160Hash(ns)
|
||||||
|
gHash := ripemd160Hash(group)
|
||||||
|
return groupSubjectPrefixFromHashes(nsHash, gHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupSubjectPrefixFromHashes(nsHash, groupHash []byte) []byte {
|
||||||
|
return append([]byte{groupSubjectsKeysPrefix}, append(nsHash, groupHash...)...)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue