Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
7c9ad0c
Fix docker build, but really this time though
Shadowfiend Feb 21, 2018
419ec50
Add stub interface for broadcast channel
Shadowfiend Feb 9, 2018
143c1ef
Add initial threshold group Member struct
Shadowfiend Feb 9, 2018
b5dbf55
Break member struct into structs for various phases
Shadowfiend Feb 9, 2018
2e4736a
Add constructor functions for broadcast and private messages
Shadowfiend Feb 12, 2018
7d2c928
Local channel sends messages in goroutines
Shadowfiend Feb 12, 2018
843c7f0
Make member BLS ID publicly accessible.
Shadowfiend Feb 12, 2018
b94058d
Set BLS id from a hex string in member instantiation
Shadowfiend Feb 12, 2018
516e738
Properly capture threshold in member constructor
Shadowfiend Feb 12, 2018
140a537
Provide access to member IDs excluding the current member
Shadowfiend Feb 12, 2018
cb13084
Add methods to know when we have all expected bits of a given type
Shadowfiend Feb 12, 2018
dd1f708
Don't accidentally accuse ourselves
Shadowfiend Feb 12, 2018
33b1378
Fix combined commitment computation
Shadowfiend Feb 12, 2018
6a8fe19
Add two chain stubs, BeaconConfig and BlockCounter
Shadowfiend Feb 12, 2018
8522f27
Add DKG package and procedure
Shadowfiend Feb 12, 2018
cd26017
Set up main to call DKG with the configured group size and threshold
Shadowfiend Feb 12, 2018
b8668f7
Move DKG into relay instead of its own package
Shadowfiend Feb 12, 2018
c535ee4
Fix group secret key share creation
Shadowfiend Feb 13, 2018
045ce6e
Clarify group publicy key extraction
Shadowfiend Feb 13, 2018
0aee2d2
Add signature share generation and verification
Shadowfiend Feb 13, 2018
1ee6a47
Whoops, fix reference to ExecuteDKG from dkg to relay package
Shadowfiend Feb 13, 2018
e65c985
Add group signature check to main
Shadowfiend Feb 13, 2018
cc4102d
Deal with unjustified accused members in a more complete way
Shadowfiend Feb 13, 2018
9465aaf
Move methods for Member to the end of member.go
Shadowfiend Feb 13, 2018
85696af
Switch the BLS config we're using to CurveFp254BNb
Shadowfiend Feb 13, 2018
15a5f0c
Add some documentation to chain.go
Shadowfiend Feb 13, 2018
e843d85
Rename localBlockCounter.heightMutex to structMutex
Shadowfiend Feb 13, 2018
68c8f86
Cover edge cases more clearly when registring a new block waiter
Shadowfiend Feb 13, 2018
b6c1c4c
Drop a spurious assignment
Shadowfiend Feb 13, 2018
37bce33
Reduce scope of localBlockCounter mutex when incrementing block height
Shadowfiend Feb 13, 2018
172c705
Notify waiters of a local block height increment in a goroutine
Shadowfiend Feb 13, 2018
af952f5
Burn some commented out code
Shadowfiend Feb 13, 2018
5a7f8d3
Switch to integer multiple of milliseconds for timer
Shadowfiend Feb 21, 2018
a1ab316
Avoid 0 ids
Shadowfiend Feb 21, 2018
e929c6b
Mutex-protect recvChans in LocalChannel
Shadowfiend Feb 21, 2018
9d39e7d
Give LocalChannel recvChans a buffer
Shadowfiend Feb 21, 2018
cfbc53b
Ignore justification/accusation messages from self
Shadowfiend Feb 21, 2018
9856a92
Increase timeouts to accommodate larger group sizes
Shadowfiend Feb 21, 2018
a2e14f4
Tweak some of the output
Shadowfiend Feb 21, 2018
5b11a83
Go back to CurveFp382_1 because it works
Shadowfiend Feb 21, 2018
657281f
Fix comment for ExecuteDKG
Shadowfiend Feb 21, 2018
1b15f8b
Add a comment clarifying the avoid-zero-ID loop
Shadowfiend Feb 22, 2018
ccff34d
Fix mutex around recvChans in localChannel.Send
Shadowfiend Feb 22, 2018
a084385
Properly capture loop index, report BLS id on DKG error
Shadowfiend Feb 22, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions go/beacon/broadcast/broadcast.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package broadcast

import (
"sync"

"github.com/dfinity/go-dfinity-crypto/bls"
)

Expand Down Expand Up @@ -41,28 +43,33 @@ type Channel interface {
}

type localChannel struct {
name string
recvChans []chan Message
name string
recvChansMutex sync.Mutex

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

recvChans []chan Message
}

func (channel *localChannel) Name() string {
return channel.name
}

func (channel *localChannel) Send(message Message) bool {
channel.recvChansMutex.Lock()
go func(recvChans []chan Message) {
for _, recvChan := range recvChans {
recvChan <- message
}
}(channel.recvChans)
channel.recvChansMutex.Unlock()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lock may potentially get let go before the go routine finishes executing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmm… Which is not fine. I meant it to be fine because we snapshot channel.recvChans when passing it to the goroutine, but we actually don't. I'll copy it <_<


return true
}

func (channel *localChannel) RecvChan() <-chan Message {
newChan := make(chan Message)
newChan := make(chan Message, 62500)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this number?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

250*250, or roughly the number of total expected private share messages we're throwing around during private share exchange with a 250-member group. I don't think it actually needs to be this high, but I didn't want to waste further time on figuring out a better number since this is really just for local smoke testing.


channel.recvChansMutex.Lock()
channel.recvChans = append(channel.recvChans, newChan)
channel.recvChansMutex.Unlock()

return newChan
}
Expand All @@ -73,5 +80,5 @@ func (channel *localChannel) RecvChan() <-chan Message {
// that is returned to the caller, so that all receive channels can receive
// the message.
func LocalChannel(name string) Channel {
return &localChannel{name, make([]chan Message, 0)}
return &localChannel{name, sync.Mutex{}, make([]chan Message, 0)}

@rargulati rargulati Feb 22, 2018 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't need the sync.Mutex (unless you'd like us to be very explicit)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel super-strongly, but the other option was to use named parameters (that is, I can't just leave the parameter off as the compiler will choke). I just did the thing that looked easiest, tbh.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a simple slice (not a slice of slices, right?) - so you can drop the zero

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't believe that's true. Slices need an initial length. Compiler seems to agree:

beacon/broadcast/broadcast.go:83:47: missing len argument to make([]chan Message)

@rargulati rargulati Feb 22, 2018 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mhmm, good to know, thanks.

}
2 changes: 1 addition & 1 deletion go/beacon/chain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (counter *localBlockCounter) BlockWaiter(numBlocks int) <-chan int {
}

func (counter *localBlockCounter) count() {
ticker := time.NewTicker(time.Duration(time.Second / 2))
ticker := time.NewTicker(time.Duration(500 * time.Millisecond))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got a bad habit of doing this too - but I think we should start documenting our tick durations and timeouts. Why do we choose these numbers and where can they be tuned from? What are the consequences of going with these numbers? I only bring this up because this isn't like saying "on every tick" which is essentially like saying "every second". This is saying every "half-tick"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anything under local* is 100% magic numbers right now heh. I just picked something that seemed reasonable out of thin air. In practice, one tick here will = one block from the chain, and the block timeouts that we're using in dkg.go will best be provided by on-chain config so that they make sense in the context of the chain we're working with (and can be adjusted if, say, block times improve dramatically).


for _ = range ticker.C {
counter.structMutex.Lock()
Expand Down
44 changes: 28 additions & 16 deletions go/beacon/relay/dkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,29 +46,31 @@ type JustificationsMessage struct {
justifications map[bls.ID]bls.SecretKey
}

// Execute runs the full distributed key generation lifecycle, given a broadcast
// channel to mediate it and a group size and threshold. It returns a threshold
// group member who is participating in the group if the generation was
// successful, and an error representing what went wrong if not.
// ExecuteDKG runs the full distributed key generation lifecycle, given a
// broadcast channel to mediate it and a group size and threshold. It returns a
// threshold group member who is participating in the group if the generation
// was successful, and an error representing what went wrong if not.
func ExecuteDKG(blockCounter chain.BlockCounter, channel broadcast.Channel, groupSize int, threshold int) (*thresholdgroup.Member, error) {
// FIXME Probably pass in a way to ask for a receiver's public key?
// FIXME Need a way to time out in a given stage, especially the waiting
// ones.

memberID := rand.NewRand().String()
memberID := "0"
for memberID = rand.NewRand().String(); memberID == "0"; {
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I'm not sure I understand what's going here and why we've got an empty for block

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'll add a comment. We're just trying to make sure we don't generate a 0 member ID, and rand.newRand() will sometimes return 0.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhhh, now I see that, interesting. Yeah, that's the right way to do that. Comment will help.

fmt.Printf("[member:%v] Initializing member.\n", memberID)
localMember := thresholdgroup.NewMember(memberID, threshold)

recvChan := channel.RecvChan()

fmt.Printf("[member:%v] Waiting for join timeout...\n", memberID)
blockCounter.WaitForBlocks(5)
blockCounter.WaitForBlocks(15)

fmt.Printf("[member:%v] Broadcasting join.\n", memberID)
channel.Send(broadcast.NewBroadcastMessage(localMember.BlsID, JoinMessage{}))

// Wait for all members.
waiter := blockCounter.BlockWaiter(3)
waiter := blockCounter.BlockWaiter(10)
fmt.Printf("[member:%v] Waiting for other members...\n", memberID)
memberIDs, err := waitForMemberIDs(&localMember.BlsID, recvChan, groupSize)
if err != nil {
Expand All @@ -78,7 +80,9 @@ func ExecuteDKG(blockCounter chain.BlockCounter, channel broadcast.Channel, grou
fmt.Printf("[member:%v] Waiting for member join timeout...\n", memberID)
<-waiter

waiter = blockCounter.BlockWaiter(3)
fmt.Printf("[member:%v] Saw IDs: %v\n", memberID, len(memberIDs))

waiter = blockCounter.BlockWaiter(15)
fmt.Printf("[member:%v] Initiating commitment broadcast phase.\n", memberID)
sharingMember := localMember.InitializeSharing(memberIDs)

Expand All @@ -97,7 +101,7 @@ func ExecuteDKG(blockCounter chain.BlockCounter, channel broadcast.Channel, grou
fmt.Printf("[member:%v] Waiting for commitment timeout...\n", memberID)
<-waiter

waiter = blockCounter.BlockWaiter(5)
waiter = blockCounter.BlockWaiter(20)
fmt.Printf("[member:%v] Sending private shares.\n", memberID)
err = sendShares(channel, &sharingMember)
if err != nil {
Expand All @@ -113,7 +117,7 @@ func ExecuteDKG(blockCounter chain.BlockCounter, channel broadcast.Channel, grou
fmt.Printf("[member:%v] Waiting for share exchange timeout...\n", memberID)
<-waiter

waiter = blockCounter.BlockWaiter(3)
waiter = blockCounter.BlockWaiter(15)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it these numbers are for demo/illustrative purposes and we'll be ripping things out from here later?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yah, see above.

fmt.Printf("[member:%v] Initiating accusation/justification phase.\n", memberID)
justifyingMember := sharingMember.InitializeJustification()
fmt.Printf("[member:%v] Broadcasting accusations.\n", memberID)
Expand All @@ -123,7 +127,7 @@ func ExecuteDKG(blockCounter chain.BlockCounter, channel broadcast.Channel, grou
}

fmt.Printf("[member:%v] Waiting for other accusations...\n", memberID)
err = waitForAccusations(recvChan, &justifyingMember)
err = waitForAccusations(&justifyingMember.BlsID, recvChan, &justifyingMember)
if err != nil {
return nil, fmt.Errorf("failed to receive all accusations: [%v]", err)
}
Expand All @@ -138,7 +142,7 @@ func ExecuteDKG(blockCounter chain.BlockCounter, channel broadcast.Channel, grou
}

fmt.Printf("[member:%v] Waiting for other justifications...\n", memberID)
err = waitForJustifications(recvChan, &justifyingMember)
err = waitForJustifications(&justifyingMember.BlsID, recvChan, &justifyingMember)
if err != nil {
return nil, fmt.Errorf("failed to receive all justifications: [%v]", err)
}
Expand Down Expand Up @@ -197,11 +201,12 @@ done:
}

func sendShares(channel broadcast.Channel, member *thresholdgroup.SharingMember) error {
fmt.Printf("[member:%v] Despatching shares!\n", member.ID)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We sure are!

for _, receiverID := range member.OtherMemberIDs() {
share := member.SecretShareForID(receiverID)
fmt.Printf("[member:%v] Despatching a share!\n", member.ID)
channel.Send(broadcast.NewPrivateMessage(member.BlsID, receiverID, MemberShareMessage{share}))
}
fmt.Printf("[member:%v] Shares despatched!\n", member.ID)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boom! 🎉


return nil
}
Expand All @@ -212,7 +217,6 @@ done:
switch shareMsg := msg.Data.(type) {
case MemberShareMessage:
if msg.Receiver.IsEqual(myID) {
fmt.Printf("[member:%v] Received one id from [%v].\n", myID.GetHexString(), msg.Sender.GetHexString())
sharingMember.AddShareFromID(msg.Sender, shareMsg.Share)

if sharingMember.SharesComplete() {
Expand All @@ -231,13 +235,17 @@ func sendAccusations(channel broadcast.Channel, member *thresholdgroup.Justifyin
return nil
}

func waitForAccusations(recvChan <-chan broadcast.Message, justifyingMember *thresholdgroup.JustifyingMember) error {
func waitForAccusations(myID *bls.ID, recvChan <-chan broadcast.Message, justifyingMember *thresholdgroup.JustifyingMember) error {
memberIDs := justifyingMember.OtherMemberIDs()
seenAccusations := make(map[bls.ID]bool, len(memberIDs))
done:
for msg := range recvChan {
switch accusationMsg := msg.Data.(type) {
case AccusationsMessage:
if msg.Sender.IsEqual(myID) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are the sender of this message, move on (ie. avoid the loopback case)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notably, we'll ultimately probably handle the “ignore messages from me” at a different level, I just needed a quick-and-dirty solution here.

continue
}

for _, accusedID := range accusationMsg.accusedIDs {
justifyingMember.AddAccusationFromID(msg.Sender, accusedID)
}
Expand All @@ -261,13 +269,17 @@ func sendJustifications(channel broadcast.Channel, justifyingMember *thresholdgr
return nil
}

func waitForJustifications(recvChan <-chan broadcast.Message, justifyingMember *thresholdgroup.JustifyingMember) error {
func waitForJustifications(myID *bls.ID, recvChan <-chan broadcast.Message, justifyingMember *thresholdgroup.JustifyingMember) error {
memberIDs := justifyingMember.OtherMemberIDs()
seenJustifications := make(map[bls.ID]bool, len(memberIDs))
done:
for msg := range recvChan {
switch justificationsMsg := msg.Data.(type) {
case JustificationsMessage:
if msg.Sender.IsEqual(myID) {
continue
}

for accuserID, justification := range justificationsMsg.justifications {
justifyingMember.RecordJustificationFromID(msg.Sender, accuserID, justification)
}
Expand Down
2 changes: 1 addition & 1 deletion go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

func main() {
bls.Init(bls.CurveFp254BNb)
bls.Init(bls.CurveFp382_1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't understand all these curves and what going with one vs the other means.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though I did kick out my old number theory book and remembered that I actually know some of this stuff ;-P

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we have a full understanding yet either.

The core of it is in #31 and #40 ; namely, we would like to use the curve that has optimized support for contract usage on the EVM, which was chosen for its relevance to zkSNARKs. This is actually neither of these curves, and @mhluongo is working on that piece. What I can say is that using CurveFp254BNb failed to produce verifiable group signatures. I didn't get a chance to investigate that in depth, and figured I'd punt since @mhluongo is playing in that space and I'd rather move forward on other stuff while he investigates. For now I stuck with the curve that is giving good sigs, which we can use to make sure we haven't broken everything :)


beaconConfig := chain.GetBeaconConfig()

Expand Down