-
Notifications
You must be signed in to change notification settings - Fork 7
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
Add Aggregator interface and implement fake aggregator #35
Changes from all commits
ce49b04
07eeb53
0ac2169
d94f81d
b836f53
88a727e
62e0df2
7dfaae3
ba3d0a6
16d0b77
75ee3c1
d02c8ad
25d40b4
26cfa46
1d45f36
09f572d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,12 +2,11 @@ package sim | |
|
||
import ( | ||
"bytes" | ||
"encoding/binary" | ||
"fmt" | ||
"github.com/filecoin-project/go-f3/f3" | ||
"io" | ||
"sort" | ||
"strings" | ||
|
||
"github.com/filecoin-project/go-f3/f3" | ||
) | ||
|
||
type AdversaryReceiver interface { | ||
|
@@ -48,6 +47,8 @@ type Network struct { | |
globalStabilisationElapsed bool | ||
// Trace level. | ||
traceLevel int | ||
|
||
actor2PubKey map[f3.ActorID]f3.PubKey | ||
} | ||
|
||
func NewNetwork(latency LatencyModel, traceLevel int) *Network { | ||
|
@@ -59,15 +60,17 @@ func NewNetwork(latency LatencyModel, traceLevel int) *Network { | |
latency: latency, | ||
globalStabilisationElapsed: false, | ||
traceLevel: traceLevel, | ||
actor2PubKey: map[f3.ActorID]f3.PubKey{}, | ||
} | ||
} | ||
|
||
func (n *Network) AddParticipant(p f3.Receiver) { | ||
func (n *Network) AddParticipant(p f3.Receiver, pubKey f3.PubKey) { | ||
if n.participants[p.ID()] != nil { | ||
panic("duplicate participant ID") | ||
} | ||
n.participantIDs = append(n.participantIDs, p.ID()) | ||
n.participants[p.ID()] = p | ||
n.actor2PubKey[p.ID()] = pubKey | ||
} | ||
|
||
////// Network interface | ||
|
@@ -107,31 +110,94 @@ func (n *Network) SetAlarm(sender f3.ActorID, payload string, at float64) { | |
|
||
func (n *Network) Sign(sender f3.ActorID, msg []byte) []byte { | ||
// Fake implementation. | ||
// Just prepends 8-byte sender ID to message. | ||
buf := new(bytes.Buffer) | ||
if err := binary.Write(buf, binary.BigEndian, sender); err != nil { | ||
panic(err) | ||
} | ||
return append(buf.Bytes(), msg...) | ||
// Just prepends the pubkey associated with the sender ID to message. | ||
aux := append([]byte{}, n.actor2PubKey[sender]...) | ||
return append(aux, msg...) | ||
} | ||
|
||
func (n *Network) Verify(sender f3.ActorID, msg, sig []byte) bool { | ||
func (n *Network) Verify(pubKey f3.PubKey, msg, sig []byte) bool { | ||
// Fake implementation. | ||
// Just checks that first 8 bytes of signature match sender ID, | ||
// Just checks that first bytes of the signature match sender ID, | ||
// and remaining bytes match message. | ||
buf := bytes.NewReader(sig) | ||
var recoveredSender uint64 | ||
if err := binary.Read(buf, binary.BigEndian, &recoveredSender); err != nil { | ||
return false | ||
aux := append([]byte{}, pubKey...) | ||
aux = append(aux, msg...) | ||
return bytes.Equal(aux, sig) | ||
} | ||
|
||
func (n *Network) Aggregate(sigs [][]byte, aggSignature []byte) []byte { | ||
// Fake implementation. | ||
// Just appends signature to aggregate signature. | ||
// This fake aggregation is not commutative (order matters) | ||
// But determinism is preserved by sorting by weight both here and in VerifyAggregate | ||
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. IMO: the deterministic sorting should happen outside the interface to match real signature schemes which can also have this behaviour. 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. But that would mean forcing outside the interface a sorting that may not be required by all signature schemes. Would it not be better to have it inside the signature schemes that need it only? 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. (I get optimizing if the real BLS is not commutative, and coupling in that particular instantiation of the interface, but I also mean in particular in the case of the sorting required by this general-purpose (or so-intending) fake aggregation and verification scheme, that just happens to need to sort) 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. I see your point, and I don't know which approach is better at this moment. I mentioned it because this requirement was unclear to me, and I immediately highlighted it as a possible difference between test/sim and reality. We can keep order independence internal, but then we should highlight it in the interface documentation. 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.
Sounds good, I suggest going back to this when we discuss interfaces in #42 not to duplicate discussions. |
||
// (That contains the sender ID in the signature) | ||
|
||
// Sort the pubKeys based on descending order of actorID | ||
// take any pubkey from the actor2pubkey | ||
var pubKeyLen int | ||
for _, pubKey := range n.actor2PubKey { | ||
pubKeyLen = len(pubKey) | ||
break | ||
} | ||
remainingBytes := sig[8:] | ||
if recoveredSender != uint64(sender) { | ||
return false | ||
|
||
msg := sigs[0][pubKeyLen:] | ||
msgLen := len(msg) | ||
|
||
// Extract existing pubKeys along with their actorIDs | ||
pubKeys := [][]byte{} | ||
if len(aggSignature) > 0 { | ||
buf := bytes.NewReader(aggSignature[msgLen:]) | ||
for { | ||
existingPubKey := make([]byte, pubKeyLen) | ||
if _, err := io.ReadFull(buf, existingPubKey); err != nil { | ||
if err == io.EOF { | ||
break // End of the aggregate signature. | ||
} else if err != nil { | ||
panic(err) // Error in reading the signature. | ||
} | ||
} | ||
pubKeys = append(pubKeys, existingPubKey) | ||
} | ||
} | ||
if !bytes.Equal(remainingBytes, msg) { | ||
return false | ||
|
||
for i := 0; i < len(sigs); i++ { | ||
if !bytes.Equal(msg, sigs[i][pubKeyLen:]) { | ||
panic("Payload mismatch") | ||
} | ||
pubKeys = append(pubKeys, sigs[i][:pubKeyLen]) | ||
} | ||
return true | ||
|
||
sort.Slice(pubKeys, func(i, j int) bool { | ||
return bytes.Compare(pubKeys[i], pubKeys[j]) > 0 | ||
}) | ||
|
||
for i := 0; i < len(pubKeys)-1; i++ { | ||
if bytes.Equal(pubKeys[i], pubKeys[i+1]) { | ||
panic("Duplicate pubkeys") | ||
} | ||
} | ||
|
||
// Reconstruct the aggregated signature in sorted order | ||
updatedAggSignature := append([]byte{}, msg...) | ||
for _, s := range pubKeys { | ||
updatedAggSignature = append(updatedAggSignature, s...) | ||
} | ||
|
||
return updatedAggSignature | ||
} | ||
|
||
func (n *Network) VerifyAggregate(payload, aggSig []byte, signers []f3.PubKey) bool { | ||
sort.Slice(signers, func(i, j int) bool { | ||
return bytes.Compare(signers[i], signers[j]) > 0 | ||
}) | ||
|
||
signersConcat := make([]byte, 0) | ||
for _, signer := range signers { | ||
signersConcat = append(signersConcat, signer...) | ||
} | ||
|
||
aux := append([]byte{}, payload...) | ||
aux = append(aux, signersConcat...) | ||
return bytes.Equal(aux, aggSig) | ||
} | ||
|
||
func (n *Network) Log(format string, args ...interface{}) { | ||
|
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.
I see that you are assuming that aggregate signatures can be combined. I think the conclusion is that it is not true, but we can address that later.