[#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/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
"github.com/nspcc-dev/neofs-contract/common" "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. // OnPayment is a callback for NEP-17 compatible native GAS and NEO contracts.
func OnPayment(from interop.Hash160, amount int, data interface{}) { func OnPayment(from interop.Hash160, amount int, data interface{}) {
caller := runtime.GetCallingScriptHash() 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") 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, indexKey, index)
storage.Put(ctx, totalKey, total) storage.Put(ctx, totalKey, total)
setSerialized(ctx, voteKey, []common.Ballot{}) common.SetSerialized(ctx, voteKey, []common.Ballot{})
runtime.Log(name + " contract initialized") 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++ { for i := 0; i < len(candidates); i++ {
cnd := candidates[i] cnd := candidates[i]
if bytesEqual(cnd.ID, id) { if common.BytesEqual(cnd.ID, id) {
voters := cnd.Voters voters := cnd.Voters
for j := range voters { for j := range voters {
if bytesEqual(voters[j], from) { if common.BytesEqual(voters[j], from) {
return len(voters) return len(voters)
} }
} }
@ -244,7 +243,7 @@ func vote(ctx storage.Context, epoch int, id, from []byte) int {
found = 1 found = 1
} }
setSerialized(ctx, voteKey, newCandidates) common.SetSerialized(ctx, voteKey, newCandidates)
return found return found
} }
@ -257,12 +256,12 @@ func removeVotes(ctx storage.Context, id []byte) {
for i := 0; i < len(candidates); i++ { for i := 0; i < len(candidates); i++ {
cnd := candidates[i] cnd := candidates[i]
if !bytesEqual(cnd.ID, id) { if !common.BytesEqual(cnd.ID, id) {
newCandidates = append(newCandidates, cnd) newCandidates = append(newCandidates, cnd)
} }
} }
setSerialized(ctx, voteKey, newCandidates) common.SetSerialized(ctx, voteKey, newCandidates)
} }
func getBallots(ctx storage.Context) []common.Ballot { func getBallots(ctx storage.Context) []common.Ballot {
@ -274,16 +273,6 @@ func getBallots(ctx storage.Context) []common.Ballot {
return []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 { func voteID(epoch interface{}, args [][]byte) []byte {
var ( var (
result []byte 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/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/interop/util" "github.com/nspcc-dev/neofs-contract/common"
) )
type ( type (
@ -78,7 +78,7 @@ func Put(rawAuditResult []byte) bool {
for i := range innerRing { for i := range innerRing {
ir := innerRing[i] ir := innerRing[i]
if bytesEqual(ir.key, hdr.from) { if common.BytesEqual(ir.key, hdr.from) {
presented = true presented = true
break break
@ -183,8 +183,3 @@ func newAuditHeader(input []byte) auditHeader {
key, 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 ( import (
"github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/binary" "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/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/crypto" "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/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
"github.com/nspcc-dev/neofs-contract/common" "github.com/nspcc-dev/neofs-contract/common"
) )
@ -45,9 +43,6 @@ const (
circulation = "MainnetGAS" circulation = "MainnetGAS"
version = 1 version = 1
voteKey = "ballots"
blockDiff = 20 // change base on performance evaluation
netmapContractKey = "netmapScriptHash" netmapContractKey = "netmapScriptHash"
containerContractKey = "containerScriptHash" containerContractKey = "containerScriptHash"
) )
@ -134,11 +129,11 @@ func TransferX(from, to interop.Hash160, amount int, details []byte) bool {
runtime.Log("transferX: processed indirect invoke") runtime.Log("transferX: processed indirect invoke")
} else { } else {
hashTxID = invokeID([]interface{}{from, to, amount}, []byte("transfer")) hashTxID = invokeID([]interface{}{from, to, amount}, []byte("transfer"))
n = vote(ctx, hashTxID, irKey) n = common.Vote(ctx, hashTxID, irKey)
} }
if n >= threshold { if n >= threshold {
removeVotes(ctx, hashTxID) common.RemoveVotes(ctx, hashTxID)
result := token.transfer(ctx, from, to, amount, true, details) result := token.transfer(ctx, from, to, amount, true, details)
if result { 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")) hashTxID := invokeID([]interface{}{txID, from, to, amount, until}, []byte("lock"))
n := vote(ctx, hashTxID, irKey) n := common.Vote(ctx, hashTxID, irKey)
if n >= threshold { if n >= threshold {
removeVotes(ctx, hashTxID) common.RemoveVotes(ctx, hashTxID)
lockAccount := Account{ lockAccount := Account{
Balance: 0, Balance: 0,
Until: until, Until: until,
Parent: from, Parent: from,
} }
setSerialized(ctx, to, lockAccount) common.SetSerialized(ctx, to, lockAccount)
result := token.transfer(ctx, from, to, amount, true, lockTransferMsg) result := token.transfer(ctx, from, to, amount, true, lockTransferMsg)
if !result { if !result {
@ -204,9 +199,9 @@ func NewEpoch(epochNum int) bool {
epochID := invokeID([]interface{}{epochNum}, []byte("epoch")) epochID := invokeID([]interface{}{epochNum}, []byte("epoch"))
n := vote(ctx, epochID, irKey) n := common.Vote(ctx, epochID, irKey)
if n >= threshold { if n >= threshold {
removeVotes(ctx, epochID) common.RemoveVotes(ctx, epochID)
it := storage.Find(ctx, []byte{}) it := storage.Find(ctx, []byte{})
for iterator.Next(it) { for iterator.Next(it) {
addr := iterator.Key(it).([]byte) 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")) mintID := invokeID([]interface{}{to, amount, details}, []byte("mint"))
n := vote(ctx, mintID, irKey) n := common.Vote(ctx, mintID, irKey)
if n >= threshold { if n >= threshold {
removeVotes(ctx, mintID) common.RemoveVotes(ctx, mintID)
ok := token.transfer(ctx, nil, to, amount, true, details) ok := token.transfer(ctx, nil, to, amount, true, details)
if !ok { if !ok {
@ -274,9 +269,9 @@ func Burn(from interop.Hash160, amount int, details []byte) bool {
burnID := invokeID([]interface{}{from, amount, details}, []byte("burn")) burnID := invokeID([]interface{}{from, amount, details}, []byte("burn"))
n := vote(ctx, burnID, irKey) n := common.Vote(ctx, burnID, irKey)
if n >= threshold { if n >= threshold {
removeVotes(ctx, burnID) common.RemoveVotes(ctx, burnID)
ok := token.transfer(ctx, from, nil, amount, true, details) ok := token.transfer(ctx, from, nil, amount, true, details)
if !ok { if !ok {
@ -329,14 +324,14 @@ func (t Token) transfer(ctx storage.Context, from, to interop.Hash160, amount in
storage.Delete(ctx, from) storage.Delete(ctx, from)
} else { } else {
amountFrom.Balance = amountFrom.Balance - amount // neo-go#953 amountFrom.Balance = amountFrom.Balance - amount // neo-go#953
setSerialized(ctx, from, amountFrom) common.SetSerialized(ctx, from, amountFrom)
} }
} }
if len(to) == 20 { if len(to) == 20 {
amountTo := getAccount(ctx, to) amountTo := getAccount(ctx, to)
amountTo.Balance = amountTo.Balance + amount // neo-go#953 amountTo.Balance = amountTo.Balance + amount // neo-go#953
setSerialized(ctx, to, amountTo) common.SetSerialized(ctx, to, amountTo)
} }
runtime.Notify("Transfer", from, to, amount) 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 // Check if a smart contract is calling script hash
callingScriptHash := runtime.GetCallingScriptHash() callingScriptHash := runtime.GetCallingScriptHash()
if bytesEqual(callingScriptHash, addr) { if common.BytesEqual(callingScriptHash, addr) {
return true return true
} }
} }
@ -398,82 +393,6 @@ func innerRingInvoker(ir []irNode) []byte {
return nil 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 { func getAccount(ctx storage.Context, key interface{}) Account {
data := storage.Get(ctx, key) data := storage.Get(ctx, key)
if data != nil { if data != nil {
@ -492,11 +411,6 @@ func invokeID(args []interface{}, prefix []byte) []byte {
return crypto.SHA256(prefix) 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. Check if invocation made from known container or audit contracts.
This is necessary because calls from these contracts require to do transfer 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 { func fromKnownContract(caller interop.Hash160) bool {
containerContractAddr := storage.Get(ctx, containerContractKey).([]byte) 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 ( import (
"github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/binary" "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/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/crypto" "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/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
"github.com/nspcc-dev/neofs-contract/common" "github.com/nspcc-dev/neofs-contract/common"
) )
@ -41,9 +39,7 @@ type (
const ( const (
version = 1 version = 1
voteKey = "ballots"
ownersKey = "ownersList" ownersKey = "ownersList"
blockDiff = 20 // change base on performance evaluation
neofsIDContractKey = "identityScriptHash" neofsIDContractKey = "identityScriptHash"
balanceContractKey = "balanceScriptHash" balanceContractKey = "balanceScriptHash"
@ -123,9 +119,9 @@ func Put(container, signature, publicKey []byte) bool {
containerFee := contract.Call(netmapContractAddr, "config", containerFeeKey).(int) containerFee := contract.Call(netmapContractAddr, "config", containerFeeKey).(int)
hashCandidate := invokeID([]interface{}{container, signature, publicKey}, []byte("put")) hashCandidate := invokeID([]interface{}{container, signature, publicKey}, []byte("put"))
n := vote(ctx, hashCandidate, irKey) n := common.Vote(ctx, hashCandidate, irKey)
if n >= threshold { if n >= threshold {
removeVotes(ctx, hashCandidate) common.RemoveVotes(ctx, hashCandidate)
// todo: check if new container with unique container id // todo: check if new container with unique container id
for i := 0; i < len(innerRing); i++ { for i := 0; i < len(innerRing); i++ {
@ -184,9 +180,9 @@ func Delete(containerID, signature []byte) bool {
hashCandidate := invokeID([]interface{}{containerID, signature}, []byte("delete")) hashCandidate := invokeID([]interface{}{containerID, signature}, []byte("delete"))
n := vote(ctx, hashCandidate, irKey) n := common.Vote(ctx, hashCandidate, irKey)
if n >= threshold { if n >= threshold {
removeVotes(ctx, hashCandidate) common.RemoveVotes(ctx, hashCandidate)
removeContainer(ctx, containerID, ownerID) removeContainer(ctx, containerID, ownerID)
runtime.Log("delete: remove container") runtime.Log("delete: remove container")
} else { } else {
@ -214,7 +210,7 @@ func List(owner []byte) [][]byte {
owners := getList(ctx, ownersKey) owners := getList(ctx, ownersKey)
for i := 0; i < len(owners); i++ { for i := 0; i < len(owners); i++ {
ownerID := owners[i] ownerID := owners[i]
if len(owner) != 0 && !bytesEqual(owner, ownerID) { if len(owner) != 0 && !common.BytesEqual(owner, ownerID) {
continue continue
} }
@ -253,7 +249,7 @@ func SetEACL(eACL, signature []byte) bool {
} }
key := append(eACLPrefix, containerID...) key := append(eACLPrefix, containerID...)
setSerialized(ctx, key, rule) common.SetSerialized(ctx, key, rule)
runtime.Log("setEACL: success") runtime.Log("setEACL: success")
@ -304,7 +300,7 @@ func PutContainerSize(epoch int, cid []byte, usedSize int, pubKey interop.Public
// do not add estimation twice // do not add estimation twice
for i := range s.estimations { for i := range s.estimations {
est := s.estimations[i] est := s.estimations[i]
if bytesEqual(est.from, pubKey) { if common.BytesEqual(est.from, pubKey) {
return false return false
} }
} }
@ -356,9 +352,9 @@ func ProcessEpoch(epochNum int) {
candidates := keysToDelete(epochNum) candidates := keysToDelete(epochNum)
epochID := invokeID([]interface{}{epochNum}, []byte("epoch")) epochID := invokeID([]interface{}{epochNum}, []byte("epoch"))
n := vote(ctx, epochID, irKey) n := common.Vote(ctx, epochID, irKey)
if n >= threshold { if n >= threshold {
removeVotes(ctx, epochID) common.RemoveVotes(ctx, epochID)
for i := range candidates { for i := range candidates {
candidate := candidates[i] candidate := candidates[i]
@ -379,9 +375,9 @@ func StartContainerEstimation(epoch int) bool {
hashCandidate := invokeID([]interface{}{epoch}, []byte("startEstimation")) hashCandidate := invokeID([]interface{}{epoch}, []byte("startEstimation"))
n := vote(ctx, hashCandidate, irKey) n := common.Vote(ctx, hashCandidate, irKey)
if n >= threshold { if n >= threshold {
removeVotes(ctx, hashCandidate) common.RemoveVotes(ctx, hashCandidate)
runtime.Notify("StartEstimation", epoch) runtime.Notify("StartEstimation", epoch)
runtime.Log("startEstimation: notification has been produced") runtime.Log("startEstimation: notification has been produced")
} else { } else {
@ -403,9 +399,9 @@ func StopContainerEstimation(epoch int) bool {
hashCandidate := invokeID([]interface{}{epoch}, []byte("stopEstimation")) hashCandidate := invokeID([]interface{}{epoch}, []byte("stopEstimation"))
n := vote(ctx, hashCandidate, irKey) n := common.Vote(ctx, hashCandidate, irKey)
if n >= threshold { if n >= threshold {
removeVotes(ctx, hashCandidate) common.RemoveVotes(ctx, hashCandidate)
runtime.Notify("StopEstimation", epoch) runtime.Notify("StopEstimation", epoch)
runtime.Log("stopEstimation: notification has been produced") runtime.Log("stopEstimation: notification has been produced")
} else { } else {
@ -439,7 +435,7 @@ func removeContainer(ctx storage.Context, id []byte, owner []byte) {
func addOrAppend(ctx storage.Context, key interface{}, value []byte) { func addOrAppend(ctx storage.Context, key interface{}, value []byte) {
list := getList(ctx, key) list := getList(ctx, key)
for i := 0; i < len(list); i++ { for i := 0; i < len(list); i++ {
if bytesEqual(list[i], value) { if common.BytesEqual(list[i], value) {
return return
} }
} }
@ -450,7 +446,7 @@ func addOrAppend(ctx storage.Context, key interface{}, value []byte) {
list = append(list, value) list = append(list, value)
} }
setSerialized(ctx, key, list) common.SetSerialized(ctx, key, list)
} }
// remove returns amount of left elements in the 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++ { for i := 0; i < len(list); i++ {
if !bytesEqual(list[i], value) { if !common.BytesEqual(list[i], value) {
newList = append(newList, list[i]) newList = append(newList, list[i])
} }
} }
@ -470,7 +466,7 @@ func remove(ctx storage.Context, key interface{}, value []byte) int {
if ln == 0 { if ln == 0 {
storage.Delete(ctx, key) storage.Delete(ctx, key)
} else { } else {
setSerialized(ctx, key, newList) common.SetSerialized(ctx, key, newList)
} }
return ln return ln
@ -487,68 +483,6 @@ func innerRingInvoker(ir []irNode) []byte {
return nil 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 { func getList(ctx storage.Context, key interface{}) [][]byte {
data := storage.Get(ctx, key) data := storage.Get(ctx, key)
if data != nil { if data != nil {
@ -572,15 +506,6 @@ func getAllContainers(ctx storage.Context) [][]byte {
return list 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 { func getEACL(ctx storage.Context, cid []byte) extendedACL {
key := append(eACLPrefix, cid...) key := append(eACLPrefix, cid...)
data := storage.Get(ctx, key) 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{}} 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 { func walletToScripHash(wallet []byte) []byte {
return wallet[1 : len(wallet)-4] 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++ { for j := 0; j < len(containers); j++ {
container := containers[j] container := containers[j]
if bytesEqual(container, id) { if common.BytesEqual(container, id) {
return ownerID return ownerID
} }
} }
@ -637,11 +557,6 @@ func getOwnerByID(ctx storage.Context, id []byte) []byte {
return nil 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 { func isSignedByOwnerKey(msg, sig, owner, key []byte) bool {
if !isOwnerFromKey(owner, key) { if !isOwnerFromKey(owner, key) {
return false return false
@ -654,7 +569,7 @@ func isOwnerFromKey(owner []byte, key []byte) bool {
ownerSH := walletToScripHash(owner) ownerSH := walletToScripHash(owner)
keySH := contract.CreateStandardAccount(key) keySH := contract.CreateStandardAccount(key)
return bytesEqual(ownerSH, keySH) return common.BytesEqual(ownerSH, keySH)
} }
func estimationKey(epoch int, cid []byte) []byte { func estimationKey(epoch int, cid []byte) []byte {
@ -688,7 +603,7 @@ func isStorageNode(key interop.PublicKey) bool {
nodeInfo := snapshot[i].info nodeInfo := snapshot[i].info
nodeKey := nodeInfo[2:35] // offset:2, len:33 nodeKey := nodeInfo[2:35] // offset:2, len:33
if bytesEqual(key, nodeKey) { if common.BytesEqual(key, nodeKey) {
return true return true
} }
} }

View file

@ -34,13 +34,11 @@ package smart_contract
import ( import (
"github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/binary" "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/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/crypto" "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/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
"github.com/nspcc-dev/neofs-contract/common" "github.com/nspcc-dev/neofs-contract/common"
) )
@ -69,11 +67,9 @@ const (
version = 3 version = 3
innerRingKey = "innerring" innerRingKey = "innerring"
voteKey = "ballots"
candidatesKey = "candidates" candidatesKey = "candidates"
cashedChequesKey = "cheques" cashedChequesKey = "cheques"
blockDiff = 20 // change base on performance evaluation
publicKeySize = 33 publicKeySize = 33
minInnerRingSize = 3 minInnerRingSize = 3
@ -121,10 +117,10 @@ func Init(args [][]byte) bool {
} }
// initialize all storage slices // initialize all storage slices
setSerialized(ctx, innerRingKey, irList) common.SetSerialized(ctx, innerRingKey, irList)
setSerialized(ctx, voteKey, []common.Ballot{}) common.InitVote(ctx)
setSerialized(ctx, candidatesKey, []node{}) common.SetSerialized(ctx, candidatesKey, []node{})
setSerialized(ctx, cashedChequesKey, []cheque{}) common.SetSerialized(ctx, cashedChequesKey, []cheque{})
runtime.Log("neofs: contract initialized") runtime.Log("neofs: contract initialized")
@ -152,14 +148,14 @@ func InnerRingCandidateRemove(key []byte) bool {
for i := range candidates { for i := range candidates {
c := candidates[i] c := candidates[i]
if !bytesEqual(c.pub, key) { if !common.BytesEqual(c.pub, key) {
nodes = append(nodes, c) nodes = append(nodes, c)
} else { } else {
runtime.Log("irCandidateRemove: candidate has been removed") runtime.Log("irCandidateRemove: candidate has been removed")
} }
} }
setSerialized(ctx, candidatesKey, nodes) common.SetSerialized(ctx, candidatesKey, nodes)
return true return true
} }
@ -190,7 +186,7 @@ func InnerRingCandidateAdd(key []byte) bool {
} }
runtime.Log("irCandidateAdd: candidate has been added") runtime.Log("irCandidateAdd: candidate has been added")
setSerialized(ctx, candidatesKey, list) common.SetSerialized(ctx, candidatesKey, list)
return true return true
} }
@ -198,12 +194,12 @@ func InnerRingCandidateAdd(key []byte) bool {
// OnPayment is a callback for NEP-17 compatible native GAS contract. // OnPayment is a callback for NEP-17 compatible native GAS contract.
func OnPayment(from interop.Hash160, amount int, data interface{}) { func OnPayment(from interop.Hash160, amount int, data interface{}) {
rcv := data.(interop.Hash160) rcv := data.(interop.Hash160)
if bytesEqual(rcv, []byte(ignoreDepositNotification)) { if common.BytesEqual(rcv, []byte(ignoreDepositNotification)) {
return return
} }
caller := runtime.GetCallingScriptHash() caller := runtime.GetCallingScriptHash()
if !bytesEqual(caller, []byte(tokenHash)) { if !common.BytesEqual(caller, []byte(tokenHash)) {
panic("onPayment: only GAS can be accepted for deposit") 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") panic("cheque: non unique id")
} }
n := vote(ctx, hashID, irKey) n := common.Vote(ctx, hashID, irKey)
if n >= threshold { if n >= threshold {
removeVotes(ctx, hashID) common.RemoveVotes(ctx, hashID)
from := runtime.GetExecutingScriptHash() from := runtime.GetExecutingScriptHash()
@ -304,7 +300,7 @@ func Cheque(id, user []byte, amount int, lockAcc []byte) bool {
runtime.Log("cheque: funds have been transferred") runtime.Log("cheque: funds have been transferred")
setSerialized(ctx, cashedChequesKey, list) common.SetSerialized(ctx, cashedChequesKey, list)
runtime.Notify("Cheque", id, user, amount, lockAcc) runtime.Notify("Cheque", id, user, amount, lockAcc)
} }
@ -385,7 +381,7 @@ loop:
// find key in actual inner ring list // find key in actual inner ring list
for j := 0; j < len(irList); j++ { for j := 0; j < len(irList); j++ {
n := irList[j] n := irList[j]
if bytesEqual(n.pub, key) { if common.BytesEqual(n.pub, key) {
newIR = append(newIR, n) newIR = append(newIR, n)
oldNodes++ oldNodes++
@ -406,13 +402,13 @@ loop:
hashID := crypto.SHA256(chequeID) hashID := crypto.SHA256(chequeID)
n := vote(ctx, hashID, irKey) n := common.Vote(ctx, hashID, irKey)
if n >= threshold { if n >= threshold {
removeVotes(ctx, hashID) common.RemoveVotes(ctx, hashID)
setSerialized(ctx, candidatesKey, candidates) common.SetSerialized(ctx, candidatesKey, candidates)
setSerialized(ctx, innerRingKey, newIR) common.SetSerialized(ctx, innerRingKey, newIR)
setSerialized(ctx, cashedChequesKey, chequesList) common.SetSerialized(ctx, cashedChequesKey, chequesList)
runtime.Notify("InnerRingUpdate", c.id, newIR) runtime.Notify("InnerRingUpdate", c.id, newIR)
runtime.Log("irUpdate: inner ring list has been updated") runtime.Log("irUpdate: inner ring list has been updated")
@ -431,7 +427,7 @@ func IsInnerRing(key []byte) bool {
for i := range irList { for i := range irList {
node := irList[i] node := irList[i]
if bytesEqual(node.pub, key) { if common.BytesEqual(node.pub, key) {
return true return true
} }
} }
@ -467,12 +463,12 @@ func SetConfig(id, key, val []byte) bool {
// vote for new configuration value // vote for new configuration value
hashID := crypto.SHA256(id) hashID := crypto.SHA256(id)
n := vote(ctx, hashID, irKey) n := common.Vote(ctx, hashID, irKey)
if n >= threshold { if n >= threshold {
removeVotes(ctx, hashID) common.RemoveVotes(ctx, hashID)
setConfig(ctx, key, val) setConfig(ctx, key, val)
setSerialized(ctx, cashedChequesKey, chequesList) common.SetSerialized(ctx, cashedChequesKey, chequesList)
runtime.Notify("SetConfig", id, key, val) runtime.Notify("SetConfig", id, key, val)
runtime.Log("setConfig: configuration has been updated") runtime.Log("setConfig: configuration has been updated")
@ -539,79 +535,6 @@ func innerRingInvoker(ir []node) []byte {
return nil 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. // getInnerRingNodes returns deserialized slice of inner ring nodes from storage.
func getInnerRingNodes(ctx storage.Context, key string) []node { func getInnerRingNodes(ctx storage.Context, key string) []node {
data := storage.Get(ctx, key) data := storage.Get(ctx, key)
@ -632,16 +555,6 @@ func getCashedCheques(ctx storage.Context) []cheque {
return []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. // getConfig returns installed neofs configuration value or nil if it is not set.
func getConfig(ctx storage.Context, key interface{}) interface{} { func getConfig(ctx storage.Context, key interface{}) interface{} {
postfix := key.([]byte) 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'. // that set to false if cheque 'c' is already presented in the slice 'lst'.
func addCheque(lst []cheque, c cheque) ([]cheque, bool) { func addCheque(lst []cheque, c cheque) ([]cheque, bool) {
for i := 0; i < len(lst); i++ { 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 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'. // that set to false if node 'n' is already presented in the slice 'lst'.
func addNode(lst []node, n node) ([]node, bool) { func addNode(lst []node, n node) ([]node, bool) {
for i := 0; i < len(lst); i++ { 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 return nil, false
} }
} }
@ -696,7 +609,7 @@ func rmNodeByKey(lst, add []node, k []byte) ([]node, []node, bool) {
) )
for i := 0; i < len(lst); i++ { 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]) add = append(add, lst[i])
flag = true flag = true
} else { } else {
@ -706,9 +619,3 @@ func rmNodeByKey(lst, add []node, k []byte) ([]node, []node, bool) {
return newLst, add, flag 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 ( import (
"github.com/nspcc-dev/neo-go/pkg/interop/binary" "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/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/crypto" "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/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "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" "github.com/nspcc-dev/neofs-contract/common"
) )
@ -22,10 +20,7 @@ type (
) )
const ( const (
version = 1 version = 1
blockDiff = 20 // change base on performance evaluation
voteKey = "ballots"
netmapContractKey = "netmapScriptHash" netmapContractKey = "netmapScriptHash"
containerContractKey = "containerScriptHash" containerContractKey = "containerScriptHash"
@ -88,7 +83,7 @@ addLoop:
for j := range info.Keys { for j := range info.Keys {
key := info.Keys[j] key := info.Keys[j]
if bytesEqual(key, pubKey) { if common.BytesEqual(key, pubKey) {
continue addLoop continue addLoop
} }
} }
@ -102,7 +97,7 @@ addLoop:
runtime.Log("addKey: processed indirect invoke") runtime.Log("addKey: processed indirect invoke")
} else { } else {
id := invokeIDKeys(owner, keys, []byte("add")) id := invokeIDKeys(owner, keys, []byte("add"))
n = vote(ctx, id, irKey) n = common.Vote(ctx, id, irKey)
} }
if n < threshold { if n < threshold {
@ -110,8 +105,8 @@ addLoop:
return true return true
} }
removeVotes(ctx, id) common.RemoveVotes(ctx, id)
setSerialized(ctx, owner, info) common.SetSerialized(ctx, owner, info)
runtime.Log("addKey: key bound to the owner") runtime.Log("addKey: key bound to the owner")
return true return true
@ -133,13 +128,13 @@ func RemoveKey(owner []byte, keys [][]byte) bool {
id := invokeIDKeys(owner, keys, []byte("remove")) id := invokeIDKeys(owner, keys, []byte("remove"))
n := vote(ctx, id, irKey) n := common.Vote(ctx, id, irKey)
if n < threshold { if n < threshold {
runtime.Log("removeKey: processed invoke from inner ring") runtime.Log("removeKey: processed invoke from inner ring")
return true return true
} }
removeVotes(ctx, id) common.RemoveVotes(ctx, id)
info := getUserInfo(ctx, owner) info := getUserInfo(ctx, owner)
var leftKeys [][]byte var leftKeys [][]byte
@ -154,7 +149,7 @@ rmLoop:
panic("removeKey: incorrect public key") panic("removeKey: incorrect public key")
} }
if bytesEqual(key, pubKey) { if common.BytesEqual(key, pubKey) {
continue rmLoop continue rmLoop
} }
} }
@ -163,7 +158,7 @@ rmLoop:
} }
info.Keys = leftKeys info.Keys = leftKeys
setSerialized(ctx, owner, info) common.SetSerialized(ctx, owner, info)
return true return true
} }
@ -191,20 +186,6 @@ func getUserInfo(ctx storage.Context, key interface{}) UserInfo {
return UserInfo{Keys: [][]byte{}} 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 { func innerRingInvoker(ir []irNode) []byte {
for i := 0; i < len(ir); i++ { for i := 0; i < len(ir); i++ {
node := ir[i] node := ir[i]
@ -216,68 +197,6 @@ func innerRingInvoker(ir []irNode) []byte {
return nil 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 { func invokeID(args []interface{}, prefix []byte) []byte {
for i := range args { for i := range args {
arg := args[i].([]byte) arg := args[i].([]byte)
@ -296,14 +215,9 @@ func invokeIDKeys(owner []byte, keys [][]byte, prefix []byte) []byte {
return crypto.SHA256(prefix) 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 { func fromKnownContract(caller []byte) bool {
containerContractAddr := storage.Get(ctx, containerContractKey).([]byte) containerContractAddr := storage.Get(ctx, containerContractKey).([]byte)
if bytesEqual(caller, containerContractAddr) { if common.BytesEqual(caller, containerContractAddr) {
return true return true
} }

View file

@ -2,12 +2,10 @@ package netmapcontract
import ( import (
"github.com/nspcc-dev/neo-go/pkg/interop/binary" "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/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
"github.com/nspcc-dev/neofs-contract/common" "github.com/nspcc-dev/neofs-contract/common"
) )
@ -34,10 +32,8 @@ type (
) )
const ( const (
version = 1 version = 1
blockDiff = 20 // change base on performance evaluation
voteKey = "ballots"
netmapKey = "netmap" netmapKey = "netmap"
innerRingKey = "innerring" innerRingKey = "innerring"
configuredKey = "initconfig" configuredKey = "initconfig"
@ -81,16 +77,16 @@ func Init(keys [][]byte) {
irList = append(irList, irNode{key: key}) 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 // epoch number is a little endian int, it doesn't need to be serialized
storage.Put(ctx, snapshotEpoch, 0) storage.Put(ctx, snapshotEpoch, 0)
// simplified: this used for const sysfee in AddPeer method // simplified: this used for const sysfee in AddPeer method
setSerialized(ctx, netmapKey, []netmapNode{}) common.SetSerialized(ctx, netmapKey, []netmapNode{})
setSerialized(ctx, snapshot0Key, []netmapNode{}) common.SetSerialized(ctx, snapshot0Key, []netmapNode{})
setSerialized(ctx, snapshot1Key, []netmapNode{}) common.SetSerialized(ctx, snapshot1Key, []netmapNode{})
setSerialized(ctx, voteKey, []common.Ballot{}) common.InitVote(ctx)
runtime.Log("netmap contract initialized") runtime.Log("netmap contract initialized")
} }
@ -118,11 +114,11 @@ func UpdateInnerRing(keys [][]byte) bool {
rawIRList := binary.Serialize(irList) rawIRList := binary.Serialize(irList)
hashIRList := crypto.SHA256(rawIRList) hashIRList := crypto.SHA256(rawIRList)
n := vote(ctx, hashIRList, irKey) n := common.Vote(ctx, hashIRList, irKey)
if n >= threshold { if n >= threshold {
runtime.Log("updateInnerRing: inner ring list updated") runtime.Log("updateInnerRing: inner ring list updated")
setSerialized(ctx, innerRingKey, irList) common.SetSerialized(ctx, innerRingKey, irList)
removeVotes(ctx, hashIRList) common.RemoveVotes(ctx, hashIRList)
} else { } else {
runtime.Log("updateInnerRing: processed invoke from inner ring") runtime.Log("updateInnerRing: processed invoke from inner ring")
} }
@ -152,15 +148,15 @@ func AddPeer(nodeInfo []byte) bool {
nm := addToNetmap(ctx, candidate) nm := addToNetmap(ctx, candidate)
n := vote(ctx, hashCandidate, irKey) n := common.Vote(ctx, hashCandidate, irKey)
if n >= threshold { if n >= threshold {
if nm == nil { if nm == nil {
runtime.Log("addPeer: storage node already in the netmap") runtime.Log("addPeer: storage node already in the netmap")
} else { } else {
setSerialized(ctx, netmapKey, nm) common.SetSerialized(ctx, netmapKey, nm)
runtime.Log("addPeer: add storage node to the network map") runtime.Log("addPeer: add storage node to the network map")
} }
removeVotes(ctx, hashCandidate) common.RemoveVotes(ctx, hashCandidate)
} else { } else {
runtime.Log("addPeer: processed invoke from inner ring") runtime.Log("addPeer: processed invoke from inner ring")
} }
@ -190,11 +186,11 @@ func UpdateState(state int, publicKey []byte) bool {
newNetmap := removeFromNetmap(ctx, publicKey) newNetmap := removeFromNetmap(ctx, publicKey)
hashID := invokeID([]interface{}{publicKey}, []byte("delete")) hashID := invokeID([]interface{}{publicKey}, []byte("delete"))
n := vote(ctx, hashID, irKey) n := common.Vote(ctx, hashID, irKey)
if n >= threshold { if n >= threshold {
runtime.Log("updateState: remove storage node from the network map") runtime.Log("updateState: remove storage node from the network map")
setSerialized(ctx, netmapKey, newNetmap) common.SetSerialized(ctx, netmapKey, newNetmap)
removeVotes(ctx, hashID) common.RemoveVotes(ctx, hashID)
} else { } else {
runtime.Log("updateState: processed invoke from inner ring") runtime.Log("updateState: processed invoke from inner ring")
} }
@ -224,7 +220,7 @@ func NewEpoch(epochNum int) bool {
hashID := invokeID([]interface{}{epochNum}, []byte("epoch")) hashID := invokeID([]interface{}{epochNum}, []byte("epoch"))
n := vote(ctx, hashID, irKey) n := common.Vote(ctx, hashID, irKey)
if n >= threshold { if n >= threshold {
runtime.Log("newEpoch: process new epoch") runtime.Log("newEpoch: process new epoch")
@ -232,12 +228,12 @@ func NewEpoch(epochNum int) bool {
storage.Put(ctx, snapshotEpoch, epochNum) storage.Put(ctx, snapshotEpoch, epochNum)
// put actual snapshot into previous snapshot // put actual snapshot into previous snapshot
setSerialized(ctx, snapshot1Key, data0snapshot) common.SetSerialized(ctx, snapshot1Key, data0snapshot)
// put netmap into actual snapshot // 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) runtime.Notify("NewEpoch", epochNum)
} else { } else {
runtime.Log("newEpoch: processed invoke from inner ring") 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 // check unique id of the operation
hashID := invokeID([]interface{}{id, key, val}, []byte("config")) hashID := invokeID([]interface{}{id, key, val}, []byte("config"))
n := vote(ctx, hashID, irKey) n := common.Vote(ctx, hashID, irKey)
if n >= threshold { if n >= threshold {
removeVotes(ctx, hashID) common.RemoveVotes(ctx, hashID)
setConfig(ctx, key, val) setConfig(ctx, key, val)
runtime.Log("setConfig: configuration has been updated") runtime.Log("setConfig: configuration has been updated")
@ -371,7 +367,7 @@ func addToNetmap(ctx storage.Context, n storageNode) []netmapNode {
netmapNode := netmap[i].node.info netmapNode := netmap[i].node.info
netmapNodeKey := netmapNode[2:35] netmapNodeKey := netmapNode[2:35]
if bytesEqual(newNodeKey, netmapNodeKey) { if common.BytesEqual(newNodeKey, netmapNodeKey) {
return nil return nil
} }
} }
@ -392,7 +388,7 @@ func removeFromNetmap(ctx storage.Context, key []byte) []netmapNode {
node := item.node.info node := item.node.info
publicKey := node[2:35] // offset:2, len:33 publicKey := node[2:35] // offset:2, len:33
if !bytesEqual(publicKey, key) { if !common.BytesEqual(publicKey, key) {
newNetmap = append(newNetmap, item) newNetmap = append(newNetmap, item)
} }
} }
@ -416,68 +412,6 @@ func filterNetmap(ctx storage.Context, st nodeState) []storageNode {
return result 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 { func getIRNodes(ctx storage.Context) []irNode {
data := storage.Get(ctx, innerRingKey) data := storage.Get(ctx, innerRingKey)
if data != nil { if data != nil {
@ -505,20 +439,6 @@ func getSnapshot(ctx storage.Context, key string) []storageNode {
return []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{} { func getConfig(ctx storage.Context, key interface{}) interface{} {
postfix := key.([]byte) postfix := key.([]byte)
storageKey := append(configPrefix, postfix...) storageKey := append(configPrefix, postfix...)
@ -541,8 +461,3 @@ func invokeID(args []interface{}, prefix []byte) []byte {
return crypto.SHA256(prefix) 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/binary"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
) )
const version = 1 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") runtime.Log("trust list was successfully updated")
@ -85,8 +86,3 @@ func getList(ctx storage.Context, key interface{}) [][]byte {
return [][]byte{} return [][]byte{}
} }
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
data := binary.Serialize(value)
storage.Put(ctx, key, data)
}