Compare commits

...

2 commits

Author SHA1 Message Date
3aa936df5c [#7] Refactor common.CheckAlphabetWitness()
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2023-02-16 14:38:50 +03:00
7acc5ecaf1 [#7] Remove notary disabled code
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2023-02-16 14:38:45 +03:00
20 changed files with 154 additions and 1198 deletions

View file

@ -5,6 +5,9 @@ Changelog for FrostFS Contract
### Added ### Added
### Changed ### Changed
### Removed
- Notary disabled code from all contracts (#7)
### Updated ### Updated
- `neo-go` to `v0.99.4` - `neo-go` to `v0.99.4`

View file

@ -4,7 +4,6 @@ import (
"github.com/TrueCloudLab/frostfs-contract/common" "github.com/TrueCloudLab/frostfs-contract/common"
"github.com/nspcc-dev/neo-go/pkg/interop" "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/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/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/neo" "github.com/nspcc-dev/neo-go/pkg/interop/native/neo"
@ -34,13 +33,17 @@ func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {
func _deploy(data interface{}, isUpdate bool) { func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext() ctx := storage.GetContext()
if isUpdate { if isUpdate {
args := data.([]interface{}) args := data.([]interface{})
common.CheckVersion(args[len(args)-1].(int)) common.CheckVersion(args[len(args)-1].(int))
//TODO(@acid-ant): #9 remove notaryDisabled from args in future version
storage.Delete(ctx, notaryDisabledKey)
return return
} }
args := data.(struct { args := data.(struct {
//TODO(@acid-ant): #9 remove notaryDisabled in future version
notaryDisabled bool notaryDisabled bool
addrNetmap interop.Hash160 addrNetmap interop.Hash160
addrProxy interop.Hash160 addrProxy interop.Hash160
@ -49,7 +52,11 @@ func _deploy(data interface{}, isUpdate bool) {
total int total int
}) })
if len(args.addrNetmap) != interop.Hash160Len || !args.notaryDisabled && len(args.addrProxy) != interop.Hash160Len { if args.notaryDisabled {
panic(common.PanicMsgForNotaryDisabledEnv)
}
if len(args.addrNetmap) != interop.Hash160Len || len(args.addrProxy) != interop.Hash160Len {
panic("incorrect length of contract script hash") panic("incorrect length of contract script hash")
} }
@ -59,13 +66,6 @@ func _deploy(data interface{}, isUpdate bool) {
storage.Put(ctx, indexKey, args.index) storage.Put(ctx, indexKey, args.index)
storage.Put(ctx, totalKey, args.total) 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") runtime.Log(args.name + " contract initialized")
} }
@ -120,15 +120,11 @@ func checkPermission(ir []interop.PublicKey) bool {
// and proxy contract. It can be invoked only by an Alphabet node of the Inner Ring. // 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 // 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 // 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. // 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() { func Emit() {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
alphabet := common.AlphabetNodes() alphabet := common.AlphabetNodes()
if !checkPermission(alphabet) { if !checkPermission(alphabet) {
@ -143,7 +139,6 @@ func Emit() {
gasBalance := gas.BalanceOf(contractHash) gasBalance := gas.BalanceOf(contractHash)
if !notaryDisabled {
proxyAddr := storage.Get(ctx, proxyKey).(interop.Hash160) proxyAddr := storage.Get(ctx, proxyKey).(interop.Hash160)
proxyGas := gasBalance / 2 proxyGas := gasBalance / 2
@ -158,16 +153,8 @@ func Emit() {
gasBalance -= proxyGas gasBalance -= proxyGas
runtime.Log("utility token has been emitted to proxy contract") runtime.Log("utility token has been emitted to proxy contract")
}
var innerRing []interop.PublicKey innerRing := common.InnerRingNodes()
if notaryDisabled {
netmapContract := storage.Get(ctx, netmapKey).(interop.Hash160)
innerRing = common.InnerRingNodesFromNetmap(netmapContract)
} else {
innerRing = common.InnerRingNodes()
}
gasPerNode := gasBalance * 7 / 8 / len(innerRing) gasPerNode := gasBalance * 7 / 8 / len(innerRing)
@ -192,25 +179,10 @@ func Emit() {
// alphabet contracts) should vote for a new committee. // alphabet contracts) should vote for a new committee.
func Vote(epoch int, candidates []interop.PublicKey) { func Vote(epoch int, candidates []interop.PublicKey) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
index := index(ctx) index := index(ctx)
name := name(ctx) name := name(ctx)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
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) curEpoch := currentEpoch(ctx)
if epoch != curEpoch { if epoch != curEpoch {
@ -220,18 +192,6 @@ func Vote(epoch int, candidates []interop.PublicKey) {
candidate := candidates[index%len(candidates)] candidate := candidates[index%len(candidates)]
address := runtime.GetExecutingScriptHash() 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) ok := neo.Vote(address, candidate)
if ok { if ok {
runtime.Log(name + ": successfully voted for validator") runtime.Log(name + ": successfully voted for validator")
@ -240,21 +200,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. // Name returns the Glagolitic name of the contract.
func Name() string { func Name() string {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()

View file

@ -44,6 +44,13 @@ const (
func _deploy(data interface{}, isUpdate bool) { func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext() ctx := storage.GetContext()
//TODO(@acid-ant): #9 remove notaryDisabled from args in future version
if data.([]interface{})[0].(bool) {
panic(common.PanicMsgForNotaryDisabledEnv)
}
storage.Delete(ctx, notaryDisabledKey)
if isUpdate { if isUpdate {
args := data.([]interface{}) args := data.([]interface{})
common.CheckVersion(args[len(args)-1].(int)) common.CheckVersion(args[len(args)-1].(int))
@ -51,6 +58,7 @@ func _deploy(data interface{}, isUpdate bool) {
} }
args := data.(struct { args := data.(struct {
//TODO(@acid-ant): #9 remove notaryDisabled in future version
notaryDisabled bool notaryDisabled bool
addrNetmap interop.Hash160 addrNetmap interop.Hash160
}) })
@ -61,12 +69,6 @@ func _deploy(data interface{}, isUpdate bool) {
storage.Put(ctx, netmapContractKey, args.addrNetmap) 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") runtime.Log("audit contract initialized")
} }
@ -90,16 +92,8 @@ func Update(script []byte, manifest []byte, data interface{}) {
// in later epochs. // in later epochs.
func Put(rawAuditResult []byte) { func Put(rawAuditResult []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var innerRing []interop.PublicKey innerRing := common.InnerRingNodes()
if notaryDisabled {
netmapContract := storage.Get(ctx, netmapContractKey).(interop.Hash160)
innerRing = common.InnerRingNodesFromNetmap(netmapContract)
} else {
innerRing = common.InnerRingNodes()
}
hdr := newAuditHeader(rawAuditResult) hdr := newAuditHeader(rawAuditResult)
presented := false presented := false
@ -182,7 +176,6 @@ func list(it iterator.Iterator) [][]byte {
ignore := [][]byte{ ignore := [][]byte{
[]byte(netmapContractKey), []byte(netmapContractKey),
[]byte(notaryDisabledKey),
} }
loop: loop:

View file

@ -60,6 +60,13 @@ func init() {
func _deploy(data interface{}, isUpdate bool) { func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext() ctx := storage.GetContext()
//TODO(@acid-ant): #9 remove notaryDisabled from args in future version
if data.([]interface{})[0].(bool) {
panic(common.PanicMsgForNotaryDisabledEnv)
}
storage.Delete(ctx, notaryDisabledKey)
if isUpdate { if isUpdate {
args := data.([]interface{}) args := data.([]interface{})
common.CheckVersion(args[len(args)-1].(int)) common.CheckVersion(args[len(args)-1].(int))
@ -67,6 +74,7 @@ func _deploy(data interface{}, isUpdate bool) {
} }
args := data.(struct { args := data.(struct {
//TODO(@acid-ant): #9 remove notaryDisabled in future version
notaryDisabled bool notaryDisabled bool
addrNetmap interop.Hash160 addrNetmap interop.Hash160
addrContainer interop.Hash160 addrContainer interop.Hash160
@ -79,13 +87,6 @@ func _deploy(data interface{}, isUpdate bool) {
storage.Put(ctx, netmapContractKey, args.addrNetmap) storage.Put(ctx, netmapContractKey, args.addrNetmap)
storage.Put(ctx, containerContractKey, args.addrContainer) 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") runtime.Log("balance contract initialized")
} }
@ -146,42 +147,8 @@ func Transfer(from, to interop.Hash160, amount int, data interface{}) bool {
// Inner Ring with multisignature. // Inner Ring with multisignature.
func TransferX(from, to interop.Hash160, amount int, details []byte) { func TransferX(from, to interop.Hash160, amount int, details []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
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) result := token.transfer(ctx, from, to, amount, true, details)
if !result { if !result {
@ -201,23 +168,8 @@ 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. // 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) { func Lock(txDetails []byte, from, to interop.Hash160, amount, until int) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
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) details := common.LockTransferDetails(txDetails)
@ -227,18 +179,6 @@ func Lock(txDetails []byte, from, to interop.Hash160, amount, until int) {
Parent: from, 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) common.SetSerialized(ctx, to, lockAccount)
result := token.transfer(ctx, from, to, amount, true, details) result := token.transfer(ctx, from, to, amount, true, details)
@ -258,21 +198,8 @@ func Lock(txDetails []byte, from, to interop.Hash160, amount, until int) {
// It produces Transfer and TransferX notifications. // It produces Transfer and TransferX notifications.
func NewEpoch(epochNum int) { func NewEpoch(epochNum int) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
if notaryDisabled { common.CheckAlphabetWitness()
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) it := storage.Find(ctx, []byte{}, storage.KeysOnly)
for iterator.Next(it) { for iterator.Next(it) {
@ -305,38 +232,11 @@ func NewEpoch(epochNum int) {
// Mint increases total supply of NEP-17 compatible FrostFS token. // Mint increases total supply of NEP-17 compatible FrostFS token.
func Mint(to interop.Hash160, amount int, txDetails []byte) { func Mint(to interop.Hash160, amount int, txDetails []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
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) 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) ok := token.transfer(ctx, nil, to, amount, true, details)
if !ok { if !ok {
panic("can't transfer assets") panic("can't transfer assets")
@ -362,38 +262,11 @@ func Mint(to interop.Hash160, amount int, txDetails []byte) {
// compatible FrostFS token. // compatible FrostFS token.
func Burn(from interop.Hash160, amount int, txDetails []byte) { func Burn(from interop.Hash160, amount int, txDetails []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
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) 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) ok := token.transfer(ctx, from, nil, amount, true, details)
if !ok { if !ok {
panic("can't transfer assets") panic("can't transfer assets")

15
common/common.go Normal file
View file

@ -0,0 +1,15 @@
package common
import (
"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))
}

View file

@ -6,28 +6,12 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" "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/neo"
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles" "github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
) )
type IRNode struct { type IRNode struct {
PublicKey interop.PublicKey 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 // InnerRingNodes return a list of inner ring nodes from state validator role
// in the sidechain. // in the sidechain.
func InnerRingNodes() []interop.PublicKey { func InnerRingNodes() []interop.PublicKey {
@ -35,18 +19,6 @@ func InnerRingNodes() []interop.PublicKey {
return roles.GetDesignatedByRole(roles.NeoFSAlphabet, uint32(blockHeight+1)) 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. // AlphabetNodes returns a list of alphabet nodes from committee in the sidechain.
func AlphabetNodes() []interop.PublicKey { func AlphabetNodes() []interop.PublicKey {
return neo.GetCommittee() return neo.GetCommittee()

View file

@ -5,15 +5,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "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. // SetSerialized serializes data and puts it into contract storage.
func SetSerialized(ctx storage.Context, key interface{}, value interface{}) { func SetSerialized(ctx storage.Context, key interface{}, value interface{}) {
data := std.Serialize(value) data := std.Serialize(value)

View file

@ -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)
}

View file

@ -16,8 +16,8 @@ var (
// CheckAlphabetWitness checks witness of the passed caller. // CheckAlphabetWitness checks witness of the passed caller.
// It panics with ErrAlphabetWitnessFailed message on fail. // It panics with ErrAlphabetWitnessFailed message on fail.
func CheckAlphabetWitness(caller []byte) { func CheckAlphabetWitness() {
checkWitnessWithPanic(caller, ErrAlphabetWitnessFailed) checkWitnessWithPanic(AlphabetAddress(), ErrAlphabetWitnessFailed)
} }
// CheckOwnerWitness checks witness of the passed caller. // CheckOwnerWitness checks witness of the passed caller.

View file

@ -92,6 +92,13 @@ func OnNEP11Payment(a interop.Hash160, b int, c []byte, d interface{}) {
func _deploy(data interface{}, isUpdate bool) { func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext() ctx := storage.GetContext()
//TODO(@acid-ant): #9 remove notaryDisabled from args in future version
if data.([]interface{})[0].(bool) {
panic(common.PanicMsgForNotaryDisabledEnv)
}
storage.Delete(ctx, notaryDisabledKey)
if isUpdate { if isUpdate {
args := data.([]interface{}) args := data.([]interface{})
common.CheckVersion(args[len(args)-1].(int)) common.CheckVersion(args[len(args)-1].(int))
@ -119,6 +126,7 @@ func _deploy(data interface{}, isUpdate bool) {
} }
args := data.(struct { args := data.(struct {
//TODO(@acid-ant): #9 remove notaryDisabled in future version
notaryDisabled bool notaryDisabled bool
addrNetmap interop.Hash160 addrNetmap interop.Hash160
addrBalance interop.Hash160 addrBalance interop.Hash160
@ -139,13 +147,6 @@ func _deploy(data interface{}, isUpdate bool) {
storage.Put(ctx, nnsContractKey, args.addrNNS) storage.Put(ctx, nnsContractKey, args.addrNNS)
storage.Put(ctx, nnsRootKey, args.nnsRoot) 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 // add NNS root for container alias domains
registerNiceNameTLD(args.addrNNS, args.nnsRoot) registerNiceNameTLD(args.addrNNS, args.nnsRoot)
@ -197,7 +198,6 @@ func PutNamed(container []byte, signature interop.Signature,
publicKey interop.PublicKey, token []byte, publicKey interop.PublicKey, token []byte,
name, zone string) { name, zone string) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
ownerID := ownerFromBinaryContainer(container) ownerID := ownerFromBinaryContainer(container)
containerID := crypto.Sha256(container) containerID := crypto.Sha256(container)
@ -238,26 +238,7 @@ func PutNamed(container []byte, signature interop.Signature,
panic("insufficient balance to create container") panic("insufficient balance to create container")
} }
if notaryDisabled { common.CheckAlphabetWitness()
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 // todo: check if new container with unique container id
details := common.ContainerFeeTransferDetails(containerID) details := common.ContainerFeeTransferDetails(containerID)
@ -336,34 +317,13 @@ func checkNiceNameAvailable(nnsContractAddr interop.Hash160, domain string) bool
// If the container doesn't exist, it panics with NotFoundError. // If the container doesn't exist, it panics with NotFoundError.
func Delete(containerID []byte, signature interop.Signature, token []byte) { func Delete(containerID []byte, signature interop.Signature, token []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
ownerID := getOwnerByID(ctx, containerID) ownerID := getOwnerByID(ctx, containerID)
if ownerID == nil { if ownerID == nil {
return return
} }
if notaryDisabled { common.CheckAlphabetWitness()
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...) key := append([]byte(nnsHasAliasKey), containerID...)
domain := storage.Get(ctx, key).(string) domain := storage.Get(ctx, key).(string)
@ -463,7 +423,6 @@ func List(owner []byte) [][]byte {
// If the container doesn't exist, it panics with NotFoundError. // If the container doesn't exist, it panics with NotFoundError.
func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) { func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
// V2 format // V2 format
// get container ID // get container ID
@ -476,27 +435,7 @@ func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicK
panic(NotFoundError) panic(NotFoundError)
} }
if notaryDisabled { common.CheckAlphabetWitness()
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{ rule := ExtendedACL{
value: eACL, value: eACL,
@ -628,21 +567,8 @@ func IterateContainerSizes(epoch int) iterator.Iterator {
// epochNum + 3. It can be invoked only by NewEpoch method of the Netmap contract. // epochNum + 3. It can be invoked only by NewEpoch method of the Netmap contract.
func NewEpoch(epochNum int) { func NewEpoch(epochNum int) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
if notaryDisabled { common.CheckAlphabetWitness()
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) cleanupContainers(ctx, epochNum)
} }
@ -650,36 +576,7 @@ func NewEpoch(epochNum int) {
// StartContainerEstimation method produces StartEstimation notification. // StartContainerEstimation method produces StartEstimation notification.
// It can be invoked only by Alphabet nodes of the Inner Ring. // It can be invoked only by Alphabet nodes of the Inner Ring.
func StartContainerEstimation(epoch int) { func StartContainerEstimation(epoch int) {
ctx := storage.GetContext() common.CheckAlphabetWitness()
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.Notify("StartEstimation", epoch)
runtime.Log("notification has been produced") runtime.Log("notification has been produced")
@ -688,36 +585,7 @@ func StartContainerEstimation(epoch int) {
// StopContainerEstimation method produces StopEstimation notification. // StopContainerEstimation method produces StopEstimation notification.
// It can be invoked only by Alphabet nodes of the Inner Ring. // It can be invoked only by Alphabet nodes of the Inner Ring.
func StopContainerEstimation(epoch int) { func StopContainerEstimation(epoch int) {
ctx := storage.GetContext() common.CheckAlphabetWitness()
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.Notify("StopEstimation", epoch)
runtime.Log("notification has been produced") runtime.Log("notification has been produced")

View file

@ -1,5 +1,5 @@
name: "FrostFS" name: "FrostFS"
safemethods: ["alphabetList", "alphabetAddress", "innerRingCandidates", "config", "listConfig", "version"] safemethods: ["alphabetAddress", "innerRingCandidates", "config", "listConfig", "version"]
permissions: permissions:
- methods: ["update", "transfer"] - methods: ["update", "transfer"]
events: events:

View file

@ -70,16 +70,6 @@ public keys with the user account (OwnerID). Keys argument is an array of ByteAr
- name: keys - name: keys
type: Array 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 SetConfig notification. This notification is produced when Alphabet nodes update
FrostFS network configuration value. FrostFS network configuration value.

View file

@ -5,7 +5,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop" "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/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator" "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/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" "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/management"
@ -48,6 +47,12 @@ var (
func _deploy(data interface{}, isUpdate bool) { func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext() ctx := storage.GetContext()
//TODO(@acid-ant): #9 remove notaryDisabled from args in future version
if data.([]interface{})[0].(bool) {
panic(common.PanicMsgForNotaryDisabledEnv)
}
storage.Delete(ctx, notaryDisabledKey)
if isUpdate { if isUpdate {
args := data.([]interface{}) args := data.([]interface{})
common.CheckVersion(args[len(args)-1].(int)) common.CheckVersion(args[len(args)-1].(int))
@ -55,6 +60,7 @@ func _deploy(data interface{}, isUpdate bool) {
} }
args := data.(struct { args := data.(struct {
//TODO(@acid-ant): #9 remove notaryDisabled in future version
notaryDisabled bool notaryDisabled bool
addrProc interop.Hash160 addrProc interop.Hash160
keys []interop.PublicKey keys []interop.PublicKey
@ -81,13 +87,6 @@ func _deploy(data interface{}, isUpdate bool) {
storage.Put(ctx, processingContractKey, args.addrProc) 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) ln := len(args.config)
if ln%2 != 0 { if ln%2 != 0 {
panic("bad configuration") panic("bad configuration")
@ -110,25 +109,15 @@ func Update(script []byte, manifest []byte, data interface{}) {
alphabetKeys := roles.GetDesignatedByRole(roles.NeoFSAlphabet, uint32(blockHeight+1)) alphabetKeys := roles.GetDesignatedByRole(roles.NeoFSAlphabet, uint32(blockHeight+1))
alphabetCommittee := common.Multiaddress(alphabetKeys, true) alphabetCommittee := common.Multiaddress(alphabetKeys, true)
common.CheckAlphabetWitness(alphabetCommittee) if !runtime.CheckWitness(alphabetCommittee) {
panic(common.ErrAlphabetWitnessFailed)
}
contract.Call(interop.Hash160(management.Hash), "update", contract.Call(interop.Hash160(management.Hash), "update",
contract.All, script, manifest, common.AppendVersion(data)) contract.All, script, manifest, common.AppendVersion(data))
runtime.Log("frostfs contract updated") 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. // AlphabetAddress returns 2\3n+1 multisignature address of alphabet nodes.
// It is used in sidechain notary disabled environment. // It is used in sidechain notary disabled environment.
func AlphabetAddress() interop.Hash160 { func AlphabetAddress() interop.Hash160 {
@ -156,42 +145,15 @@ func InnerRingCandidates() []common.IRNode {
// This method does not return fee back to the candidate. // This method does not return fee back to the candidate.
func InnerRingCandidateRemove(key interop.PublicKey) { func InnerRingCandidateRemove(key interop.PublicKey) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary
alphabet []interop.PublicKey
nodeKey []byte
)
keyOwner := runtime.CheckWitness(key) keyOwner := runtime.CheckWitness(key)
if !keyOwner { 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() multiaddr := AlphabetAddress()
if !runtime.CheckWitness(multiaddr) { if !runtime.CheckWitness(multiaddr) {
panic("this method must be invoked by candidate or alphabet") 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) prefix := []byte(candidatesKey)
stKey := append(prefix, key...) stKey := append(prefix, key...)
@ -285,29 +247,16 @@ func Withdraw(user interop.Hash160, amount int) {
} }
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
// transfer fee to proxy contract to pay cheque invocation // transfer fee to proxy contract to pay cheque invocation
fee := getConfig(ctx, withdrawFeeConfigKey).(int) 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) processingAddr := storage.Get(ctx, processingContractKey).(interop.Hash160)
transferred := gas.Transfer(user, processingAddr, fee, []byte{}) transferred := gas.Transfer(user, processingAddr, fee, []byte{})
if !transferred { if !transferred {
panic("failed to transfer withdraw fee, aborting") panic("failed to transfer withdraw fee, aborting")
} }
}
// notify alphabet nodes // notify alphabet nodes
amount = amount * 100000000 amount = amount * 100000000
@ -322,38 +271,10 @@ func Withdraw(user interop.Hash160, amount int) {
// //
// This method produces Cheque notification to burn assets in sidechain. // This method produces Cheque notification to burn assets in sidechain.
func Cheque(id []byte, user interop.Hash160, amount int, lockAcc []byte) { func Cheque(id []byte, user interop.Hash160, amount int, lockAcc []byte) {
ctx := storage.GetContext() common.CheckAlphabetWitness()
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() 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) transferred := gas.Transfer(from, user, amount, nil)
if !transferred { if !transferred {
panic("failed to transfer funds, aborting") panic("failed to transfer funds, aborting")
@ -403,63 +324,6 @@ func Unbind(user []byte, keys []interop.PublicKey) {
runtime.Notify("Unbind", user, keys) 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 // Config returns configuration value of FrostFS configuration. If the key does
// not exists, returns nil. // not exists, returns nil.
func Config(key []byte) interface{} { func Config(key []byte) interface{} {
@ -471,34 +335,8 @@ func Config(key []byte) interface{} {
// only by Alphabet nodes. // only by Alphabet nodes.
func SetConfig(id, key, val []byte) { func SetConfig(id, key, val []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
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) setConfig(ctx, key, val)

View file

@ -5,7 +5,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop" "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/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator" "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/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
@ -31,6 +30,12 @@ const (
func _deploy(data interface{}, isUpdate bool) { func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext() ctx := storage.GetContext()
//TODO(@acid-ant): #9 remove notaryDisabled from args in future version
if data.([]interface{})[0].(bool) {
panic(common.PanicMsgForNotaryDisabledEnv)
}
storage.Delete(ctx, notaryDisabledKey)
if isUpdate { if isUpdate {
args := data.([]interface{}) args := data.([]interface{})
common.CheckVersion(args[len(args)-1].(int)) common.CheckVersion(args[len(args)-1].(int))
@ -38,6 +43,7 @@ func _deploy(data interface{}, isUpdate bool) {
} }
args := data.(struct { args := data.(struct {
//TODO(@acid-ant): #9 remove notaryDisabled in future version
notaryDisabled bool notaryDisabled bool
addrNetmap interop.Hash160 addrNetmap interop.Hash160
addrContainer interop.Hash160 addrContainer interop.Hash160
@ -50,13 +56,6 @@ func _deploy(data interface{}, isUpdate bool) {
storage.Put(ctx, netmapContractKey, args.addrNetmap) storage.Put(ctx, netmapContractKey, args.addrNetmap)
storage.Put(ctx, containerContractKey, args.addrContainer) 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") runtime.Log("frostfsid contract initialized")
} }
@ -90,41 +89,8 @@ func AddKey(owner []byte, keys []interop.PublicKey) {
} }
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
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...) ownerKey := append([]byte{ownerKeysPrefix}, owner...)
for i := range keys { for i := range keys {
@ -153,35 +119,11 @@ func RemoveKey(owner []byte, keys []interop.PublicKey) {
} }
ctx := storage.GetContext() 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() multiaddr := common.AlphabetAddress()
if !runtime.CheckWitness(multiaddr) { if !runtime.CheckWitness(multiaddr) {
panic("invocation from non inner ring node") panic("invocation from non inner ring node")
} }
}
ownerKey := append([]byte{ownerKeysPrefix}, owner...) ownerKey := append([]byte{ownerKeysPrefix}, owner...)
for i := range keys { for i := range keys {
@ -222,12 +164,3 @@ func getUserInfo(ctx storage.Context, key interface{}) UserInfo {
return UserInfo{Keys: pubs} 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)
}

View file

@ -1,5 +1,5 @@
name: "FrostFS Netmap" name: "FrostFS Netmap"
safemethods: ["innerRingList", "epoch", "netmap", "netmapCandidates", "snapshot", "snapshotByEpoch", "config", "listConfig", "version"] safemethods: ["epoch", "netmap", "netmapCandidates", "snapshot", "snapshotByEpoch", "config", "listConfig", "version"]
permissions: permissions:
- methods: ["update", "newEpoch"] - methods: ["update", "newEpoch"]
events: events:

View file

@ -2,8 +2,7 @@
Netmap contract is a contract deployed in FrostFS sidechain. Netmap contract is a contract deployed in FrostFS sidechain.
Netmap contract stores and manages FrostFS network map, Storage node candidates Netmap contract stores and manages FrostFS network map, Storage node candidates
and epoch number counter. In notary disabled environment, contract also stores and epoch number counter.
a list of Inner Ring node keys.
# Contract notifications # Contract notifications

View file

@ -5,7 +5,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop" "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/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator" "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/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std" "github.com/nspcc-dev/neo-go/pkg/interop/native/std"
@ -71,7 +70,14 @@ var (
func _deploy(data interface{}, isUpdate bool) { func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext() ctx := storage.GetContext()
//TODO(@acid-ant): #9 remove notaryDisabled from args in future version
if data.([]interface{})[0].(bool) {
panic(common.PanicMsgForNotaryDisabledEnv)
}
storage.Delete(ctx, notaryDisabledKey)
var args = data.(struct { var args = data.(struct {
//TODO(@acid-ant): #9 remove notaryDisabled in future version
notaryDisabled bool notaryDisabled bool
addrBalance interop.Hash160 addrBalance interop.Hash160
addrContainer interop.Hash160 addrContainer interop.Hash160
@ -115,14 +121,6 @@ func _deploy(data interface{}, isUpdate bool) {
storage.Put(ctx, balanceContractKey, args.addrBalance) storage.Put(ctx, balanceContractKey, args.addrBalance)
storage.Put(ctx, containerContractKey, args.addrContainer) 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") runtime.Log("netmap contract initialized")
} }
@ -138,62 +136,6 @@ func Update(script []byte, manifest []byte, data interface{}) {
runtime.Log("netmap contract updated") 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 // AddPeerIR accepts Alphabet calls in the notary-enabled contract setting and
// behaves similar to AddPeer in the notary-disabled one. // behaves similar to AddPeer in the notary-disabled one.
// //
@ -201,12 +143,8 @@ func UpdateInnerRing(keys []interop.PublicKey) {
// AddPeerIR MUST be called by the Alphabet member only. // AddPeerIR MUST be called by the Alphabet member only.
func AddPeerIR(nodeInfo []byte) { func AddPeerIR(nodeInfo []byte) {
ctx := storage.GetContext() 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()) common.CheckAlphabetWitness()
publicKey := nodeInfo[2:35] // V2 format: offset:2, len:33 publicKey := nodeInfo[2:35] // V2 format: offset:2, len:33
@ -217,79 +155,15 @@ func AddPeerIR(nodeInfo []byte) {
} }
// AddPeer accepts information about the network map candidate in the FrostFS // AddPeer accepts information about the network map candidate in the FrostFS
// binary protocol format, identifies the caller and behaves depending on different // binary protocol format and does nothing. Keep method because storage node
// conditions listed below. // creates a notary transaction with this method, which produces a notary
// // notification (implicit here).
// 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.
func AddPeer(nodeInfo []byte) { func AddPeer(nodeInfo []byte) {
ctx := storage.GetContext() // V2 format - offset:2, len:33
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool) common.CheckWitness(nodeInfo[2:35])
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)
}
return 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 // updates state of the network map candidate by its public key in the contract
// storage, and throws UpdateStateSuccess notification after this. // storage, and throws UpdateStateSuccess notification after this.
// //
@ -310,13 +184,8 @@ func updateCandidateState(ctx storage.Context, publicKey interop.PublicKey, stat
} }
// UpdateState accepts new state to be assigned to network map candidate // UpdateState accepts new state to be assigned to network map candidate
// identified by the given public key, identifies the signer and behaves // identified by the given public key, identifies the signer.
// depending on different conditions listed below. // Applicable only for notary-enabled environment.
//
// Contract settings:
//
// (1) notary-enabled
// (2) notary-disabled
// //
// Signers: // Signers:
// //
@ -327,13 +196,10 @@ func updateCandidateState(ctx storage.Context, publicKey interop.PublicKey, stat
// //
// UpdateState case-by-case behavior: // UpdateState case-by-case behavior:
// //
// (1a) panics // (a) panics
// (1b) like (1a) // (b) panics
// (1ab) updates candidate's state in the contract storage (*), and throws // (ab) updates candidate's state in the contract storage (*), and throws
// UpdateStateSuccess with the provided key and new state // 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 // (c) panics
// //
// (*) Candidate is removed from the candidate set if state is NodeStateOffline. // (*) Candidate is removed from the candidate set if state is NodeStateOffline.
@ -349,33 +215,9 @@ func UpdateState(state NodeState, publicKey interop.PublicKey) {
} }
ctx := storage.GetContext() 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) common.CheckWitness(publicKey)
runtime.Notify("UpdateState", state, publicKey) common.CheckAlphabetWitness()
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) updateCandidateState(ctx, publicKey, state)
} }
@ -388,12 +230,8 @@ func UpdateState(state NodeState, publicKey interop.PublicKey) {
// UpdateStateIR MUST be called by the Alphabet member only. // UpdateStateIR MUST be called by the Alphabet member only.
func UpdateStateIR(state NodeState, publicKey interop.PublicKey) { func UpdateStateIR(state NodeState, publicKey interop.PublicKey) {
ctx := storage.GetContext() 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()) common.CheckAlphabetWitness()
updateCandidateState(ctx, publicKey, state) updateCandidateState(ctx, publicKey, state)
} }
@ -409,35 +247,8 @@ func UpdateStateIR(state NodeState, publicKey interop.PublicKey) {
// It produces NewEpoch notification. // It produces NewEpoch notification.
func NewEpoch(epochNum int) { func NewEpoch(epochNum int) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
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) currentEpoch := storage.Get(ctx, snapshotEpoch).(int)
if epochNum <= currentEpoch { if epochNum <= currentEpoch {
@ -535,7 +346,7 @@ func getSnapshotCount(ctx storage.Context) int {
// //
// Count MUST NOT be negative. // Count MUST NOT be negative.
func UpdateSnapshotCount(count int) { func UpdateSnapshotCount(count int) {
common.CheckAlphabetWitness(common.AlphabetAddress()) common.CheckAlphabetWitness()
if count < 0 { if count < 0 {
panic("count must be positive") panic("count must be positive")
} }
@ -634,34 +445,8 @@ func Config(key []byte) interface{} {
// only by Alphabet nodes. // only by Alphabet nodes.
func SetConfig(id, key, val []byte) { func SetConfig(id, key, val []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
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) setConfig(ctx, key, val)
@ -784,26 +569,3 @@ func cleanup(ctx storage.Context, epoch int) {
containerContractAddr := storage.Get(ctx, containerContractKey).(interop.Hash160) containerContractAddr := storage.Get(ctx, containerContractKey).(interop.Hash160)
contract.Call(containerContractAddr, cleanupEpochMethod, contract.All, epoch) 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)
}

View file

@ -18,25 +18,18 @@ const (
) )
func _deploy(data interface{}, isUpdate bool) { func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext() //TODO(@acid-ant): #9 remove notaryDisabled from args in future version
args := data.([]interface{})
if args[0].(bool) {
panic(common.PanicMsgForNotaryDisabledEnv)
}
storage.Delete(storage.GetContext(), notaryDisabledKey)
if isUpdate { if isUpdate {
args := data.([]interface{})
common.CheckVersion(args[len(args)-1].(int)) common.CheckVersion(args[len(args)-1].(int))
return 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") runtime.Log("reputation contract initialized")
} }
@ -60,40 +53,14 @@ func Update(script []byte, manifest []byte, data interface{}) {
// Value contains a stable marshaled structure of DataAuditResult. // Value contains a stable marshaled structure of DataAuditResult.
func Put(epoch int, peerID []byte, value []byte) { func Put(epoch int, peerID []byte, value []byte) {
ctx := storage.GetContext() 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() multiaddr := common.AlphabetAddress()
alphabetCall = runtime.CheckWitness(multiaddr) if !runtime.CheckWitness(multiaddr) {
}
if !alphabetCall {
runtime.Notify("reputationPut", epoch, peerID, value) runtime.Notify("reputationPut", epoch, peerID, value)
return return
} }
id := storageID(epoch, peerID) 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) key := getReputationKey(reputationCountPrefix, id)
rawCnt := storage.Get(ctx, key) rawCnt := storage.Get(ctx, key)
cnt := 0 cnt := 0

View file

@ -27,14 +27,6 @@ const (
ErrInvalidUser = "invalid user" ErrInvalidUser = "invalid user"
// ErrInvalidNode is thrown when node has invalid format. // ErrInvalidNode is thrown when node has invalid format.
ErrInvalidNode = "invalid node key" 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 is thrown when operation is denied for caller.
ErrAccessDenied = "access denied" ErrAccessDenied = "access denied"
) )
@ -57,18 +49,17 @@ const (
// _deploy function sets up initial list of inner ring public keys. // _deploy function sets up initial list of inner ring public keys.
func _deploy(data interface{}, isUpdate bool) { func _deploy(data interface{}, isUpdate bool) {
if isUpdate { //TODO(@acid-ant): #9 remove notaryDisabled from args in future version
args := data.([]interface{}) args := data.([]interface{})
if args[0].(bool) {
panic(common.PanicMsgForNotaryDisabledEnv)
}
storage.Delete(storage.GetContext(), notaryDisabledKey)
if isUpdate {
common.CheckVersion(args[len(args)-1].(int)) common.CheckVersion(args[len(args)-1].(int))
return 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 // Update method updates contract source code and manifest. It can be invoked
@ -99,30 +90,9 @@ func Put(id []byte, ownerKey interop.PublicKey, info []byte) {
panic(ErrAlreadyExists) 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) common.CheckOwnerWitness(ownerKey)
multiaddr := common.AlphabetAddress() common.CheckAlphabetWitness()
common.CheckAlphabetWitness(multiaddr)
}
storage.Put(ctx, stKey, ownerKey) storage.Put(ctx, stKey, ownerKey)
stKey[0] = infoPrefix stKey[0] = infoPrefix

View file

@ -79,20 +79,6 @@ func newFrostFSInvoker(t *testing.T, n int, config ...interface{}) (*neotest.Con
return e.CommitteeInvoker(h).WithSigners(alphabet), alphabet, pubs 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) { func TestFrostFS_InnerRingCandidate(t *testing.T) {
e, _, _ := newFrostFSInvoker(t, 4, frostfs.CandidateFeeConfigKey, int64(10)) e, _, _ := newFrostFSInvoker(t, 4, frostfs.CandidateFeeConfigKey, int64(10))