forked from TrueCloudLab/frostfs-contract
[#42] Share Vote/RemoteVotes between contracts
Replace vote/remoteVotes functions from all contracts (except alphabet) to common package. Additionally replace setSerialized and bytesEqual. Create InitVote function and use it in NeoFS and Netmap contracts. Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
parent
f9f2a03078
commit
c3efe152d6
9 changed files with 208 additions and 559 deletions
|
@ -7,7 +7,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
"github.com/nspcc-dev/neo-go/pkg/interop/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
|
||||||
|
|
|
@ -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))
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
104
common/vote.go
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const voteKey = "ballots"
|
||||||
|
|
||||||
|
const blockDiff = 20 // change base on performance evaluation
|
||||||
|
|
||||||
|
func InitVote(ctx storage.Context) {
|
||||||
|
SetSerialized(ctx, voteKey, []Ballot{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vote adds ballot for the decision with specific 'id' and returns amount
|
||||||
|
// on unique voters for that decision.
|
||||||
|
func Vote(ctx storage.Context, id, from []byte) int {
|
||||||
|
var (
|
||||||
|
newCandidates []Ballot
|
||||||
|
candidates = getBallots(ctx)
|
||||||
|
found = -1
|
||||||
|
blockHeight = blockchain.GetHeight()
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := 0; i < len(candidates); i++ {
|
||||||
|
cnd := candidates[i]
|
||||||
|
|
||||||
|
if blockHeight-cnd.Height > blockDiff {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if BytesEqual(cnd.ID, id) {
|
||||||
|
voters := cnd.Voters
|
||||||
|
|
||||||
|
for j := range voters {
|
||||||
|
if BytesEqual(voters[j], from) {
|
||||||
|
return len(voters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
voters = append(voters, from)
|
||||||
|
cnd = Ballot{ID: id, Voters: voters, Height: blockHeight}
|
||||||
|
found = len(voters)
|
||||||
|
}
|
||||||
|
|
||||||
|
newCandidates = append(newCandidates, cnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
if found < 0 {
|
||||||
|
voters := [][]byte{from}
|
||||||
|
newCandidates = append(newCandidates, Ballot{
|
||||||
|
ID: id,
|
||||||
|
Voters: voters,
|
||||||
|
Height: blockHeight})
|
||||||
|
found = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
SetSerialized(ctx, voteKey, newCandidates)
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveVotes clears ballots of the decision that has been accepted by
|
||||||
|
// inner ring nodes.
|
||||||
|
func RemoveVotes(ctx storage.Context, id []byte) {
|
||||||
|
var (
|
||||||
|
newCandidates []Ballot
|
||||||
|
candidates = getBallots(ctx)
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := 0; i < len(candidates); i++ {
|
||||||
|
cnd := candidates[i]
|
||||||
|
if !BytesEqual(cnd.ID, id) {
|
||||||
|
newCandidates = append(newCandidates, cnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetSerialized(ctx, voteKey, newCandidates)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getBallots returns deserialized slice of vote ballots.
|
||||||
|
func getBallots(ctx storage.Context) []Ballot {
|
||||||
|
data := storage.Get(ctx, voteKey)
|
||||||
|
if data != nil {
|
||||||
|
return binary.Deserialize(data.([]byte)).([]Ballot)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []Ballot{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesEqual compares two slice of bytes by wrapping them into strings,
|
||||||
|
// which is necessary with new util.Equal interop behaviour, see neo-go#1176.
|
||||||
|
func BytesEqual(a []byte, b []byte) bool {
|
||||||
|
return util.Equals(string(a), string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSerialized serializes data and puts it into contract storage.
|
||||||
|
func SetSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||||
|
data := binary.Serialize(value)
|
||||||
|
storage.Put(ctx, key, data)
|
||||||
|
}
|
|
@ -3,13 +3,11 @@ package containercontract
|
||||||
import (
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
|
||||||
}
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,9 +21,6 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,9 +33,7 @@ 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))
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue