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 |
|
| `G` + [ RIPEMD160 of namespace ] + [ 8 byte group id ] + [ subject address ] | []byte{1} | subject that belongs to the group |
|
||||||
| `c` | Int | group id counter |
|
| `c` | Int | group id counter |
|
||||||
| `m` + [ RIPEMD160 of namespace ] + [ RIPEMD160 of subject name ] | Serialized group id int | group name to group id index |
|
| `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'
|
groupSubjectsKeysPrefix = 'G'
|
||||||
groupCounterKey = 'c'
|
groupCounterKey = 'c'
|
||||||
namespaceGroupsNamesPrefix = 'm'
|
namespaceGroupsNamesPrefix = 'm'
|
||||||
|
addressPrefix = 'A'
|
||||||
)
|
)
|
||||||
|
|
||||||
func _deploy(data any, isUpdate bool) {
|
func _deploy(data any, isUpdate bool) {
|
||||||
|
@ -112,6 +113,27 @@ func _deploy(data any, isUpdate bool) {
|
||||||
storage.Put(ctx, adminKey, args.admin)
|
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, groupCounterKey, 0)
|
||||||
storage.Put(ctx, namespaceKey(""), std.Serialize(Namespace{}))
|
storage.Put(ctx, namespaceKey(""), std.Serialize(Namespace{}))
|
||||||
|
|
||||||
|
@ -182,6 +204,11 @@ func CreateSubject(ns string, key interop.PublicKey) {
|
||||||
panic("key is occupied")
|
panic("key is occupied")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allAddressKey := addressKey(addr)
|
||||||
|
if storage.Get(ctx, allAddressKey) != nil {
|
||||||
|
panic("key is occupied by another additional key")
|
||||||
|
}
|
||||||
|
|
||||||
nsKey := namespaceKey(ns)
|
nsKey := namespaceKey(ns)
|
||||||
data = storage.Get(ctx, nsKey).([]byte)
|
data = storage.Get(ctx, nsKey).([]byte)
|
||||||
if data == nil {
|
if data == nil {
|
||||||
|
@ -197,6 +224,7 @@ func CreateSubject(ns string, key interop.PublicKey) {
|
||||||
|
|
||||||
nsSubjKey := namespaceSubjectKey(ns, addr)
|
nsSubjKey := namespaceSubjectKey(ns, addr)
|
||||||
storage.Put(ctx, nsSubjKey, []byte{1})
|
storage.Put(ctx, nsSubjKey, []byte{1})
|
||||||
|
storage.Put(ctx, allAddressKey, true)
|
||||||
|
|
||||||
runtime.Notify("CreateSubject", interop.Hash160(addr))
|
runtime.Notify("CreateSubject", interop.Hash160(addr))
|
||||||
}
|
}
|
||||||
|
@ -213,6 +241,11 @@ func AddSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
||||||
panic("incorrect public key length")
|
panic("incorrect public key length")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addressKey := addressKey(contract.CreateStandardAccount(key))
|
||||||
|
if storage.Get(ctx, addressKey) != nil {
|
||||||
|
panic("key is occupied")
|
||||||
|
}
|
||||||
|
|
||||||
saKey := subjectAdditionalKey(key, addr)
|
saKey := subjectAdditionalKey(key, addr)
|
||||||
data := storage.Get(ctx, saKey).([]byte)
|
data := storage.Get(ctx, saKey).([]byte)
|
||||||
if data != nil {
|
if data != nil {
|
||||||
|
@ -230,6 +263,7 @@ func AddSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
||||||
subject.AdditionalKeys = append(subject.AdditionalKeys, key)
|
subject.AdditionalKeys = append(subject.AdditionalKeys, key)
|
||||||
|
|
||||||
storage.Put(ctx, sKey, std.Serialize(subject))
|
storage.Put(ctx, sKey, std.Serialize(subject))
|
||||||
|
storage.Put(ctx, addressKey, true)
|
||||||
runtime.Notify("AddSubjectKey", addr, key)
|
runtime.Notify("AddSubjectKey", addr, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +303,7 @@ func RemoveSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
||||||
subject.AdditionalKeys = additionalKeys
|
subject.AdditionalKeys = additionalKeys
|
||||||
|
|
||||||
storage.Put(ctx, sKey, std.Serialize(subject))
|
storage.Put(ctx, sKey, std.Serialize(subject))
|
||||||
|
storage.Delete(ctx, addressKey(contract.CreateStandardAccount(key)))
|
||||||
runtime.Notify("RemoveSubjectKey", addr, key)
|
runtime.Notify("RemoveSubjectKey", addr, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,6 +397,7 @@ func DeleteSubject(addr interop.Hash160) {
|
||||||
|
|
||||||
for i := 0; i < len(subj.AdditionalKeys); i++ {
|
for i := 0; i < len(subj.AdditionalKeys); i++ {
|
||||||
storage.Delete(ctx, subjectAdditionalKey(subj.AdditionalKeys[i], addr))
|
storage.Delete(ctx, subjectAdditionalKey(subj.AdditionalKeys[i], addr))
|
||||||
|
storage.Delete(ctx, addressKey(contract.CreateStandardAccount(subj.AdditionalKeys[i])))
|
||||||
}
|
}
|
||||||
storage.Delete(ctx, sKey)
|
storage.Delete(ctx, sKey)
|
||||||
|
|
||||||
|
@ -1029,3 +1065,7 @@ func idToBytes(itemID int) []byte {
|
||||||
zeros := make([]byte, 8-ln)
|
zeros := make([]byte, 8-ln)
|
||||||
return append(b, zeros...)
|
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) {
|
func checkPublicKeyResult(t *testing.T, s *vm.Stack, err error, key *keys.PrivateKey) {
|
||||||
if key == nil {
|
if key == nil {
|
||||||
require.ErrorContains(t, err, "not found")
|
require.ErrorContains(t, err, "not found")
|
||||||
|
|
Loading…
Reference in a new issue