forked from TrueCloudLab/frostfs-contract
[#118] frostfsid: Restrict keys to a single subject
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
This commit is contained in:
parent
8b586081eb
commit
7a8c64b966
3 changed files with 79 additions and 0 deletions
|
@ -20,6 +20,7 @@ FrostFSID contract does not produce notifications to process.
|
|||
| `G` + [ RIPEMD160 of namespace ] + [ 8 byte group id ] + [ subject address ] | []byte{1} | subject that belongs to the group |
|
||||
| `c` | Int | group id counter |
|
||||
| `m` + [ RIPEMD160 of namespace ] + [ RIPEMD160 of subject name ] | Serialized group id int | group name to group id index |
|
||||
| `A` + [ subject address ] | bool | means that the wallet has been used |
|
||||
|
||||
|
||||
*/
|
||||
|
|
|
@ -96,6 +96,7 @@ const (
|
|||
groupSubjectsKeysPrefix = 'G'
|
||||
groupCounterKey = 'c'
|
||||
namespaceGroupsNamesPrefix = 'm'
|
||||
addressPrefix = 'A'
|
||||
)
|
||||
|
||||
func _deploy(data any, isUpdate bool) {
|
||||
|
@ -112,6 +113,27 @@ func _deploy(data any, isUpdate bool) {
|
|||
storage.Put(ctx, adminKey, args.admin)
|
||||
}
|
||||
|
||||
if isUpdate {
|
||||
it := storage.Find(ctx, subjectKeysPrefix, storage.ValuesOnly)
|
||||
for iterator.Next(it) {
|
||||
subjectRaw := iterator.Value(it)
|
||||
subject := std.Deserialize(subjectRaw.([]byte)).(Subject)
|
||||
address := addressKey(contract.CreateStandardAccount(subject.PrimaryKey))
|
||||
if storage.Get(ctx, address) != nil {
|
||||
panic("frostfsid contract contains duplicate keys")
|
||||
}
|
||||
storage.Put(ctx, address, true)
|
||||
|
||||
for i := 0; i < len(subject.AdditionalKeys); i++ {
|
||||
address = addressKey(contract.CreateStandardAccount(subject.AdditionalKeys[i]))
|
||||
if storage.Get(ctx, address) != nil {
|
||||
panic("frostfsid contract contains duplicate keys")
|
||||
}
|
||||
storage.Put(ctx, address, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
storage.Put(ctx, groupCounterKey, 0)
|
||||
storage.Put(ctx, namespaceKey(""), std.Serialize(Namespace{}))
|
||||
|
||||
|
@ -182,6 +204,11 @@ func CreateSubject(ns string, key interop.PublicKey) {
|
|||
panic("key is occupied")
|
||||
}
|
||||
|
||||
allAddressKey := addressKey(addr)
|
||||
if storage.Get(ctx, allAddressKey) != nil {
|
||||
panic("key is occupied by another additional key")
|
||||
}
|
||||
|
||||
nsKey := namespaceKey(ns)
|
||||
data = storage.Get(ctx, nsKey).([]byte)
|
||||
if data == nil {
|
||||
|
@ -197,6 +224,7 @@ func CreateSubject(ns string, key interop.PublicKey) {
|
|||
|
||||
nsSubjKey := namespaceSubjectKey(ns, addr)
|
||||
storage.Put(ctx, nsSubjKey, []byte{1})
|
||||
storage.Put(ctx, allAddressKey, true)
|
||||
|
||||
runtime.Notify("CreateSubject", interop.Hash160(addr))
|
||||
}
|
||||
|
@ -213,6 +241,11 @@ func AddSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
|||
panic("incorrect public key length")
|
||||
}
|
||||
|
||||
addressKey := addressKey(contract.CreateStandardAccount(key))
|
||||
if storage.Get(ctx, addressKey) != nil {
|
||||
panic("key is occupied")
|
||||
}
|
||||
|
||||
saKey := subjectAdditionalKey(key, addr)
|
||||
data := storage.Get(ctx, saKey).([]byte)
|
||||
if data != nil {
|
||||
|
@ -230,6 +263,7 @@ func AddSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
|||
subject.AdditionalKeys = append(subject.AdditionalKeys, key)
|
||||
|
||||
storage.Put(ctx, sKey, std.Serialize(subject))
|
||||
storage.Put(ctx, addressKey, true)
|
||||
runtime.Notify("AddSubjectKey", addr, key)
|
||||
}
|
||||
|
||||
|
@ -269,6 +303,7 @@ func RemoveSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
|||
subject.AdditionalKeys = additionalKeys
|
||||
|
||||
storage.Put(ctx, sKey, std.Serialize(subject))
|
||||
storage.Delete(ctx, addressKey(contract.CreateStandardAccount(key)))
|
||||
runtime.Notify("RemoveSubjectKey", addr, key)
|
||||
}
|
||||
|
||||
|
@ -362,6 +397,7 @@ func DeleteSubject(addr interop.Hash160) {
|
|||
|
||||
for i := 0; i < len(subj.AdditionalKeys); i++ {
|
||||
storage.Delete(ctx, subjectAdditionalKey(subj.AdditionalKeys[i], addr))
|
||||
storage.Delete(ctx, addressKey(contract.CreateStandardAccount(subj.AdditionalKeys[i])))
|
||||
}
|
||||
storage.Delete(ctx, sKey)
|
||||
|
||||
|
@ -1029,3 +1065,7 @@ func idToBytes(itemID int) []byte {
|
|||
zeros := make([]byte, 8-ln)
|
||||
return append(b, zeros...)
|
||||
}
|
||||
|
||||
func addressKey(address []byte) []byte {
|
||||
return append([]byte{addressPrefix}, address...)
|
||||
}
|
||||
|
|
|
@ -604,6 +604,44 @@ func TestFrostFSID_GroupManagement(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAdditionalKeyFromPrimarySubject(t *testing.T) {
|
||||
f := newFrostFSIDInvoker(t)
|
||||
invoker := f.OwnerInvoker()
|
||||
|
||||
subjAPrimaryKey, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
subjAKeyAddr := subjAPrimaryKey.PublicKey().GetScriptHash()
|
||||
|
||||
subjBPrimaryKey, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
subjBKeyAddr := subjBPrimaryKey.PublicKey().GetScriptHash()
|
||||
|
||||
subjCPrimaryKey, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
subjDPrimaryKey, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
invoker.Invoke(t, stackitem.Null{}, createSubjectMethod, defaultNamespace, subjAPrimaryKey.PublicKey().Bytes())
|
||||
|
||||
invoker.Invoke(t, stackitem.Null{}, createSubjectMethod, defaultNamespace, subjBPrimaryKey.PublicKey().Bytes())
|
||||
|
||||
invoker.InvokeFail(t, "key is occupied", addSubjectKeyMethod, subjBKeyAddr, subjAPrimaryKey.PublicKey().Bytes())
|
||||
|
||||
invoker.Invoke(t, stackitem.Null{}, addSubjectKeyMethod, subjAKeyAddr, subjCPrimaryKey.PublicKey().Bytes())
|
||||
invoker.InvokeFail(t, "key is occupied", addSubjectKeyMethod, subjBKeyAddr, subjCPrimaryKey.PublicKey().Bytes())
|
||||
|
||||
invoker.Invoke(t, stackitem.Null{}, addSubjectKeyMethod, subjAKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
||||
|
||||
invoker.InvokeFail(t, "key is occupied", addSubjectKeyMethod, subjBKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
||||
invoker.Invoke(t, stackitem.Null{}, removeSubjectKeyMethod, subjAKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
||||
invoker.Invoke(t, stackitem.Null{}, addSubjectKeyMethod, subjBKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
||||
|
||||
invoker.InvokeFail(t, "key is occupied", addSubjectKeyMethod, subjAKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
||||
invoker.Invoke(t, stackitem.Null{}, deleteSubjectMethod, subjBKeyAddr)
|
||||
invoker.Invoke(t, stackitem.Null{}, addSubjectKeyMethod, subjAKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
||||
}
|
||||
|
||||
func checkPublicKeyResult(t *testing.T, s *vm.Stack, err error, key *keys.PrivateKey) {
|
||||
if key == nil {
|
||||
require.ErrorContains(t, err, "not found")
|
||||
|
|
Loading…
Reference in a new issue