Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ COPY --from=cbuild $INCLUDE_DIR $INCLUDE_DIR
ENTRYPOINT ["keep-client", "-config", "/keepclient/config.toml"]

# docker caches more when using CMD [] resulting in a faster build.
CMD []
CMD []
4 changes: 2 additions & 2 deletions go/Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

86 changes: 86 additions & 0 deletions go/beacon/broadcast/broadcast.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package broadcast

import (
"sync"

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

// Message represents a message to communicate over a broadcast channel.
// TODO Combine with Raghav's work on protobuf messages.
type Message struct {
Sender bls.ID
Receiver *bls.ID // pointer so it can be nil for broadcast messages
Data interface{}
}

// NewBroadcastMessage creates a new message from the given sender, carrying the
// given data payload, meant for broacast into a channel watched by others. The
// message is signed, but not encrypted.
func NewBroadcastMessage(sender bls.ID, data interface{}) Message {
// FIXME Sign, will require private key...
return Message{sender, nil, data}
}

// NewPrivateMessage creates a new private message from the given sender to the
// given receiver, carrying the given data payload, meant for broacast into a
// channel watched by others. The message is signed and encrypted.
func NewPrivateMessage(sender bls.ID, receiver bls.ID, data interface{}) Message {
// FIXME Actually encrypt here... Will require a key, best taken from
// FIXME chain...
return Message{sender, &receiver, data}
}

// Channel represents a named broadcast channel. It allows consumers to send
// messages to the channel (via Send) and to access a low-level receive chan
// that furnishes messages sent onto the broadcast channel.
type Channel interface {
Name() string

Send(message Message) bool

RecvChan() <-chan Message
}

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

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

func (channel *localChannel) Send(message Message) bool {
channel.recvChansMutex.Lock()
snapshot := make([]chan Message, len(channel.recvChans))
copy(snapshot, channel.recvChans)
channel.recvChansMutex.Unlock()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would defer make sense here? In this case I don't think it matters much, but bare resource frees make me nervous, and are best avoided in most langs

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.

Mostly my goal here was to keep the the scope of the lock super-tight. It doesn't really matter that much though---this is a local implementation that we'll be swapping out for a network-based one in practice.

That said, it seems odd that sync.Mutex doesn't provide a Wrapped method or something that basically lets you run a func inside the lock and return a result. Guess that would be too functional for the Go folks :p

go func() {
for _, recvChan := range snapshot {
recvChan <- message
}
}()

return true
}

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

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

return newChan
}

// LocalChannel returns a Channel designed to mediate between local
// participants. It delivers all messages sent to the channel through its
// receive channels. RecvChan on a LocalChannel creates a new receive channel
// that is returned to the caller, so that all receive channels can receive
// the message.
func LocalChannel(name string) Channel {
return &localChannel{name, sync.Mutex{}, make([]chan Message, 0)}
}
96 changes: 96 additions & 0 deletions go/beacon/chain/chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package chain

import (
"sync"
"time"
)

// BlockCounter is an interface that provides the ability to wait for a certain
// number of abstract blocks. It provides for two ways to wait, one blocking and
// one chan-based. Block height is expected to increase monotonically, though
// the time between blocks will depend on the underlying implementation. See
// LocalBlockCounter() for a local implementation.
type BlockCounter interface {
// WaitForBlocks blocks at the caller until numBlocks new blocks have been
// seen.
WaitForBlocks(numBlocks int)
// BlockWaiter returns a channel that will emit the current block height
// after the given number of blocks has elapsed and then immediately close.
BlockWaiter(numBlocks int) <-chan int

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The name here is unfortunate. How do folks typically distinguish between sync and async in Go func names?

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.

time.Timer just exposes the channel directly. Doesn't work as well here with multiple timers and consumers though. Could just be that instead of a BlockCounter interface, we have a package with a function that returns a block waiter that is for a particular block count. It could look basically the same as time.Timer.

That said, (a) the naming seems all right to me (a waiter is something you wait on, no?) and (b) this interface needs to be a little more abstract anyway (discovered in super-smush-bros). Let's keep the issue in mind but handle it when we hit the right abstraction level.

}

type localBlockCounter struct {
structMutex sync.Mutex
blockHeight int
waiters map[int][]chan int
}

func (counter *localBlockCounter) WaitForBlocks(numBlocks int) {
waiter := counter.BlockWaiter(numBlocks)
<-waiter
return
}

func (counter *localBlockCounter) BlockWaiter(numBlocks int) <-chan int {
newWaiter := make(chan int)

counter.structMutex.Lock()
defer counter.structMutex.Unlock()
notifyBlockHeight := counter.blockHeight + numBlocks

if notifyBlockHeight <= counter.blockHeight {
newWaiter <- notifyBlockHeight
} else {
waiterList, exists := counter.waiters[notifyBlockHeight]
if !exists {
waiterList = make([]chan int, 0)
}

counter.waiters[notifyBlockHeight] = append(waiterList, newWaiter)
}

return newWaiter
}

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

for _ = range ticker.C {
counter.structMutex.Lock()
counter.blockHeight++
height := counter.blockHeight
waiters, exists := counter.waiters[height]
delete(counter.waiters, height)
counter.structMutex.Unlock()

if exists {
for _, waiter := range waiters {
go func(w chan int) { w <- height }(waiter)
}
}
}
}

// LocalBlockCounter creates a BlockCounter that runs completely locally. It is
// designed to simply increase block height at a set time interval in the
// background.
func LocalBlockCounter() BlockCounter {
counter := localBlockCounter{blockHeight: 0, waiters: make(map[int][]chan int)}

go counter.count()

return &counter
}

// BeaconConfig contains configuration for the threshold relay beacon, typically
// from the underlying blockchain.
type BeaconConfig struct {
GroupSize int
Threshold int
}

// GetBeaconConfig Get the latest threshold relay beacon configuration.
// TODO Make this actually look up/update from chain information.
func GetBeaconConfig() BeaconConfig {
return BeaconConfig{10, 4}
}
Loading