frostfs-contract/neofsid/neofsid_contract.go
Leonard Lyubich 8ceba2a7c2 [#42] Share InnerRingList function between contracts
Define InnerRingList function that calls "innerRingList" method of
particular smart contract. Define InnerRingListViaStorage function
that reads address of smart contract from the storage by key, and
calls InnerRingList with the result. Reuse these functions in all
contracts.

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
2021-02-04 10:31:24 +03:00

202 lines
4.1 KiB
Go

package neofsidcontract
import (
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
)
type (
UserInfo struct {
Keys [][]byte
}
)
const (
version = 1
netmapContractKey = "netmapScriptHash"
containerContractKey = "containerScriptHash"
)
var (
ctx storage.Context
)
func init() {
if runtime.GetTrigger() != runtime.Application {
panic("contract has not been called in application node")
}
ctx = storage.GetContext()
}
func Init(addrNetmap, addrContainer []byte) {
if storage.Get(ctx, netmapContractKey) != nil {
panic("init: contract already deployed")
}
if len(addrNetmap) != 20 || len(addrContainer) != 20 {
panic("init: incorrect length of contract script hash")
}
storage.Put(ctx, netmapContractKey, addrNetmap)
storage.Put(ctx, containerContractKey, addrContainer)
runtime.Log("neofsid contract initialized")
}
func AddKey(owner []byte, keys [][]byte) bool {
var (
n int // number of votes for inner ring invoke
id []byte // ballot key of the inner ring invocation
)
if len(owner) != 25 {
panic("addKey: incorrect owner")
}
innerRing := irList()
threshold := len(innerRing)/3*2 + 1
irKey := common.InnerRingInvoker(innerRing)
if len(irKey) == 0 {
panic("addKey: invocation from non inner ring node")
}
info := getUserInfo(ctx, owner)
addLoop:
for i := 0; i < len(keys); i++ {
pubKey := keys[i]
if len(pubKey) != 33 {
panic("addKey: incorrect public key")
}
for j := range info.Keys {
key := info.Keys[j]
if common.BytesEqual(key, pubKey) {
continue addLoop
}
}
info.Keys = append(info.Keys, pubKey)
}
fromKnownContract := fromKnownContract(runtime.GetCallingScriptHash())
if fromKnownContract {
n = threshold
runtime.Log("addKey: processed indirect invoke")
} else {
id := invokeIDKeys(owner, keys, []byte("add"))
n = common.Vote(ctx, id, irKey)
}
if n < threshold {
runtime.Log("addKey: processed invoke from inner ring")
return true
}
common.RemoveVotes(ctx, id)
common.SetSerialized(ctx, owner, info)
runtime.Log("addKey: key bound to the owner")
return true
}
func RemoveKey(owner []byte, keys [][]byte) bool {
if len(owner) != 25 {
panic("removeKey: incorrect owner")
}
innerRing := irList()
threshold := len(innerRing)/3*2 + 1
irKey := common.InnerRingInvoker(innerRing)
if len(irKey) == 0 {
panic("removeKey: invocation from non inner ring node")
}
id := invokeIDKeys(owner, keys, []byte("remove"))
n := common.Vote(ctx, id, irKey)
if n < threshold {
runtime.Log("removeKey: processed invoke from inner ring")
return true
}
common.RemoveVotes(ctx, id)
info := getUserInfo(ctx, owner)
var leftKeys [][]byte
rmLoop:
for i := range info.Keys {
key := info.Keys[i]
for j := 0; j < len(keys); j++ {
pubKey := keys[j]
if len(pubKey) != 33 {
panic("removeKey: incorrect public key")
}
if common.BytesEqual(key, pubKey) {
continue rmLoop
}
}
leftKeys = append(leftKeys, key)
}
info.Keys = leftKeys
common.SetSerialized(ctx, owner, info)
return true
}
func Key(owner []byte) [][]byte {
if len(owner) != 25 {
panic("key: incorrect owner")
}
info := getUserInfo(ctx, owner)
return info.Keys
}
func Version() int {
return version
}
func getUserInfo(ctx storage.Context, key interface{}) UserInfo {
data := storage.Get(ctx, key)
if data != nil {
return binary.Deserialize(data.([]byte)).(UserInfo)
}
return UserInfo{Keys: [][]byte{}}
}
func invokeIDKeys(owner []byte, keys [][]byte, prefix []byte) []byte {
prefix = append(prefix, owner...)
for i := range keys {
prefix = append(prefix, keys[i]...)
}
return crypto.SHA256(prefix)
}
func fromKnownContract(caller []byte) bool {
containerContractAddr := storage.Get(ctx, containerContractKey).([]byte)
if common.BytesEqual(caller, containerContractAddr) {
return true
}
return false
}
func irList() []common.IRNode {
return common.InnerRingListViaStorage(ctx, netmapContractKey)
}