forked from TrueCloudLab/frostfs-contract
[#42] Share Vote/RemoteVotes between contracts
Replace vote/remoteVotes functions from all contracts (except alphabet) to common package. Additionally replace setSerialized and bytesEqual. Create InitVote function and use it in NeoFS and Netmap contracts. Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
parent
f9f2a03078
commit
c3efe152d6
9 changed files with 208 additions and 559 deletions
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/util"
|
||||
"github.com/nspcc-dev/neofs-contract/common"
|
||||
)
|
||||
|
||||
|
@ -46,7 +45,7 @@ func init() {
|
|||
// OnPayment is a callback for NEP-17 compatible native GAS and NEO contracts.
|
||||
func OnPayment(from interop.Hash160, amount int, data interface{}) {
|
||||
caller := runtime.GetCallingScriptHash()
|
||||
if !bytesEqual(caller, []byte(gasHash)) && !bytesEqual(caller, []byte(neoHash)) {
|
||||
if !common.BytesEqual(caller, []byte(gasHash)) && !common.BytesEqual(caller, []byte(neoHash)) {
|
||||
panic("onPayment: alphabet contract accepts GAS and NEO only")
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +64,7 @@ func Init(addrNetmap []byte, name string, index, total int) {
|
|||
storage.Put(ctx, indexKey, index)
|
||||
storage.Put(ctx, totalKey, total)
|
||||
|
||||
setSerialized(ctx, voteKey, []common.Ballot{})
|
||||
common.SetSerialized(ctx, voteKey, []common.Ballot{})
|
||||
|
||||
runtime.Log(name + " contract initialized")
|
||||
}
|
||||
|
@ -215,11 +214,11 @@ func vote(ctx storage.Context, epoch int, id, from []byte) int {
|
|||
|
||||
for i := 0; i < len(candidates); i++ {
|
||||
cnd := candidates[i]
|
||||
if bytesEqual(cnd.ID, id) {
|
||||
if common.BytesEqual(cnd.ID, id) {
|
||||
voters := cnd.Voters
|
||||
|
||||
for j := range voters {
|
||||
if bytesEqual(voters[j], from) {
|
||||
if common.BytesEqual(voters[j], from) {
|
||||
return len(voters)
|
||||
}
|
||||
}
|
||||
|
@ -244,7 +243,7 @@ func vote(ctx storage.Context, epoch int, id, from []byte) int {
|
|||
found = 1
|
||||
}
|
||||
|
||||
setSerialized(ctx, voteKey, newCandidates)
|
||||
common.SetSerialized(ctx, voteKey, newCandidates)
|
||||
|
||||
return found
|
||||
}
|
||||
|
@ -257,12 +256,12 @@ func removeVotes(ctx storage.Context, id []byte) {
|
|||
|
||||
for i := 0; i < len(candidates); i++ {
|
||||
cnd := candidates[i]
|
||||
if !bytesEqual(cnd.ID, id) {
|
||||
if !common.BytesEqual(cnd.ID, id) {
|
||||
newCandidates = append(newCandidates, cnd)
|
||||
}
|
||||
}
|
||||
|
||||
setSerialized(ctx, voteKey, newCandidates)
|
||||
common.SetSerialized(ctx, voteKey, newCandidates)
|
||||
}
|
||||
|
||||
func getBallots(ctx storage.Context) []common.Ballot {
|
||||
|
@ -274,16 +273,6 @@ func getBallots(ctx storage.Context) []common.Ballot {
|
|||
return []common.Ballot{}
|
||||
}
|
||||
|
||||
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||
data := binary.Serialize(value)
|
||||
storage.Put(ctx, key, data)
|
||||
}
|
||||
|
||||
// neo-go#1176
|
||||
func bytesEqual(a []byte, b []byte) bool {
|
||||
return util.Equals(string(a), string(b))
|
||||
}
|
||||
|
||||
func voteID(epoch interface{}, args [][]byte) []byte {
|
||||
var (
|
||||
result []byte
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
|
||||
"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/util"
|
||||
"github.com/nspcc-dev/neofs-contract/common"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -78,7 +78,7 @@ func Put(rawAuditResult []byte) bool {
|
|||
|
||||
for i := range innerRing {
|
||||
ir := innerRing[i]
|
||||
if bytesEqual(ir.key, hdr.from) {
|
||||
if common.BytesEqual(ir.key, hdr.from) {
|
||||
presented = true
|
||||
|
||||
break
|
||||
|
@ -183,8 +183,3 @@ func newAuditHeader(input []byte) auditHeader {
|
|||
key,
|
||||
}
|
||||
}
|
||||
|
||||
// neo-go#1176
|
||||
func bytesEqual(a []byte, b []byte) bool {
|
||||
return util.Equals(string(a), string(b))
|
||||
}
|
||||
|
|
|
@ -3,13 +3,11 @@ package balancecontract
|
|||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
|
||||
"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/util"
|
||||
"github.com/nspcc-dev/neofs-contract/common"
|
||||
)
|
||||
|
||||
|
@ -45,9 +43,6 @@ const (
|
|||
circulation = "MainnetGAS"
|
||||
version = 1
|
||||
|
||||
voteKey = "ballots"
|
||||
blockDiff = 20 // change base on performance evaluation
|
||||
|
||||
netmapContractKey = "netmapScriptHash"
|
||||
containerContractKey = "containerScriptHash"
|
||||
)
|
||||
|
@ -134,11 +129,11 @@ func TransferX(from, to interop.Hash160, amount int, details []byte) bool {
|
|||
runtime.Log("transferX: processed indirect invoke")
|
||||
} else {
|
||||
hashTxID = invokeID([]interface{}{from, to, amount}, []byte("transfer"))
|
||||
n = vote(ctx, hashTxID, irKey)
|
||||
n = common.Vote(ctx, hashTxID, irKey)
|
||||
}
|
||||
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, hashTxID)
|
||||
common.RemoveVotes(ctx, hashTxID)
|
||||
|
||||
result := token.transfer(ctx, from, to, amount, true, details)
|
||||
if result {
|
||||
|
@ -166,16 +161,16 @@ func Lock(txID []byte, from, to interop.Hash160, amount, until int) bool {
|
|||
|
||||
hashTxID := invokeID([]interface{}{txID, from, to, amount, until}, []byte("lock"))
|
||||
|
||||
n := vote(ctx, hashTxID, irKey)
|
||||
n := common.Vote(ctx, hashTxID, irKey)
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, hashTxID)
|
||||
common.RemoveVotes(ctx, hashTxID)
|
||||
|
||||
lockAccount := Account{
|
||||
Balance: 0,
|
||||
Until: until,
|
||||
Parent: from,
|
||||
}
|
||||
setSerialized(ctx, to, lockAccount)
|
||||
common.SetSerialized(ctx, to, lockAccount)
|
||||
|
||||
result := token.transfer(ctx, from, to, amount, true, lockTransferMsg)
|
||||
if !result {
|
||||
|
@ -204,9 +199,9 @@ func NewEpoch(epochNum int) bool {
|
|||
|
||||
epochID := invokeID([]interface{}{epochNum}, []byte("epoch"))
|
||||
|
||||
n := vote(ctx, epochID, irKey)
|
||||
n := common.Vote(ctx, epochID, irKey)
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, epochID)
|
||||
common.RemoveVotes(ctx, epochID)
|
||||
it := storage.Find(ctx, []byte{})
|
||||
for iterator.Next(it) {
|
||||
addr := iterator.Key(it).([]byte)
|
||||
|
@ -243,9 +238,9 @@ func Mint(to interop.Hash160, amount int, details []byte) bool {
|
|||
|
||||
mintID := invokeID([]interface{}{to, amount, details}, []byte("mint"))
|
||||
|
||||
n := vote(ctx, mintID, irKey)
|
||||
n := common.Vote(ctx, mintID, irKey)
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, mintID)
|
||||
common.RemoveVotes(ctx, mintID)
|
||||
|
||||
ok := token.transfer(ctx, nil, to, amount, true, details)
|
||||
if !ok {
|
||||
|
@ -274,9 +269,9 @@ func Burn(from interop.Hash160, amount int, details []byte) bool {
|
|||
|
||||
burnID := invokeID([]interface{}{from, amount, details}, []byte("burn"))
|
||||
|
||||
n := vote(ctx, burnID, irKey)
|
||||
n := common.Vote(ctx, burnID, irKey)
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, burnID)
|
||||
common.RemoveVotes(ctx, burnID)
|
||||
|
||||
ok := token.transfer(ctx, from, nil, amount, true, details)
|
||||
if !ok {
|
||||
|
@ -329,14 +324,14 @@ func (t Token) transfer(ctx storage.Context, from, to interop.Hash160, amount in
|
|||
storage.Delete(ctx, from)
|
||||
} else {
|
||||
amountFrom.Balance = amountFrom.Balance - amount // neo-go#953
|
||||
setSerialized(ctx, from, amountFrom)
|
||||
common.SetSerialized(ctx, from, amountFrom)
|
||||
}
|
||||
}
|
||||
|
||||
if len(to) == 20 {
|
||||
amountTo := getAccount(ctx, to)
|
||||
amountTo.Balance = amountTo.Balance + amount // neo-go#953
|
||||
setSerialized(ctx, to, amountTo)
|
||||
common.SetSerialized(ctx, to, amountTo)
|
||||
}
|
||||
|
||||
runtime.Notify("Transfer", from, to, amount)
|
||||
|
@ -379,7 +374,7 @@ func isUsableAddress(addr interop.Hash160) bool {
|
|||
|
||||
// Check if a smart contract is calling script hash
|
||||
callingScriptHash := runtime.GetCallingScriptHash()
|
||||
if bytesEqual(callingScriptHash, addr) {
|
||||
if common.BytesEqual(callingScriptHash, addr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -398,82 +393,6 @@ func innerRingInvoker(ir []irNode) []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
func vote(ctx storage.Context, id, from []byte) int {
|
||||
var (
|
||||
newCandidates []common.Ballot
|
||||
candidates = getBallots(ctx)
|
||||
found = -1
|
||||
blockHeight = blockchain.GetHeight()
|
||||
)
|
||||
|
||||
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 = common.Ballot{ID: id, Voters: voters, Height: blockHeight}
|
||||
found = len(voters)
|
||||
}
|
||||
|
||||
newCandidates = append(newCandidates, cnd)
|
||||
}
|
||||
|
||||
if found < 0 {
|
||||
voters := [][]byte{from}
|
||||
newCandidates = append(newCandidates, common.Ballot{
|
||||
ID: id,
|
||||
Voters: voters,
|
||||
Height: blockHeight})
|
||||
found = 1
|
||||
}
|
||||
|
||||
setSerialized(ctx, voteKey, newCandidates)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
func removeVotes(ctx storage.Context, id []byte) {
|
||||
var (
|
||||
newCandidates []common.Ballot
|
||||
candidates = getBallots(ctx)
|
||||
)
|
||||
|
||||
for i := 0; i < len(candidates); i++ {
|
||||
cnd := candidates[i]
|
||||
if !bytesEqual(cnd.ID, id) {
|
||||
newCandidates = append(newCandidates, cnd)
|
||||
}
|
||||
}
|
||||
|
||||
setSerialized(ctx, voteKey, newCandidates)
|
||||
}
|
||||
|
||||
func getBallots(ctx storage.Context) []common.Ballot {
|
||||
data := storage.Get(ctx, voteKey)
|
||||
if data != nil {
|
||||
return binary.Deserialize(data.([]byte)).([]common.Ballot)
|
||||
}
|
||||
|
||||
return []common.Ballot{}
|
||||
}
|
||||
|
||||
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||
data := binary.Serialize(value)
|
||||
storage.Put(ctx, key, data)
|
||||
}
|
||||
|
||||
func getAccount(ctx storage.Context, key interface{}) Account {
|
||||
data := storage.Get(ctx, key)
|
||||
if data != nil {
|
||||
|
@ -492,11 +411,6 @@ func invokeID(args []interface{}, prefix []byte) []byte {
|
|||
return crypto.SHA256(prefix)
|
||||
}
|
||||
|
||||
// neo-go#1176
|
||||
func bytesEqual(a []byte, b []byte) bool {
|
||||
return util.Equals(string(a), string(b))
|
||||
}
|
||||
|
||||
/*
|
||||
Check if invocation made from known container or audit contracts.
|
||||
This is necessary because calls from these contracts require to do transfer
|
||||
|
@ -520,5 +434,5 @@ func bytesEqual(a []byte, b []byte) bool {
|
|||
func fromKnownContract(caller interop.Hash160) bool {
|
||||
containerContractAddr := storage.Get(ctx, containerContractKey).([]byte)
|
||||
|
||||
return bytesEqual(caller, containerContractAddr)
|
||||
return common.BytesEqual(caller, containerContractAddr)
|
||||
}
|
||||
|
|
104
common/vote.go
Normal file
104
common/vote.go
Normal file
|
@ -0,0 +1,104 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/util"
|
||||
)
|
||||
|
||||
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 specific 'id' and returns amount
|
||||
// on unique voters for that decision.
|
||||
func Vote(ctx storage.Context, id, from []byte) int {
|
||||
var (
|
||||
newCandidates []Ballot
|
||||
candidates = getBallots(ctx)
|
||||
found = -1
|
||||
blockHeight = blockchain.GetHeight()
|
||||
)
|
||||
|
||||
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 := [][]byte{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 (
|
||||
newCandidates []Ballot
|
||||
candidates = getBallots(ctx)
|
||||
)
|
||||
|
||||
for i := 0; i < len(candidates); i++ {
|
||||
cnd := candidates[i]
|
||||
if !BytesEqual(cnd.ID, id) {
|
||||
newCandidates = append(newCandidates, cnd)
|
||||
}
|
||||
}
|
||||
|
||||
SetSerialized(ctx, voteKey, newCandidates)
|
||||
}
|
||||
|
||||
// getBallots returns deserialized slice of vote ballots.
|
||||
func getBallots(ctx storage.Context) []Ballot {
|
||||
data := storage.Get(ctx, voteKey)
|
||||
if data != nil {
|
||||
return binary.Deserialize(data.([]byte)).([]Ballot)
|
||||
}
|
||||
|
||||
return []Ballot{}
|
||||
}
|
||||
|
||||
// BytesEqual compares two slice of bytes by wrapping them into strings,
|
||||
// which is necessary with new util.Equal interop behaviour, see neo-go#1176.
|
||||
func BytesEqual(a []byte, b []byte) bool {
|
||||
return util.Equals(string(a), string(b))
|
||||
}
|
||||
|
||||
// SetSerialized serializes data and puts it into contract storage.
|
||||
func SetSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||
data := binary.Serialize(value)
|
||||
storage.Put(ctx, key, data)
|
||||
}
|
|
@ -3,13 +3,11 @@ package containercontract
|
|||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
|
||||
"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/util"
|
||||
"github.com/nspcc-dev/neofs-contract/common"
|
||||
)
|
||||
|
||||
|
@ -41,9 +39,7 @@ type (
|
|||
|
||||
const (
|
||||
version = 1
|
||||
voteKey = "ballots"
|
||||
ownersKey = "ownersList"
|
||||
blockDiff = 20 // change base on performance evaluation
|
||||
|
||||
neofsIDContractKey = "identityScriptHash"
|
||||
balanceContractKey = "balanceScriptHash"
|
||||
|
@ -123,9 +119,9 @@ func Put(container, signature, publicKey []byte) bool {
|
|||
containerFee := contract.Call(netmapContractAddr, "config", containerFeeKey).(int)
|
||||
hashCandidate := invokeID([]interface{}{container, signature, publicKey}, []byte("put"))
|
||||
|
||||
n := vote(ctx, hashCandidate, irKey)
|
||||
n := common.Vote(ctx, hashCandidate, irKey)
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, hashCandidate)
|
||||
common.RemoveVotes(ctx, hashCandidate)
|
||||
// todo: check if new container with unique container id
|
||||
|
||||
for i := 0; i < len(innerRing); i++ {
|
||||
|
@ -184,9 +180,9 @@ func Delete(containerID, signature []byte) bool {
|
|||
|
||||
hashCandidate := invokeID([]interface{}{containerID, signature}, []byte("delete"))
|
||||
|
||||
n := vote(ctx, hashCandidate, irKey)
|
||||
n := common.Vote(ctx, hashCandidate, irKey)
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, hashCandidate)
|
||||
common.RemoveVotes(ctx, hashCandidate)
|
||||
removeContainer(ctx, containerID, ownerID)
|
||||
runtime.Log("delete: remove container")
|
||||
} else {
|
||||
|
@ -214,7 +210,7 @@ func List(owner []byte) [][]byte {
|
|||
owners := getList(ctx, ownersKey)
|
||||
for i := 0; i < len(owners); i++ {
|
||||
ownerID := owners[i]
|
||||
if len(owner) != 0 && !bytesEqual(owner, ownerID) {
|
||||
if len(owner) != 0 && !common.BytesEqual(owner, ownerID) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -253,7 +249,7 @@ func SetEACL(eACL, signature []byte) bool {
|
|||
}
|
||||
|
||||
key := append(eACLPrefix, containerID...)
|
||||
setSerialized(ctx, key, rule)
|
||||
common.SetSerialized(ctx, key, rule)
|
||||
|
||||
runtime.Log("setEACL: success")
|
||||
|
||||
|
@ -304,7 +300,7 @@ func PutContainerSize(epoch int, cid []byte, usedSize int, pubKey interop.Public
|
|||
// do not add estimation twice
|
||||
for i := range s.estimations {
|
||||
est := s.estimations[i]
|
||||
if bytesEqual(est.from, pubKey) {
|
||||
if common.BytesEqual(est.from, pubKey) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -356,9 +352,9 @@ func ProcessEpoch(epochNum int) {
|
|||
candidates := keysToDelete(epochNum)
|
||||
epochID := invokeID([]interface{}{epochNum}, []byte("epoch"))
|
||||
|
||||
n := vote(ctx, epochID, irKey)
|
||||
n := common.Vote(ctx, epochID, irKey)
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, epochID)
|
||||
common.RemoveVotes(ctx, epochID)
|
||||
|
||||
for i := range candidates {
|
||||
candidate := candidates[i]
|
||||
|
@ -379,9 +375,9 @@ func StartContainerEstimation(epoch int) bool {
|
|||
|
||||
hashCandidate := invokeID([]interface{}{epoch}, []byte("startEstimation"))
|
||||
|
||||
n := vote(ctx, hashCandidate, irKey)
|
||||
n := common.Vote(ctx, hashCandidate, irKey)
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, hashCandidate)
|
||||
common.RemoveVotes(ctx, hashCandidate)
|
||||
runtime.Notify("StartEstimation", epoch)
|
||||
runtime.Log("startEstimation: notification has been produced")
|
||||
} else {
|
||||
|
@ -403,9 +399,9 @@ func StopContainerEstimation(epoch int) bool {
|
|||
|
||||
hashCandidate := invokeID([]interface{}{epoch}, []byte("stopEstimation"))
|
||||
|
||||
n := vote(ctx, hashCandidate, irKey)
|
||||
n := common.Vote(ctx, hashCandidate, irKey)
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, hashCandidate)
|
||||
common.RemoveVotes(ctx, hashCandidate)
|
||||
runtime.Notify("StopEstimation", epoch)
|
||||
runtime.Log("stopEstimation: notification has been produced")
|
||||
} else {
|
||||
|
@ -439,7 +435,7 @@ func removeContainer(ctx storage.Context, id []byte, owner []byte) {
|
|||
func addOrAppend(ctx storage.Context, key interface{}, value []byte) {
|
||||
list := getList(ctx, key)
|
||||
for i := 0; i < len(list); i++ {
|
||||
if bytesEqual(list[i], value) {
|
||||
if common.BytesEqual(list[i], value) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -450,7 +446,7 @@ func addOrAppend(ctx storage.Context, key interface{}, value []byte) {
|
|||
list = append(list, value)
|
||||
}
|
||||
|
||||
setSerialized(ctx, key, list)
|
||||
common.SetSerialized(ctx, key, list)
|
||||
}
|
||||
|
||||
// remove returns amount of left elements in the list
|
||||
|
@ -461,7 +457,7 @@ func remove(ctx storage.Context, key interface{}, value []byte) int {
|
|||
)
|
||||
|
||||
for i := 0; i < len(list); i++ {
|
||||
if !bytesEqual(list[i], value) {
|
||||
if !common.BytesEqual(list[i], value) {
|
||||
newList = append(newList, list[i])
|
||||
}
|
||||
}
|
||||
|
@ -470,7 +466,7 @@ func remove(ctx storage.Context, key interface{}, value []byte) int {
|
|||
if ln == 0 {
|
||||
storage.Delete(ctx, key)
|
||||
} else {
|
||||
setSerialized(ctx, key, newList)
|
||||
common.SetSerialized(ctx, key, newList)
|
||||
}
|
||||
|
||||
return ln
|
||||
|
@ -487,68 +483,6 @@ func innerRingInvoker(ir []irNode) []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
func vote(ctx storage.Context, id, from []byte) int {
|
||||
var (
|
||||
newCandidates []common.Ballot
|
||||
candidates = getBallots(ctx)
|
||||
found = -1
|
||||
blockHeight = blockchain.GetHeight()
|
||||
)
|
||||
|
||||
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 = common.Ballot{ID: id, Voters: voters, Height: blockHeight}
|
||||
found = len(voters)
|
||||
}
|
||||
|
||||
newCandidates = append(newCandidates, cnd)
|
||||
}
|
||||
|
||||
if found < 0 {
|
||||
voters := [][]byte{from}
|
||||
newCandidates = append(newCandidates, common.Ballot{
|
||||
ID: id,
|
||||
Voters: voters,
|
||||
Height: blockHeight})
|
||||
found = 1
|
||||
}
|
||||
|
||||
setSerialized(ctx, voteKey, newCandidates)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
func removeVotes(ctx storage.Context, id []byte) {
|
||||
var (
|
||||
newCandidates []common.Ballot
|
||||
candidates = getBallots(ctx)
|
||||
)
|
||||
|
||||
for i := 0; i < len(candidates); i++ {
|
||||
cnd := candidates[i]
|
||||
if !bytesEqual(cnd.ID, id) {
|
||||
newCandidates = append(newCandidates, cnd)
|
||||
}
|
||||
}
|
||||
|
||||
setSerialized(ctx, voteKey, newCandidates)
|
||||
}
|
||||
|
||||
func getList(ctx storage.Context, key interface{}) [][]byte {
|
||||
data := storage.Get(ctx, key)
|
||||
if data != nil {
|
||||
|
@ -572,15 +506,6 @@ func getAllContainers(ctx storage.Context) [][]byte {
|
|||
return list
|
||||
}
|
||||
|
||||
func getBallots(ctx storage.Context) []common.Ballot {
|
||||
data := storage.Get(ctx, voteKey)
|
||||
if data != nil {
|
||||
return binary.Deserialize(data.([]byte)).([]common.Ballot)
|
||||
}
|
||||
|
||||
return []common.Ballot{}
|
||||
}
|
||||
|
||||
func getEACL(ctx storage.Context, cid []byte) extendedACL {
|
||||
key := append(eACLPrefix, cid...)
|
||||
data := storage.Get(ctx, key)
|
||||
|
@ -591,11 +516,6 @@ func getEACL(ctx storage.Context, cid []byte) extendedACL {
|
|||
return extendedACL{val: []byte{}, sig: []byte{}, pub: []byte{}}
|
||||
}
|
||||
|
||||
func setSerialized(ctx storage.Context, key, value interface{}) {
|
||||
data := binary.Serialize(value)
|
||||
storage.Put(ctx, key, data)
|
||||
}
|
||||
|
||||
func walletToScripHash(wallet []byte) []byte {
|
||||
return wallet[1 : len(wallet)-4]
|
||||
}
|
||||
|
@ -628,7 +548,7 @@ func getOwnerByID(ctx storage.Context, id []byte) []byte {
|
|||
|
||||
for j := 0; j < len(containers); j++ {
|
||||
container := containers[j]
|
||||
if bytesEqual(container, id) {
|
||||
if common.BytesEqual(container, id) {
|
||||
return ownerID
|
||||
}
|
||||
}
|
||||
|
@ -637,11 +557,6 @@ func getOwnerByID(ctx storage.Context, id []byte) []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
// neo-go#1176
|
||||
func bytesEqual(a []byte, b []byte) bool {
|
||||
return util.Equals(string(a), string(b))
|
||||
}
|
||||
|
||||
func isSignedByOwnerKey(msg, sig, owner, key []byte) bool {
|
||||
if !isOwnerFromKey(owner, key) {
|
||||
return false
|
||||
|
@ -654,7 +569,7 @@ func isOwnerFromKey(owner []byte, key []byte) bool {
|
|||
ownerSH := walletToScripHash(owner)
|
||||
keySH := contract.CreateStandardAccount(key)
|
||||
|
||||
return bytesEqual(ownerSH, keySH)
|
||||
return common.BytesEqual(ownerSH, keySH)
|
||||
}
|
||||
|
||||
func estimationKey(epoch int, cid []byte) []byte {
|
||||
|
@ -688,7 +603,7 @@ func isStorageNode(key interop.PublicKey) bool {
|
|||
nodeInfo := snapshot[i].info
|
||||
nodeKey := nodeInfo[2:35] // offset:2, len:33
|
||||
|
||||
if bytesEqual(key, nodeKey) {
|
||||
if common.BytesEqual(key, nodeKey) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,13 +34,11 @@ package smart_contract
|
|||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
|
||||
"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/util"
|
||||
"github.com/nspcc-dev/neofs-contract/common"
|
||||
)
|
||||
|
||||
|
@ -69,11 +67,9 @@ const (
|
|||
version = 3
|
||||
|
||||
innerRingKey = "innerring"
|
||||
voteKey = "ballots"
|
||||
candidatesKey = "candidates"
|
||||
cashedChequesKey = "cheques"
|
||||
|
||||
blockDiff = 20 // change base on performance evaluation
|
||||
publicKeySize = 33
|
||||
minInnerRingSize = 3
|
||||
|
||||
|
@ -121,10 +117,10 @@ func Init(args [][]byte) bool {
|
|||
}
|
||||
|
||||
// initialize all storage slices
|
||||
setSerialized(ctx, innerRingKey, irList)
|
||||
setSerialized(ctx, voteKey, []common.Ballot{})
|
||||
setSerialized(ctx, candidatesKey, []node{})
|
||||
setSerialized(ctx, cashedChequesKey, []cheque{})
|
||||
common.SetSerialized(ctx, innerRingKey, irList)
|
||||
common.InitVote(ctx)
|
||||
common.SetSerialized(ctx, candidatesKey, []node{})
|
||||
common.SetSerialized(ctx, cashedChequesKey, []cheque{})
|
||||
|
||||
runtime.Log("neofs: contract initialized")
|
||||
|
||||
|
@ -152,14 +148,14 @@ func InnerRingCandidateRemove(key []byte) bool {
|
|||
|
||||
for i := range candidates {
|
||||
c := candidates[i]
|
||||
if !bytesEqual(c.pub, key) {
|
||||
if !common.BytesEqual(c.pub, key) {
|
||||
nodes = append(nodes, c)
|
||||
} else {
|
||||
runtime.Log("irCandidateRemove: candidate has been removed")
|
||||
}
|
||||
}
|
||||
|
||||
setSerialized(ctx, candidatesKey, nodes)
|
||||
common.SetSerialized(ctx, candidatesKey, nodes)
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -190,7 +186,7 @@ func InnerRingCandidateAdd(key []byte) bool {
|
|||
}
|
||||
|
||||
runtime.Log("irCandidateAdd: candidate has been added")
|
||||
setSerialized(ctx, candidatesKey, list)
|
||||
common.SetSerialized(ctx, candidatesKey, list)
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -198,12 +194,12 @@ func InnerRingCandidateAdd(key []byte) bool {
|
|||
// OnPayment is a callback for NEP-17 compatible native GAS contract.
|
||||
func OnPayment(from interop.Hash160, amount int, data interface{}) {
|
||||
rcv := data.(interop.Hash160)
|
||||
if bytesEqual(rcv, []byte(ignoreDepositNotification)) {
|
||||
if common.BytesEqual(rcv, []byte(ignoreDepositNotification)) {
|
||||
return
|
||||
}
|
||||
|
||||
caller := runtime.GetCallingScriptHash()
|
||||
if !bytesEqual(caller, []byte(tokenHash)) {
|
||||
if !common.BytesEqual(caller, []byte(tokenHash)) {
|
||||
panic("onPayment: only GAS can be accepted for deposit")
|
||||
}
|
||||
|
||||
|
@ -290,9 +286,9 @@ func Cheque(id, user []byte, amount int, lockAcc []byte) bool {
|
|||
panic("cheque: non unique id")
|
||||
}
|
||||
|
||||
n := vote(ctx, hashID, irKey)
|
||||
n := common.Vote(ctx, hashID, irKey)
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, hashID)
|
||||
common.RemoveVotes(ctx, hashID)
|
||||
|
||||
from := runtime.GetExecutingScriptHash()
|
||||
|
||||
|
@ -304,7 +300,7 @@ func Cheque(id, user []byte, amount int, lockAcc []byte) bool {
|
|||
|
||||
runtime.Log("cheque: funds have been transferred")
|
||||
|
||||
setSerialized(ctx, cashedChequesKey, list)
|
||||
common.SetSerialized(ctx, cashedChequesKey, list)
|
||||
runtime.Notify("Cheque", id, user, amount, lockAcc)
|
||||
}
|
||||
|
||||
|
@ -385,7 +381,7 @@ loop:
|
|||
// find key in actual inner ring list
|
||||
for j := 0; j < len(irList); j++ {
|
||||
n := irList[j]
|
||||
if bytesEqual(n.pub, key) {
|
||||
if common.BytesEqual(n.pub, key) {
|
||||
newIR = append(newIR, n)
|
||||
oldNodes++
|
||||
|
||||
|
@ -406,13 +402,13 @@ loop:
|
|||
|
||||
hashID := crypto.SHA256(chequeID)
|
||||
|
||||
n := vote(ctx, hashID, irKey)
|
||||
n := common.Vote(ctx, hashID, irKey)
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, hashID)
|
||||
common.RemoveVotes(ctx, hashID)
|
||||
|
||||
setSerialized(ctx, candidatesKey, candidates)
|
||||
setSerialized(ctx, innerRingKey, newIR)
|
||||
setSerialized(ctx, cashedChequesKey, chequesList)
|
||||
common.SetSerialized(ctx, candidatesKey, candidates)
|
||||
common.SetSerialized(ctx, innerRingKey, newIR)
|
||||
common.SetSerialized(ctx, cashedChequesKey, chequesList)
|
||||
|
||||
runtime.Notify("InnerRingUpdate", c.id, newIR)
|
||||
runtime.Log("irUpdate: inner ring list has been updated")
|
||||
|
@ -431,7 +427,7 @@ func IsInnerRing(key []byte) bool {
|
|||
for i := range irList {
|
||||
node := irList[i]
|
||||
|
||||
if bytesEqual(node.pub, key) {
|
||||
if common.BytesEqual(node.pub, key) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -467,12 +463,12 @@ func SetConfig(id, key, val []byte) bool {
|
|||
// vote for new configuration value
|
||||
hashID := crypto.SHA256(id)
|
||||
|
||||
n := vote(ctx, hashID, irKey)
|
||||
n := common.Vote(ctx, hashID, irKey)
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, hashID)
|
||||
common.RemoveVotes(ctx, hashID)
|
||||
|
||||
setConfig(ctx, key, val)
|
||||
setSerialized(ctx, cashedChequesKey, chequesList)
|
||||
common.SetSerialized(ctx, cashedChequesKey, chequesList)
|
||||
|
||||
runtime.Notify("SetConfig", id, key, val)
|
||||
runtime.Log("setConfig: configuration has been updated")
|
||||
|
@ -539,79 +535,6 @@ func innerRingInvoker(ir []node) []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
// vote adds ballot for the decision with specific 'id' and returns amount
|
||||
// on unique voters for that decision.
|
||||
func vote(ctx storage.Context, id, from []byte) int {
|
||||
var (
|
||||
newCandidates = []common.Ballot{} // it is explicit declaration of empty slice, not nil
|
||||
candidates = getBallots(ctx)
|
||||
found = -1
|
||||
blockHeight = blockchain.GetHeight()
|
||||
)
|
||||
|
||||
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 = common.Ballot{ID: id, Voters: voters, Height: blockHeight}
|
||||
found = len(voters)
|
||||
}
|
||||
|
||||
newCandidates = append(newCandidates, cnd)
|
||||
}
|
||||
|
||||
if found < 0 {
|
||||
found = 1
|
||||
voters := [][]byte{from}
|
||||
|
||||
newCandidates = append(newCandidates, common.Ballot{
|
||||
ID: id,
|
||||
Voters: voters,
|
||||
Height: blockHeight})
|
||||
}
|
||||
|
||||
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 (
|
||||
newCandidates = []common.Ballot{} // it is explicit declaration of empty slice, not nil
|
||||
candidates = getBallots(ctx)
|
||||
)
|
||||
|
||||
for i := 0; i < len(candidates); i++ {
|
||||
cnd := candidates[i]
|
||||
if !bytesEqual(cnd.ID, id) {
|
||||
newCandidates = append(newCandidates, cnd)
|
||||
}
|
||||
}
|
||||
|
||||
setSerialized(ctx, voteKey, newCandidates)
|
||||
}
|
||||
|
||||
// setSerialized serializes data and puts it into contract storage.
|
||||
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||
data := binary.Serialize(value)
|
||||
storage.Put(ctx, key, data)
|
||||
}
|
||||
|
||||
// getInnerRingNodes returns deserialized slice of inner ring nodes from storage.
|
||||
func getInnerRingNodes(ctx storage.Context, key string) []node {
|
||||
data := storage.Get(ctx, key)
|
||||
|
@ -632,16 +555,6 @@ func getCashedCheques(ctx storage.Context) []cheque {
|
|||
return []cheque{}
|
||||
}
|
||||
|
||||
// getInnerRingNodes returns deserialized slice of vote ballots.
|
||||
func getBallots(ctx storage.Context) []common.Ballot {
|
||||
data := storage.Get(ctx, voteKey)
|
||||
if data != nil {
|
||||
return binary.Deserialize(data.([]byte)).([]common.Ballot)
|
||||
}
|
||||
|
||||
return []common.Ballot{}
|
||||
}
|
||||
|
||||
// getConfig returns installed neofs configuration value or nil if it is not set.
|
||||
func getConfig(ctx storage.Context, key interface{}) interface{} {
|
||||
postfix := key.([]byte)
|
||||
|
@ -662,7 +575,7 @@ func setConfig(ctx storage.Context, key, val interface{}) {
|
|||
// that set to false if cheque 'c' is already presented in the slice 'lst'.
|
||||
func addCheque(lst []cheque, c cheque) ([]cheque, bool) {
|
||||
for i := 0; i < len(lst); i++ {
|
||||
if bytesEqual(c.id, lst[i].id) {
|
||||
if common.BytesEqual(c.id, lst[i].id) {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
@ -676,7 +589,7 @@ func addCheque(lst []cheque, c cheque) ([]cheque, bool) {
|
|||
// that set to false if node 'n' is already presented in the slice 'lst'.
|
||||
func addNode(lst []node, n node) ([]node, bool) {
|
||||
for i := 0; i < len(lst); i++ {
|
||||
if bytesEqual(n.pub, lst[i].pub) {
|
||||
if common.BytesEqual(n.pub, lst[i].pub) {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
@ -696,7 +609,7 @@ func rmNodeByKey(lst, add []node, k []byte) ([]node, []node, bool) {
|
|||
)
|
||||
|
||||
for i := 0; i < len(lst); i++ {
|
||||
if bytesEqual(k, lst[i].pub) {
|
||||
if common.BytesEqual(k, lst[i].pub) {
|
||||
add = append(add, lst[i])
|
||||
flag = true
|
||||
} else {
|
||||
|
@ -706,9 +619,3 @@ func rmNodeByKey(lst, add []node, k []byte) ([]node, []node, bool) {
|
|||
|
||||
return newLst, add, flag
|
||||
}
|
||||
|
||||
// bytesEqual compares two slice of bytes by wrapping them into strings,
|
||||
// which is necessary with new util.Equal interop behaviour, see neo-go#1176.
|
||||
func bytesEqual(a []byte, b []byte) bool {
|
||||
return util.Equals(string(a), string(b))
|
||||
}
|
||||
|
|
|
@ -2,12 +2,10 @@ package neofsidcontract
|
|||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/util"
|
||||
"github.com/nspcc-dev/neofs-contract/common"
|
||||
)
|
||||
|
||||
|
@ -22,10 +20,7 @@ type (
|
|||
)
|
||||
|
||||
const (
|
||||
version = 1
|
||||
blockDiff = 20 // change base on performance evaluation
|
||||
|
||||
voteKey = "ballots"
|
||||
version = 1
|
||||
|
||||
netmapContractKey = "netmapScriptHash"
|
||||
containerContractKey = "containerScriptHash"
|
||||
|
@ -88,7 +83,7 @@ addLoop:
|
|||
|
||||
for j := range info.Keys {
|
||||
key := info.Keys[j]
|
||||
if bytesEqual(key, pubKey) {
|
||||
if common.BytesEqual(key, pubKey) {
|
||||
continue addLoop
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +97,7 @@ addLoop:
|
|||
runtime.Log("addKey: processed indirect invoke")
|
||||
} else {
|
||||
id := invokeIDKeys(owner, keys, []byte("add"))
|
||||
n = vote(ctx, id, irKey)
|
||||
n = common.Vote(ctx, id, irKey)
|
||||
}
|
||||
|
||||
if n < threshold {
|
||||
|
@ -110,8 +105,8 @@ addLoop:
|
|||
return true
|
||||
}
|
||||
|
||||
removeVotes(ctx, id)
|
||||
setSerialized(ctx, owner, info)
|
||||
common.RemoveVotes(ctx, id)
|
||||
common.SetSerialized(ctx, owner, info)
|
||||
runtime.Log("addKey: key bound to the owner")
|
||||
|
||||
return true
|
||||
|
@ -133,13 +128,13 @@ func RemoveKey(owner []byte, keys [][]byte) bool {
|
|||
|
||||
id := invokeIDKeys(owner, keys, []byte("remove"))
|
||||
|
||||
n := vote(ctx, id, irKey)
|
||||
n := common.Vote(ctx, id, irKey)
|
||||
if n < threshold {
|
||||
runtime.Log("removeKey: processed invoke from inner ring")
|
||||
return true
|
||||
}
|
||||
|
||||
removeVotes(ctx, id)
|
||||
common.RemoveVotes(ctx, id)
|
||||
|
||||
info := getUserInfo(ctx, owner)
|
||||
var leftKeys [][]byte
|
||||
|
@ -154,7 +149,7 @@ rmLoop:
|
|||
panic("removeKey: incorrect public key")
|
||||
}
|
||||
|
||||
if bytesEqual(key, pubKey) {
|
||||
if common.BytesEqual(key, pubKey) {
|
||||
continue rmLoop
|
||||
}
|
||||
}
|
||||
|
@ -163,7 +158,7 @@ rmLoop:
|
|||
}
|
||||
|
||||
info.Keys = leftKeys
|
||||
setSerialized(ctx, owner, info)
|
||||
common.SetSerialized(ctx, owner, info)
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -191,20 +186,6 @@ func getUserInfo(ctx storage.Context, key interface{}) UserInfo {
|
|||
return UserInfo{Keys: [][]byte{}}
|
||||
}
|
||||
|
||||
func getBallots(ctx storage.Context) []common.Ballot {
|
||||
data := storage.Get(ctx, voteKey)
|
||||
if data != nil {
|
||||
return binary.Deserialize(data.([]byte)).([]common.Ballot)
|
||||
}
|
||||
|
||||
return []common.Ballot{}
|
||||
}
|
||||
|
||||
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||
data := binary.Serialize(value)
|
||||
storage.Put(ctx, key, data)
|
||||
}
|
||||
|
||||
func innerRingInvoker(ir []irNode) []byte {
|
||||
for i := 0; i < len(ir); i++ {
|
||||
node := ir[i]
|
||||
|
@ -216,68 +197,6 @@ func innerRingInvoker(ir []irNode) []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
func vote(ctx storage.Context, id, from []byte) int {
|
||||
var (
|
||||
newCandidates []common.Ballot
|
||||
candidates = getBallots(ctx)
|
||||
found = -1
|
||||
blockHeight = blockchain.GetHeight()
|
||||
)
|
||||
|
||||
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 = common.Ballot{ID: id, Voters: voters, Height: blockHeight}
|
||||
found = len(voters)
|
||||
}
|
||||
|
||||
newCandidates = append(newCandidates, cnd)
|
||||
}
|
||||
|
||||
if found < 0 {
|
||||
voters := [][]byte{from}
|
||||
newCandidates = append(newCandidates, common.Ballot{
|
||||
ID: id,
|
||||
Voters: voters,
|
||||
Height: blockHeight})
|
||||
found = 1
|
||||
}
|
||||
|
||||
setSerialized(ctx, voteKey, newCandidates)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
func removeVotes(ctx storage.Context, id []byte) {
|
||||
var (
|
||||
newCandidates []common.Ballot
|
||||
candidates = getBallots(ctx)
|
||||
)
|
||||
|
||||
for i := 0; i < len(candidates); i++ {
|
||||
cnd := candidates[i]
|
||||
if !bytesEqual(cnd.ID, id) {
|
||||
newCandidates = append(newCandidates, cnd)
|
||||
}
|
||||
}
|
||||
|
||||
setSerialized(ctx, voteKey, newCandidates)
|
||||
}
|
||||
|
||||
func invokeID(args []interface{}, prefix []byte) []byte {
|
||||
for i := range args {
|
||||
arg := args[i].([]byte)
|
||||
|
@ -296,14 +215,9 @@ func invokeIDKeys(owner []byte, keys [][]byte, prefix []byte) []byte {
|
|||
return crypto.SHA256(prefix)
|
||||
}
|
||||
|
||||
// neo-go#1176
|
||||
func bytesEqual(a []byte, b []byte) bool {
|
||||
return util.Equals(string(a), string(b))
|
||||
}
|
||||
|
||||
func fromKnownContract(caller []byte) bool {
|
||||
containerContractAddr := storage.Get(ctx, containerContractKey).([]byte)
|
||||
if bytesEqual(caller, containerContractAddr) {
|
||||
if common.BytesEqual(caller, containerContractAddr) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -2,12 +2,10 @@ package netmapcontract
|
|||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
|
||||
"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/util"
|
||||
"github.com/nspcc-dev/neofs-contract/common"
|
||||
)
|
||||
|
||||
|
@ -34,10 +32,8 @@ type (
|
|||
)
|
||||
|
||||
const (
|
||||
version = 1
|
||||
blockDiff = 20 // change base on performance evaluation
|
||||
version = 1
|
||||
|
||||
voteKey = "ballots"
|
||||
netmapKey = "netmap"
|
||||
innerRingKey = "innerring"
|
||||
configuredKey = "initconfig"
|
||||
|
@ -81,16 +77,16 @@ func Init(keys [][]byte) {
|
|||
irList = append(irList, irNode{key: key})
|
||||
}
|
||||
|
||||
setSerialized(ctx, innerRingKey, irList)
|
||||
common.SetSerialized(ctx, innerRingKey, irList)
|
||||
|
||||
// epoch number is a little endian int, it doesn't need to be serialized
|
||||
storage.Put(ctx, snapshotEpoch, 0)
|
||||
|
||||
// simplified: this used for const sysfee in AddPeer method
|
||||
setSerialized(ctx, netmapKey, []netmapNode{})
|
||||
setSerialized(ctx, snapshot0Key, []netmapNode{})
|
||||
setSerialized(ctx, snapshot1Key, []netmapNode{})
|
||||
setSerialized(ctx, voteKey, []common.Ballot{})
|
||||
common.SetSerialized(ctx, netmapKey, []netmapNode{})
|
||||
common.SetSerialized(ctx, snapshot0Key, []netmapNode{})
|
||||
common.SetSerialized(ctx, snapshot1Key, []netmapNode{})
|
||||
common.InitVote(ctx)
|
||||
|
||||
runtime.Log("netmap contract initialized")
|
||||
}
|
||||
|
@ -118,11 +114,11 @@ func UpdateInnerRing(keys [][]byte) bool {
|
|||
rawIRList := binary.Serialize(irList)
|
||||
hashIRList := crypto.SHA256(rawIRList)
|
||||
|
||||
n := vote(ctx, hashIRList, irKey)
|
||||
n := common.Vote(ctx, hashIRList, irKey)
|
||||
if n >= threshold {
|
||||
runtime.Log("updateInnerRing: inner ring list updated")
|
||||
setSerialized(ctx, innerRingKey, irList)
|
||||
removeVotes(ctx, hashIRList)
|
||||
common.SetSerialized(ctx, innerRingKey, irList)
|
||||
common.RemoveVotes(ctx, hashIRList)
|
||||
} else {
|
||||
runtime.Log("updateInnerRing: processed invoke from inner ring")
|
||||
}
|
||||
|
@ -152,15 +148,15 @@ func AddPeer(nodeInfo []byte) bool {
|
|||
|
||||
nm := addToNetmap(ctx, candidate)
|
||||
|
||||
n := vote(ctx, hashCandidate, irKey)
|
||||
n := common.Vote(ctx, hashCandidate, irKey)
|
||||
if n >= threshold {
|
||||
if nm == nil {
|
||||
runtime.Log("addPeer: storage node already in the netmap")
|
||||
} else {
|
||||
setSerialized(ctx, netmapKey, nm)
|
||||
common.SetSerialized(ctx, netmapKey, nm)
|
||||
runtime.Log("addPeer: add storage node to the network map")
|
||||
}
|
||||
removeVotes(ctx, hashCandidate)
|
||||
common.RemoveVotes(ctx, hashCandidate)
|
||||
} else {
|
||||
runtime.Log("addPeer: processed invoke from inner ring")
|
||||
}
|
||||
|
@ -190,11 +186,11 @@ func UpdateState(state int, publicKey []byte) bool {
|
|||
newNetmap := removeFromNetmap(ctx, publicKey)
|
||||
|
||||
hashID := invokeID([]interface{}{publicKey}, []byte("delete"))
|
||||
n := vote(ctx, hashID, irKey)
|
||||
n := common.Vote(ctx, hashID, irKey)
|
||||
if n >= threshold {
|
||||
runtime.Log("updateState: remove storage node from the network map")
|
||||
setSerialized(ctx, netmapKey, newNetmap)
|
||||
removeVotes(ctx, hashID)
|
||||
common.SetSerialized(ctx, netmapKey, newNetmap)
|
||||
common.RemoveVotes(ctx, hashID)
|
||||
} else {
|
||||
runtime.Log("updateState: processed invoke from inner ring")
|
||||
}
|
||||
|
@ -224,7 +220,7 @@ func NewEpoch(epochNum int) bool {
|
|||
|
||||
hashID := invokeID([]interface{}{epochNum}, []byte("epoch"))
|
||||
|
||||
n := vote(ctx, hashID, irKey)
|
||||
n := common.Vote(ctx, hashID, irKey)
|
||||
if n >= threshold {
|
||||
runtime.Log("newEpoch: process new epoch")
|
||||
|
||||
|
@ -232,12 +228,12 @@ func NewEpoch(epochNum int) bool {
|
|||
storage.Put(ctx, snapshotEpoch, epochNum)
|
||||
|
||||
// put actual snapshot into previous snapshot
|
||||
setSerialized(ctx, snapshot1Key, data0snapshot)
|
||||
common.SetSerialized(ctx, snapshot1Key, data0snapshot)
|
||||
|
||||
// put netmap into actual snapshot
|
||||
setSerialized(ctx, snapshot0Key, dataOnlineState)
|
||||
common.SetSerialized(ctx, snapshot0Key, dataOnlineState)
|
||||
|
||||
removeVotes(ctx, hashID)
|
||||
common.RemoveVotes(ctx, hashID)
|
||||
runtime.Notify("NewEpoch", epochNum)
|
||||
} else {
|
||||
runtime.Log("newEpoch: processed invoke from inner ring")
|
||||
|
@ -290,10 +286,10 @@ func SetConfig(id, key, val []byte) bool {
|
|||
|
||||
// check unique id of the operation
|
||||
hashID := invokeID([]interface{}{id, key, val}, []byte("config"))
|
||||
n := vote(ctx, hashID, irKey)
|
||||
n := common.Vote(ctx, hashID, irKey)
|
||||
|
||||
if n >= threshold {
|
||||
removeVotes(ctx, hashID)
|
||||
common.RemoveVotes(ctx, hashID)
|
||||
setConfig(ctx, key, val)
|
||||
|
||||
runtime.Log("setConfig: configuration has been updated")
|
||||
|
@ -371,7 +367,7 @@ func addToNetmap(ctx storage.Context, n storageNode) []netmapNode {
|
|||
netmapNode := netmap[i].node.info
|
||||
netmapNodeKey := netmapNode[2:35]
|
||||
|
||||
if bytesEqual(newNodeKey, netmapNodeKey) {
|
||||
if common.BytesEqual(newNodeKey, netmapNodeKey) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -392,7 +388,7 @@ func removeFromNetmap(ctx storage.Context, key []byte) []netmapNode {
|
|||
node := item.node.info
|
||||
publicKey := node[2:35] // offset:2, len:33
|
||||
|
||||
if !bytesEqual(publicKey, key) {
|
||||
if !common.BytesEqual(publicKey, key) {
|
||||
newNetmap = append(newNetmap, item)
|
||||
}
|
||||
}
|
||||
|
@ -416,68 +412,6 @@ func filterNetmap(ctx storage.Context, st nodeState) []storageNode {
|
|||
return result
|
||||
}
|
||||
|
||||
func vote(ctx storage.Context, id, from []byte) int {
|
||||
var (
|
||||
newCandidates []common.Ballot
|
||||
candidates = getBallots(ctx)
|
||||
found = -1
|
||||
blockHeight = blockchain.GetHeight()
|
||||
)
|
||||
|
||||
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 = common.Ballot{ID: id, Voters: voters, Height: blockHeight}
|
||||
found = len(voters)
|
||||
}
|
||||
|
||||
newCandidates = append(newCandidates, cnd)
|
||||
}
|
||||
|
||||
if found < 0 {
|
||||
voters := [][]byte{from}
|
||||
newCandidates = append(newCandidates, common.Ballot{
|
||||
ID: id,
|
||||
Voters: voters,
|
||||
Height: blockHeight})
|
||||
found = 1
|
||||
}
|
||||
|
||||
setSerialized(ctx, voteKey, newCandidates)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
func removeVotes(ctx storage.Context, id []byte) {
|
||||
var (
|
||||
newCandidates []common.Ballot
|
||||
candidates = getBallots(ctx)
|
||||
)
|
||||
|
||||
for i := 0; i < len(candidates); i++ {
|
||||
cnd := candidates[i]
|
||||
if !bytesEqual(cnd.ID, id) {
|
||||
newCandidates = append(newCandidates, cnd)
|
||||
}
|
||||
}
|
||||
|
||||
setSerialized(ctx, voteKey, newCandidates)
|
||||
}
|
||||
|
||||
func getIRNodes(ctx storage.Context) []irNode {
|
||||
data := storage.Get(ctx, innerRingKey)
|
||||
if data != nil {
|
||||
|
@ -505,20 +439,6 @@ func getSnapshot(ctx storage.Context, key string) []storageNode {
|
|||
return []storageNode{}
|
||||
}
|
||||
|
||||
func getBallots(ctx storage.Context) []common.Ballot {
|
||||
data := storage.Get(ctx, voteKey)
|
||||
if data != nil {
|
||||
return binary.Deserialize(data.([]byte)).([]common.Ballot)
|
||||
}
|
||||
|
||||
return []common.Ballot{}
|
||||
}
|
||||
|
||||
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||
data := binary.Serialize(value)
|
||||
storage.Put(ctx, key, data)
|
||||
}
|
||||
|
||||
func getConfig(ctx storage.Context, key interface{}) interface{} {
|
||||
postfix := key.([]byte)
|
||||
storageKey := append(configPrefix, postfix...)
|
||||
|
@ -541,8 +461,3 @@ func invokeID(args []interface{}, prefix []byte) []byte {
|
|||
|
||||
return crypto.SHA256(prefix)
|
||||
}
|
||||
|
||||
// neo-go#1176
|
||||
func bytesEqual(a []byte, b []byte) bool {
|
||||
return util.Equals(string(a), string(b))
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
"github.com/nspcc-dev/neofs-contract/common"
|
||||
)
|
||||
|
||||
const version = 1
|
||||
|
@ -64,7 +65,7 @@ func Put(manager, epoch, typ []byte, newTrustList [][]byte) bool {
|
|||
}
|
||||
}
|
||||
|
||||
setSerialized(ctx, key, trustList)
|
||||
common.SetSerialized(ctx, key, trustList)
|
||||
|
||||
runtime.Log("trust list was successfully updated")
|
||||
|
||||
|
@ -85,8 +86,3 @@ func getList(ctx storage.Context, key interface{}) [][]byte {
|
|||
|
||||
return [][]byte{}
|
||||
}
|
||||
|
||||
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||
data := binary.Serialize(value)
|
||||
storage.Put(ctx, key, data)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue