From c3efe152d629463ccdd8e2ad6480831138c06d93 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 2 Feb 2021 20:36:20 +0300 Subject: [PATCH] [#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 --- alphabet/alphabet_contract.go | 25 ++---- audit/audit_contract.go | 9 +- balance/balance_contract.go | 116 ++++-------------------- common/vote.go | 104 ++++++++++++++++++++++ container/container_contract.go | 125 +++++--------------------- neofs/neofs_contract.go | 143 ++++++------------------------ neofsid/neofsid_contract.go | 106 +++------------------- netmap/netmap_contract.go | 131 +++++---------------------- reputation/reputation_contract.go | 8 +- 9 files changed, 208 insertions(+), 559 deletions(-) create mode 100644 common/vote.go diff --git a/alphabet/alphabet_contract.go b/alphabet/alphabet_contract.go index e33cf81..e31ff2a 100644 --- a/alphabet/alphabet_contract.go +++ b/alphabet/alphabet_contract.go @@ -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 diff --git a/audit/audit_contract.go b/audit/audit_contract.go index a8d951f..eed9c88 100644 --- a/audit/audit_contract.go +++ b/audit/audit_contract.go @@ -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)) -} diff --git a/balance/balance_contract.go b/balance/balance_contract.go index 9b08fd2..398f616 100644 --- a/balance/balance_contract.go +++ b/balance/balance_contract.go @@ -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) } diff --git a/common/vote.go b/common/vote.go new file mode 100644 index 0000000..722ff1e --- /dev/null +++ b/common/vote.go @@ -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) +} diff --git a/container/container_contract.go b/container/container_contract.go index ac64894..89084c2 100644 --- a/container/container_contract.go +++ b/container/container_contract.go @@ -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 } } diff --git a/neofs/neofs_contract.go b/neofs/neofs_contract.go index cc25a6d..e2178a7 100644 --- a/neofs/neofs_contract.go +++ b/neofs/neofs_contract.go @@ -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)) -} diff --git a/neofsid/neofsid_contract.go b/neofsid/neofsid_contract.go index a60a545..c40fb13 100644 --- a/neofsid/neofsid_contract.go +++ b/neofsid/neofsid_contract.go @@ -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 } diff --git a/netmap/netmap_contract.go b/netmap/netmap_contract.go index 5c31794..bc2b6e3 100644 --- a/netmap/netmap_contract.go +++ b/netmap/netmap_contract.go @@ -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)) -} diff --git a/reputation/reputation_contract.go b/reputation/reputation_contract.go index 2a62cf1..5f5242c 100644 --- a/reputation/reputation_contract.go +++ b/reputation/reputation_contract.go @@ -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) -}