diff --git a/packages/binary/drng/dispatcher.go b/packages/binary/drng/dispatcher.go index 657382fb065a91506d11f76af7ec7c4d57ee6573..d1b1b66b3d03ca48e827bc000029c29971b8a127 100644 --- a/packages/binary/drng/dispatcher.go +++ b/packages/binary/drng/dispatcher.go @@ -2,13 +2,20 @@ package drng import ( "github.com/iotaledger/goshimmer/packages/binary/drng/payload/header" + "github.com/iotaledger/goshimmer/packages/binary/drng/subtypes/collectiveBeacon" "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" ) -func Dispatch(subtype header.Type, tx *transaction.Transaction) { +func (drng *Instance) Dispatch(subtype header.Type, tx *transaction.Transaction) { switch subtype { case header.CollectiveBeaconType(): - //do stuff + // process collectiveBeacon + if err := collectiveBeacon.ProcessTransaction(drng.State, drng.Events.CollectiveBeacon, tx); err != nil { + return + } + // trigger RandomnessEvent + drng.Events.Randomness.Trigger(drng.State.Randomness()) + default: //do other stuff } diff --git a/packages/binary/drng/drng.go b/packages/binary/drng/drng.go index 24c17ba20cbc3c015ee0e65a534b1a243552db8b..b2123a221f8964584ab7858c514cdbfbc334e058 100644 --- a/packages/binary/drng/drng.go +++ b/packages/binary/drng/drng.go @@ -2,16 +2,16 @@ package drng import ( "github.com/iotaledger/goshimmer/packages/binary/drng/state" - "github.com/iotaledger/hive.go/events" ) type Instance struct { State *state.State - Events *events.Event + Events *Event } -func New() *Instance { +func New(setters ...state.Option) *Instance { return &Instance{ - State: state.New(), + State: state.New(setters...), + Events: NewEvent(), } } diff --git a/packages/binary/drng/events.go b/packages/binary/drng/events.go index f0532f4717e1ab6dec297fe058cf86279ae3106d..8945d915bbd30b6acbbcba5679cd3c4771133098 100644 --- a/packages/binary/drng/events.go +++ b/packages/binary/drng/events.go @@ -1,15 +1,23 @@ package drng import ( + "github.com/iotaledger/goshimmer/packages/binary/drng/state" + cbEvents "github.com/iotaledger/goshimmer/packages/binary/drng/subtypes/collectiveBeacon/events" "github.com/iotaledger/hive.go/events" ) -var Events = struct { - NewRandomness *events.Event -}{ - NewRandomness: events.NewEvent(transactionCaller), +type Event struct { + CollectiveBeacon cbEvents.CollectiveBeacon + Randomness *events.Event } -func transactionCaller(handler interface{}, params ...interface{}) { - //handler.(func(*value_transaction.ValueTransaction))(params[0].(*value_transaction.ValueTransaction)) +func NewEvent() *Event { + return &Event{ + CollectiveBeacon: cbEvents.NewCollectiveBeaconEvent(), + Randomness: events.NewEvent(randomnessReceived), + } +} + +func randomnessReceived(handler interface{}, params ...interface{}) { + handler.(func(*state.Randomness))(params[0].(*state.Randomness)) } diff --git a/packages/binary/drng/state/options.go b/packages/binary/drng/state/options.go index 0bdcde9e319a7655955f53b7afbaf32043ca06b1..357c2eb67ca2ecd8b35d08a296b3fdbd50be1b72 100644 --- a/packages/binary/drng/state/options.go +++ b/packages/binary/drng/state/options.go @@ -5,17 +5,17 @@ type Options struct { Randomness *Randomness } -type option func(*Options) +type Option func(*Options) // SetCommittee sets the initial committee -func SetCommittee(c *Committee) option { +func SetCommittee(c *Committee) Option { return func(args *Options) { args.Committee = c } } // SetRandomness sets the initial randomness -func SetRandomness(r *Randomness) option { +func SetRandomness(r *Randomness) Option { return func(args *Options) { args.Randomness = r } diff --git a/packages/binary/drng/state/state.go b/packages/binary/drng/state/state.go index 5c66c0ed46305e86e408178a79866c7ebfc6b9eb..c474638475038ca4cf3079b72e086b80f3844ec8 100644 --- a/packages/binary/drng/state/state.go +++ b/packages/binary/drng/state/state.go @@ -27,7 +27,7 @@ type State struct { mutex sync.RWMutex } -func New(setters ...option) *State { +func New(setters ...Option) *State { args := &Options{} for _, setter := range setters { diff --git a/packages/binary/drng/subtypes/collectiveBeacon/collective_beacon.go b/packages/binary/drng/subtypes/collectiveBeacon/collective_beacon.go index e9998f78360772e0c4333aea9c4d90021901284b..852cda3c1f75e01d73277e33834667f4a62e6739 100644 --- a/packages/binary/drng/subtypes/collectiveBeacon/collective_beacon.go +++ b/packages/binary/drng/subtypes/collectiveBeacon/collective_beacon.go @@ -6,23 +6,99 @@ import ( "github.com/drand/drand/beacon" "github.com/drand/drand/key" + "github.com/iotaledger/goshimmer/packages/binary/drng/state" + "github.com/iotaledger/goshimmer/packages/binary/drng/subtypes/collectiveBeacon/events" "github.com/iotaledger/goshimmer/packages/binary/drng/subtypes/collectiveBeacon/payload" + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" + "github.com/iotaledger/goshimmer/packages/binary/signature/ed25119" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" ) -// VerifyCollectiveBeacon checks the current signature against the distributed public key -func VerifyCollectiveBeacon(data *payload.Payload) error { +// ProcessTransaction performs the following tasks: +// 1 - parse as CollectiveBeaconType +// 2 - trigger CollectiveBeaconEvent +// 3 - verify that we have a valid random +// 4 - update drng state +func ProcessTransaction(drng *state.State, event events.CollectiveBeacon, tx *transaction.Transaction) error { + // 1 - parse as CollectiveBeaconType + marshalUtil := marshalutil.New(tx.GetPayload().Bytes()) + parsedPayload, err := payload.Parse(marshalUtil) + if err != nil { + return err + } + + // 2 - trigger CollectiveBeaconEvent + cbEvent := &events.CollectiveBeaconEvent{ + IssuerPublicKey: tx.IssuerPublicKey(), + Timestamp: tx.IssuingTime(), + InstanceID: parsedPayload.Instance(), + Round: parsedPayload.Round(), + PrevSignature: parsedPayload.PrevSignature(), + Signature: parsedPayload.Signature(), + Dpk: parsedPayload.DistributedPK(), + } + event.Trigger(cbEvent) + + // 3 - verify that we have a valid random + if err := VerifyCollectiveBeacon(drng, cbEvent); err != nil { + //TODO: handle error + return err + } + + // 4 - update drng state + randomness, err := GetRandomness(cbEvent.Signature) + if err != nil { + //TODO: handle error + return err + } + newRandomness := &state.Randomness{ + Round: cbEvent.Round, + Randomness: randomness, + Timestamp: cbEvent.Timestamp, + } + drng.SetRandomness(newRandomness) + return nil +} + +// VerifyCollectiveBeacon verifies against a given state that +// the given CollectiveBeaconEvent contains a valid beacon +func VerifyCollectiveBeacon(state *state.State, data *events.CollectiveBeaconEvent) error { + + if err := verifyIssuer(state, data.IssuerPublicKey); err != nil { + return err + } + + if err := verifySignature(data); err != nil { + return err + } + + return nil +} + +// verifyIssuer checks the given issuer is a member of the committee +func verifyIssuer(state *state.State, issuer ed25119.PublicKey) error { + for _, member := range state.Committee().Identities { + if member == issuer { + return nil + } + } + return errors.New("Invalid Issuer") +} + +// verifySignature checks the current signature against the distributed public key +func verifySignature(data *events.CollectiveBeaconEvent) error { if data == nil { return errors.New("nil data") } dpk := key.KeyGroup.Point() - if err := dpk.UnmarshalBinary(data.DistributedPK()); err != nil { + if err := dpk.UnmarshalBinary(data.Dpk); err != nil { return err } - msg := beacon.Message(data.PrevSignature(), data.Round()) + msg := beacon.Message(data.PrevSignature, data.Round) - if err := key.Scheme.VerifyRecovered(dpk, msg, data.Signature()); err != nil { + if err := key.Scheme.VerifyRecovered(dpk, msg, data.Signature); err != nil { return err } diff --git a/packages/binary/drng/subtypes/collectiveBeacon/collective_beacon_test.go b/packages/binary/drng/subtypes/collectiveBeacon/collective_beacon_test.go index 6eeb237c2544014d0cfddc920f42e2ab1c0bbd51..7dd2332d5408c211c2ad89c79a1f405a3afe1e6e 100644 --- a/packages/binary/drng/subtypes/collectiveBeacon/collective_beacon_test.go +++ b/packages/binary/drng/subtypes/collectiveBeacon/collective_beacon_test.go @@ -4,12 +4,12 @@ import ( "encoding/hex" "testing" - "github.com/iotaledger/goshimmer/packages/binary/drng/subtypes/collectiveBeacon/payload" + "github.com/iotaledger/goshimmer/packages/binary/drng/subtypes/collectiveBeacon/events" "github.com/stretchr/testify/require" ) var ( - payloadTest *payload.Payload + eventTest *events.CollectiveBeaconEvent prevSignatureTest []byte signatureTest []byte dpkTest []byte @@ -19,18 +19,24 @@ func init() { prevSignatureTest, _ = hex.DecodeString("ae9ba6d1445bffea8e66cb7d28fe5924e0a8d31b11b62a8710204e56e1ba84bc3694a3033e5793fcee6e75e956e5da3016cd0e22aa46fa419cd06343a7ff9d1e9c5c08f660f0bdec099e97ef99f470bb8c607ce9667a165e9caa474710f62ffd") signatureTest, _ = hex.DecodeString("8dee56fae60dcad960f7176d0813d5415b930cf6e20c299ec2c2dfc5f2ad4903916fd462ba1abf5c32a5bfd94dcc8eba062d011a548d99df7fa1e3bbbc9a0455663d60f6ccc736c1d5b6de727dbe4427e21fb660925518be386265913f447c94") dpkTest, _ = hex.DecodeString("a02fcd15edd52c8e134027491a43b597505b466d1679e88f70f927e57c45a93ae0765ff02fc2d015e3a02fd8748e2103") - payloadTest = payload.New(1, 1, prevSignatureTest, signatureTest, dpkTest) + eventTest = &events.CollectiveBeaconEvent{ + InstanceID: 1, + Round: 1, + PrevSignature: prevSignatureTest, + Signature: signatureTest, + Dpk: dpkTest, + } } -func TestVerifyCollectiveBeacon(t *testing.T) { +func TestVerifySignature(t *testing.T) { //payload := dkgShares(t, 5, 3) - err := VerifyCollectiveBeacon(payloadTest) + err := verifySignature(eventTest) require.NoError(t, err) } func TestGetRandomness(t *testing.T) { //payload := dkgShares(t, 5, 3) - _, err := GetRandomness(payloadTest.Signature()) + _, err := GetRandomness(eventTest.Signature) require.NoError(t, err) } diff --git a/packages/binary/drng/subtypes/collectiveBeacon/events/events.go b/packages/binary/drng/subtypes/collectiveBeacon/events/events.go new file mode 100644 index 0000000000000000000000000000000000000000..bb05a6d136169df5f456c53ef8a0fd3c88b06a9f --- /dev/null +++ b/packages/binary/drng/subtypes/collectiveBeacon/events/events.go @@ -0,0 +1,28 @@ +package events + +import ( + "time" + + "github.com/iotaledger/goshimmer/packages/binary/signature/ed25119" + "github.com/iotaledger/hive.go/events" +) + +type CollectiveBeacon = *events.Event + +func NewCollectiveBeaconEvent() *events.Event { + return events.NewEvent(collectiveBeaconReceived) +} + +type CollectiveBeaconEvent struct { + IssuerPublicKey ed25119.PublicKey // public key of the issuer + Timestamp time.Time // timestamp when the beacon was issued + InstanceID uint32 // instanceID of the beacon + Round uint64 // round of the current beacon + PrevSignature []byte // collective signature of the previous beacon + Signature []byte // collective signature of the current beacon + Dpk []byte // distributed public key +} + +func collectiveBeaconReceived(handler interface{}, params ...interface{}) { + handler.(func(*CollectiveBeaconEvent))(params[0].(*CollectiveBeaconEvent)) +} diff --git a/packages/binary/drng/subtypes/collectiveBeacon/events/events_test.go b/packages/binary/drng/subtypes/collectiveBeacon/events/events_test.go new file mode 100644 index 0000000000000000000000000000000000000000..101655a60097e79a267c633bf0d92e9b21954b74 --- /dev/null +++ b/packages/binary/drng/subtypes/collectiveBeacon/events/events_test.go @@ -0,0 +1,27 @@ +package events + +import ( + "testing" + "time" + + "github.com/iotaledger/hive.go/events" + "github.com/stretchr/testify/require" +) + +func TestCollectiveBeaconEvent(t *testing.T) { + var cbReceived *CollectiveBeaconEvent + + eventTest := NewCollectiveBeaconEvent() + + eventTest.Attach(events.NewClosure(func(cb *CollectiveBeaconEvent) { + cbReceived = cb + })) + + cbTriggered := &CollectiveBeaconEvent{ + Timestamp: time.Now(), + } + eventTest.Trigger(cbTriggered) + + require.Equal(t, cbTriggered, cbReceived) + +} diff --git a/packages/binary/tangle/model/transaction/transaction.go b/packages/binary/tangle/model/transaction/transaction.go index d24202247602394f82ddf028ba65bc452384f723..f9c22cab3ef70787f992cf9fd34d947d9a782986 100644 --- a/packages/binary/tangle/model/transaction/transaction.go +++ b/packages/binary/tangle/model/transaction/transaction.go @@ -163,6 +163,11 @@ func (transaction *Transaction) IssuingTime() time.Time { return transaction.issuingTime } +// IssuerPublicKey returns the issuer PublicKey of the transaction. +func (transaction *Transaction) IssuerPublicKey() ed25119.PublicKey { + return transaction.issuerPublicKey +} + // SequenceNumber returns the sequence number of this transaction. func (transaction *Transaction) SequenceNumber() uint64 { return transaction.sequenceNumber diff --git a/plugins/drng/plugin.go b/plugins/drng/plugin.go new file mode 100644 index 0000000000000000000000000000000000000000..176750b984a4b0425fddbe00daa9d01c0bca7df4 --- /dev/null +++ b/plugins/drng/plugin.go @@ -0,0 +1,41 @@ +package drng + +import ( + "github.com/iotaledger/goshimmer/packages/binary/drng" + "github.com/iotaledger/goshimmer/packages/binary/drng/payload" + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata" + "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/hive.go/events" + "github.com/iotaledger/hive.go/node" +) + +const name = "DRNG" // name of the plugin + +var PLUGIN = node.NewPlugin(name, node.Enabled, configure, run) + +var Instance *drng.Instance + +func configure(*node.Plugin) { + Instance = drng.New() + configureEvents() +} + +func run(*node.Plugin) {} + +func configureEvents() { + tangle.Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { + cachedTransactionMetadata.Release() + + cachedTransaction.Consume(func(transaction *transaction.Transaction) { + marshalUtil := marshalutil.New(transaction.GetPayload().Bytes()) + parsedPayload, err := payload.Parse(marshalUtil) + if err != nil { + return + } + Instance.Dispatch(parsedPayload.SubType(), transaction) + }) + })) + +}