[#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:
Leonard Lyubich 2021-02-02 20:36:20 +03:00 committed by Alex Vanin
parent f9f2a03078
commit c3efe152d6
9 changed files with 208 additions and 559 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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