Commit e815b3a5 authored by Anders Jensen Løvig's avatar Anders Jensen Løvig
Browse files

Max-subset agreement

parent 3e09f85e
Pipeline #21827 failed with stages
in 1 minute and 3 seconds
......@@ -116,7 +116,7 @@ func (s *Params) VerifyDisjunction(p0, p1 *Proof) bool {
func Hash(ls ...*big.Int) *big.Int {
hash := sha512.New()
for _, e := range ls {
hash.Write(e.Bytes())
_, _ = hash.Write(e.Bytes())
}
sum := hash.Sum([]byte{})
return new(big.Int).SetBytes(sum)
......
package election
import (
"bsc-shamir/crypto/pedersen"
"fmt"
"log"
"sync"
"github.com/gonum/stat/combin"
......@@ -17,6 +17,7 @@ type Agreement struct {
// The minimum servers required for being able to tally
required int
// Map of ballot lists receveid from servers
ourList BallotList
ballotLists map[UniqueID]BallotList
}
......@@ -40,80 +41,40 @@ func (a *Agreement) AddList(serverID UniqueID, ballotList map[string]uuid.UUID)
return nil
}
func (a *Agreement) TallyList() []uuid.UUID {
for _, ll := range a.ballotLists {
list := make([]uuid.UUID, 0, len(ll))
for _, id := range ll {
list = append(list, id)
}
return list
func (a *Agreement) TallyList() ([]uuid.UUID, error) {
a.Lock()
defer a.Unlock()
bls := make([]BallotList, 0, len(a.ballotLists))
for _, b := range a.ballotLists {
bls = append(bls, b)
}
panic("Fuck")
}
type Whitelist map[uuid.UUID]pedersen.Proof
type Judge struct {
mutex sync.Mutex
Threadshold int
Points map[uuid.UUID]int
Answers map[uuid.UUID]pedersen.Proof
BallotIDs []uuid.UUID
}
func NewJudge(threadshold int, box *BallotBox) *Judge {
m := len(box.ballots)
j := &Judge{
mutex: sync.Mutex{},
Threadshold: threadshold,
Points: make(map[uuid.UUID]int, m),
Answers: make(map[uuid.UUID]pedersen.Proof, m),
BallotIDs: []uuid.UUID{},
}
for _, id := range box.Filter() {
ballot := box.ballots[id]
j.Answers[id] = ballot.Commits
j.Points[id] = 0
j.BallotIDs = append(j.BallotIDs, id)
}
return j
}
hashes := MaxSubset(a.required, bls)
func (j *Judge) HandleWhitelist(wl Whitelist) {
j.mutex.Lock()
defer j.mutex.Unlock()
for _, id := range j.BallotIDs {
proof := wl[id]
answer := j.Answers[id]
if proof.Equals(answer) {
j.Points[id]++
list := make([]uuid.UUID, 0, len(a.ourList))
for h := range hashes {
if id, ok := a.ourList[h]; ok {
list = append(list, id)
delete(hashes, h)
}
}
}
func (j *Judge) GoodIds() []uuid.UUID {
j.mutex.Lock()
defer j.mutex.Unlock()
good := []uuid.UUID{}
for _, id := range j.BallotIDs {
if j.Points[id] >= j.Threadshold {
good = append(good, id)
}
if len(hashes) != 0 {
return nil, fmt.Errorf("missing %d ballots to tally", len(hashes))
}
return good
return list, nil
}
func MaxSubset(t int, superset [][]uuid.UUID) map[uuid.UUID]bool {
n := len(superset)
func MaxSubset(t int, ballotLists []BallotList) map[string]bool {
n := len(ballotLists)
combis := combin.Combinations(n, t)
subsets := make([]map[uuid.UUID]bool, len(combis), n)
log.Printf("N: %d, combis: %d\n", n, len(combis))
subsets := make([]map[string]bool, len(combis), n)
for i, ls := range combis {
sets := [][]uuid.UUID{}
sets := make([]BallotList, 0, len(ls))
for _, i := range ls {
sets = append(sets, superset[i])
sets = append(sets, ballotLists[i])
}
subsets[i] = Intersection(sets)
}
......@@ -126,17 +87,17 @@ func MaxSubset(t int, superset [][]uuid.UUID) map[uuid.UUID]bool {
return subsets[max]
}
func Intersection(superset [][]uuid.UUID) map[uuid.UUID]bool {
section := make(map[uuid.UUID]int, len(superset[0]))
func Intersection(superset []BallotList) map[string]bool {
section := make(map[string]int, len(superset[0]))
for _, set := range superset {
for _, e := range set {
section[e]++
for hash := range set {
section[hash]++
}
}
set := make(map[uuid.UUID]bool)
for uuid, num := range section {
set := make(map[string]bool)
for hash, num := range section {
if num >= len(superset) {
set[uuid] = true
set[hash] = true
}
}
return set
......
......@@ -12,33 +12,34 @@ func TestIntersection(t *testing.T) {
for i := 0; i < N; i++ {
elem[i] = uuid.New()
}
superset := [][]uuid.UUID{
{elem[0], elem[1], elem[2], elem[3]},
{elem[0], elem[1], elem[2], elem[4]},
{elem[0], elem[1], elem[2], elem[5]},
{elem[0], elem[1], elem[2], elem[6]},
superset := []BallotList{
{"0": elem[0], "1": elem[1], "2": elem[2], "3": elem[3]},
{"0": elem[0], "1": elem[1], "2": elem[2], "4": elem[4]},
{"0": elem[0], "1": elem[1], "2": elem[2], "5": elem[5]},
{"0": elem[0], "1": elem[1], "2": elem[2], "6": elem[6]},
}
subset := Intersection(superset)
if !subset[elem[0]] {
t.Fail()
if !subset["0"] {
t.Error()
}
if !subset[elem[1]] {
t.Fail()
if !subset["1"] {
t.Error()
}
if !subset[elem[2]] {
t.Fail()
if !subset["2"] {
t.Error()
}
if subset[elem[3]] {
t.Fail()
if subset["3"] {
t.Error()
}
if subset[elem[4]] {
t.Fail()
if subset["4"] {
t.Error()
}
if subset[elem[5]] {
t.Fail()
if subset["5"] {
t.Error()
}
if subset[elem[6]] {
t.Fail()
if subset["6"] {
t.Error()
}
}
......@@ -48,33 +49,33 @@ func TestMaxSubset(t *testing.T) {
for i := 0; i < N; i++ {
elem[i] = uuid.New()
}
superset := [][]uuid.UUID{
{elem[0], elem[1], elem[2], elem[3]},
{elem[0], elem[1], elem[2], elem[4]},
{elem[0], elem[1], elem[2], elem[5]},
{elem[0], elem[1], elem[2], elem[6]},
{elem[6]},
superset := []BallotList{
{"0": elem[0], "1": elem[1], "2": elem[2], "3": elem[3], "4": elem[4]},
{"0": elem[0], "1": elem[1], "2": elem[2], "4": elem[4]},
{"0": elem[0], "1": elem[1], "2": elem[2], "5": elem[5]},
{"0": elem[0], "1": elem[1], "2": elem[2], "4": elem[4], "6": elem[6]},
{"6": elem[6]},
}
subset := MaxSubset(4, superset)
if !subset[elem[0]] {
t.Fail()
if !subset["0"] {
t.Error()
}
if !subset[elem[1]] {
t.Fail()
if !subset["1"] {
t.Error()
}
if !subset[elem[2]] {
t.Fail()
if !subset["2"] {
t.Error()
}
if subset[elem[3]] {
t.Fail()
if subset["3"] {
t.Error()
}
if subset[elem[4]] {
t.Fail()
if subset["4"] {
t.Error()
}
if subset[elem[5]] {
t.Fail()
if subset["5"] {
t.Error()
}
if subset[elem[6]] {
t.Fail()
if subset["6"] {
t.Error()
}
}
......@@ -257,15 +257,18 @@ func (election *Election) nextPhase(reason Reason) {
// Only tally if we have ballots
if election.ballotBox.Size() != 0 {
list := election.Agreement.TallyList()
tally := election.ballotBox.Tally(list)
election.Status.TalliedServers[election.ServerID] = true
err := election.tallyBox.Put(tally)
if err != nil {
panic(err) // Should not happen
list, err := election.Agreement.TallyList()
if err == nil {
tally := election.ballotBox.Tally(list)
election.Status.TalliedServers[election.ServerID] = true
err = election.tallyBox.Put(tally)
if err != nil {
panic(err) // Should not happen
}
election.server.Broadcast(network.NewMessage(MsgTally, tally))
break
}
election.server.Broadcast(network.NewMessage(MsgTally, tally))
break
log.Println("Should really wait for tallies from other servers !!! :D")
}
fallthrough
......
......@@ -93,17 +93,22 @@ func benchmarkTally(votecount, serverCount, reqServer int, b *testing.B) {
//Setup
b.StopTimer()
box := NewBallotBox(big.NewInt(1), common.DefaultParams())
judge := NewJudge(reqServer, box)
goodIds := judge.GoodIds()
id := UniqueID("1")
for i := 0; i < votecount; i++ {
_ = box.Put(CreateBallots(reqServer, createXS(serverCount), big.NewInt(1))[id])
}
ballotList := box.ballotList()
ids := make([]uuid.UUID, 0, len(ballotList))
for _, id := range ballotList {
ids = append(ids, id)
}
//Actual benchmark
b.StartTimer()
for i := 0; i < b.N; i++ {
box.Tally(goodIds)
box.Tally(ids)
}
}
......@@ -158,10 +163,13 @@ func benchmarkCombine(votecount, serverCount, reqServer int, b *testing.B) {
_ = ballotbox.Put(ballot[ID])
}
judge := NewJudge(reqServer, ballotbox)
goodIds := judge.GoodIds()
ballotList := ballotbox.ballotList()
ids := make([]uuid.UUID, 0, len(ballotList))
for _, id := range ballotList {
ids = append(ids, id)
}
tally := ballotbox.Tally(goodIds)
tally := ballotbox.Tally(ids)
for i := 0; i < reqServer; i++ {
_ = tallyBoxes[0].Put(tally)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment