forked from TrueCloudLab/frostfs-contract
[#19] alphabet: Add signature collection in Vote method
Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
parent
2a509ebbee
commit
24b7bc5c77
8 changed files with 1168 additions and 57 deletions
|
@ -1,16 +1,26 @@
|
||||||
package alphabetcontract
|
package alphabetcontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
irNode struct {
|
irNode struct {
|
||||||
key []byte
|
key []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ballot struct {
|
||||||
|
id []byte // id of the voting decision
|
||||||
|
n [][]byte // already voted inner ring nodes
|
||||||
|
block int // block with the last vote
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -24,6 +34,13 @@ const (
|
||||||
index = {{ .Index }}
|
index = {{ .Index }}
|
||||||
|
|
||||||
netmapContractKey = "netmapScriptHash"
|
netmapContractKey = "netmapScriptHash"
|
||||||
|
|
||||||
|
blockDiff = 20 // amount of blocks when ballot get discarded
|
||||||
|
|
||||||
|
threshold = totalAlphabetContracts * 2 / 3 + 1
|
||||||
|
voteKey = "ballots"
|
||||||
|
|
||||||
|
totalAlphabetContracts = 7
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -49,6 +66,8 @@ func Init(addrNetmap []byte) {
|
||||||
|
|
||||||
storage.Put(ctx, netmapContractKey, addrNetmap)
|
storage.Put(ctx, netmapContractKey, addrNetmap)
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, []ballot{})
|
||||||
|
|
||||||
runtime.Log(name + " contract initialized")
|
runtime.Log(name + " contract initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +100,21 @@ func checkPermission(ir []irNode) bool {
|
||||||
return runtime.CheckWitness(node.key)
|
return runtime.CheckWitness(node.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func innerRingInvoker(ir []irNode) []byte {
|
||||||
|
for i := 0; i < len(ir); i++ {
|
||||||
|
if i >= totalAlphabetContracts {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
node := ir[i]
|
||||||
|
if runtime.CheckWitness(node.key) {
|
||||||
|
return node.key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func Emit() bool {
|
func Emit() bool {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
if !checkPermission(innerRingKeys) {
|
||||||
|
@ -111,21 +145,31 @@ func Emit() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Vote(candidate []byte) {
|
func Vote(epoch int, candidates [][]byte) {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
|
||||||
|
key := innerRingInvoker(innerRingKeys)
|
||||||
|
if len(key) == 0 {
|
||||||
panic("invalid invoker")
|
panic("invalid invoker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id := voteID(epoch, candidates)
|
||||||
|
n := vote(ctx, id, key)
|
||||||
|
|
||||||
|
if n >= threshold {
|
||||||
|
candidate := candidates[index%len(candidates)]
|
||||||
address := runtime.GetExecutingScriptHash()
|
address := runtime.GetExecutingScriptHash()
|
||||||
|
|
||||||
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
||||||
if ok {
|
if ok {
|
||||||
runtime.Log("successfully voted for validator")
|
runtime.Log(name + ": successfully voted for validator")
|
||||||
|
removeVotes(ctx, id)
|
||||||
} else {
|
} else {
|
||||||
runtime.Log("vote has been failed")
|
runtime.Log(name + ": vote has been failed")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runtime.Log(name + ": saved vote for validator")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -133,3 +177,97 @@ func Vote(candidate []byte) {
|
||||||
func Name() string {
|
func Name() string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 bytesEqual(cnd.id, id) {
|
||||||
|
voters := cnd.n
|
||||||
|
|
||||||
|
for j := range voters {
|
||||||
|
if bytesEqual(voters[j], from) {
|
||||||
|
return len(voters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
voters = append(voters, from)
|
||||||
|
cnd = ballot{id: id, n: voters, block: blockHeight}
|
||||||
|
found = len(voters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not add old ballots, they are invalid
|
||||||
|
if blockHeight-cnd.block <= blockDiff {
|
||||||
|
newCandidates = append(newCandidates, cnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found < 0 {
|
||||||
|
voters := [][]byte{from}
|
||||||
|
newCandidates = append(newCandidates, ballot{
|
||||||
|
id: id,
|
||||||
|
n: voters,
|
||||||
|
block: blockHeight})
|
||||||
|
found = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, newCandidates)
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBallots(ctx storage.Context) []ballot {
|
||||||
|
data := storage.Get(ctx, voteKey)
|
||||||
|
if data != nil {
|
||||||
|
return binary.Deserialize(data.([]byte)).([]ballot)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []ballot{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||||
|
data := binary.Serialize(value)
|
||||||
|
storage.Put(ctx, key, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// neo-go#1176
|
||||||
|
func bytesEqual(a []byte, b []byte) bool {
|
||||||
|
return util.Equals(string(a), string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func voteID(epoch interface{}, args [][]byte) []byte {
|
||||||
|
var (
|
||||||
|
result []byte
|
||||||
|
epochBytes = epoch.([]byte)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = append(result, epochBytes...)
|
||||||
|
|
||||||
|
for i := range args {
|
||||||
|
result = append(result, args[i]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.SHA256(result)
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,26 @@
|
||||||
package alphabetcontract
|
package alphabetcontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
irNode struct {
|
irNode struct {
|
||||||
key []byte
|
key []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ballot struct {
|
||||||
|
id []byte // id of the voting decision
|
||||||
|
n [][]byte // already voted inner ring nodes
|
||||||
|
block int // block with the last vote
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -24,6 +34,13 @@ const (
|
||||||
index = 0
|
index = 0
|
||||||
|
|
||||||
netmapContractKey = "netmapScriptHash"
|
netmapContractKey = "netmapScriptHash"
|
||||||
|
|
||||||
|
blockDiff = 20 // amount of blocks when ballot get discarded
|
||||||
|
|
||||||
|
threshold = totalAlphabetContracts*2/3 + 1
|
||||||
|
voteKey = "ballots"
|
||||||
|
|
||||||
|
totalAlphabetContracts = 7
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -49,6 +66,8 @@ func Init(addrNetmap []byte) {
|
||||||
|
|
||||||
storage.Put(ctx, netmapContractKey, addrNetmap)
|
storage.Put(ctx, netmapContractKey, addrNetmap)
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, []ballot{})
|
||||||
|
|
||||||
runtime.Log(name + " contract initialized")
|
runtime.Log(name + " contract initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +100,21 @@ func checkPermission(ir []irNode) bool {
|
||||||
return runtime.CheckWitness(node.key)
|
return runtime.CheckWitness(node.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func innerRingInvoker(ir []irNode) []byte {
|
||||||
|
for i := 0; i < len(ir); i++ {
|
||||||
|
if i >= totalAlphabetContracts {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
node := ir[i]
|
||||||
|
if runtime.CheckWitness(node.key) {
|
||||||
|
return node.key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func Emit() bool {
|
func Emit() bool {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
if !checkPermission(innerRingKeys) {
|
||||||
|
@ -111,19 +145,30 @@ func Emit() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Vote(candidate []byte) {
|
func Vote(epoch int, candidates [][]byte) {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
|
||||||
|
key := innerRingInvoker(innerRingKeys)
|
||||||
|
if len(key) == 0 {
|
||||||
panic("invalid invoker")
|
panic("invalid invoker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id := voteID(epoch, candidates)
|
||||||
|
n := vote(ctx, id, key)
|
||||||
|
|
||||||
|
if n >= threshold {
|
||||||
|
candidate := candidates[index%len(candidates)]
|
||||||
address := runtime.GetExecutingScriptHash()
|
address := runtime.GetExecutingScriptHash()
|
||||||
|
|
||||||
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
||||||
if ok {
|
if ok {
|
||||||
runtime.Log("successfully voted for validator")
|
runtime.Log(name + ": successfully voted for validator")
|
||||||
|
removeVotes(ctx, id)
|
||||||
} else {
|
} else {
|
||||||
runtime.Log("vote has been failed")
|
runtime.Log(name + ": vote has been failed")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runtime.Log(name + ": saved vote for validator")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -132,3 +177,97 @@ func Vote(candidate []byte) {
|
||||||
func Name() string {
|
func Name() string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 bytesEqual(cnd.id, id) {
|
||||||
|
voters := cnd.n
|
||||||
|
|
||||||
|
for j := range voters {
|
||||||
|
if bytesEqual(voters[j], from) {
|
||||||
|
return len(voters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
voters = append(voters, from)
|
||||||
|
cnd = ballot{id: id, n: voters, block: blockHeight}
|
||||||
|
found = len(voters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not add old ballots, they are invalid
|
||||||
|
if blockHeight-cnd.block <= blockDiff {
|
||||||
|
newCandidates = append(newCandidates, cnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found < 0 {
|
||||||
|
voters := [][]byte{from}
|
||||||
|
newCandidates = append(newCandidates, ballot{
|
||||||
|
id: id,
|
||||||
|
n: voters,
|
||||||
|
block: blockHeight})
|
||||||
|
found = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, newCandidates)
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBallots(ctx storage.Context) []ballot {
|
||||||
|
data := storage.Get(ctx, voteKey)
|
||||||
|
if data != nil {
|
||||||
|
return binary.Deserialize(data.([]byte)).([]ballot)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []ballot{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||||
|
data := binary.Serialize(value)
|
||||||
|
storage.Put(ctx, key, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// neo-go#1176
|
||||||
|
func bytesEqual(a []byte, b []byte) bool {
|
||||||
|
return util.Equals(string(a), string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func voteID(epoch interface{}, args [][]byte) []byte {
|
||||||
|
var (
|
||||||
|
result []byte
|
||||||
|
epochBytes = epoch.([]byte)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = append(result, epochBytes...)
|
||||||
|
|
||||||
|
for i := range args {
|
||||||
|
result = append(result, args[i]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.SHA256(result)
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,26 @@
|
||||||
package alphabetcontract
|
package alphabetcontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
irNode struct {
|
irNode struct {
|
||||||
key []byte
|
key []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ballot struct {
|
||||||
|
id []byte // id of the voting decision
|
||||||
|
n [][]byte // already voted inner ring nodes
|
||||||
|
block int // block with the last vote
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -24,6 +34,13 @@ const (
|
||||||
index = 1
|
index = 1
|
||||||
|
|
||||||
netmapContractKey = "netmapScriptHash"
|
netmapContractKey = "netmapScriptHash"
|
||||||
|
|
||||||
|
blockDiff = 20 // amount of blocks when ballot get discarded
|
||||||
|
|
||||||
|
threshold = totalAlphabetContracts*2/3 + 1
|
||||||
|
voteKey = "ballots"
|
||||||
|
|
||||||
|
totalAlphabetContracts = 7
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -49,6 +66,8 @@ func Init(addrNetmap []byte) {
|
||||||
|
|
||||||
storage.Put(ctx, netmapContractKey, addrNetmap)
|
storage.Put(ctx, netmapContractKey, addrNetmap)
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, []ballot{})
|
||||||
|
|
||||||
runtime.Log(name + " contract initialized")
|
runtime.Log(name + " contract initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +100,21 @@ func checkPermission(ir []irNode) bool {
|
||||||
return runtime.CheckWitness(node.key)
|
return runtime.CheckWitness(node.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func innerRingInvoker(ir []irNode) []byte {
|
||||||
|
for i := 0; i < len(ir); i++ {
|
||||||
|
if i >= totalAlphabetContracts {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
node := ir[i]
|
||||||
|
if runtime.CheckWitness(node.key) {
|
||||||
|
return node.key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func Emit() bool {
|
func Emit() bool {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
if !checkPermission(innerRingKeys) {
|
||||||
|
@ -111,19 +145,30 @@ func Emit() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Vote(candidate []byte) {
|
func Vote(epoch int, candidates [][]byte) {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
|
||||||
|
key := innerRingInvoker(innerRingKeys)
|
||||||
|
if len(key) == 0 {
|
||||||
panic("invalid invoker")
|
panic("invalid invoker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id := voteID(epoch, candidates)
|
||||||
|
n := vote(ctx, id, key)
|
||||||
|
|
||||||
|
if n >= threshold {
|
||||||
|
candidate := candidates[index%len(candidates)]
|
||||||
address := runtime.GetExecutingScriptHash()
|
address := runtime.GetExecutingScriptHash()
|
||||||
|
|
||||||
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
||||||
if ok {
|
if ok {
|
||||||
runtime.Log("successfully voted for validator")
|
runtime.Log(name + ": successfully voted for validator")
|
||||||
|
removeVotes(ctx, id)
|
||||||
} else {
|
} else {
|
||||||
runtime.Log("vote has been failed")
|
runtime.Log(name + ": vote has been failed")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runtime.Log(name + ": saved vote for validator")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -132,3 +177,97 @@ func Vote(candidate []byte) {
|
||||||
func Name() string {
|
func Name() string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 bytesEqual(cnd.id, id) {
|
||||||
|
voters := cnd.n
|
||||||
|
|
||||||
|
for j := range voters {
|
||||||
|
if bytesEqual(voters[j], from) {
|
||||||
|
return len(voters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
voters = append(voters, from)
|
||||||
|
cnd = ballot{id: id, n: voters, block: blockHeight}
|
||||||
|
found = len(voters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not add old ballots, they are invalid
|
||||||
|
if blockHeight-cnd.block <= blockDiff {
|
||||||
|
newCandidates = append(newCandidates, cnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found < 0 {
|
||||||
|
voters := [][]byte{from}
|
||||||
|
newCandidates = append(newCandidates, ballot{
|
||||||
|
id: id,
|
||||||
|
n: voters,
|
||||||
|
block: blockHeight})
|
||||||
|
found = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, newCandidates)
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBallots(ctx storage.Context) []ballot {
|
||||||
|
data := storage.Get(ctx, voteKey)
|
||||||
|
if data != nil {
|
||||||
|
return binary.Deserialize(data.([]byte)).([]ballot)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []ballot{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||||
|
data := binary.Serialize(value)
|
||||||
|
storage.Put(ctx, key, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// neo-go#1176
|
||||||
|
func bytesEqual(a []byte, b []byte) bool {
|
||||||
|
return util.Equals(string(a), string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func voteID(epoch interface{}, args [][]byte) []byte {
|
||||||
|
var (
|
||||||
|
result []byte
|
||||||
|
epochBytes = epoch.([]byte)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = append(result, epochBytes...)
|
||||||
|
|
||||||
|
for i := range args {
|
||||||
|
result = append(result, args[i]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.SHA256(result)
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,26 @@
|
||||||
package alphabetcontract
|
package alphabetcontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
irNode struct {
|
irNode struct {
|
||||||
key []byte
|
key []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ballot struct {
|
||||||
|
id []byte // id of the voting decision
|
||||||
|
n [][]byte // already voted inner ring nodes
|
||||||
|
block int // block with the last vote
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -24,6 +34,13 @@ const (
|
||||||
index = 4
|
index = 4
|
||||||
|
|
||||||
netmapContractKey = "netmapScriptHash"
|
netmapContractKey = "netmapScriptHash"
|
||||||
|
|
||||||
|
blockDiff = 20 // amount of blocks when ballot get discarded
|
||||||
|
|
||||||
|
threshold = totalAlphabetContracts*2/3 + 1
|
||||||
|
voteKey = "ballots"
|
||||||
|
|
||||||
|
totalAlphabetContracts = 7
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -49,6 +66,8 @@ func Init(addrNetmap []byte) {
|
||||||
|
|
||||||
storage.Put(ctx, netmapContractKey, addrNetmap)
|
storage.Put(ctx, netmapContractKey, addrNetmap)
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, []ballot{})
|
||||||
|
|
||||||
runtime.Log(name + " contract initialized")
|
runtime.Log(name + " contract initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +100,21 @@ func checkPermission(ir []irNode) bool {
|
||||||
return runtime.CheckWitness(node.key)
|
return runtime.CheckWitness(node.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func innerRingInvoker(ir []irNode) []byte {
|
||||||
|
for i := 0; i < len(ir); i++ {
|
||||||
|
if i >= totalAlphabetContracts {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
node := ir[i]
|
||||||
|
if runtime.CheckWitness(node.key) {
|
||||||
|
return node.key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func Emit() bool {
|
func Emit() bool {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
if !checkPermission(innerRingKeys) {
|
||||||
|
@ -111,19 +145,30 @@ func Emit() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Vote(candidate []byte) {
|
func Vote(epoch int, candidates [][]byte) {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
|
||||||
|
key := innerRingInvoker(innerRingKeys)
|
||||||
|
if len(key) == 0 {
|
||||||
panic("invalid invoker")
|
panic("invalid invoker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id := voteID(epoch, candidates)
|
||||||
|
n := vote(ctx, id, key)
|
||||||
|
|
||||||
|
if n >= threshold {
|
||||||
|
candidate := candidates[index%len(candidates)]
|
||||||
address := runtime.GetExecutingScriptHash()
|
address := runtime.GetExecutingScriptHash()
|
||||||
|
|
||||||
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
||||||
if ok {
|
if ok {
|
||||||
runtime.Log("successfully voted for validator")
|
runtime.Log(name + ": successfully voted for validator")
|
||||||
|
removeVotes(ctx, id)
|
||||||
} else {
|
} else {
|
||||||
runtime.Log("vote has been failed")
|
runtime.Log(name + ": vote has been failed")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runtime.Log(name + ": saved vote for validator")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -132,3 +177,97 @@ func Vote(candidate []byte) {
|
||||||
func Name() string {
|
func Name() string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 bytesEqual(cnd.id, id) {
|
||||||
|
voters := cnd.n
|
||||||
|
|
||||||
|
for j := range voters {
|
||||||
|
if bytesEqual(voters[j], from) {
|
||||||
|
return len(voters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
voters = append(voters, from)
|
||||||
|
cnd = ballot{id: id, n: voters, block: blockHeight}
|
||||||
|
found = len(voters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not add old ballots, they are invalid
|
||||||
|
if blockHeight-cnd.block <= blockDiff {
|
||||||
|
newCandidates = append(newCandidates, cnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found < 0 {
|
||||||
|
voters := [][]byte{from}
|
||||||
|
newCandidates = append(newCandidates, ballot{
|
||||||
|
id: id,
|
||||||
|
n: voters,
|
||||||
|
block: blockHeight})
|
||||||
|
found = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, newCandidates)
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBallots(ctx storage.Context) []ballot {
|
||||||
|
data := storage.Get(ctx, voteKey)
|
||||||
|
if data != nil {
|
||||||
|
return binary.Deserialize(data.([]byte)).([]ballot)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []ballot{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||||
|
data := binary.Serialize(value)
|
||||||
|
storage.Put(ctx, key, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// neo-go#1176
|
||||||
|
func bytesEqual(a []byte, b []byte) bool {
|
||||||
|
return util.Equals(string(a), string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func voteID(epoch interface{}, args [][]byte) []byte {
|
||||||
|
var (
|
||||||
|
result []byte
|
||||||
|
epochBytes = epoch.([]byte)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = append(result, epochBytes...)
|
||||||
|
|
||||||
|
for i := range args {
|
||||||
|
result = append(result, args[i]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.SHA256(result)
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,26 @@
|
||||||
package alphabetcontract
|
package alphabetcontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
irNode struct {
|
irNode struct {
|
||||||
key []byte
|
key []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ballot struct {
|
||||||
|
id []byte // id of the voting decision
|
||||||
|
n [][]byte // already voted inner ring nodes
|
||||||
|
block int // block with the last vote
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -24,6 +34,13 @@ const (
|
||||||
index = 3
|
index = 3
|
||||||
|
|
||||||
netmapContractKey = "netmapScriptHash"
|
netmapContractKey = "netmapScriptHash"
|
||||||
|
|
||||||
|
blockDiff = 20 // amount of blocks when ballot get discarded
|
||||||
|
|
||||||
|
threshold = totalAlphabetContracts*2/3 + 1
|
||||||
|
voteKey = "ballots"
|
||||||
|
|
||||||
|
totalAlphabetContracts = 7
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -49,6 +66,8 @@ func Init(addrNetmap []byte) {
|
||||||
|
|
||||||
storage.Put(ctx, netmapContractKey, addrNetmap)
|
storage.Put(ctx, netmapContractKey, addrNetmap)
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, []ballot{})
|
||||||
|
|
||||||
runtime.Log(name + " contract initialized")
|
runtime.Log(name + " contract initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +100,21 @@ func checkPermission(ir []irNode) bool {
|
||||||
return runtime.CheckWitness(node.key)
|
return runtime.CheckWitness(node.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func innerRingInvoker(ir []irNode) []byte {
|
||||||
|
for i := 0; i < len(ir); i++ {
|
||||||
|
if i >= totalAlphabetContracts {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
node := ir[i]
|
||||||
|
if runtime.CheckWitness(node.key) {
|
||||||
|
return node.key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func Emit() bool {
|
func Emit() bool {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
if !checkPermission(innerRingKeys) {
|
||||||
|
@ -111,19 +145,30 @@ func Emit() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Vote(candidate []byte) {
|
func Vote(epoch int, candidates [][]byte) {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
|
||||||
|
key := innerRingInvoker(innerRingKeys)
|
||||||
|
if len(key) == 0 {
|
||||||
panic("invalid invoker")
|
panic("invalid invoker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id := voteID(epoch, candidates)
|
||||||
|
n := vote(ctx, id, key)
|
||||||
|
|
||||||
|
if n >= threshold {
|
||||||
|
candidate := candidates[index%len(candidates)]
|
||||||
address := runtime.GetExecutingScriptHash()
|
address := runtime.GetExecutingScriptHash()
|
||||||
|
|
||||||
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
||||||
if ok {
|
if ok {
|
||||||
runtime.Log("successfully voted for validator")
|
runtime.Log(name + ": successfully voted for validator")
|
||||||
|
removeVotes(ctx, id)
|
||||||
} else {
|
} else {
|
||||||
runtime.Log("vote has been failed")
|
runtime.Log(name + ": vote has been failed")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runtime.Log(name + ": saved vote for validator")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -132,3 +177,97 @@ func Vote(candidate []byte) {
|
||||||
func Name() string {
|
func Name() string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 bytesEqual(cnd.id, id) {
|
||||||
|
voters := cnd.n
|
||||||
|
|
||||||
|
for j := range voters {
|
||||||
|
if bytesEqual(voters[j], from) {
|
||||||
|
return len(voters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
voters = append(voters, from)
|
||||||
|
cnd = ballot{id: id, n: voters, block: blockHeight}
|
||||||
|
found = len(voters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not add old ballots, they are invalid
|
||||||
|
if blockHeight-cnd.block <= blockDiff {
|
||||||
|
newCandidates = append(newCandidates, cnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found < 0 {
|
||||||
|
voters := [][]byte{from}
|
||||||
|
newCandidates = append(newCandidates, ballot{
|
||||||
|
id: id,
|
||||||
|
n: voters,
|
||||||
|
block: blockHeight})
|
||||||
|
found = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, newCandidates)
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBallots(ctx storage.Context) []ballot {
|
||||||
|
data := storage.Get(ctx, voteKey)
|
||||||
|
if data != nil {
|
||||||
|
return binary.Deserialize(data.([]byte)).([]ballot)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []ballot{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||||
|
data := binary.Serialize(value)
|
||||||
|
storage.Put(ctx, key, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// neo-go#1176
|
||||||
|
func bytesEqual(a []byte, b []byte) bool {
|
||||||
|
return util.Equals(string(a), string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func voteID(epoch interface{}, args [][]byte) []byte {
|
||||||
|
var (
|
||||||
|
result []byte
|
||||||
|
epochBytes = epoch.([]byte)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = append(result, epochBytes...)
|
||||||
|
|
||||||
|
for i := range args {
|
||||||
|
result = append(result, args[i]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.SHA256(result)
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,26 @@
|
||||||
package alphabetcontract
|
package alphabetcontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
irNode struct {
|
irNode struct {
|
||||||
key []byte
|
key []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ballot struct {
|
||||||
|
id []byte // id of the voting decision
|
||||||
|
n [][]byte // already voted inner ring nodes
|
||||||
|
block int // block with the last vote
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -24,6 +34,13 @@ const (
|
||||||
index = 5
|
index = 5
|
||||||
|
|
||||||
netmapContractKey = "netmapScriptHash"
|
netmapContractKey = "netmapScriptHash"
|
||||||
|
|
||||||
|
blockDiff = 20 // amount of blocks when ballot get discarded
|
||||||
|
|
||||||
|
threshold = totalAlphabetContracts*2/3 + 1
|
||||||
|
voteKey = "ballots"
|
||||||
|
|
||||||
|
totalAlphabetContracts = 7
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -49,6 +66,8 @@ func Init(addrNetmap []byte) {
|
||||||
|
|
||||||
storage.Put(ctx, netmapContractKey, addrNetmap)
|
storage.Put(ctx, netmapContractKey, addrNetmap)
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, []ballot{})
|
||||||
|
|
||||||
runtime.Log(name + " contract initialized")
|
runtime.Log(name + " contract initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +100,21 @@ func checkPermission(ir []irNode) bool {
|
||||||
return runtime.CheckWitness(node.key)
|
return runtime.CheckWitness(node.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func innerRingInvoker(ir []irNode) []byte {
|
||||||
|
for i := 0; i < len(ir); i++ {
|
||||||
|
if i >= totalAlphabetContracts {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
node := ir[i]
|
||||||
|
if runtime.CheckWitness(node.key) {
|
||||||
|
return node.key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func Emit() bool {
|
func Emit() bool {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
if !checkPermission(innerRingKeys) {
|
||||||
|
@ -111,19 +145,30 @@ func Emit() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Vote(candidate []byte) {
|
func Vote(epoch int, candidates [][]byte) {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
|
||||||
|
key := innerRingInvoker(innerRingKeys)
|
||||||
|
if len(key) == 0 {
|
||||||
panic("invalid invoker")
|
panic("invalid invoker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id := voteID(epoch, candidates)
|
||||||
|
n := vote(ctx, id, key)
|
||||||
|
|
||||||
|
if n >= threshold {
|
||||||
|
candidate := candidates[index%len(candidates)]
|
||||||
address := runtime.GetExecutingScriptHash()
|
address := runtime.GetExecutingScriptHash()
|
||||||
|
|
||||||
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
||||||
if ok {
|
if ok {
|
||||||
runtime.Log("successfully voted for validator")
|
runtime.Log(name + ": successfully voted for validator")
|
||||||
|
removeVotes(ctx, id)
|
||||||
} else {
|
} else {
|
||||||
runtime.Log("vote has been failed")
|
runtime.Log(name + ": vote has been failed")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runtime.Log(name + ": saved vote for validator")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -132,3 +177,97 @@ func Vote(candidate []byte) {
|
||||||
func Name() string {
|
func Name() string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 bytesEqual(cnd.id, id) {
|
||||||
|
voters := cnd.n
|
||||||
|
|
||||||
|
for j := range voters {
|
||||||
|
if bytesEqual(voters[j], from) {
|
||||||
|
return len(voters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
voters = append(voters, from)
|
||||||
|
cnd = ballot{id: id, n: voters, block: blockHeight}
|
||||||
|
found = len(voters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not add old ballots, they are invalid
|
||||||
|
if blockHeight-cnd.block <= blockDiff {
|
||||||
|
newCandidates = append(newCandidates, cnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found < 0 {
|
||||||
|
voters := [][]byte{from}
|
||||||
|
newCandidates = append(newCandidates, ballot{
|
||||||
|
id: id,
|
||||||
|
n: voters,
|
||||||
|
block: blockHeight})
|
||||||
|
found = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, newCandidates)
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBallots(ctx storage.Context) []ballot {
|
||||||
|
data := storage.Get(ctx, voteKey)
|
||||||
|
if data != nil {
|
||||||
|
return binary.Deserialize(data.([]byte)).([]ballot)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []ballot{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||||
|
data := binary.Serialize(value)
|
||||||
|
storage.Put(ctx, key, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// neo-go#1176
|
||||||
|
func bytesEqual(a []byte, b []byte) bool {
|
||||||
|
return util.Equals(string(a), string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func voteID(epoch interface{}, args [][]byte) []byte {
|
||||||
|
var (
|
||||||
|
result []byte
|
||||||
|
epochBytes = epoch.([]byte)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = append(result, epochBytes...)
|
||||||
|
|
||||||
|
for i := range args {
|
||||||
|
result = append(result, args[i]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.SHA256(result)
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,26 @@
|
||||||
package alphabetcontract
|
package alphabetcontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
irNode struct {
|
irNode struct {
|
||||||
key []byte
|
key []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ballot struct {
|
||||||
|
id []byte // id of the voting decision
|
||||||
|
n [][]byte // already voted inner ring nodes
|
||||||
|
block int // block with the last vote
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -24,6 +34,13 @@ const (
|
||||||
index = 2
|
index = 2
|
||||||
|
|
||||||
netmapContractKey = "netmapScriptHash"
|
netmapContractKey = "netmapScriptHash"
|
||||||
|
|
||||||
|
blockDiff = 20 // amount of blocks when ballot get discarded
|
||||||
|
|
||||||
|
threshold = totalAlphabetContracts*2/3 + 1
|
||||||
|
voteKey = "ballots"
|
||||||
|
|
||||||
|
totalAlphabetContracts = 7
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -49,6 +66,8 @@ func Init(addrNetmap []byte) {
|
||||||
|
|
||||||
storage.Put(ctx, netmapContractKey, addrNetmap)
|
storage.Put(ctx, netmapContractKey, addrNetmap)
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, []ballot{})
|
||||||
|
|
||||||
runtime.Log(name + " contract initialized")
|
runtime.Log(name + " contract initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +100,21 @@ func checkPermission(ir []irNode) bool {
|
||||||
return runtime.CheckWitness(node.key)
|
return runtime.CheckWitness(node.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func innerRingInvoker(ir []irNode) []byte {
|
||||||
|
for i := 0; i < len(ir); i++ {
|
||||||
|
if i >= totalAlphabetContracts {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
node := ir[i]
|
||||||
|
if runtime.CheckWitness(node.key) {
|
||||||
|
return node.key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func Emit() bool {
|
func Emit() bool {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
if !checkPermission(innerRingKeys) {
|
||||||
|
@ -111,19 +145,30 @@ func Emit() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Vote(candidate []byte) {
|
func Vote(epoch int, candidates [][]byte) {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
|
||||||
|
key := innerRingInvoker(innerRingKeys)
|
||||||
|
if len(key) == 0 {
|
||||||
panic("invalid invoker")
|
panic("invalid invoker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id := voteID(epoch, candidates)
|
||||||
|
n := vote(ctx, id, key)
|
||||||
|
|
||||||
|
if n >= threshold {
|
||||||
|
candidate := candidates[index%len(candidates)]
|
||||||
address := runtime.GetExecutingScriptHash()
|
address := runtime.GetExecutingScriptHash()
|
||||||
|
|
||||||
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
||||||
if ok {
|
if ok {
|
||||||
runtime.Log("successfully voted for validator")
|
runtime.Log(name + ": successfully voted for validator")
|
||||||
|
removeVotes(ctx, id)
|
||||||
} else {
|
} else {
|
||||||
runtime.Log("vote has been failed")
|
runtime.Log(name + ": vote has been failed")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runtime.Log(name + ": saved vote for validator")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -132,3 +177,97 @@ func Vote(candidate []byte) {
|
||||||
func Name() string {
|
func Name() string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 bytesEqual(cnd.id, id) {
|
||||||
|
voters := cnd.n
|
||||||
|
|
||||||
|
for j := range voters {
|
||||||
|
if bytesEqual(voters[j], from) {
|
||||||
|
return len(voters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
voters = append(voters, from)
|
||||||
|
cnd = ballot{id: id, n: voters, block: blockHeight}
|
||||||
|
found = len(voters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not add old ballots, they are invalid
|
||||||
|
if blockHeight-cnd.block <= blockDiff {
|
||||||
|
newCandidates = append(newCandidates, cnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found < 0 {
|
||||||
|
voters := [][]byte{from}
|
||||||
|
newCandidates = append(newCandidates, ballot{
|
||||||
|
id: id,
|
||||||
|
n: voters,
|
||||||
|
block: blockHeight})
|
||||||
|
found = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, newCandidates)
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBallots(ctx storage.Context) []ballot {
|
||||||
|
data := storage.Get(ctx, voteKey)
|
||||||
|
if data != nil {
|
||||||
|
return binary.Deserialize(data.([]byte)).([]ballot)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []ballot{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||||
|
data := binary.Serialize(value)
|
||||||
|
storage.Put(ctx, key, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// neo-go#1176
|
||||||
|
func bytesEqual(a []byte, b []byte) bool {
|
||||||
|
return util.Equals(string(a), string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func voteID(epoch interface{}, args [][]byte) []byte {
|
||||||
|
var (
|
||||||
|
result []byte
|
||||||
|
epochBytes = epoch.([]byte)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = append(result, epochBytes...)
|
||||||
|
|
||||||
|
for i := range args {
|
||||||
|
result = append(result, args[i]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.SHA256(result)
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,26 @@
|
||||||
package alphabetcontract
|
package alphabetcontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
"github.com/nspcc-dev/neo-go/pkg/interop/engine"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
irNode struct {
|
irNode struct {
|
||||||
key []byte
|
key []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ballot struct {
|
||||||
|
id []byte // id of the voting decision
|
||||||
|
n [][]byte // already voted inner ring nodes
|
||||||
|
block int // block with the last vote
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -24,6 +34,13 @@ const (
|
||||||
index = 6
|
index = 6
|
||||||
|
|
||||||
netmapContractKey = "netmapScriptHash"
|
netmapContractKey = "netmapScriptHash"
|
||||||
|
|
||||||
|
blockDiff = 20 // amount of blocks when ballot get discarded
|
||||||
|
|
||||||
|
threshold = totalAlphabetContracts*2/3 + 1
|
||||||
|
voteKey = "ballots"
|
||||||
|
|
||||||
|
totalAlphabetContracts = 7
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -49,6 +66,8 @@ func Init(addrNetmap []byte) {
|
||||||
|
|
||||||
storage.Put(ctx, netmapContractKey, addrNetmap)
|
storage.Put(ctx, netmapContractKey, addrNetmap)
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, []ballot{})
|
||||||
|
|
||||||
runtime.Log(name + " contract initialized")
|
runtime.Log(name + " contract initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +100,21 @@ func checkPermission(ir []irNode) bool {
|
||||||
return runtime.CheckWitness(node.key)
|
return runtime.CheckWitness(node.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func innerRingInvoker(ir []irNode) []byte {
|
||||||
|
for i := 0; i < len(ir); i++ {
|
||||||
|
if i >= totalAlphabetContracts {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
node := ir[i]
|
||||||
|
if runtime.CheckWitness(node.key) {
|
||||||
|
return node.key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func Emit() bool {
|
func Emit() bool {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
if !checkPermission(innerRingKeys) {
|
||||||
|
@ -111,19 +145,30 @@ func Emit() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Vote(candidate []byte) {
|
func Vote(epoch int, candidates [][]byte) {
|
||||||
innerRingKeys := irList()
|
innerRingKeys := irList()
|
||||||
if !checkPermission(innerRingKeys) {
|
|
||||||
|
key := innerRingInvoker(innerRingKeys)
|
||||||
|
if len(key) == 0 {
|
||||||
panic("invalid invoker")
|
panic("invalid invoker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id := voteID(epoch, candidates)
|
||||||
|
n := vote(ctx, id, key)
|
||||||
|
|
||||||
|
if n >= threshold {
|
||||||
|
candidate := candidates[index%len(candidates)]
|
||||||
address := runtime.GetExecutingScriptHash()
|
address := runtime.GetExecutingScriptHash()
|
||||||
|
|
||||||
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
ok := engine.AppCall([]byte(neoHash), "vote", address, candidate).(bool)
|
||||||
if ok {
|
if ok {
|
||||||
runtime.Log("successfully voted for validator")
|
runtime.Log(name + ": successfully voted for validator")
|
||||||
|
removeVotes(ctx, id)
|
||||||
} else {
|
} else {
|
||||||
runtime.Log("vote has been failed")
|
runtime.Log(name + ": vote has been failed")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runtime.Log(name + ": saved vote for validator")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -132,3 +177,97 @@ func Vote(candidate []byte) {
|
||||||
func Name() string {
|
func Name() string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 bytesEqual(cnd.id, id) {
|
||||||
|
voters := cnd.n
|
||||||
|
|
||||||
|
for j := range voters {
|
||||||
|
if bytesEqual(voters[j], from) {
|
||||||
|
return len(voters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
voters = append(voters, from)
|
||||||
|
cnd = ballot{id: id, n: voters, block: blockHeight}
|
||||||
|
found = len(voters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not add old ballots, they are invalid
|
||||||
|
if blockHeight-cnd.block <= blockDiff {
|
||||||
|
newCandidates = append(newCandidates, cnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found < 0 {
|
||||||
|
voters := [][]byte{from}
|
||||||
|
newCandidates = append(newCandidates, ballot{
|
||||||
|
id: id,
|
||||||
|
n: voters,
|
||||||
|
block: blockHeight})
|
||||||
|
found = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
setSerialized(ctx, voteKey, newCandidates)
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBallots(ctx storage.Context) []ballot {
|
||||||
|
data := storage.Get(ctx, voteKey)
|
||||||
|
if data != nil {
|
||||||
|
return binary.Deserialize(data.([]byte)).([]ballot)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []ballot{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSerialized(ctx storage.Context, key interface{}, value interface{}) {
|
||||||
|
data := binary.Serialize(value)
|
||||||
|
storage.Put(ctx, key, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// neo-go#1176
|
||||||
|
func bytesEqual(a []byte, b []byte) bool {
|
||||||
|
return util.Equals(string(a), string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func voteID(epoch interface{}, args [][]byte) []byte {
|
||||||
|
var (
|
||||||
|
result []byte
|
||||||
|
epochBytes = epoch.([]byte)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = append(result, epochBytes...)
|
||||||
|
|
||||||
|
for i := range args {
|
||||||
|
result = append(result, args[i]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.SHA256(result)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue