forked from TrueCloudLab/frostfs-contract
[#58] neofs: Store alphabet keys instead of inner ring
Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
parent
c3a7c6ad23
commit
e74d8bf48d
2 changed files with 52 additions and 95 deletions
|
@ -1,5 +1,5 @@
|
||||||
name: "NeoFS"
|
name: "NeoFS"
|
||||||
safemethods: ["innerRingList", "innerRingCandidates", "isInnerRing", "config", "listConfig", "version"]
|
safemethods: ["alphabetList", "innerRingCandidates", "config", "listConfig", "version"]
|
||||||
events:
|
events:
|
||||||
- name: Deposit
|
- name: Deposit
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -41,11 +41,11 @@ events:
|
||||||
type: ByteArray
|
type: ByteArray
|
||||||
- name: keys
|
- name: keys
|
||||||
type: Array
|
type: Array
|
||||||
- name: InnerRingUpdate
|
- name: AlphabetUpdate
|
||||||
parameters:
|
parameters:
|
||||||
- name: id
|
- name: id
|
||||||
type: ByteArray
|
type: ByteArray
|
||||||
- name: innerRingList
|
- name: alphabet
|
||||||
type: Array
|
type: Array
|
||||||
- name: SetConfig
|
- name: SetConfig
|
||||||
parameters:
|
parameters:
|
||||||
|
|
|
@ -14,12 +14,11 @@ package smart_contract
|
||||||
- Unbind
|
- Unbind
|
||||||
|
|
||||||
Inner ring list related methods:
|
Inner ring list related methods:
|
||||||
- InnerRingList
|
- AlphabetList
|
||||||
- InnerRingCandidates
|
- InnerRingCandidates
|
||||||
- IsInnerRing
|
|
||||||
- InnerRingCandidateAdd
|
- InnerRingCandidateAdd
|
||||||
- InnerRingCandidateRemove
|
- InnerRingCandidateRemove
|
||||||
- InnerRingUpdate
|
- AlphabetUpdate
|
||||||
|
|
||||||
Config methods:
|
Config methods:
|
||||||
- Config
|
- Config
|
||||||
|
@ -61,12 +60,11 @@ const (
|
||||||
|
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
innerRingKey = "innerring"
|
alphabetKey = "alphabet"
|
||||||
candidatesKey = "candidates"
|
candidatesKey = "candidates"
|
||||||
cashedChequesKey = "cheques"
|
cashedChequesKey = "cheques"
|
||||||
|
|
||||||
publicKeySize = 33
|
publicKeySize = 33
|
||||||
minInnerRingSize = 3
|
|
||||||
|
|
||||||
maxBalanceAmount = 9000 // Max integer of Fixed12 in JSON bound (2**53-1)
|
maxBalanceAmount = 9000 // Max integer of Fixed12 in JSON bound (2**53-1)
|
||||||
|
|
||||||
|
@ -78,7 +76,7 @@ var (
|
||||||
configPrefix = []byte("config")
|
configPrefix = []byte("config")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init set up initial inner ring node keys.
|
// Init set up initial alphabet node keys.
|
||||||
func Init(owner interop.PublicKey, args []interop.PublicKey) bool {
|
func Init(owner interop.PublicKey, args []interop.PublicKey) bool {
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
|
@ -89,7 +87,7 @@ func Init(owner interop.PublicKey, args []interop.PublicKey) bool {
|
||||||
var irList []common.IRNode
|
var irList []common.IRNode
|
||||||
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
panic("neofs: at least one inner ring key must be provided")
|
panic("neofs: at least one alphabet key must be provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(args); i++ {
|
for i := 0; i < len(args); i++ {
|
||||||
|
@ -101,7 +99,7 @@ func Init(owner interop.PublicKey, args []interop.PublicKey) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize all storage slices
|
// initialize all storage slices
|
||||||
common.SetSerialized(ctx, innerRingKey, irList)
|
common.SetSerialized(ctx, alphabetKey, irList)
|
||||||
common.InitVote(ctx)
|
common.InitVote(ctx)
|
||||||
common.SetSerialized(ctx, candidatesKey, []common.IRNode{})
|
common.SetSerialized(ctx, candidatesKey, []common.IRNode{})
|
||||||
common.SetSerialized(ctx, cashedChequesKey, []cheque{})
|
common.SetSerialized(ctx, cashedChequesKey, []cheque{})
|
||||||
|
@ -128,16 +126,16 @@ func Migrate(script []byte, manifest []byte) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// InnerRingList returns array of inner ring node keys.
|
// AlphabetList returns array of alphabet node keys.
|
||||||
func InnerRingList() []common.IRNode {
|
func AlphabetList() []common.IRNode {
|
||||||
ctx := storage.GetReadOnlyContext()
|
ctx := storage.GetReadOnlyContext()
|
||||||
return getInnerRingNodes(ctx, innerRingKey)
|
return getNodes(ctx, alphabetKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InnerRingCandidates returns array of inner ring candidate node keys.
|
// InnerRingCandidates returns array of inner ring candidate node keys.
|
||||||
func InnerRingCandidates() []common.IRNode {
|
func InnerRingCandidates() []common.IRNode {
|
||||||
ctx := storage.GetReadOnlyContext()
|
ctx := storage.GetReadOnlyContext()
|
||||||
return getInnerRingNodes(ctx, candidatesKey)
|
return getNodes(ctx, candidatesKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InnerRingCandidateRemove removes key from the list of inner ring candidates.
|
// InnerRingCandidateRemove removes key from the list of inner ring candidates.
|
||||||
|
@ -149,7 +147,7 @@ func InnerRingCandidateRemove(key interop.PublicKey) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := []common.IRNode{} // it is explicit declaration of empty slice, not nil
|
nodes := []common.IRNode{} // it is explicit declaration of empty slice, not nil
|
||||||
candidates := getInnerRingNodes(ctx, candidatesKey)
|
candidates := getNodes(ctx, candidatesKey)
|
||||||
|
|
||||||
for i := range candidates {
|
for i := range candidates {
|
||||||
c := candidates[i]
|
c := candidates[i]
|
||||||
|
@ -174,7 +172,7 @@ func InnerRingCandidateAdd(key interop.PublicKey) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
c := common.IRNode{PublicKey: key}
|
c := common.IRNode{PublicKey: key}
|
||||||
candidates := getInnerRingNodes(ctx, candidatesKey)
|
candidates := getNodes(ctx, candidatesKey)
|
||||||
|
|
||||||
list, ok := addNode(candidates, c)
|
list, ok := addNode(candidates, c)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -273,15 +271,15 @@ func Withdraw(user []byte, amount int) bool {
|
||||||
// locked in NeoFS balance contract.
|
// locked in NeoFS balance contract.
|
||||||
func Cheque(id []byte, user interop.Hash160, amount int, lockAcc []byte) bool {
|
func Cheque(id []byte, user interop.Hash160, amount int, lockAcc []byte) bool {
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
irList := getInnerRingNodes(ctx, innerRingKey)
|
alphabet := getNodes(ctx, alphabetKey)
|
||||||
threshold := len(irList)/3*2 + 1
|
threshold := len(alphabet)/3*2 + 1
|
||||||
|
|
||||||
cashedCheques := getCashedCheques(ctx)
|
cashedCheques := getCashedCheques(ctx)
|
||||||
hashID := crypto.Sha256(id)
|
hashID := crypto.Sha256(id)
|
||||||
|
|
||||||
irKey := common.InnerRingInvoker(irList)
|
key := common.InnerRingInvoker(alphabet)
|
||||||
if len(irKey) == 0 {
|
if len(key) == 0 {
|
||||||
panic("cheque: invoked by non inner ring node")
|
panic("cheque: invoked by non alphabet node")
|
||||||
}
|
}
|
||||||
|
|
||||||
c := cheque{id: id}
|
c := cheque{id: id}
|
||||||
|
@ -291,7 +289,7 @@ func Cheque(id []byte, user interop.Hash160, amount int, lockAcc []byte) bool {
|
||||||
panic("cheque: non unique id")
|
panic("cheque: non unique id")
|
||||||
}
|
}
|
||||||
|
|
||||||
n := common.Vote(ctx, hashID, irKey)
|
n := common.Vote(ctx, hashID, key)
|
||||||
if n >= threshold {
|
if n >= threshold {
|
||||||
common.RemoveVotes(ctx, hashID)
|
common.RemoveVotes(ctx, hashID)
|
||||||
|
|
||||||
|
@ -347,21 +345,21 @@ func Unbind(user []byte, keys []interop.PublicKey) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// InnerRingUpdate updates list of inner ring nodes with provided list of
|
// AlphabetUpdate updates list of alphabet nodes with provided list of
|
||||||
// public keys.
|
// public keys.
|
||||||
func InnerRingUpdate(chequeID []byte, args []interop.PublicKey) bool {
|
func AlphabetUpdate(chequeID []byte, args []interop.PublicKey) bool {
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
if len(args) < minInnerRingSize {
|
if len(args) == 0 {
|
||||||
panic("irUpdate: bad arguments")
|
panic("alphabetUpdate: bad arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
irList := getInnerRingNodes(ctx, innerRingKey)
|
alphabet := getNodes(ctx, alphabetKey)
|
||||||
threshold := len(irList)/3*2 + 1
|
threshold := len(alphabet)/3*2 + 1
|
||||||
|
|
||||||
irKey := common.InnerRingInvoker(irList)
|
key := common.InnerRingInvoker(alphabet)
|
||||||
if len(irKey) == 0 {
|
if len(key) == 0 {
|
||||||
panic("innerRingUpdate: invoked by non inner ring node")
|
panic("innerRingUpdate: invoked by non alphabet node")
|
||||||
}
|
}
|
||||||
|
|
||||||
c := cheque{id: chequeID}
|
c := cheque{id: chequeID}
|
||||||
|
@ -373,76 +371,35 @@ func InnerRingUpdate(chequeID []byte, args []interop.PublicKey) bool {
|
||||||
panic("irUpdate: non unique chequeID")
|
panic("irUpdate: non unique chequeID")
|
||||||
}
|
}
|
||||||
|
|
||||||
oldNodes := 0
|
newAlphabet := []common.IRNode{}
|
||||||
candidates := getInnerRingNodes(ctx, candidatesKey)
|
|
||||||
newIR := []common.IRNode{}
|
|
||||||
|
|
||||||
loop:
|
|
||||||
for i := 0; i < len(args); i++ {
|
for i := 0; i < len(args); i++ {
|
||||||
key := args[i]
|
pubKey := args[i]
|
||||||
if len(key) != publicKeySize {
|
if len(pubKey) != publicKeySize {
|
||||||
panic("irUpdate: invalid public key in inner ring list")
|
panic("alphabetUpdate: invalid public key in alphabet list")
|
||||||
}
|
}
|
||||||
|
|
||||||
// find key in actual inner ring list
|
newAlphabet = append(newAlphabet, common.IRNode{
|
||||||
for j := 0; j < len(irList); j++ {
|
PublicKey: pubKey,
|
||||||
n := irList[j]
|
})
|
||||||
if common.BytesEqual(n.PublicKey, key) {
|
|
||||||
newIR = append(newIR, n)
|
|
||||||
oldNodes++
|
|
||||||
|
|
||||||
continue loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// find key in candidates list
|
|
||||||
candidates, newIR, ok = rmNodeByKey(candidates, newIR, key)
|
|
||||||
if !ok {
|
|
||||||
panic("irUpdate: unknown public key in inner ring list")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if oldNodes < len(newIR)*2/3+1 {
|
|
||||||
panic("irUpdate: inner ring change rate must not be more than 1/3 ")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hashID := crypto.Sha256(chequeID)
|
hashID := crypto.Sha256(chequeID)
|
||||||
|
|
||||||
n := common.Vote(ctx, hashID, irKey)
|
n := common.Vote(ctx, hashID, key)
|
||||||
if n >= threshold {
|
if n >= threshold {
|
||||||
common.RemoveVotes(ctx, hashID)
|
common.RemoveVotes(ctx, hashID)
|
||||||
|
|
||||||
common.SetSerialized(ctx, candidatesKey, candidates)
|
common.SetSerialized(ctx, alphabetKey, newAlphabet)
|
||||||
common.SetSerialized(ctx, innerRingKey, newIR)
|
|
||||||
common.SetSerialized(ctx, cashedChequesKey, chequesList)
|
common.SetSerialized(ctx, cashedChequesKey, chequesList)
|
||||||
|
|
||||||
runtime.Notify("InnerRingUpdate", c.id, newIR)
|
runtime.Notify("AlphabetUpdate", c.id, newAlphabet)
|
||||||
runtime.Log("irUpdate: inner ring list has been updated")
|
runtime.Log("alphabetUpdate: alphabet list has been updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsInnerRing returns 'true' if key is inside of inner ring list.
|
|
||||||
func IsInnerRing(key []byte) bool {
|
|
||||||
ctx := storage.GetReadOnlyContext()
|
|
||||||
|
|
||||||
if len(key) != publicKeySize {
|
|
||||||
panic("isInnerRing: incorrect public key")
|
|
||||||
}
|
|
||||||
|
|
||||||
irList := getInnerRingNodes(ctx, innerRingKey)
|
|
||||||
for i := range irList {
|
|
||||||
node := irList[i]
|
|
||||||
|
|
||||||
if common.BytesEqual(node.PublicKey, key) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config returns value of NeoFS configuration with provided key.
|
// Config returns value of NeoFS configuration with provided key.
|
||||||
func Config(key []byte) interface{} {
|
func Config(key []byte) interface{} {
|
||||||
ctx := storage.GetReadOnlyContext()
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
@ -453,13 +410,13 @@ func Config(key []byte) interface{} {
|
||||||
func SetConfig(id, key, val []byte) bool {
|
func SetConfig(id, key, val []byte) bool {
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
// check if it is inner ring invocation
|
// check if it is alphabet invocation
|
||||||
irList := getInnerRingNodes(ctx, innerRingKey)
|
alphabet := getNodes(ctx, alphabetKey)
|
||||||
threshold := len(irList)/3*2 + 1
|
threshold := len(alphabet)/3*2 + 1
|
||||||
|
|
||||||
irKey := common.InnerRingInvoker(irList)
|
nodeKey := common.InnerRingInvoker(alphabet)
|
||||||
if len(irKey) == 0 {
|
if len(nodeKey) == 0 {
|
||||||
panic("setConfig: invoked by non inner ring node")
|
panic("setConfig: invoked by non alphabet node")
|
||||||
}
|
}
|
||||||
|
|
||||||
// check unique id of the operation
|
// check unique id of the operation
|
||||||
|
@ -474,7 +431,7 @@ func SetConfig(id, key, val []byte) bool {
|
||||||
// vote for new configuration value
|
// vote for new configuration value
|
||||||
hashID := crypto.Sha256(id)
|
hashID := crypto.Sha256(id)
|
||||||
|
|
||||||
n := common.Vote(ctx, hashID, irKey)
|
n := common.Vote(ctx, hashID, nodeKey)
|
||||||
if n >= threshold {
|
if n >= threshold {
|
||||||
common.RemoveVotes(ctx, hashID)
|
common.RemoveVotes(ctx, hashID)
|
||||||
|
|
||||||
|
@ -539,8 +496,8 @@ func Version() int {
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInnerRingNodes returns deserialized slice of inner ring nodes from storage.
|
// getNodes returns deserialized slice of nodes from storage.
|
||||||
func getInnerRingNodes(ctx storage.Context, key string) []common.IRNode {
|
func getNodes(ctx storage.Context, key string) []common.IRNode {
|
||||||
data := storage.Get(ctx, key)
|
data := storage.Get(ctx, key)
|
||||||
if data != nil {
|
if data != nil {
|
||||||
return std.Deserialize(data.([]byte)).([]common.IRNode)
|
return std.Deserialize(data.([]byte)).([]common.IRNode)
|
||||||
|
@ -549,7 +506,7 @@ func getInnerRingNodes(ctx storage.Context, key string) []common.IRNode {
|
||||||
return []common.IRNode{}
|
return []common.IRNode{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInnerRingNodes returns deserialized slice of used cheques.
|
// getCashedCheques returns deserialized slice of used cheques.
|
||||||
func getCashedCheques(ctx storage.Context) []cheque {
|
func getCashedCheques(ctx storage.Context) []cheque {
|
||||||
data := storage.Get(ctx, cashedChequesKey)
|
data := storage.Get(ctx, cashedChequesKey)
|
||||||
if data != nil {
|
if data != nil {
|
||||||
|
|
Loading…
Reference in a new issue