-
Notifications
You must be signed in to change notification settings - Fork 87
Guard Tower Redux: Initial interface and struct setups for distributed key generation #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7c9ad0c
419ec50
143c1ef
b5dbf55
2e4736a
7d2c928
843c7f0
b94058d
516e738
140a537
cb13084
dd1f708
33b1378
6a8fe19
8522f27
cd26017
b8668f7
c535ee4
045ce6e
0aee2d2
1ee6a47
e65c985
cc4102d
9465aaf
85696af
15a5f0c
e843d85
68c8f86
b6c1c4c
37bce33
172c705
af952f5
5a7f8d3
a1ab316
e929c6b
9d39e7d
cfbc53b
9856a92
a2e14f4
5b11a83
657281f
1b15f8b
ccff34d
a084385
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| 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() | ||
| 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)} | ||
| } | ||
| 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 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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} | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would
defermake 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 langsThere was a problem hiding this comment.
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.Mutexdoesn't provide aWrappedmethod or something that basically lets you run afuncinside the lock and return a result. Guess that would be too functional for the Go folks :p