[#7] Remove notary disabled code
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
This commit is contained in:
parent
6b13a83736
commit
d8530284fd
19 changed files with 137 additions and 1190 deletions
|
@ -5,6 +5,9 @@ Changelog for FrostFS Contract
|
|||
|
||||
### Added
|
||||
### Changed
|
||||
### Removed
|
||||
- Notary disabled code from all contracts (#7)
|
||||
|
||||
### Updated
|
||||
- `neo-go` to `v0.99.4`
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
||||
"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/native/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/neo"
|
||||
|
@ -34,6 +33,9 @@ func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {
|
|||
|
||||
func _deploy(data interface{}, isUpdate bool) {
|
||||
ctx := storage.GetContext()
|
||||
|
||||
common.RmAndCheckNotaryDisabledKey(data, notaryDisabledKey)
|
||||
|
||||
if isUpdate {
|
||||
args := data.([]interface{})
|
||||
common.CheckVersion(args[len(args)-1].(int))
|
||||
|
@ -41,6 +43,7 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
}
|
||||
|
||||
args := data.(struct {
|
||||
//TODO(@acid-ant): #9 remove notaryDisabled in future version
|
||||
notaryDisabled bool
|
||||
addrNetmap interop.Hash160
|
||||
addrProxy interop.Hash160
|
||||
|
@ -49,7 +52,7 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
total int
|
||||
})
|
||||
|
||||
if len(args.addrNetmap) != interop.Hash160Len || !args.notaryDisabled && len(args.addrProxy) != interop.Hash160Len {
|
||||
if len(args.addrNetmap) != interop.Hash160Len || len(args.addrProxy) != interop.Hash160Len {
|
||||
panic("incorrect length of contract script hash")
|
||||
}
|
||||
|
||||
|
@ -59,13 +62,6 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
storage.Put(ctx, indexKey, args.index)
|
||||
storage.Put(ctx, totalKey, args.total)
|
||||
|
||||
// initialize the way to collect signatures
|
||||
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
|
||||
if args.notaryDisabled {
|
||||
common.InitVote(ctx)
|
||||
runtime.Log(args.name + " notary disabled")
|
||||
}
|
||||
|
||||
runtime.Log(args.name + " contract initialized")
|
||||
}
|
||||
|
||||
|
@ -120,15 +116,11 @@ func checkPermission(ir []interop.PublicKey) bool {
|
|||
// and proxy contract. It can be invoked only by an Alphabet node of the Inner Ring.
|
||||
//
|
||||
// To produce GAS, an alphabet contract transfers all available NEO from the contract
|
||||
// account to itself. If notary is enabled, 50% of the GAS in the contract account
|
||||
// account to itself. 50% of the GAS in the contract account
|
||||
// are transferred to proxy contract. 43.75% of the GAS are equally distributed
|
||||
// among all Inner Ring nodes. Remaining 6.25% of the GAS stay in the contract.
|
||||
//
|
||||
// If notary is disabled, 87.5% of the GAS are equally distributed among all
|
||||
// Inner Ring nodes. Remaining 12.5% of the GAS stay in the contract.
|
||||
func Emit() {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
alphabet := common.AlphabetNodes()
|
||||
if !checkPermission(alphabet) {
|
||||
|
@ -143,7 +135,6 @@ func Emit() {
|
|||
|
||||
gasBalance := gas.BalanceOf(contractHash)
|
||||
|
||||
if !notaryDisabled {
|
||||
proxyAddr := storage.Get(ctx, proxyKey).(interop.Hash160)
|
||||
|
||||
proxyGas := gasBalance / 2
|
||||
|
@ -158,16 +149,8 @@ func Emit() {
|
|||
gasBalance -= proxyGas
|
||||
|
||||
runtime.Log("utility token has been emitted to proxy contract")
|
||||
}
|
||||
|
||||
var innerRing []interop.PublicKey
|
||||
|
||||
if notaryDisabled {
|
||||
netmapContract := storage.Get(ctx, netmapKey).(interop.Hash160)
|
||||
innerRing = common.InnerRingNodesFromNetmap(netmapContract)
|
||||
} else {
|
||||
innerRing = common.InnerRingNodes()
|
||||
}
|
||||
innerRing := common.InnerRingNodes()
|
||||
|
||||
gasPerNode := gasBalance * 7 / 8 / len(innerRing)
|
||||
|
||||
|
@ -192,25 +175,11 @@ func Emit() {
|
|||
// alphabet contracts) should vote for a new committee.
|
||||
func Vote(epoch int, candidates []interop.PublicKey) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
index := index(ctx)
|
||||
name := name(ctx)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("invalid invoker")
|
||||
}
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
curEpoch := currentEpoch(ctx)
|
||||
if epoch != curEpoch {
|
||||
|
@ -220,18 +189,6 @@ func Vote(epoch int, candidates []interop.PublicKey) {
|
|||
candidate := candidates[index%len(candidates)]
|
||||
address := runtime.GetExecutingScriptHash()
|
||||
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := voteID(epoch, candidates)
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
ok := neo.Vote(address, candidate)
|
||||
if ok {
|
||||
runtime.Log(name + ": successfully voted for validator")
|
||||
|
@ -240,21 +197,6 @@ func Vote(epoch int, candidates []interop.PublicKey) {
|
|||
}
|
||||
}
|
||||
|
||||
func voteID(epoch interface{}, args []interop.PublicKey) []byte {
|
||||
var (
|
||||
result []byte
|
||||
epochBytes = epoch.([]byte)
|
||||
)
|
||||
|
||||
result = append(result, epochBytes...)
|
||||
|
||||
for i := range args {
|
||||
result = append(result, args[i]...)
|
||||
}
|
||||
|
||||
return crypto.Sha256(result)
|
||||
}
|
||||
|
||||
// Name returns the Glagolitic name of the contract.
|
||||
func Name() string {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
|
|
|
@ -44,6 +44,9 @@ const (
|
|||
|
||||
func _deploy(data interface{}, isUpdate bool) {
|
||||
ctx := storage.GetContext()
|
||||
|
||||
common.RmAndCheckNotaryDisabledKey(data, notaryDisabledKey)
|
||||
|
||||
if isUpdate {
|
||||
args := data.([]interface{})
|
||||
common.CheckVersion(args[len(args)-1].(int))
|
||||
|
@ -51,6 +54,7 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
}
|
||||
|
||||
args := data.(struct {
|
||||
//TODO(@acid-ant): #9 remove notaryDisabled in future version
|
||||
notaryDisabled bool
|
||||
addrNetmap interop.Hash160
|
||||
})
|
||||
|
@ -61,12 +65,6 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
|
||||
storage.Put(ctx, netmapContractKey, args.addrNetmap)
|
||||
|
||||
// initialize the way to collect signatures
|
||||
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
|
||||
if args.notaryDisabled {
|
||||
runtime.Log("audit contract notary disabled")
|
||||
}
|
||||
|
||||
runtime.Log("audit contract initialized")
|
||||
}
|
||||
|
||||
|
@ -90,16 +88,8 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
|||
// in later epochs.
|
||||
func Put(rawAuditResult []byte) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var innerRing []interop.PublicKey
|
||||
|
||||
if notaryDisabled {
|
||||
netmapContract := storage.Get(ctx, netmapContractKey).(interop.Hash160)
|
||||
innerRing = common.InnerRingNodesFromNetmap(netmapContract)
|
||||
} else {
|
||||
innerRing = common.InnerRingNodes()
|
||||
}
|
||||
innerRing := common.InnerRingNodes()
|
||||
|
||||
hdr := newAuditHeader(rawAuditResult)
|
||||
presented := false
|
||||
|
@ -182,7 +172,6 @@ func list(it iterator.Iterator) [][]byte {
|
|||
|
||||
ignore := [][]byte{
|
||||
[]byte(netmapContractKey),
|
||||
[]byte(notaryDisabledKey),
|
||||
}
|
||||
|
||||
loop:
|
||||
|
|
|
@ -60,6 +60,9 @@ func init() {
|
|||
|
||||
func _deploy(data interface{}, isUpdate bool) {
|
||||
ctx := storage.GetContext()
|
||||
|
||||
common.RmAndCheckNotaryDisabledKey(data, notaryDisabledKey)
|
||||
|
||||
if isUpdate {
|
||||
args := data.([]interface{})
|
||||
common.CheckVersion(args[len(args)-1].(int))
|
||||
|
@ -67,6 +70,7 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
}
|
||||
|
||||
args := data.(struct {
|
||||
//TODO(@acid-ant): #9 remove notaryDisabled in future version
|
||||
notaryDisabled bool
|
||||
addrNetmap interop.Hash160
|
||||
addrContainer interop.Hash160
|
||||
|
@ -79,13 +83,6 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
storage.Put(ctx, netmapContractKey, args.addrNetmap)
|
||||
storage.Put(ctx, containerContractKey, args.addrContainer)
|
||||
|
||||
// initialize the way to collect signatures
|
||||
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
|
||||
if args.notaryDisabled {
|
||||
common.InitVote(ctx)
|
||||
runtime.Log("balance contract notary disabled")
|
||||
}
|
||||
|
||||
runtime.Log("balance contract initialized")
|
||||
}
|
||||
|
||||
|
@ -146,42 +143,9 @@ func Transfer(from, to interop.Hash160, amount int, data interface{}) bool {
|
|||
// Inner Ring with multisignature.
|
||||
func TransferX(from, to interop.Hash160, amount int, details []byte) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
indirectCall bool
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("this method must be invoked from inner ring")
|
||||
}
|
||||
|
||||
indirectCall = common.FromKnownContract(
|
||||
ctx,
|
||||
runtime.GetCallingScriptHash(),
|
||||
containerContractKey,
|
||||
)
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
if notaryDisabled && !indirectCall {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := common.InvokeID([]interface{}{from, to, amount}, []byte("transfer"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
result := token.transfer(ctx, from, to, amount, true, details)
|
||||
if !result {
|
||||
|
@ -201,23 +165,9 @@ func TransferX(from, to interop.Hash160, amount int, details []byte) {
|
|||
// to a new lock account that won't be used for anything beside Unlock and Burn.
|
||||
func Lock(txDetails []byte, from, to interop.Hash160, amount, until int) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("this method must be invoked from inner ring")
|
||||
}
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
details := common.LockTransferDetails(txDetails)
|
||||
|
||||
|
@ -227,18 +177,6 @@ func Lock(txDetails []byte, from, to interop.Hash160, amount, until int) {
|
|||
Parent: from,
|
||||
}
|
||||
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := common.InvokeID([]interface{}{txDetails}, []byte("lock"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
common.SetSerialized(ctx, to, lockAccount)
|
||||
|
||||
result := token.transfer(ctx, from, to, amount, true, details)
|
||||
|
@ -258,21 +196,9 @@ func Lock(txDetails []byte, from, to interop.Hash160, amount, until int) {
|
|||
// It produces Transfer and TransferX notifications.
|
||||
func NewEpoch(epochNum int) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
if notaryDisabled {
|
||||
indirectCall := common.FromKnownContract(
|
||||
ctx,
|
||||
runtime.GetCallingScriptHash(),
|
||||
netmapContractKey,
|
||||
)
|
||||
if !indirectCall {
|
||||
panic("this method must be invoked from inner ring")
|
||||
}
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
it := storage.Find(ctx, []byte{}, storage.KeysOnly)
|
||||
for iterator.Next(it) {
|
||||
|
@ -305,38 +231,12 @@ func NewEpoch(epochNum int) {
|
|||
// Mint increases total supply of NEP-17 compatible FrostFS token.
|
||||
func Mint(to interop.Hash160, amount int, txDetails []byte) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("this method must be invoked from inner ring")
|
||||
}
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
details := common.MintTransferDetails(txDetails)
|
||||
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := common.InvokeID([]interface{}{txDetails}, []byte("mint"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
ok := token.transfer(ctx, nil, to, amount, true, details)
|
||||
if !ok {
|
||||
panic("can't transfer assets")
|
||||
|
@ -362,38 +262,12 @@ func Mint(to interop.Hash160, amount int, txDetails []byte) {
|
|||
// compatible FrostFS token.
|
||||
func Burn(from interop.Hash160, amount int, txDetails []byte) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("this method must be invoked from inner ring")
|
||||
}
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
details := common.BurnTransferDetails(txDetails)
|
||||
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := common.InvokeID([]interface{}{txDetails}, []byte("burn"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
ok := token.transfer(ctx, from, nil, amount, true, details)
|
||||
if !ok {
|
||||
panic("can't transfer assets")
|
||||
|
|
26
common/common.go
Normal file
26
common/common.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/util"
|
||||
)
|
||||
|
||||
const (
|
||||
panicMsgForNotaryDisabledEnv = "contract not applicable for notary-disabled environment"
|
||||
)
|
||||
|
||||
// BytesEqual compares two slices of bytes by wrapping them into strings,
|
||||
// which is necessary with new util.Equals interop behaviour, see neo-go#1176.
|
||||
func BytesEqual(a []byte, b []byte) bool {
|
||||
return util.Equals(string(a), string(b))
|
||||
}
|
||||
|
||||
// RmAndCheckNotaryDisabledKey remove notary disabled key from storage and
|
||||
// panic in notary disabled environment
|
||||
func RmAndCheckNotaryDisabledKey(data interface{}, key interface{}) {
|
||||
//TODO(@acid-ant): #9 remove notaryDisabled from args in future version
|
||||
storage.Delete(storage.GetContext(), key)
|
||||
if data.([]interface{})[0].(bool) {
|
||||
panic(panicMsgForNotaryDisabledEnv)
|
||||
}
|
||||
}
|
28
common/ir.go
28
common/ir.go
|
@ -6,28 +6,12 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/neo"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
)
|
||||
|
||||
type IRNode struct {
|
||||
PublicKey interop.PublicKey
|
||||
}
|
||||
|
||||
const irListMethod = "innerRingList"
|
||||
|
||||
// InnerRingInvoker returns the public key of the inner ring node that has invoked the contract.
|
||||
// Work around for environments without notary support.
|
||||
func InnerRingInvoker(ir []interop.PublicKey) interop.PublicKey {
|
||||
for i := 0; i < len(ir); i++ {
|
||||
node := ir[i]
|
||||
if runtime.CheckWitness(node) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InnerRingNodes return a list of inner ring nodes from state validator role
|
||||
// in the sidechain.
|
||||
func InnerRingNodes() []interop.PublicKey {
|
||||
|
@ -35,18 +19,6 @@ func InnerRingNodes() []interop.PublicKey {
|
|||
return roles.GetDesignatedByRole(roles.NeoFSAlphabet, uint32(blockHeight+1))
|
||||
}
|
||||
|
||||
// InnerRingNodesFromNetmap gets a list of inner ring nodes through
|
||||
// calling "innerRingList" method of smart contract.
|
||||
// Work around for environments without notary support.
|
||||
func InnerRingNodesFromNetmap(sc interop.Hash160) []interop.PublicKey {
|
||||
nodes := contract.Call(sc, irListMethod, contract.ReadOnly).([]IRNode)
|
||||
pubs := []interop.PublicKey{}
|
||||
for i := range nodes {
|
||||
pubs = append(pubs, nodes[i].PublicKey)
|
||||
}
|
||||
return pubs
|
||||
}
|
||||
|
||||
// AlphabetNodes returns a list of alphabet nodes from committee in the sidechain.
|
||||
func AlphabetNodes() []interop.PublicKey {
|
||||
return neo.GetCommittee()
|
||||
|
|
|
@ -5,15 +5,6 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
)
|
||||
|
||||
func GetList(ctx storage.Context, key interface{}) [][]byte {
|
||||
data := storage.Get(ctx, key)
|
||||
if data != nil {
|
||||
return std.Deserialize(data.([]byte)).([][]byte)
|
||||
}
|
||||
|
||||
return [][]byte{}
|
||||
}
|
||||
|
||||
// SetSerialized serializes data and puts it into contract storage.
|
||||
func SetSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||
data := std.Serialize(value)
|
||||
|
|
149
common/vote.go
149
common/vote.go
|
@ -1,149 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/util"
|
||||
)
|
||||
|
||||
type Ballot struct {
|
||||
// ID of the voting decision.
|
||||
ID []byte
|
||||
|
||||
// Public keys of the already voted inner ring nodes.
|
||||
Voters []interop.PublicKey
|
||||
|
||||
// Height of block with the last vote.
|
||||
Height int
|
||||
}
|
||||
|
||||
const voteKey = "ballots"
|
||||
|
||||
const blockDiff = 20 // change base on performance evaluation
|
||||
|
||||
func InitVote(ctx storage.Context) {
|
||||
SetSerialized(ctx, voteKey, []Ballot{})
|
||||
}
|
||||
|
||||
// Vote adds ballot for the decision with a specific 'id' and returns the amount
|
||||
// of unique voters for that decision.
|
||||
func Vote(ctx storage.Context, id, from []byte) int {
|
||||
var (
|
||||
newCandidates []Ballot
|
||||
candidates = getBallots(ctx)
|
||||
found = -1
|
||||
blockHeight = ledger.CurrentIndex()
|
||||
)
|
||||
|
||||
for i := 0; i < len(candidates); i++ {
|
||||
cnd := candidates[i]
|
||||
|
||||
if blockHeight-cnd.Height > blockDiff {
|
||||
continue
|
||||
}
|
||||
|
||||
if BytesEqual(cnd.ID, id) {
|
||||
voters := cnd.Voters
|
||||
|
||||
for j := range voters {
|
||||
if BytesEqual(voters[j], from) {
|
||||
return len(voters)
|
||||
}
|
||||
}
|
||||
|
||||
voters = append(voters, from)
|
||||
cnd = Ballot{ID: id, Voters: voters, Height: blockHeight}
|
||||
found = len(voters)
|
||||
}
|
||||
|
||||
newCandidates = append(newCandidates, cnd)
|
||||
}
|
||||
|
||||
if found < 0 {
|
||||
voters := []interop.PublicKey{from}
|
||||
newCandidates = append(newCandidates, Ballot{
|
||||
ID: id,
|
||||
Voters: voters,
|
||||
Height: blockHeight})
|
||||
found = 1
|
||||
}
|
||||
|
||||
SetSerialized(ctx, voteKey, newCandidates)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
// RemoveVotes clears ballots of the decision that has been accepted by
|
||||
// inner ring nodes.
|
||||
func RemoveVotes(ctx storage.Context, id []byte) {
|
||||
var (
|
||||
candidates = getBallots(ctx)
|
||||
index int
|
||||
)
|
||||
|
||||
for i := 0; i < len(candidates); i++ {
|
||||
cnd := candidates[i]
|
||||
if BytesEqual(cnd.ID, id) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
util.Remove(candidates, index)
|
||||
SetSerialized(ctx, voteKey, candidates)
|
||||
}
|
||||
|
||||
// getBallots returns a deserialized slice of vote ballots.
|
||||
func getBallots(ctx storage.Context) []Ballot {
|
||||
data := storage.Get(ctx, voteKey)
|
||||
if data != nil {
|
||||
return std.Deserialize(data.([]byte)).([]Ballot)
|
||||
}
|
||||
|
||||
return []Ballot{}
|
||||
}
|
||||
|
||||
// BytesEqual compares two slices of bytes by wrapping them into strings,
|
||||
// which is necessary with new util.Equals interop behaviour, see neo-go#1176.
|
||||
func BytesEqual(a []byte, b []byte) bool {
|
||||
return util.Equals(string(a), string(b))
|
||||
}
|
||||
|
||||
// InvokeID returns hashed value of prefix and args concatenation. Iy is used to
|
||||
// identify different ballots.
|
||||
func InvokeID(args []interface{}, prefix []byte) []byte {
|
||||
for i := range args {
|
||||
arg := args[i].([]byte)
|
||||
prefix = append(prefix, arg...)
|
||||
}
|
||||
|
||||
return crypto.Sha256(prefix)
|
||||
}
|
||||
|
||||
/*
|
||||
Check if the invocation is made from known container or audit contracts.
|
||||
This is necessary because calls from these contracts require to do transfer
|
||||
without signature collection (1 invoke transfer).
|
||||
|
||||
IR1, IR2, IR3, IR4 -(4 invokes)-> [ Container Contract ] -(1 invoke)-> [ Balance Contract ]
|
||||
|
||||
We can do 1 invoke transfer if:
|
||||
- invokation has happened from inner ring node,
|
||||
- it is indirect invocation from another smart-contract.
|
||||
|
||||
However, there is a possible attack, when a malicious inner ring node creates
|
||||
a malicious smart-contract in the morph chain to do indirect call.
|
||||
|
||||
MaliciousIR -(1 invoke)-> [ Malicious Contract ] -(1 invoke)-> [ Balance Contract ]
|
||||
|
||||
To prevent that, we have to allow 1 invoke transfer from authorised well-known
|
||||
smart-contracts, that will be set up at `Init` method.
|
||||
*/
|
||||
|
||||
func FromKnownContract(ctx storage.Context, caller interop.Hash160, key string) bool {
|
||||
addr := storage.Get(ctx, key).(interop.Hash160)
|
||||
return BytesEqual(caller, addr)
|
||||
}
|
|
@ -92,6 +92,9 @@ func OnNEP11Payment(a interop.Hash160, b int, c []byte, d interface{}) {
|
|||
|
||||
func _deploy(data interface{}, isUpdate bool) {
|
||||
ctx := storage.GetContext()
|
||||
|
||||
common.RmAndCheckNotaryDisabledKey(data, notaryDisabledKey)
|
||||
|
||||
if isUpdate {
|
||||
args := data.([]interface{})
|
||||
common.CheckVersion(args[len(args)-1].(int))
|
||||
|
@ -119,6 +122,7 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
}
|
||||
|
||||
args := data.(struct {
|
||||
//TODO(@acid-ant): #9 remove notaryDisabled in future version
|
||||
notaryDisabled bool
|
||||
addrNetmap interop.Hash160
|
||||
addrBalance interop.Hash160
|
||||
|
@ -139,13 +143,6 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
storage.Put(ctx, nnsContractKey, args.addrNNS)
|
||||
storage.Put(ctx, nnsRootKey, args.nnsRoot)
|
||||
|
||||
// initialize the way to collect signatures
|
||||
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
|
||||
if args.notaryDisabled {
|
||||
common.InitVote(ctx)
|
||||
runtime.Log("container contract notary disabled")
|
||||
}
|
||||
|
||||
// add NNS root for container alias domains
|
||||
registerNiceNameTLD(args.addrNNS, args.nnsRoot)
|
||||
|
||||
|
@ -197,7 +194,6 @@ func PutNamed(container []byte, signature interop.Signature,
|
|||
publicKey interop.PublicKey, token []byte,
|
||||
name, zone string) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
ownerID := ownerFromBinaryContainer(container)
|
||||
containerID := crypto.Sha256(container)
|
||||
|
@ -238,26 +234,8 @@ func PutNamed(container []byte, signature interop.Signature,
|
|||
panic("insufficient balance to create container")
|
||||
}
|
||||
|
||||
if notaryDisabled {
|
||||
nodeKey := common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
runtime.Notify("containerPut", container, signature, publicKey, token)
|
||||
return
|
||||
}
|
||||
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := common.InvokeID([]interface{}{container, signature, publicKey}, []byte("put"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
// todo: check if new container with unique container id
|
||||
|
||||
details := common.ContainerFeeTransferDetails(containerID)
|
||||
|
@ -336,34 +314,14 @@ func checkNiceNameAvailable(nnsContractAddr interop.Hash160, domain string) bool
|
|||
// If the container doesn't exist, it panics with NotFoundError.
|
||||
func Delete(containerID []byte, signature interop.Signature, token []byte) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
ownerID := getOwnerByID(ctx, containerID)
|
||||
if ownerID == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet := common.AlphabetNodes()
|
||||
nodeKey := common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
runtime.Notify("containerDelete", containerID, signature, token)
|
||||
return
|
||||
}
|
||||
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := common.InvokeID([]interface{}{containerID, signature}, []byte("delete"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
key := append([]byte(nnsHasAliasKey), containerID...)
|
||||
domain := storage.Get(ctx, key).(string)
|
||||
|
@ -463,7 +421,6 @@ func List(owner []byte) [][]byte {
|
|||
// If the container doesn't exist, it panics with NotFoundError.
|
||||
func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
// V2 format
|
||||
// get container ID
|
||||
|
@ -476,27 +433,8 @@ func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicK
|
|||
panic(NotFoundError)
|
||||
}
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet := common.AlphabetNodes()
|
||||
nodeKey := common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
runtime.Notify("setEACL", eACL, signature, publicKey, token)
|
||||
return
|
||||
}
|
||||
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := common.InvokeID([]interface{}{eACL}, []byte("setEACL"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
rule := ExtendedACL{
|
||||
value: eACL,
|
||||
|
@ -628,21 +566,9 @@ func IterateContainerSizes(epoch int) iterator.Iterator {
|
|||
// epochNum + 3. It can be invoked only by NewEpoch method of the Netmap contract.
|
||||
func NewEpoch(epochNum int) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
if notaryDisabled {
|
||||
indirectCall := common.FromKnownContract(
|
||||
ctx,
|
||||
runtime.GetCallingScriptHash(),
|
||||
netmapContractKey,
|
||||
)
|
||||
if !indirectCall {
|
||||
panic("method must be invoked by inner ring")
|
||||
}
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
cleanupContainers(ctx, epochNum)
|
||||
}
|
||||
|
@ -650,36 +576,8 @@ func NewEpoch(epochNum int) {
|
|||
// StartContainerEstimation method produces StartEstimation notification.
|
||||
// It can be invoked only by Alphabet nodes of the Inner Ring.
|
||||
func StartContainerEstimation(epoch int) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("method must be invoked by inner ring")
|
||||
}
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := common.InvokeID([]interface{}{epoch}, []byte("startEstimation"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
runtime.Notify("StartEstimation", epoch)
|
||||
runtime.Log("notification has been produced")
|
||||
|
@ -688,36 +586,8 @@ func StartContainerEstimation(epoch int) {
|
|||
// StopContainerEstimation method produces StopEstimation notification.
|
||||
// It can be invoked only by Alphabet nodes of the Inner Ring.
|
||||
func StopContainerEstimation(epoch int) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("method must be invoked by inner ring")
|
||||
}
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := common.InvokeID([]interface{}{epoch}, []byte("stopEstimation"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
runtime.Notify("StopEstimation", epoch)
|
||||
runtime.Log("notification has been produced")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: "FrostFS"
|
||||
safemethods: ["alphabetList", "alphabetAddress", "innerRingCandidates", "config", "listConfig", "version"]
|
||||
safemethods: ["alphabetAddress", "innerRingCandidates", "config", "listConfig", "version"]
|
||||
permissions:
|
||||
- methods: ["update", "transfer"]
|
||||
events:
|
||||
|
|
|
@ -70,16 +70,6 @@ public keys with the user account (OwnerID). Keys argument is an array of ByteAr
|
|||
- name: keys
|
||||
type: Array
|
||||
|
||||
AlphabetUpdate notification. This notification is produced when Alphabet nodes
|
||||
have updated their lists in the contract. Alphabet argument is an array of ByteArray. It
|
||||
contains public keys of new alphabet nodes.
|
||||
|
||||
AlphabetUpdate:
|
||||
- name: id
|
||||
type: ByteArray
|
||||
- name: alphabet
|
||||
type: Array
|
||||
|
||||
SetConfig notification. This notification is produced when Alphabet nodes update
|
||||
FrostFS network configuration value.
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"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/native/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
|
||||
|
@ -48,6 +47,8 @@ var (
|
|||
func _deploy(data interface{}, isUpdate bool) {
|
||||
ctx := storage.GetContext()
|
||||
|
||||
common.RmAndCheckNotaryDisabledKey(data, notaryDisabledKey)
|
||||
|
||||
if isUpdate {
|
||||
args := data.([]interface{})
|
||||
common.CheckVersion(args[len(args)-1].(int))
|
||||
|
@ -55,6 +56,7 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
}
|
||||
|
||||
args := data.(struct {
|
||||
//TODO(@acid-ant): #9 remove notaryDisabled in future version
|
||||
notaryDisabled bool
|
||||
addrProc interop.Hash160
|
||||
keys []interop.PublicKey
|
||||
|
@ -81,13 +83,6 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
|
||||
storage.Put(ctx, processingContractKey, args.addrProc)
|
||||
|
||||
// initialize the way to collect signatures
|
||||
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
|
||||
if args.notaryDisabled {
|
||||
common.InitVote(ctx)
|
||||
runtime.Log("frostfs contract notary disabled")
|
||||
}
|
||||
|
||||
ln := len(args.config)
|
||||
if ln%2 != 0 {
|
||||
panic("bad configuration")
|
||||
|
@ -117,18 +112,6 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
|||
runtime.Log("frostfs contract updated")
|
||||
}
|
||||
|
||||
// AlphabetList returns an array of alphabet node keys. It is used in sidechain notary
|
||||
// disabled environment.
|
||||
func AlphabetList() []common.IRNode {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
pubs := getAlphabetNodes(ctx)
|
||||
nodes := []common.IRNode{}
|
||||
for i := range pubs {
|
||||
nodes = append(nodes, common.IRNode{PublicKey: pubs[i]})
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
// AlphabetAddress returns 2\3n+1 multisignature address of alphabet nodes.
|
||||
// It is used in sidechain notary disabled environment.
|
||||
func AlphabetAddress() interop.Hash160 {
|
||||
|
@ -156,42 +139,15 @@ func InnerRingCandidates() []common.IRNode {
|
|||
// This method does not return fee back to the candidate.
|
||||
func InnerRingCandidateRemove(key interop.PublicKey) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
keyOwner := runtime.CheckWitness(key)
|
||||
|
||||
if !keyOwner {
|
||||
if notaryDisabled {
|
||||
alphabet = getAlphabetNodes(ctx)
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("this method must be invoked by candidate or alphabet")
|
||||
}
|
||||
} else {
|
||||
multiaddr := AlphabetAddress()
|
||||
if !runtime.CheckWitness(multiaddr) {
|
||||
panic("this method must be invoked by candidate or alphabet")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if notaryDisabled && !keyOwner {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := append(key, []byte("delete")...)
|
||||
hashID := crypto.Sha256(id)
|
||||
|
||||
n := common.Vote(ctx, hashID, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, hashID)
|
||||
}
|
||||
|
||||
prefix := []byte(candidatesKey)
|
||||
stKey := append(prefix, key...)
|
||||
|
@ -285,29 +241,16 @@ func Withdraw(user interop.Hash160, amount int) {
|
|||
}
|
||||
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
// transfer fee to proxy contract to pay cheque invocation
|
||||
fee := getConfig(ctx, withdrawFeeConfigKey).(int)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet := getAlphabetNodes(ctx)
|
||||
for _, node := range alphabet {
|
||||
processingAddr := contract.CreateStandardAccount(node)
|
||||
|
||||
transferred := gas.Transfer(user, processingAddr, fee, []byte{})
|
||||
if !transferred {
|
||||
panic("failed to transfer withdraw fee, aborting")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
processingAddr := storage.Get(ctx, processingContractKey).(interop.Hash160)
|
||||
|
||||
transferred := gas.Transfer(user, processingAddr, fee, []byte{})
|
||||
if !transferred {
|
||||
panic("failed to transfer withdraw fee, aborting")
|
||||
}
|
||||
}
|
||||
|
||||
// notify alphabet nodes
|
||||
amount = amount * 100000000
|
||||
|
@ -322,38 +265,11 @@ func Withdraw(user interop.Hash160, amount int) {
|
|||
//
|
||||
// This method produces Cheque notification to burn assets in sidechain.
|
||||
func Cheque(id []byte, user interop.Hash160, amount int, lockAcc []byte) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = getAlphabetNodes(ctx)
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("this method must be invoked by alphabet")
|
||||
}
|
||||
} else {
|
||||
multiaddr := AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
from := runtime.GetExecutingScriptHash()
|
||||
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
transferred := gas.Transfer(from, user, amount, nil)
|
||||
if !transferred {
|
||||
panic("failed to transfer funds, aborting")
|
||||
|
@ -403,63 +319,6 @@ func Unbind(user []byte, keys []interop.PublicKey) {
|
|||
runtime.Notify("Unbind", user, keys)
|
||||
}
|
||||
|
||||
// AlphabetUpdate updates a list of alphabet nodes with the provided list of
|
||||
// public keys. It can be invoked only by alphabet nodes.
|
||||
//
|
||||
// This method is used in notary disabled sidechain environment. In this case,
|
||||
// the actual alphabet list should be stored in the FrostFS contract.
|
||||
func AlphabetUpdate(id []byte, args []interop.PublicKey) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
if len(args) == 0 {
|
||||
panic("bad arguments")
|
||||
}
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = getAlphabetNodes(ctx)
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("this method must be invoked by alphabet")
|
||||
}
|
||||
} else {
|
||||
multiaddr := AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
newAlphabet := []interop.PublicKey{}
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
pubKey := args[i]
|
||||
if len(pubKey) != interop.PublicKeyCompressedLen {
|
||||
panic("invalid public key in alphabet list")
|
||||
}
|
||||
|
||||
newAlphabet = append(newAlphabet, pubKey)
|
||||
}
|
||||
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
common.SetSerialized(ctx, alphabetKey, newAlphabet)
|
||||
|
||||
runtime.Notify("AlphabetUpdate", id, newAlphabet)
|
||||
runtime.Log("alphabet list has been updated")
|
||||
}
|
||||
|
||||
// Config returns configuration value of FrostFS configuration. If the key does
|
||||
// not exists, returns nil.
|
||||
func Config(key []byte) interface{} {
|
||||
|
@ -471,34 +330,9 @@ func Config(key []byte) interface{} {
|
|||
// only by Alphabet nodes.
|
||||
func SetConfig(id, key, val []byte) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = getAlphabetNodes(ctx)
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(key) == 0 {
|
||||
panic("this method must be invoked by alphabet")
|
||||
}
|
||||
} else {
|
||||
multiaddr := AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
setConfig(ctx, key, val)
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"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/native/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
|
@ -31,6 +30,8 @@ const (
|
|||
func _deploy(data interface{}, isUpdate bool) {
|
||||
ctx := storage.GetContext()
|
||||
|
||||
common.RmAndCheckNotaryDisabledKey(data, notaryDisabledKey)
|
||||
|
||||
if isUpdate {
|
||||
args := data.([]interface{})
|
||||
common.CheckVersion(args[len(args)-1].(int))
|
||||
|
@ -38,6 +39,7 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
}
|
||||
|
||||
args := data.(struct {
|
||||
//TODO(@acid-ant): #9 remove notaryDisabled in future version
|
||||
notaryDisabled bool
|
||||
addrNetmap interop.Hash160
|
||||
addrContainer interop.Hash160
|
||||
|
@ -50,13 +52,6 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
storage.Put(ctx, netmapContractKey, args.addrNetmap)
|
||||
storage.Put(ctx, containerContractKey, args.addrContainer)
|
||||
|
||||
// initialize the way to collect signatures
|
||||
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
|
||||
if args.notaryDisabled {
|
||||
common.InitVote(ctx)
|
||||
runtime.Log("frostfsid contract notary disabled")
|
||||
}
|
||||
|
||||
runtime.Log("frostfsid contract initialized")
|
||||
}
|
||||
|
||||
|
@ -90,41 +85,9 @@ func AddKey(owner []byte, keys []interop.PublicKey) {
|
|||
}
|
||||
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
indirectCall bool
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("invocation from non inner ring node")
|
||||
}
|
||||
|
||||
indirectCall = common.FromKnownContract(
|
||||
ctx,
|
||||
runtime.GetCallingScriptHash(),
|
||||
containerContractKey)
|
||||
|
||||
if indirectCall {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := invokeIDKeys(owner, keys, []byte("add"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
ownerKey := append([]byte{ownerKeysPrefix}, owner...)
|
||||
for i := range keys {
|
||||
|
@ -153,35 +116,11 @@ func RemoveKey(owner []byte, keys []interop.PublicKey) {
|
|||
}
|
||||
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("invocation from non inner ring node")
|
||||
}
|
||||
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := invokeIDKeys(owner, keys, []byte("remove"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
if !runtime.CheckWitness(multiaddr) {
|
||||
panic("invocation from non inner ring node")
|
||||
}
|
||||
}
|
||||
|
||||
ownerKey := append([]byte{ownerKeysPrefix}, owner...)
|
||||
for i := range keys {
|
||||
|
@ -222,12 +161,3 @@ func getUserInfo(ctx storage.Context, key interface{}) UserInfo {
|
|||
|
||||
return UserInfo{Keys: pubs}
|
||||
}
|
||||
|
||||
func invokeIDKeys(owner []byte, keys []interop.PublicKey, prefix []byte) []byte {
|
||||
prefix = append(prefix, owner...)
|
||||
for i := range keys {
|
||||
prefix = append(prefix, keys[i]...)
|
||||
}
|
||||
|
||||
return crypto.Sha256(prefix)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: "Netmap"
|
||||
safemethods: ["innerRingList", "epoch", "netmap", "netmapCandidates", "snapshot", "snapshotByEpoch", "config", "listConfig", "version"]
|
||||
safemethods: ["epoch", "netmap", "netmapCandidates", "snapshot", "snapshotByEpoch", "config", "listConfig", "version"]
|
||||
permissions:
|
||||
- methods: ["update", "newEpoch"]
|
||||
events:
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
Netmap contract is a contract deployed in FrostFS sidechain.
|
||||
|
||||
Netmap contract stores and manages FrostFS network map, Storage node candidates
|
||||
and epoch number counter. In notary disabled environment, contract also stores
|
||||
a list of Inner Ring node keys.
|
||||
and epoch number counter.
|
||||
|
||||
# Contract notifications
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"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/native/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
|
||||
|
@ -71,7 +70,10 @@ var (
|
|||
func _deploy(data interface{}, isUpdate bool) {
|
||||
ctx := storage.GetContext()
|
||||
|
||||
common.RmAndCheckNotaryDisabledKey(data, notaryDisabledKey)
|
||||
|
||||
var args = data.(struct {
|
||||
//TODO(@acid-ant): #9 remove notaryDisabled in future version
|
||||
notaryDisabled bool
|
||||
addrBalance interop.Hash160
|
||||
addrContainer interop.Hash160
|
||||
|
@ -115,14 +117,6 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
storage.Put(ctx, balanceContractKey, args.addrBalance)
|
||||
storage.Put(ctx, containerContractKey, args.addrContainer)
|
||||
|
||||
// initialize the way to collect signatures
|
||||
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
|
||||
if args.notaryDisabled {
|
||||
common.SetSerialized(ctx, innerRingKey, args.keys)
|
||||
common.InitVote(ctx)
|
||||
runtime.Log("netmap contract notary disabled")
|
||||
}
|
||||
|
||||
runtime.Log("netmap contract initialized")
|
||||
}
|
||||
|
||||
|
@ -138,62 +132,6 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
|||
runtime.Log("netmap contract updated")
|
||||
}
|
||||
|
||||
// InnerRingList method returns a slice of structures that contains the public key of
|
||||
// an Inner Ring node. It should be used in notary disabled environment only.
|
||||
//
|
||||
// If notary is enabled, look to NeoFSAlphabet role in native RoleManagement
|
||||
// contract of the sidechain.
|
||||
func InnerRingList() []common.IRNode {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
pubs := getIRNodes(ctx)
|
||||
nodes := []common.IRNode{}
|
||||
for i := range pubs {
|
||||
nodes = append(nodes, common.IRNode{PublicKey: pubs[i]})
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
// UpdateInnerRing method updates a list of Inner Ring node keys. It should be used
|
||||
// only in notary disabled environment. It can be invoked only by Alphabet nodes.
|
||||
//
|
||||
// If notary is enabled, update NeoFSAlphabet role in native RoleManagement
|
||||
// contract of the sidechain. Use notary service to collect multisignature.
|
||||
func UpdateInnerRing(keys []interop.PublicKey) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("this method must be invoked by alphabet nodes")
|
||||
}
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := keysID(keys, []byte("updateIR"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
runtime.Log("inner ring list updated")
|
||||
common.SetSerialized(ctx, innerRingKey, keys)
|
||||
}
|
||||
|
||||
// AddPeerIR accepts Alphabet calls in the notary-enabled contract setting and
|
||||
// behaves similar to AddPeer in the notary-disabled one.
|
||||
//
|
||||
|
@ -201,10 +139,6 @@ func UpdateInnerRing(keys []interop.PublicKey) {
|
|||
// AddPeerIR MUST be called by the Alphabet member only.
|
||||
func AddPeerIR(nodeInfo []byte) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
if notaryDisabled {
|
||||
panic("AddPeerIR should only be called in notary-enabled environment")
|
||||
}
|
||||
|
||||
common.CheckAlphabetWitness(common.AlphabetAddress())
|
||||
|
||||
|
@ -217,79 +151,15 @@ func AddPeerIR(nodeInfo []byte) {
|
|||
}
|
||||
|
||||
// AddPeer accepts information about the network map candidate in the FrostFS
|
||||
// binary protocol format, identifies the caller and behaves depending on different
|
||||
// conditions listed below.
|
||||
//
|
||||
// Contract settings:
|
||||
//
|
||||
// (1) notary-enabled
|
||||
// (2) notary-disabled
|
||||
//
|
||||
// Callers:
|
||||
//
|
||||
// (a) candidate himself, if node's public key corresponds to the signer
|
||||
// (b) Alphabet member
|
||||
// (c) others
|
||||
//
|
||||
// AddPeer case-by-case behavior:
|
||||
//
|
||||
// (1a) does nothing
|
||||
// (1b) panics. Notice that AddPeerIR MUST be used for this purpose.
|
||||
// (2a) throws AddPeer notification with the provided BLOB
|
||||
// (2b) accepts Alphabet vote. If the threshold of votes is reached, adds
|
||||
// new element to the candidate set, and throws AddPeerSuccess notification.
|
||||
// (c) panics
|
||||
//
|
||||
// Candidate MUST call AddPeer with "online" state in its descriptor. Alphabet
|
||||
// members MUST NOT call AddPeer with any other states.
|
||||
// binary protocol format and does nothing. Keep method because storage node
|
||||
// creates a notary transaction with this method, which produces a notary
|
||||
// notification (implicit here).
|
||||
func AddPeer(nodeInfo []byte) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
}
|
||||
|
||||
// V2 format
|
||||
publicKey := nodeInfo[2:35] // offset:2, len:33
|
||||
|
||||
// If notary is enabled or caller is not an alphabet node,
|
||||
// just emit the notification for alphabet.
|
||||
if !notaryDisabled || len(nodeKey) == 0 {
|
||||
common.CheckWitness(publicKey)
|
||||
if notaryDisabled {
|
||||
runtime.Notify("AddPeer", nodeInfo)
|
||||
}
|
||||
// V2 format - offset:2, len:33
|
||||
common.CheckWitness(nodeInfo[2:35])
|
||||
return
|
||||
}
|
||||
|
||||
candidate := Node{
|
||||
BLOB: nodeInfo,
|
||||
State: NodeStateOnline,
|
||||
}
|
||||
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
rawCandidate := std.Serialize(candidate)
|
||||
id := crypto.Sha256(rawCandidate)
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
addToNetmap(ctx, publicKey, candidate)
|
||||
}
|
||||
|
||||
// updates state of the network map candidate by its public key in the contract
|
||||
// storage, and throws UpdateStateSuccess notification after this.
|
||||
//
|
||||
|
@ -310,13 +180,8 @@ func updateCandidateState(ctx storage.Context, publicKey interop.PublicKey, stat
|
|||
}
|
||||
|
||||
// UpdateState accepts new state to be assigned to network map candidate
|
||||
// identified by the given public key, identifies the signer and behaves
|
||||
// depending on different conditions listed below.
|
||||
//
|
||||
// Contract settings:
|
||||
//
|
||||
// (1) notary-enabled
|
||||
// (2) notary-disabled
|
||||
// identified by the given public key, identifies the signer.
|
||||
// Applicable only for notary-enabled environment.
|
||||
//
|
||||
// Signers:
|
||||
//
|
||||
|
@ -327,13 +192,10 @@ func updateCandidateState(ctx storage.Context, publicKey interop.PublicKey, stat
|
|||
//
|
||||
// UpdateState case-by-case behavior:
|
||||
//
|
||||
// (1a) panics
|
||||
// (1b) like (1a)
|
||||
// (1ab) updates candidate's state in the contract storage (*), and throws
|
||||
// (a) panics
|
||||
// (b) panics
|
||||
// (ab) updates candidate's state in the contract storage (*), and throws
|
||||
// UpdateStateSuccess with the provided key and new state
|
||||
// (2a) throws UpdateState notification with the provided key and new state
|
||||
// (2b) accepts Alphabet vote. If the threshold of votes is reached, behaves
|
||||
// like (1ab).
|
||||
// (c) panics
|
||||
//
|
||||
// (*) Candidate is removed from the candidate set if state is NodeStateOffline.
|
||||
|
@ -349,33 +211,9 @@ func UpdateState(state NodeState, publicKey interop.PublicKey) {
|
|||
}
|
||||
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet := common.AlphabetNodes()
|
||||
nodeKey := common.InnerRingInvoker(alphabet)
|
||||
|
||||
// If caller is not an alphabet node,
|
||||
// just emit the notification for alphabet.
|
||||
if len(nodeKey) == 0 {
|
||||
common.CheckWitness(publicKey)
|
||||
runtime.Notify("UpdateState", state, publicKey)
|
||||
return
|
||||
}
|
||||
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := common.InvokeID([]interface{}{state, publicKey}, []byte("update"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
} else {
|
||||
common.CheckWitness(publicKey)
|
||||
common.CheckAlphabetWitness(common.AlphabetAddress())
|
||||
}
|
||||
|
||||
updateCandidateState(ctx, publicKey, state)
|
||||
}
|
||||
|
@ -388,10 +226,6 @@ func UpdateState(state NodeState, publicKey interop.PublicKey) {
|
|||
// UpdateStateIR MUST be called by the Alphabet member only.
|
||||
func UpdateStateIR(state NodeState, publicKey interop.PublicKey) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
if notaryDisabled {
|
||||
panic("UpdateStateIR should only be called in notary-enabled environment")
|
||||
}
|
||||
|
||||
common.CheckAlphabetWitness(common.AlphabetAddress())
|
||||
|
||||
|
@ -409,35 +243,9 @@ func UpdateStateIR(state NodeState, publicKey interop.PublicKey) {
|
|||
// It produces NewEpoch notification.
|
||||
func NewEpoch(epochNum int) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("this method must be invoked by inner ring nodes")
|
||||
}
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := common.InvokeID([]interface{}{epochNum}, []byte("epoch"))
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
currentEpoch := storage.Get(ctx, snapshotEpoch).(int)
|
||||
if epochNum <= currentEpoch {
|
||||
|
@ -634,34 +442,9 @@ func Config(key []byte) interface{} {
|
|||
// only by Alphabet nodes.
|
||||
func SetConfig(id, key, val []byte) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
panic("invoked by non inner ring node")
|
||||
}
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
setConfig(ctx, key, val)
|
||||
|
||||
|
@ -784,26 +567,3 @@ func cleanup(ctx storage.Context, epoch int) {
|
|||
containerContractAddr := storage.Get(ctx, containerContractKey).(interop.Hash160)
|
||||
contract.Call(containerContractAddr, cleanupEpochMethod, contract.All, epoch)
|
||||
}
|
||||
|
||||
func getIRNodes(ctx storage.Context) []interop.PublicKey {
|
||||
data := storage.Get(ctx, innerRingKey)
|
||||
if data != nil {
|
||||
return std.Deserialize(data.([]byte)).([]interop.PublicKey)
|
||||
}
|
||||
|
||||
return []interop.PublicKey{}
|
||||
}
|
||||
|
||||
func keysID(args []interop.PublicKey, prefix []byte) []byte {
|
||||
var (
|
||||
result []byte
|
||||
)
|
||||
|
||||
result = append(result, prefix...)
|
||||
|
||||
for i := range args {
|
||||
result = append(result, args[i]...)
|
||||
}
|
||||
|
||||
return crypto.Sha256(result)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ const (
|
|||
)
|
||||
|
||||
func _deploy(data interface{}, isUpdate bool) {
|
||||
ctx := storage.GetContext()
|
||||
common.RmAndCheckNotaryDisabledKey(data, notaryDisabledKey)
|
||||
|
||||
if isUpdate {
|
||||
args := data.([]interface{})
|
||||
|
@ -26,17 +26,6 @@ func _deploy(data interface{}, isUpdate bool) {
|
|||
return
|
||||
}
|
||||
|
||||
args := data.(struct {
|
||||
notaryDisabled bool
|
||||
})
|
||||
|
||||
// initialize the way to collect signatures
|
||||
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
|
||||
if args.notaryDisabled {
|
||||
common.InitVote(ctx)
|
||||
runtime.Log("reputation contract notary disabled")
|
||||
}
|
||||
|
||||
runtime.Log("reputation contract initialized")
|
||||
}
|
||||
|
||||
|
@ -60,40 +49,14 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
|||
// Value contains a stable marshaled structure of DataAuditResult.
|
||||
func Put(epoch int, peerID []byte, value []byte) {
|
||||
ctx := storage.GetContext()
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
|
||||
var ( // for invocation collection without notary
|
||||
alphabet []interop.PublicKey
|
||||
nodeKey []byte
|
||||
alphabetCall bool
|
||||
)
|
||||
|
||||
if notaryDisabled {
|
||||
alphabet = common.AlphabetNodes()
|
||||
nodeKey = common.InnerRingInvoker(alphabet)
|
||||
alphabetCall = len(nodeKey) != 0
|
||||
} else {
|
||||
multiaddr := common.AlphabetAddress()
|
||||
alphabetCall = runtime.CheckWitness(multiaddr)
|
||||
}
|
||||
|
||||
if !alphabetCall {
|
||||
if !runtime.CheckWitness(multiaddr) {
|
||||
runtime.Notify("reputationPut", epoch, peerID, value)
|
||||
return
|
||||
}
|
||||
|
||||
id := storageID(epoch, peerID)
|
||||
if notaryDisabled {
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
}
|
||||
|
||||
key := getReputationKey(reputationCountPrefix, id)
|
||||
rawCnt := storage.Get(ctx, key)
|
||||
cnt := 0
|
||||
|
|
|
@ -27,14 +27,6 @@ const (
|
|||
ErrInvalidUser = "invalid user"
|
||||
// ErrInvalidNode is thrown when node has invalid format.
|
||||
ErrInvalidNode = "invalid node key"
|
||||
// ErrNodeAdmNotExist is thrown when node admin is not found.
|
||||
ErrNodeAdmNotExist = "node admin not found"
|
||||
// ErrClientAdmNotExist is thrown when client admin is not found.
|
||||
ErrClientAdmNotExist = "client admin not found"
|
||||
// ErrNodeNotExist is thrown when node is not found.
|
||||
ErrNodeNotExist = "node not found"
|
||||
// ErrUserNotExist is thrown when user is not found.
|
||||
ErrUserNotExist = "user not found"
|
||||
// ErrAccessDenied is thrown when operation is denied for caller.
|
||||
ErrAccessDenied = "access denied"
|
||||
)
|
||||
|
@ -57,18 +49,13 @@ const (
|
|||
|
||||
// _deploy function sets up initial list of inner ring public keys.
|
||||
func _deploy(data interface{}, isUpdate bool) {
|
||||
common.RmAndCheckNotaryDisabledKey(data, notaryDisabledKey)
|
||||
|
||||
if isUpdate {
|
||||
args := data.([]interface{})
|
||||
common.CheckVersion(args[len(args)-1].(int))
|
||||
return
|
||||
}
|
||||
|
||||
args := data.(struct {
|
||||
notaryDisabled bool
|
||||
})
|
||||
|
||||
ctx := storage.GetContext()
|
||||
storage.Put(ctx, []byte{notaryDisabledKey}, args.notaryDisabled)
|
||||
}
|
||||
|
||||
// Update method updates contract source code and manifest. It can be invoked
|
||||
|
@ -99,30 +86,10 @@ func Put(id []byte, ownerKey interop.PublicKey, info []byte) {
|
|||
panic(ErrAlreadyExists)
|
||||
}
|
||||
|
||||
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
|
||||
if notaryDisabled {
|
||||
alphabet := common.AlphabetNodes()
|
||||
nodeKey := common.InnerRingInvoker(alphabet)
|
||||
if len(nodeKey) == 0 {
|
||||
common.CheckWitness(ownerKey)
|
||||
runtime.Notify("Put", id, ownerKey, info)
|
||||
return
|
||||
}
|
||||
|
||||
threshold := len(alphabet)*2/3 + 1
|
||||
id := common.InvokeID([]interface{}{ownerKey, info}, []byte("put"))
|
||||
n := common.Vote(ctx, id, nodeKey)
|
||||
if n < threshold {
|
||||
return
|
||||
}
|
||||
|
||||
common.RemoveVotes(ctx, id)
|
||||
} else {
|
||||
common.CheckOwnerWitness(ownerKey)
|
||||
|
||||
multiaddr := common.AlphabetAddress()
|
||||
common.CheckAlphabetWitness(multiaddr)
|
||||
}
|
||||
|
||||
storage.Put(ctx, stKey, ownerKey)
|
||||
stKey[0] = infoPrefix
|
||||
|
|
|
@ -79,20 +79,6 @@ func newFrostFSInvoker(t *testing.T, n int, config ...interface{}) (*neotest.Con
|
|||
return e.CommitteeInvoker(h).WithSigners(alphabet), alphabet, pubs
|
||||
}
|
||||
|
||||
func TestFrostFS_AlphabetList(t *testing.T) {
|
||||
const alphabetSize = 4
|
||||
|
||||
e, _, pubs := newFrostFSInvoker(t, alphabetSize)
|
||||
arr := make([]stackitem.Item, len(pubs))
|
||||
for i := range arr {
|
||||
arr[i] = stackitem.NewStruct([]stackitem.Item{
|
||||
stackitem.NewByteArray(pubs[i].Bytes()),
|
||||
})
|
||||
}
|
||||
|
||||
e.Invoke(t, stackitem.NewArray(arr), "alphabetList")
|
||||
}
|
||||
|
||||
func TestFrostFS_InnerRingCandidate(t *testing.T) {
|
||||
e, _, _ := newFrostFSInvoker(t, 4, frostfs.CandidateFeeConfigKey, int64(10))
|
||||
|
||||
|
|
Loading…
Reference in a new issue