diff --git a/packages/chain/chainmanager/chain_manager.go b/packages/chain/chainmanager/chain_manager.go index 8c26c2a3be..0d32b53e17 100644 --- a/packages/chain/chainmanager/chain_manager.go +++ b/packages/chain/chainmanager/chain_manager.go @@ -76,13 +76,13 @@ package chainmanager import ( "errors" "fmt" + "slices" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/samber/lo" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/log" - "github.com/iotaledger/wasp/v2/clients/iota-go/iotago" "github.com/iotaledger/wasp/v2/clients/iota-go/iotasigner" "github.com/iotaledger/wasp/v2/packages/chain/committeelog" @@ -186,7 +186,7 @@ type committeeLogInst struct { committeeAddr cryptolib.Address dkShare tcrypto.DKShare gpaInstance gpa.GPA - pendingMsgs []gpa.Message + pendingMsgs []gpa.MessageOut } type ChainMgr struct { @@ -272,7 +272,7 @@ func (cmi *ChainMgr) AsGPA() gpa.GPA { } // Input implements the gpa.GPA interface. -func (cmi *ChainMgr) Input(input gpa.Input) gpa.OutMessages { +func (cmi *ChainMgr) Input(input gpa.Input) []gpa.MessageOut { switch input := input.(type) { case *inputAnchorConfirmed: return cmi.handleInputAnchorConfirmed(input) @@ -291,12 +291,12 @@ func (cmi *ChainMgr) Input(input gpa.Input) gpa.OutMessages { } // Message implements the gpa.GPA interface. -func (cmi *ChainMgr) Message(msg gpa.Message) gpa.OutMessages { - switch msg := msg.(type) { +func (cmi *ChainMgr) Message(msg gpa.MessageIn) []gpa.MessageOut { + switch msg.Payload.(type) { case *msgCommitteeLog: - return cmi.handleMsgCommitteeLog(msg) + return cmi.handleMsgCommitteeLog(gpa.AsTypedMessageIn[*msgCommitteeLog](msg)) case *msgBlockProduced: - return cmi.handleMsgBlockProduced(msg) + return cmi.handleMsgBlockProduced(gpa.AsTypedMessageIn[*msgBlockProduced](msg)) } panic(fmt.Errorf("unexpected message %T: %+v", msg, msg)) } @@ -310,13 +310,13 @@ func (cmi *ChainMgr) Message(msg gpa.Message) gpa.OutMessages { // > Send Suspend to Last Active CommitteeLog; HandleCommitteeLogOutput(LatestActiveCmt) // > Set LatestActiveCmt <- NIL // > Set NeedConsensus <- NIL -func (cmi *ChainMgr) handleInputAnchorConfirmed(input *inputAnchorConfirmed) gpa.OutMessages { +func (cmi *ChainMgr) handleInputAnchorConfirmed(input *inputAnchorConfirmed) []gpa.MessageOut { cmi.log.LogDebugf("handleInputAnchorConfirmed: %+v", input) // // > Set LatestConfirmedAnchor <- ConfirmedAnchor vsaTip, vsaUpdated := cmi.varAccessNodeState.BlockConfirmed(input.anchor) cmi.latestConfirmedAnchor = input.anchor - msgs := gpa.NoMessages() + var msgs []gpa.MessageOut committeeLog, err := cmi.ensureCommitteeLog(*input.stateController) // TODO: input.stateController.Key() if errors.Is(err, ErrNotInCommittee) { // > IF this node is in the committee THEN ... ELSE @@ -325,7 +325,7 @@ func (cmi *ChainMgr) handleInputAnchorConfirmed(input *inputAnchorConfirmed) gpa // > Set LatestActiveCmt <- NIL // > Set NeedConsensus <- NIL if cmi.latestActiveCommittee != nil { - msgs.AddAll(cmi.suspendCommittee(cmi.latestActiveCommittee)) + msgs = slices.Concat(msgs, cmi.suspendCommittee(cmi.latestActiveCommittee)) cmi.committeeUpdatedCB(nil) cmi.latestActiveCommittee = nil } @@ -343,7 +343,7 @@ func (cmi *ChainMgr) handleInputAnchorConfirmed(input *inputAnchorConfirmed) gpa } // > IF this node is in the committee THEN // > Pass it to the corresponding CommitteeLog; HandleCommitteeLogOutput. - msgs.AddAll(cmi.handleCommitteeLogOutput( + msgs = slices.Concat(msgs, cmi.handleCommitteeLogOutput( committeeLog, committeeLog.gpaInstance.Input(committeelog.NewInputAnchorConfirmed(input.anchor)), )) @@ -356,7 +356,7 @@ func (cmi *ChainMgr) handleInputAnchorConfirmed(input *inputAnchorConfirmed) gpa // > Forward it to ChainMgr; HandleCommitteeLogOutput. // > ELSE // > NOP // Anchor has to be received as Confirmed Anchor. -func (cmi *ChainMgr) handleInputChainTxPublishResult(input *inputChainTxPublishResult) gpa.OutMessages { +func (cmi *ChainMgr) handleInputChainTxPublishResult(input *inputChainTxPublishResult) []gpa.MessageOut { cmi.log.LogDebugf("handleInputChainTxPublishResult: %+v", input) // > Clear the TX from the NeedPublishTX variable. if cmi.needPublishTX.Has(input.txDigest.HashValue()) { @@ -366,13 +366,13 @@ func (cmi *ChainMgr) handleInputChainTxPublishResult(input *inputChainTxPublishR if input.confirmed { // > If result.confirmed = false THEN ... ELSE // > NOP // Anchor has to be received as Confirmed Anchor. // TODO: Not true, anymore. - return cmi.withCommitteeLog(input.committeeAddr, func(cl gpa.GPA) gpa.OutMessages { + return cmi.withCommitteeLog(input.committeeAddr, func(cl gpa.GPA) []gpa.MessageOut { return cl.Input(committeelog.NewInputConsensusOutputConfirmed(input.anchor, input.logIndex)) }) } // > If result.confirmed = false THEN // > Forward it to ChainMgr; HandleCommitteeLogOutput. - return cmi.withCommitteeLog(input.committeeAddr, func(cl gpa.GPA) gpa.OutMessages { + return cmi.withCommitteeLog(input.committeeAddr, func(cl gpa.GPA) []gpa.MessageOut { return cl.Input(committeelog.NewInputConsensusOutputRejected(input.anchor, input.logIndex)) }) } @@ -382,9 +382,9 @@ func (cmi *ChainMgr) handleInputChainTxPublishResult(input *inputChainTxPublishR // > Add ConsensusOutput.TX to NeedPublishTX // > Forward the message to the corresponding CommitteeLog; HandleCommitteeLogOutput. // > Update AccessNodes. -func (cmi *ChainMgr) handleInputConsensusOutputDone(input *inputConsensusOutputDone) gpa.OutMessages { +func (cmi *ChainMgr) handleInputConsensusOutputDone(input *inputConsensusOutputDone) []gpa.MessageOut { cmi.log.LogDebugf("handleInputConsensusOutputDone: %+v", input) - msgs := gpa.NoMessages() + var msgs []gpa.MessageOut baseAnchorRef := input.consensusResult.Transaction.FindInputByID(cmi.chainID.AsObjectID()) if baseAnchorRef == nil { @@ -407,7 +407,7 @@ func (cmi *ChainMgr) handleInputConsensusOutputDone(input *inputConsensusOutputD if lo.Contains(activeCommitteeNodes, activeAccessNodes[i]) { continue } - msgs.Add(NewMsgBlockProduced(cmi.nodeIDFromPubKey(activeAccessNodes[i]), input.consensusResult.Transaction, block)) + msgs = append(msgs, NewMsgBlockProduced(cmi.nodeIDFromPubKey(activeAccessNodes[i]), input.consensusResult.Transaction, block)) } } if !cmi.needPublishTX.Has(txDigest.HashValue()) { @@ -424,7 +424,7 @@ func (cmi *ChainMgr) handleInputConsensusOutputDone(input *inputConsensusOutputD // > Forward the message to the corresponding CommitteeLog; HandleCommitteeLogOutput. // // TODO: This event is not needed anymore. - // msgs.AddAll(cmi.withCommitteeLog(input.committeeAddr, func(cl gpa.GPA) gpa.OutMessages { + // msgs.AddAll(cmi.withCommitteeLog(input.committeeAddr, func(cl gpa.GPA) []gpa.MessageOut { // return cl.Input(cmtlog.NewInputConsensusOutputDone(input.logIndex, input.proposedBaseAnchor, input.consensusResult)) // })) return msgs @@ -432,46 +432,46 @@ func (cmi *ChainMgr) handleInputConsensusOutputDone(input *inputConsensusOutputD // > UPON Reception of Consensus Output/SKIP: // > Forward the message to the corresponding CommitteeLog; HandleCommitteeLogOutput. -func (cmi *ChainMgr) handleInputConsensusOutputSkip(input *inputConsensusOutputSkip) gpa.OutMessages { - return cmi.withCommitteeLog(input.committeeAddr, func(cl gpa.GPA) gpa.OutMessages { +func (cmi *ChainMgr) handleInputConsensusOutputSkip(input *inputConsensusOutputSkip) []gpa.MessageOut { + return cmi.withCommitteeLog(input.committeeAddr, func(cl gpa.GPA) []gpa.MessageOut { return cl.Input(committeelog.NewInputConsensusOutputSkip(input.logIndex)) }) } // > UPON Reception of Consensus Timeout: // > Forward the message to the corresponding CommitteeLog; HandleCommitteeLogOutput. -func (cmi *ChainMgr) handleInputConsensusTimeout(input *inputConsensusTimeout) gpa.OutMessages { +func (cmi *ChainMgr) handleInputConsensusTimeout(input *inputConsensusTimeout) []gpa.MessageOut { cmi.log.LogDebugf("handleInputConsensusTimeout: %+v", input) - return cmi.withCommitteeLog(input.committeeAddr, func(cl gpa.GPA) gpa.OutMessages { + return cmi.withCommitteeLog(input.committeeAddr, func(cl gpa.GPA) []gpa.MessageOut { return cl.Input(committeelog.NewInputConsensusTimeout(input.logIndex)) }) } -func (cmi *ChainMgr) handleInputCanPropose() gpa.OutMessages { +func (cmi *ChainMgr) handleInputCanPropose() []gpa.MessageOut { cmi.log.LogDebugf("handleInputCanPropose") - return cmi.withAllCommitteeLogs(func(cl gpa.GPA) gpa.OutMessages { + return cmi.withAllCommitteeLogs(func(cl gpa.GPA) []gpa.MessageOut { return cl.Input(committeelog.NewInputCanPropose()) }) } // > UPON Reception of CommitteeLog.NextLI message: // > Forward it to the corresponding CommitteeLog; HandleCommitteeLogOutput. -func (cmi *ChainMgr) handleMsgCommitteeLog(msg *msgCommitteeLog) gpa.OutMessages { +func (cmi *ChainMgr) handleMsgCommitteeLog(msg gpa.TypedMessageIn[*msgCommitteeLog]) []gpa.MessageOut { cmi.log.LogDebugf("handleMsgCommitteeLog: %+v", msg) - return cmi.withCommitteeLog(msg.committeeAddr, func(cl gpa.GPA) gpa.OutMessages { - return cl.Message(msg.wrapped) + return cmi.withCommitteeLog(msg.Payload.committeeAddr, func(cl gpa.GPA) []gpa.MessageOut { + return cl.Message(gpa.NewMessageIn(msg.Sender, msg.Payload.wrapped)) }) } -func (cmi *ChainMgr) handleMsgBlockProduced(msg *msgBlockProduced) gpa.OutMessages { +func (cmi *ChainMgr) handleMsgBlockProduced(msg gpa.TypedMessageIn[*msgBlockProduced]) []gpa.MessageOut { cmi.log.LogDebugf("handleMsgBlockProduced: %+v", msg) - vsaTip, vsaUpdated, l1Commitment := cmi.varAccessNodeState.BlockProduced(msg.tx) + vsaTip, vsaUpdated, l1Commitment := cmi.varAccessNodeState.BlockProduced(msg.Payload.tx) // // Save the block, if it matches all the signatures by the current committee. // This will save us a round-trip to query the block from the sender. if l1Commitment != nil { - if msg.block.L1Commitment().Equals(l1Commitment) { - cmi.savePreliminaryBlockCB(msg.block) + if msg.Payload.block.L1Commitment().Equals(l1Commitment) { + cmi.savePreliminaryBlockCB(msg.Payload.block) } else { cmi.log.LogWarnf("Received msgBlockProduced, but publishedAnchor.l1Commitment != block.l1Commitment.") } @@ -497,11 +497,10 @@ func (cmi *ChainMgr) handleMsgBlockProduced(msg *msgBlockProduced) gpa.OutMessag // > Suspend(LatestActiveCmt) // > Set LatestActiveCmt <- cmt // > Set NeedConsensus <- output.NeedConsensus -func (cmi *ChainMgr) handleCommitteeLogOutput(cli *committeeLogInst, cliMsgs gpa.OutMessages) gpa.OutMessages { +func (cmi *ChainMgr) handleCommitteeLogOutput(cli *committeeLogInst, cliMsgs []gpa.MessageOut) []gpa.MessageOut { // // > Wrap out messages. - msgs := gpa.NoMessages() - msgs.AddAll(cmi.wrapCommitteeLogMsgs(cli, cliMsgs)) + msgs := cmi.wrapCommitteeLogMsgs(cli, cliMsgs) outputUntyped := cli.gpaInstance.Output() // > IF cmt == LatestActiveCmt || LatestActiveCmt == NIL THEN // > Set LatestActiveCmt <- cmt @@ -523,7 +522,7 @@ func (cmi *ChainMgr) handleCommitteeLogOutput(cli *committeeLogInst, cliMsgs gpa return msgs } if !cmi.latestActiveCommittee.Equals(&cli.committeeAddr) { - msgs.AddAll(cmi.suspendCommittee(cmi.latestActiveCommittee)) + msgs = slices.Concat(msgs, cmi.suspendCommittee(cmi.latestActiveCommittee)) cmi.committeeUpdatedCB(cli.dkShare) cmi.latestActiveCommittee = &cli.committeeAddr } @@ -605,15 +604,13 @@ func (cmi *ChainMgr) StatusString() string { // TODO: Call it periodically. Show //////////////////////////////////////////////////////////////////////////////// // Helper functions. -func (cmi *ChainMgr) wrapCommitteeLogMsgs(cli *committeeLogInst, outMsgs gpa.OutMessages) gpa.OutMessages { - wrappedMsgs := gpa.NoMessages() - outMsgs.MustIterate(func(msg gpa.Message) { - wrappedMsgs.Add(NewMsgCommitteeLog(cli.committeeAddr, msg)) +func (cmi *ChainMgr) wrapCommitteeLogMsgs(cli *committeeLogInst, outMsgs []gpa.MessageOut) []gpa.MessageOut { + return lo.Map(outMsgs, func(msg gpa.MessageOut, _ int) gpa.MessageOut { + return gpa.NewMessageOut(msg.Recipient, NewMsgCommitteeLog(cli.committeeAddr, msg.Payload)) }) - return wrappedMsgs } -func (cmi *ChainMgr) suspendCommittee(committeeAddr *cryptolib.Address) gpa.OutMessages { +func (cmi *ChainMgr) suspendCommittee(committeeAddr *cryptolib.Address) []gpa.MessageOut { for _, cli := range cmi.committeeLogs { if !cli.committeeAddr.Equals(committeeAddr) { continue @@ -623,19 +620,19 @@ func (cmi *ChainMgr) suspendCommittee(committeeAddr *cryptolib.Address) gpa.OutM return nil } -func (cmi *ChainMgr) withCommitteeLog(committeeAddr cryptolib.Address, handler func(cl gpa.GPA) gpa.OutMessages) gpa.OutMessages { +func (cmi *ChainMgr) withCommitteeLog(committeeAddr cryptolib.Address, handler func(cl gpa.GPA) []gpa.MessageOut) []gpa.MessageOut { cli, err := cmi.ensureCommitteeLog(committeeAddr) if err != nil { cmi.log.LogWarnf("cannot find committee: %v", committeeAddr) return nil } - return gpa.NoMessages().AddAll(cmi.handleCommitteeLogOutput(cli, handler(cli.gpaInstance))) + return cmi.handleCommitteeLogOutput(cli, handler(cli.gpaInstance)) } -func (cmi *ChainMgr) withAllCommitteeLogs(handler func(cl gpa.GPA) gpa.OutMessages) gpa.OutMessages { - msgs := gpa.NoMessages() +func (cmi *ChainMgr) withAllCommitteeLogs(handler func(cl gpa.GPA) []gpa.MessageOut) []gpa.MessageOut { + var msgs []gpa.MessageOut for _, cli := range cmi.committeeLogs { - msgs.AddAll(cmi.handleCommitteeLogOutput(cli, handler(cli.gpaInstance))) + msgs = slices.Concat(msgs, cmi.handleCommitteeLogOutput(cli, handler(cli.gpaInstance))) } return msgs } @@ -687,7 +684,7 @@ func (cmi *ChainMgr) ensureCommitteeLog(committeeAddr cryptolib.Address) (*commi committeeAddr: committeeAddr, dkShare: dkShare, gpaInstance: clGPA, - pendingMsgs: []gpa.Message{}, + pendingMsgs: []gpa.MessageOut{}, } cmi.committeeLogs[committeeAddr.Key()] = cli return cli, nil diff --git a/packages/chain/chainmanager/msg.go b/packages/chain/chainmanager/msg.go index f2b43c1f61..cde3c09ca2 100644 --- a/packages/chain/chainmanager/msg.go +++ b/packages/chain/chainmanager/msg.go @@ -13,10 +13,10 @@ const ( msgTypeBlockProduced ) -func (cmi *ChainMgr) UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{ - msgTypeCommitteeLog: func() gpa.Message { return new(msgCommitteeLog) }, - msgTypeBlockProduced: func() gpa.Message { +func (cmi *ChainMgr) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{ + msgTypeCommitteeLog: func() gpa.MessagePayload { return new(msgCommitteeLog) }, + msgTypeBlockProduced: func() gpa.MessagePayload { msgBlock := new(msgBlockProduced) // TODO: Validate if we ever have different block implementations. diff --git a/packages/chain/chainmanager/msg_block_produced.go b/packages/chain/chainmanager/msg_block_produced.go index dc2c287c3a..34c8e6a88f 100644 --- a/packages/chain/chainmanager/msg_block_produced.go +++ b/packages/chain/chainmanager/msg_block_produced.go @@ -13,19 +13,17 @@ import ( // This message is used to inform access nodes on new blocks // produced so that they can update their active state faster. type msgBlockProduced struct { - gpa.BasicMessage tx *iotasigner.SignedTransaction `bcs:"export"` block state.Block `bcs:"export"` } -var _ gpa.Message = new(msgBlockProduced) +var _ gpa.MessagePayload = new(msgBlockProduced) -func NewMsgBlockProduced(recipient gpa.NodeID, tx *iotasigner.SignedTransaction, block state.Block) gpa.Message { - return &msgBlockProduced{ - BasicMessage: gpa.NewBasicMessage(recipient), - tx: tx, - block: block, - } +func NewMsgBlockProduced(recipient gpa.NodeID, tx *iotasigner.SignedTransaction, block state.Block) gpa.MessageOut { + return gpa.NewMessageOut(recipient, &msgBlockProduced{ + tx: tx, + block: block, + }) } func (msg *msgBlockProduced) MsgType() gpa.MessageType { diff --git a/packages/chain/chainmanager/msg_block_produced_test.go b/packages/chain/chainmanager/msg_block_produced_test.go index 66b37e5240..f76253eac0 100644 --- a/packages/chain/chainmanager/msg_block_produced_test.go +++ b/packages/chain/chainmanager/msg_block_produced_test.go @@ -5,7 +5,6 @@ import ( bcs "github.com/iotaledger/bcs-go" "github.com/iotaledger/wasp/v2/clients/iota-go/iotasigner/iotasignertest" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/state" "github.com/iotaledger/wasp/v2/packages/state/statetest" ) @@ -13,7 +12,6 @@ import ( func TestMsgBlockProducedSerialization(t *testing.T) { randomSignedTransaction := iotasignertest.RandomSignedTransaction() msg := &msgBlockProduced{ - gpa.BasicMessage{}, &randomSignedTransaction, statetest.RandomBlock(), } @@ -23,7 +21,6 @@ func TestMsgBlockProducedSerialization(t *testing.T) { }) msg = &msgBlockProduced{ - gpa.BasicMessage{}, &iotasignertest.TestSignedTransaction, statetest.TestBlock(), } diff --git a/packages/chain/chainmanager/msg_cmt_log.go b/packages/chain/chainmanager/msg_cmt_log.go index 01d5962f95..6b3ff6b8bb 100644 --- a/packages/chain/chainmanager/msg_cmt_log.go +++ b/packages/chain/chainmanager/msg_cmt_log.go @@ -13,12 +13,12 @@ import ( // is by CommitteeID, not by integer index. type msgCommitteeLog struct { committeeAddr cryptolib.Address - wrapped gpa.Message + wrapped gpa.MessagePayload } -var _ gpa.Message = new(msgCommitteeLog) +var _ gpa.MessagePayload = new(msgCommitteeLog) -func NewMsgCommitteeLog(committeeAddr cryptolib.Address, wrapped gpa.Message) gpa.Message { +func NewMsgCommitteeLog(committeeAddr cryptolib.Address, wrapped gpa.MessagePayload) gpa.MessagePayload { return &msgCommitteeLog{ committeeAddr: committeeAddr, wrapped: wrapped, @@ -33,16 +33,8 @@ func (msg *msgCommitteeLog) String() string { return fmt.Sprintf("{chainMgr.msgCommitteeLog, committeeAddr=%v, wrapped=%+v}", msg.committeeAddr.String(), msg.wrapped) } -func (msg *msgCommitteeLog) Recipient() gpa.NodeID { - return msg.wrapped.Recipient() -} - -func (msg *msgCommitteeLog) SetSender(sender gpa.NodeID) { - msg.wrapped.SetSender(sender) -} - func (msg *msgCommitteeLog) MarshalBCS(e *bcs.Encoder) error { - wrappedBytes, err := gpa.MarshalMessage(msg.wrapped) + wrappedBytes, err := gpa.MarshalPayload(msg.wrapped) if err != nil { return fmt.Errorf("marshaling wrapped message: %w", err) } @@ -58,7 +50,7 @@ func (msg *msgCommitteeLog) UnmarshalBCS(d *bcs.Decoder) error { wrappedBytes := bcs.Decode[[]byte](d) var err error - msg.wrapped, err = committeelog.UnmarshalMessage(wrappedBytes) + msg.wrapped, err = committeelog.UnmarshalPayload(wrappedBytes) if err != nil { return fmt.Errorf("unmarshaling wrapped message: %w", err) } diff --git a/packages/chain/chainmanager/msg_cmt_log_test.go b/packages/chain/chainmanager/msg_cmt_log_test.go index f9f9958c8e..7c1df9eca8 100644 --- a/packages/chain/chainmanager/msg_cmt_log_test.go +++ b/packages/chain/chainmanager/msg_cmt_log_test.go @@ -7,7 +7,6 @@ import ( bcs "github.com/iotaledger/bcs-go" "github.com/iotaledger/wasp/v2/packages/chain/committeelog" "github.com/iotaledger/wasp/v2/packages/cryptolib" - "github.com/iotaledger/wasp/v2/packages/gpa" ) func TestMsgCommitteeLogSerialization(t *testing.T) { @@ -15,7 +14,6 @@ func TestMsgCommitteeLogSerialization(t *testing.T) { msg := &msgCommitteeLog{ *address, &committeelog.MsgNextLogIndex{ - BasicMessage: gpa.BasicMessage{}, NextLogIndex: committeelog.LogIndex(rand.Int31()), PleaseRepeat: false, }, @@ -26,7 +24,6 @@ func TestMsgCommitteeLogSerialization(t *testing.T) { msg = &msgCommitteeLog{ *cryptolib.TestAddress, &committeelog.MsgNextLogIndex{ - BasicMessage: gpa.BasicMessage{}, NextLogIndex: committeelog.LogIndex(1234567890), PleaseRepeat: false, }, diff --git a/packages/chain/committeelog/cmt_log.go b/packages/chain/committeelog/cmt_log.go index 41d39f1688..3783275688 100644 --- a/packages/chain/committeelog/cmt_log.go +++ b/packages/chain/committeelog/cmt_log.go @@ -22,6 +22,7 @@ package committeelog import ( "errors" "fmt" + "slices" "github.com/iotaledger/hive.go/log" @@ -136,11 +137,11 @@ func New( log.LogDebugf("VarConsInsts: Output received, %v", out) cl.output = out }, log.NewChildLogger("VCI")) - cl.varLogIndex = NewVarLogIndex(nodeIDs, n, f, prevLI, func(li LogIndex) gpa.OutMessages { + cl.varLogIndex = NewVarLogIndex(nodeIDs, n, f, prevLI, func(li LogIndex) []gpa.MessageOut { log.LogDebugf("VarLogIndex: Output received, %v", li) return cl.varConsInsts.LatestSeenLI(li, cl.varLogIndex.ConsensusStarted) }, cclMetrics, log.NewChildLogger("VLI")) - cl.varLocalView = NewVarLocalView(pipeliningLimit, func(ao *isc.StateAnchor) gpa.OutMessages { + cl.varLocalView = NewVarLocalView(pipeliningLimit, func(ao *isc.StateAnchor) []gpa.MessageOut { log.LogDebugf("VarLocalView: Output received, %v", ao) return cl.varConsInsts.LatestL1Anchor(ao, cl.varLogIndex.ConsensusStarted) }, log.NewChildLogger("VLV")) @@ -154,7 +155,7 @@ func (cl *CommitteeLog) AsGPA() gpa.GPA { } // Input implements the gpa.GPA interface. -func (cl *CommitteeLog) Input(input gpa.Input) gpa.OutMessages { +func (cl *CommitteeLog) Input(input gpa.Input) []gpa.MessageOut { switch input.(type) { case *inputCanPropose: break // Don't log, its periodic. @@ -182,51 +183,50 @@ func (cl *CommitteeLog) Input(input gpa.Input) gpa.OutMessages { } // Message implements the gpa.GPA interface. -func (cl *CommitteeLog) Message(msg gpa.Message) gpa.OutMessages { - msgNLI, ok := msg.(*MsgNextLogIndex) +func (cl *CommitteeLog) Message(msg gpa.MessageIn) []gpa.MessageOut { + _, ok := msg.Payload.(*MsgNextLogIndex) if !ok { cl.log.LogWarnf("dropping unexpected message %T: %+v", msg, msg) return nil } - return cl.handleMsgNextLogIndex(msgNLI) + return cl.handleMsgNextLogIndex(gpa.AsTypedMessageIn[*MsgNextLogIndex](msg)) } // The latest anchor object's version confirmed at the L1. -func (cl *CommitteeLog) handleInputAnchorConfirmed(input *inputAnchorConfirmed) gpa.OutMessages { +func (cl *CommitteeLog) handleInputAnchorConfirmed(input *inputAnchorConfirmed) []gpa.MessageOut { cl.suspended = false return cl.varLocalView.AnchorConfirmed(input.anchor) } // Consensus completed with a decision to SKIP/⊥. -func (cl *CommitteeLog) handleInputConsensusOutputSkip(input *inputConsensusOutputSkip) gpa.OutMessages { +func (cl *CommitteeLog) handleInputConsensusOutputSkip(input *inputConsensusOutputSkip) []gpa.MessageOut { return cl.varConsInsts.ConsOutputSkip(input.logIndex, cl.varLogIndex.ConsensusStarted) } // Consensus has decided, produced a TX and it is now confirmed by L1. -func (cl *CommitteeLog) handleInputConsensusOutputConfirmed(input *inputConsensusOutputConfirmed) gpa.OutMessages { +func (cl *CommitteeLog) handleInputConsensusOutputConfirmed(input *inputConsensusOutputConfirmed) []gpa.MessageOut { return cl.varConsInsts.ConsOutputDone(input.logIndex, input.nextAnchor, cl.varLogIndex.ConsensusStarted) } // Consensus has decided, produced a TX but it was rejected by L1. -func (cl *CommitteeLog) handleInputConsensusOutputRejected(input *inputConsensusOutputRejected) gpa.OutMessages { +func (cl *CommitteeLog) handleInputConsensusOutputRejected(input *inputConsensusOutputRejected) []gpa.MessageOut { return cl.varConsInsts.ConsOutputSkip(input.logIndex, cl.varLogIndex.ConsensusStarted) // This will cause proposal of our latest L1 Anchor. } // Consensus tries to decide for too long. Maybe quorum assumption has been violated. -func (cl *CommitteeLog) handleInputConsensusTimeout(input *inputConsensusTimeout) gpa.OutMessages { +func (cl *CommitteeLog) handleInputConsensusTimeout(input *inputConsensusTimeout) []gpa.MessageOut { return cl.varConsInsts.ConsOutputTimeout(input.logIndex, cl.varLogIndex.ConsensusStarted) } -func (cl *CommitteeLog) handleInputCanPropose() gpa.OutMessages { - msgs := gpa.NoMessages() - msgs.AddAll(cl.varConsInsts.Tick(cl.varLogIndex.ConsensusStarted)) +func (cl *CommitteeLog) handleInputCanPropose() []gpa.MessageOut { + msgs := cl.varConsInsts.Tick(cl.varLogIndex.ConsensusStarted) if cl.first && cl.output != nil && len(cl.output) > 0 { // This is a workaround for sending initial NextLI messages on boot. cl.first = false for li := range cl.output { cl.log.LogDebugf("Sending initial NextLI messages for LI=%v", li) - msgs.AddAll(cl.varLogIndex.ConsensusStarted(li)) + msgs = slices.Concat(msgs, cl.varLogIndex.ConsensusStarted(li)) } return msgs } @@ -239,7 +239,7 @@ func (cl *CommitteeLog) handleInputSuspend() { // > ON Reception of ⟨NextLI, •⟩ message: // > ... -func (cl *CommitteeLog) handleMsgNextLogIndex(msg *MsgNextLogIndex) gpa.OutMessages { +func (cl *CommitteeLog) handleMsgNextLogIndex(msg gpa.TypedMessageIn[*MsgNextLogIndex]) []gpa.MessageOut { return cl.varLogIndex.MsgNextLogIndexReceived(msg) } diff --git a/packages/chain/committeelog/msg.go b/packages/chain/committeelog/msg.go index 5480844c3c..d246403bcb 100644 --- a/packages/chain/committeelog/msg.go +++ b/packages/chain/committeelog/msg.go @@ -11,12 +11,12 @@ const ( msgTypeNextLogIndex gpa.MessageType = iota ) -func (cl *CommitteeLog) UnmarshalMessage(data []byte) (gpa.Message, error) { - return UnmarshalMessage(data) +func (cl *CommitteeLog) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return UnmarshalPayload(data) } -func UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{ - msgTypeNextLogIndex: func() gpa.Message { return new(MsgNextLogIndex) }, +func UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{ + msgTypeNextLogIndex: func() gpa.MessagePayload { return new(MsgNextLogIndex) }, }) } diff --git a/packages/chain/committeelog/msg_next_log_index.go b/packages/chain/committeelog/msg_next_log_index.go index a2befc5805..0c45827e20 100644 --- a/packages/chain/committeelog/msg_next_log_index.go +++ b/packages/chain/committeelog/msg_next_log_index.go @@ -25,17 +25,15 @@ const ( ) type MsgNextLogIndex struct { - gpa.BasicMessage NextLogIndex LogIndex // Proposal is to go to this LI without waiting for a consensus. Cause MsgNextLogIndexCause // Reason for the proposal. PleaseRepeat bool // If true, the receiver should resend its latest message back to the sender. } -var _ gpa.Message = new(MsgNextLogIndex) +var _ gpa.MessagePayload = new(MsgNextLogIndex) -func NewMsgNextLogIndex(recipient gpa.NodeID, nextLogIndex LogIndex, cause MsgNextLogIndexCause, pleaseRepeat bool) *MsgNextLogIndex { +func NewMsgNextLogIndex(nextLogIndex LogIndex, cause MsgNextLogIndexCause, pleaseRepeat bool) *MsgNextLogIndex { return &MsgNextLogIndex{ - BasicMessage: gpa.NewBasicMessage(recipient), NextLogIndex: nextLogIndex, Cause: cause, PleaseRepeat: pleaseRepeat, @@ -46,7 +44,6 @@ func NewMsgNextLogIndex(recipient gpa.NodeID, nextLogIndex LogIndex, cause MsgNe // We set pleaseResend to false to avoid accidental loops. func (msg *MsgNextLogIndex) AsResent() *MsgNextLogIndex { return &MsgNextLogIndex{ - BasicMessage: gpa.NewBasicMessage(msg.Recipient()), NextLogIndex: msg.NextLogIndex, Cause: msg.Cause, PleaseRepeat: false, @@ -59,7 +56,7 @@ func (msg *MsgNextLogIndex) MsgType() gpa.MessageType { func (msg *MsgNextLogIndex) String() string { return fmt.Sprintf( - "{MsgNextLogIndex[%v], sender=%v, nextLogIndex=%v, pleaseRepeat=%v", - msg.Cause, msg.Sender().ShortString(), msg.NextLogIndex, msg.PleaseRepeat, + "{MsgNextLogIndex[%v], nextLogIndex=%v, pleaseRepeat=%v", + msg.Cause, msg.NextLogIndex, msg.PleaseRepeat, ) } diff --git a/packages/chain/committeelog/msg_next_log_index_test.go b/packages/chain/committeelog/msg_next_log_index_test.go index da7493a0f3..db72181fc9 100644 --- a/packages/chain/committeelog/msg_next_log_index_test.go +++ b/packages/chain/committeelog/msg_next_log_index_test.go @@ -9,13 +9,11 @@ import ( bcs "github.com/iotaledger/bcs-go" "github.com/iotaledger/wasp/v2/packages/chain/committeelog" - "github.com/iotaledger/wasp/v2/packages/gpa" ) func TestMsgNextLogIndexSerialization(t *testing.T) { { msg := &committeelog.MsgNextLogIndex{ - gpa.BasicMessage{}, committeelog.LogIndex(rand.Int31()), committeelog.MsgNextLogIndexCauseStarted, false, @@ -25,7 +23,6 @@ func TestMsgNextLogIndexSerialization(t *testing.T) { } { msg := &committeelog.MsgNextLogIndex{ - gpa.BasicMessage{}, committeelog.LogIndex(758493), committeelog.MsgNextLogIndexCauseStarted, false, @@ -35,7 +32,6 @@ func TestMsgNextLogIndexSerialization(t *testing.T) { } { msg := &committeelog.MsgNextLogIndex{ - gpa.BasicMessage{}, committeelog.LogIndex(rand.Int31()), committeelog.MsgNextLogIndexCauseStarted, true, @@ -45,7 +41,6 @@ func TestMsgNextLogIndexSerialization(t *testing.T) { } { msg := &committeelog.MsgNextLogIndex{ - gpa.BasicMessage{}, committeelog.LogIndex(59329892), committeelog.MsgNextLogIndexCauseStarted, true, diff --git a/packages/chain/committeelog/quorum_counter.go b/packages/chain/committeelog/quorum_counter.go index 51b307b86f..e938de5a85 100644 --- a/packages/chain/committeelog/quorum_counter.go +++ b/packages/chain/committeelog/quorum_counter.go @@ -25,17 +25,17 @@ func NewQuorumCounter(msgCause MsgNextLogIndexCause, nodeIDs []gpa.NodeID, log l } } -func (qc *QuorumCounter) MaybeSendVote(li LogIndex) gpa.OutMessages { +func (qc *QuorumCounter) MaybeSendVote(li LogIndex) []gpa.MessageOut { if li <= qc.myLastVoteLI { return nil } qc.myLastVoteLI = li - msgs := gpa.NoMessages() + var msgs []gpa.MessageOut for _, nodeID := range qc.nodeIDs { _, haveMsgFrom := qc.maxPeerVotes[nodeID] // It might happen, that we rebooted and lost the state. - msg := NewMsgNextLogIndex(nodeID, li, qc.msgCause, !haveMsgFrom) + msg := NewMsgNextLogIndex(li, qc.msgCause, !haveMsgFrom) qc.lastSentMsgs[nodeID] = msg - msgs.Add(msg) + msgs = append(msgs, gpa.NewMessageOut(nodeID, msg)) } return msgs } @@ -44,25 +44,25 @@ func (qc *QuorumCounter) MyLastVote() LogIndex { return qc.myLastVoteLI } -func (qc *QuorumCounter) LastMessageForPeer(peer gpa.NodeID, msgs gpa.OutMessages) gpa.OutMessages { +func (qc *QuorumCounter) LastMessageForPeer(peer gpa.NodeID) []gpa.MessageOut { if msg, ok := qc.lastSentMsgs[peer]; ok { - msgs.Add(msg.AsResent()) + return []gpa.MessageOut{gpa.NewMessageOut(peer, msg.AsResent())} } - return msgs + return nil } -func (qc *QuorumCounter) VoteReceived(vote *MsgNextLogIndex) { - sender := vote.Sender() +func (qc *QuorumCounter) VoteReceived(vote gpa.TypedMessageIn[*MsgNextLogIndex]) { + sender := vote.Sender var prevPeerLI LogIndex if prevPeerNLI, ok := qc.maxPeerVotes[sender]; ok { prevPeerLI = prevPeerNLI.NextLogIndex } else { prevPeerLI = NilLogIndex() } - if prevPeerLI.AsUint32() >= vote.NextLogIndex.AsUint32() { + if prevPeerLI.AsUint32() >= vote.Payload.NextLogIndex.AsUint32() { return } - qc.maxPeerVotes[sender] = vote + qc.maxPeerVotes[sender] = vote.Payload } func (qc *QuorumCounter) HaveVoteFrom(from gpa.NodeID) bool { diff --git a/packages/chain/committeelog/quorum_counter_test.go b/packages/chain/committeelog/quorum_counter_test.go index 9555ed926c..f074c3a97a 100644 --- a/packages/chain/committeelog/quorum_counter_test.go +++ b/packages/chain/committeelog/quorum_counter_test.go @@ -23,10 +23,12 @@ func TestQuorumCounter(t *testing.T) { require.Equal(t, lin, qc.EnoughVotes(f+1)) - makeVote := func(from gpa.NodeID, li committeelog.LogIndex) *committeelog.MsgNextLogIndex { - vote := committeelog.NewMsgNextLogIndex(nodeIDs[0], li, committeelog.MsgNextLogIndexCauseStarted, false) - vote.SetSender(from) - return vote + makeVote := func(from gpa.NodeID, li committeelog.LogIndex) gpa.TypedMessageIn[*committeelog.MsgNextLogIndex] { + vote := committeelog.NewMsgNextLogIndex(li, committeelog.MsgNextLogIndexCauseStarted, false) + return gpa.TypedMessageIn[*committeelog.MsgNextLogIndex]{ + Sender: from, + Payload: vote, + } } qc.VoteReceived(makeVote(nodeIDs[0], li7)) diff --git a/packages/chain/committeelog/var_cons_insts.go b/packages/chain/committeelog/var_cons_insts.go index a765bf87cb..9d3d7d7fb4 100644 --- a/packages/chain/committeelog/var_cons_insts.go +++ b/packages/chain/committeelog/var_cons_insts.go @@ -3,6 +3,7 @@ package committeelog import ( "fmt" "maps" + "slices" "github.com/iotaledger/hive.go/log" @@ -10,7 +11,7 @@ import ( "github.com/iotaledger/wasp/v2/packages/isc" ) -type onLIInc = func(li LogIndex) gpa.OutMessages +type onLIInc = func(li LogIndex) []gpa.MessageOut // VarConsInsts implements the algorithm modeled in WaspChainCommitteeLogSUI.tla type VarConsInsts struct { @@ -54,13 +55,13 @@ func NewVarConsInsts( } // ConsOutputDone - Consensus at LI produced a TX. -func (vci *VarConsInsts) ConsOutputDone(li LogIndex, producedAnchor *isc.StateAnchor, cb onLIInc) gpa.OutMessages { +func (vci *VarConsInsts) ConsOutputDone(li LogIndex, producedAnchor *isc.StateAnchor, cb onLIInc) []gpa.MessageOut { vci.haveConsOut = true return vci.trySet(li.Next(), producedAnchor, cb) } // ConsOutputSkip - Consensus at LI terminate with a SKIP/⊥ decision. -func (vci *VarConsInsts) ConsOutputSkip(li LogIndex, cb onLIInc) gpa.OutMessages { +func (vci *VarConsInsts) ConsOutputSkip(li LogIndex, cb onLIInc) []gpa.MessageOut { vci.haveConsOut = true if vci.lastAnchor == nil { vci.lastLI = li.Next() // Will be set in LatestL1Anchor. @@ -70,14 +71,13 @@ func (vci *VarConsInsts) ConsOutputSkip(li LogIndex, cb onLIInc) gpa.OutMessages } // ConsOutputTimeout - Consensus at LI indicated a timeout. -func (vci *VarConsInsts) ConsOutputTimeout(li LogIndex, cb onLIInc) gpa.OutMessages { +func (vci *VarConsInsts) ConsOutputTimeout(li LogIndex, cb onLIInc) []gpa.MessageOut { return vci.trySet(li.Next(), nil, cb) } // LatestSeenLI - If we see consensus proposals from F+1 nodes at seenLI... -func (vci *VarConsInsts) LatestSeenLI(seenLI LogIndex, cb onLIInc) gpa.OutMessages { - msgs := gpa.NoMessages() - msgs.AddAll(vci.trySet(seenLI.Prev(), nil, cb)) +func (vci *VarConsInsts) LatestSeenLI(seenLI LogIndex, cb onLIInc) []gpa.MessageOut { + msgs := vci.trySet(seenLI.Prev(), nil, cb) if !vci.haveConsOut { // Still don't have the initial round succeeded, thus keep proposing the NIL. // A race condition is possible between receiving the next LI from the VarLogIndex, @@ -90,12 +90,12 @@ func (vci *VarConsInsts) LatestSeenLI(seenLI LogIndex, cb onLIInc) gpa.OutMessag } // LatestL1Anchor - Here we get the latest L1 state. -func (vci *VarConsInsts) LatestL1Anchor(ao *isc.StateAnchor, cb onLIInc) gpa.OutMessages { +func (vci *VarConsInsts) LatestL1Anchor(ao *isc.StateAnchor, cb onLIInc) []gpa.MessageOut { vci.lastAnchor = ao return vci.trySet(vci.lastLI, ao, cb) // Finish ConsOutputSkipBase, if pending. } -func (vci *VarConsInsts) Tick(cb onLIInc) gpa.OutMessages { +func (vci *VarConsInsts) Tick(cb onLIInc) []gpa.MessageOut { n := len(vci.delayed) last := vci.delayed[n-1] for i := n - 1; i > 0; i-- { @@ -108,7 +108,7 @@ func (vci *VarConsInsts) Tick(cb onLIInc) gpa.OutMessages { return vci.trySet(last, nil, cb) } -func (vci *VarConsInsts) trySet(li LogIndex, ao *isc.StateAnchor, cb onLIInc) gpa.OutMessages { +func (vci *VarConsInsts) trySet(li LogIndex, ao *isc.StateAnchor, cb onLIInc) []gpa.MessageOut { // // Is it outdated? if li < vci.minLI { @@ -124,12 +124,12 @@ func (vci *VarConsInsts) trySet(li LogIndex, ao *isc.StateAnchor, cb onLIInc) gp vci.lis[li] = ao // // Track the max. - msgs := gpa.NoMessages() + var msgs []gpa.MessageOut if li > vci.maxLI { vci.persistCB(li) vci.maxLI = li vci.minLI = MaxLogIndex(vci.minLI, vci.maxLI.Sub(vci.hist)) - msgs.AddAll(cb(li)) + msgs = slices.Concat(msgs, cb(li)) } // // Cleanup old instances. diff --git a/packages/chain/committeelog/var_localview.go b/packages/chain/committeelog/var_localview.go index 8fcba849ca..68f4ed0087 100644 --- a/packages/chain/committeelog/var_localview.go +++ b/packages/chain/committeelog/var_localview.go @@ -70,12 +70,12 @@ type VarLocalView struct { // Transactions that are ready to be posted. pendingTXes *shrinkingmap.ShrinkingMap[uint32, []*varLocalViewEntry] // Callback for the TIP changes. - tipUpdatedCB func(ao *isc.StateAnchor) gpa.OutMessages + tipUpdatedCB func(ao *isc.StateAnchor) []gpa.MessageOut // Just a logger. log log.Logger } -func NewVarLocalView(pipeliningLimit int, tipUpdatedCB func(ao *isc.StateAnchor) gpa.OutMessages, log log.Logger) *VarLocalView { +func NewVarLocalView(pipeliningLimit int, tipUpdatedCB func(ao *isc.StateAnchor) []gpa.MessageOut, log log.Logger) *VarLocalView { log.LogDebugf("NewVarLocalView, pipeliningLimit=%v", pipeliningLimit) return &VarLocalView{ latestTip: nil, @@ -86,12 +86,12 @@ func NewVarLocalView(pipeliningLimit int, tipUpdatedCB func(ao *isc.StateAnchor) } } -func (lvi *VarLocalView) AnchorConfirmed(confirmedAnchor *isc.StateAnchor) gpa.OutMessages { +func (lvi *VarLocalView) AnchorConfirmed(confirmedAnchor *isc.StateAnchor) []gpa.MessageOut { lvi.confirmedAnchor = confirmedAnchor return lvi.processIt() } -func (lvi *VarLocalView) TransactionProduced(logIndex LogIndex, consumedAnchor *isc.StateAnchor, tx *iotasigner.SignedTransaction) gpa.OutMessages { +func (lvi *VarLocalView) TransactionProduced(logIndex LogIndex, consumedAnchor *isc.StateAnchor, tx *iotasigner.SignedTransaction) []gpa.MessageOut { stateIndex := consumedAnchor.GetStateIndex() stateIndexEntries, _ := lvi.pendingTXes.GetOrCreate(stateIndex, func() []*varLocalViewEntry { return []*varLocalViewEntry{} }) contains := lo.ContainsBy(stateIndexEntries, func(entry *varLocalViewEntry) bool { @@ -108,7 +108,7 @@ func (lvi *VarLocalView) TransactionProduced(logIndex LogIndex, consumedAnchor * return lvi.processIt() } -func (lvi *VarLocalView) TransactionRejected(logIndex LogIndex) gpa.OutMessages { +func (lvi *VarLocalView) TransactionRejected(logIndex LogIndex) []gpa.MessageOut { lvi.pendingTXes.ForEach(func(stateIndex uint32, entries []*varLocalViewEntry) bool { entries = lo.Filter(entries, func(entry *varLocalViewEntry, index int) bool { return entry.logIndex != logIndex @@ -127,7 +127,7 @@ func (lvi *VarLocalView) StatusString() string { return fmt.Sprintf("{varLocalView: confirmedAnchor=%v, |pendingTxIndexes|=%v}", lvi.confirmedAnchor, lvi.pendingTXes.Size()) } -func (lvi *VarLocalView) processIt() gpa.OutMessages { +func (lvi *VarLocalView) processIt() []gpa.MessageOut { if lvi.confirmedAnchor == nil { lvi.updateVal(nil) return nil @@ -151,7 +151,7 @@ func (lvi *VarLocalView) processIt() gpa.OutMessages { return lvi.updateVal(lvi.confirmedAnchor) } -func (lvi *VarLocalView) updateVal(tip *isc.StateAnchor) gpa.OutMessages { +func (lvi *VarLocalView) updateVal(tip *isc.StateAnchor) []gpa.MessageOut { if tip == nil && lvi.latestTip == nil { return nil } diff --git a/packages/chain/committeelog/var_log_index.go b/packages/chain/committeelog/var_log_index.go index 57341bb85a..dacf1f654e 100644 --- a/packages/chain/committeelog/var_log_index.go +++ b/packages/chain/committeelog/var_log_index.go @@ -2,6 +2,7 @@ package committeelog import ( "fmt" + "slices" "github.com/samber/lo" @@ -19,7 +20,7 @@ type VarLogIndex struct { agreedLI LogIndex // LI for which we have N-F proposals (when reached, consensus starts, the LI is persisted). lastMsgs map[gpa.NodeID]*MsgNextLogIndex // Latest messages we have sent to other peers. qcStarted *QuorumCounter - outputCB func(li LogIndex) gpa.OutMessages + outputCB func(li LogIndex) []gpa.MessageOut metrics *metrics.ChainCommitteeLogMetrics log log.Logger } @@ -29,7 +30,7 @@ func NewVarLogIndex( n int, f int, persistedLI LogIndex, - outputCB func(li LogIndex) gpa.OutMessages, + outputCB func(li LogIndex) []gpa.MessageOut, metrics *metrics.ChainCommitteeLogMetrics, log log.Logger, ) *VarLogIndex { @@ -55,23 +56,23 @@ func (vli *VarLogIndex) StatusString() string { ) } -func (vli *VarLogIndex) ConsensusStarted(consensusLI LogIndex) gpa.OutMessages { +func (vli *VarLogIndex) ConsensusStarted(consensusLI LogIndex) []gpa.MessageOut { vli.log.LogDebugf("ConsensusStarted: consensusLI=%v", consensusLI) - msgs := gpa.NoMessages() - msgs.AddAll(vli.qcStarted.MaybeSendVote(consensusLI)) - msgs.AddAll(vli.tryOutputOnStarted()) - return msgs + return slices.Concat( + vli.qcStarted.MaybeSendVote(consensusLI), + vli.tryOutputOnStarted(), + ) } -func (vli *VarLogIndex) MsgNextLogIndexReceived(msg *MsgNextLogIndex) gpa.OutMessages { +func (vli *VarLogIndex) MsgNextLogIndexReceived(msg gpa.TypedMessageIn[*MsgNextLogIndex]) []gpa.MessageOut { vli.log.LogDebugf("MsgNextLogIndexReceived, %v", msg) - sender := msg.Sender() + sender := msg.Sender if !vli.knownNodeID(sender) { vli.log.LogWarnf("⊢ MsgNextLogIndex from unknown sender: %+v", msg) return nil } - switch msg.Cause { + switch msg.Payload.Cause { case MsgNextLogIndexCauseStarted: return vli.msgNextLogIndexOnStarted(msg) default: @@ -80,18 +81,18 @@ func (vli *VarLogIndex) MsgNextLogIndexReceived(msg *MsgNextLogIndex) gpa.OutMes } } -func (vli *VarLogIndex) msgNextLogIndexOnStarted(msg *MsgNextLogIndex) gpa.OutMessages { +func (vli *VarLogIndex) msgNextLogIndexOnStarted(msg gpa.TypedMessageIn[*MsgNextLogIndex]) []gpa.MessageOut { vli.qcStarted.VoteReceived(msg) return vli.tryOutputOnStarted() } -func (vli *VarLogIndex) tryOutputOnStarted() gpa.OutMessages { +func (vli *VarLogIndex) tryOutputOnStarted() []gpa.MessageOut { ali := vli.qcStarted.EnoughVotes(vli.f + 1) return vli.tryOutput(ali, MsgNextLogIndexCauseStarted) } // That's output for the consensus. We will start consensus instances with strictly increasing LIs with non-nil Anchors. -func (vli *VarLogIndex) tryOutput(li LogIndex, cause MsgNextLogIndexCause) gpa.OutMessages { +func (vli *VarLogIndex) tryOutput(li LogIndex, cause MsgNextLogIndexCause) []gpa.MessageOut { if li <= vli.agreedLI || li < vli.minLI { return nil } diff --git a/packages/chain/committeelog/var_log_index_test.go b/packages/chain/committeelog/var_log_index_test.go index 6a1292f1ae..13644639b8 100644 --- a/packages/chain/committeelog/var_log_index_test.go +++ b/packages/chain/committeelog/var_log_index_test.go @@ -23,17 +23,19 @@ func TestVarLogIndexV2Basic(t *testing.T) { initLI := committeelog.NilLogIndex().Next() // vliOut := committeelog.NilLogIndex() - vli := committeelog.NewVarLogIndex(nodeIDs, n, f, initLI, func(li committeelog.LogIndex) gpa.OutMessages { + vli := committeelog.NewVarLogIndex(nodeIDs, n, f, initLI, func(li committeelog.LogIndex) []gpa.MessageOut { vliOut = li return nil }, nil, log) // nextLI := initLI.Next() require.NotEqual(t, nextLI, vliOut) - nextLIMsg := committeelog.NewMsgNextLogIndex(nodeIDs[0], nextLI, committeelog.MsgNextLogIndexCauseStarted, false) + nextLIMsg := committeelog.NewMsgNextLogIndex(nextLI, committeelog.MsgNextLogIndexCauseStarted, false) for i := 0; i < n-f; i++ { - nextLIMsg.SetSender(nodeIDs[i]) - vli.MsgNextLogIndexReceived(nextLIMsg) + vli.MsgNextLogIndexReceived(gpa.TypedMessageIn[*committeelog.MsgNextLogIndex]{ + Sender: nodeIDs[i], + Payload: nextLIMsg, + }) } require.Equal(t, nextLI, vliOut) } @@ -48,7 +50,7 @@ func TestVarLogIndexV2Other(t *testing.T) { initLI := committeelog.NilLogIndex().Next() // vliOut := committeelog.NilLogIndex() - vli := committeelog.NewVarLogIndex(nodeIDs, n, f, initLI, func(li committeelog.LogIndex) gpa.OutMessages { + vli := committeelog.NewVarLogIndex(nodeIDs, n, f, initLI, func(li committeelog.LogIndex) []gpa.MessageOut { vliOut = li return nil }, nil, log) @@ -57,10 +59,12 @@ func TestVarLogIndexV2Other(t *testing.T) { li18 := committeelog.LogIndex(18) require.Equal(t, committeelog.NilLogIndex(), vliOut) - msgWithSender := func(sender gpa.NodeID, li committeelog.LogIndex) *committeelog.MsgNextLogIndex { - msg := committeelog.NewMsgNextLogIndex(nodeIDs[0], li, committeelog.MsgNextLogIndexCauseStarted, false) - msg.SetSender(sender) - return msg + msgWithSender := func(sender gpa.NodeID, li committeelog.LogIndex) gpa.TypedMessageIn[*committeelog.MsgNextLogIndex] { + msg := committeelog.NewMsgNextLogIndex(li, committeelog.MsgNextLogIndexCauseStarted, false) + return gpa.TypedMessageIn[*committeelog.MsgNextLogIndex]{ + Sender: sender, + Payload: msg, + } } vli.MsgNextLogIndexReceived(msgWithSender(nodeIDs[0], li15)) diff --git a/packages/chain/consensus/cons.go b/packages/chain/consensus/cons.go index 2dfcd36bf8..75b3731c8f 100644 --- a/packages/chain/consensus/cons.go +++ b/packages/chain/consensus/cons.go @@ -12,10 +12,10 @@ import ( "encoding/binary" "encoding/hex" "fmt" + "slices" "time" "fortio.org/safecast" - "github.com/minio/blake2b-simd" "github.com/samber/lo" "go.dedis.ch/kyber/v3" @@ -235,7 +235,7 @@ func (c *Consensus) AsGPA() gpa.GPA { return c.asGPA } -func (c *Consensus) Input(input gpa.Input) gpa.OutMessages { +func (c *Consensus) Input(input gpa.Input) []gpa.MessageOut { switch input := input.(type) { case *inputTimeData: // ignore this to filter out ridiculously excessive logging @@ -246,11 +246,12 @@ func (c *Consensus) Input(input gpa.Input) gpa.OutMessages { switch input := input.(type) { case *inputProposal: c.log.LogInfof("Consensus started, received %v", input.String()) - return gpa.NoMessages(). - AddAll(c.subNodeconn.HaveInputAnchor(input.baseAnchor)). - AddAll(c.subMempool.BaseAnchorReceived(input.baseAnchor)). - AddAll(c.subStateMgr.ProposedBaseAnchorReceived(input.baseAnchor)). - AddAll(c.subDistributedSignature.InitialInputReceived()) + return slices.Concat( + c.subNodeconn.HaveInputAnchor(input.baseAnchor), + c.subMempool.BaseAnchorReceived(input.baseAnchor), + c.subStateMgr.ProposedBaseAnchorReceived(input.baseAnchor), + c.subDistributedSignature.InitialInputReceived(), + ) case *inputRotateTo: // We can update the rotation address while consensus is running. // New value will be used, if decision has not been made yet. @@ -278,22 +279,22 @@ func (c *Consensus) Input(input gpa.Input) gpa.OutMessages { // Message implements the gpa.GPA interface. // Here we route all the messages. -func (c *Consensus) Message(msg gpa.Message) gpa.OutMessages { - switch msgT := msg.(type) { +func (c *Consensus) Message(msg gpa.MessageIn) []gpa.MessageOut { + switch msgT := msg.Payload.(type) { case *msgBLSPartialSig: - return c.subRND.BLSPartialSigReceived(msgT.Sender(), msgT.partialSig) + return c.subRND.BLSPartialSigReceived(msg.Sender, msgT.partialSig) case *gpa.WrappingMsg: - sub, subMsgs, err := c.msgWrapper.DelegateMessage(msgT) + sub, subMsgs, err := c.msgWrapper.DelegateMessage(gpa.AsTypedMessageIn[*gpa.WrappingMsg](msg)) if err != nil { c.log.LogWarnf("unexpected wrapped message: %w", err) return nil } - msgs := gpa.NoMessages().AddAll(subMsgs) + msgs := subMsgs switch msgT.Subsystem() { case subsystemTypeACS: - return msgs.AddAll(c.subACS.ACSOutputReceived(sub.Output())) + return slices.Concat(msgs, c.subACS.ACSOutputReceived(sub.Output())) case subsystemTypeDistributedSignature: - return msgs.AddAll(c.subDistributedSignature.DistributedSignatureReady(sub.Output())) + return slices.Concat(msgs, c.subDistributedSignature.DistributedSignatureReady(sub.Output())) default: c.log.LogWarnf("unexpected subsystem after check: %+v", msg) return nil @@ -323,7 +324,7 @@ func (c *Consensus) StatusString() string { //////////////////////////////////////////////////////////////////////////////// // MP -- MemPool -func (c *Consensus) uponMempoolProposalInputsReady(baseAnchor *isc.StateAnchor) gpa.OutMessages { +func (c *Consensus) uponMempoolProposalInputsReady(baseAnchor *isc.StateAnchor) []gpa.MessageOut { if baseAnchor == nil { // If the base Anchor is nil, we are not going to propose any requests. return c.subMempool.ProposalReceived([]*isc.RequestRef{}) @@ -332,20 +333,20 @@ func (c *Consensus) uponMempoolProposalInputsReady(baseAnchor *isc.StateAnchor) return nil } -func (c *Consensus) uponMempoolProposalReceived(requestRefs []*isc.RequestRef) gpa.OutMessages { +func (c *Consensus) uponMempoolProposalReceived(requestRefs []*isc.RequestRef) []gpa.MessageOut { c.output.NeedMempoolProposal = nil - msgs := gpa.NoMessages() - msgs.AddAll(c.subACS.MempoolRequestsReceived(requestRefs)) - msgs.AddAll(c.subNodeconn.HaveRequests()) - return msgs + return slices.Concat( + c.subACS.MempoolRequestsReceived(requestRefs), + c.subNodeconn.HaveRequests(), + ) } -func (c *Consensus) uponMempoolRequestsNeeded(requestRefs []*isc.RequestRef) gpa.OutMessages { +func (c *Consensus) uponMempoolRequestsNeeded(requestRefs []*isc.RequestRef) []gpa.MessageOut { c.output.NeedMempoolRequests = requestRefs return nil } -func (c *Consensus) uponMempoolRequestsReceived(requests []isc.Request) gpa.OutMessages { +func (c *Consensus) uponMempoolRequestsReceived(requests []isc.Request) []gpa.MessageOut { c.output.NeedMempoolRequests = nil return c.subVM.RequestsReceived(requests) } @@ -353,7 +354,7 @@ func (c *Consensus) uponMempoolRequestsReceived(requests []isc.Request) gpa.OutM //////////////////////////////////////////////////////////////////////////////// // SM -- StateManager -func (c *Consensus) uponStateMgrStateProposalQueryInputsReady(baseAnchor *isc.StateAnchor) gpa.OutMessages { +func (c *Consensus) uponStateMgrStateProposalQueryInputsReady(baseAnchor *isc.StateAnchor) []gpa.MessageOut { if baseAnchor == nil { // Don't wait for the state if no base Anchor is known. return c.subStateMgr.StateProposalConfirmedByStateMgr() @@ -362,25 +363,25 @@ func (c *Consensus) uponStateMgrStateProposalQueryInputsReady(baseAnchor *isc.St return nil } -func (c *Consensus) uponStateMgrStateProposalReceived(proposedAnchor *isc.StateAnchor) gpa.OutMessages { +func (c *Consensus) uponStateMgrStateProposalReceived(proposedAnchor *isc.StateAnchor) []gpa.MessageOut { c.output.NeedStateMgrStateProposal = nil - msgs := gpa.NoMessages() - msgs.AddAll(c.subACS.StateProposalReceived(proposedAnchor)) - msgs.AddAll(c.subNodeconn.HaveState()) - return msgs + return slices.Concat( + c.subACS.StateProposalReceived(proposedAnchor), + c.subNodeconn.HaveState(), + ) } -func (c *Consensus) uponStateMgrDecidedStateQueryInputsReady(decidedBaseAnchor *isc.StateAnchor) gpa.OutMessages { +func (c *Consensus) uponStateMgrDecidedStateQueryInputsReady(decidedBaseAnchor *isc.StateAnchor) []gpa.MessageOut { c.output.NeedStateMgrDecidedState = decidedBaseAnchor return nil } -func (c *Consensus) uponStateMgrDecidedStateReceived(chainState state.State) gpa.OutMessages { +func (c *Consensus) uponStateMgrDecidedStateReceived(chainState state.State) []gpa.MessageOut { c.output.NeedStateMgrDecidedState = nil return c.subVM.DecidedStateReceived(chainState) } -func (c *Consensus) uponStateMgrSaveProducedBlockInputsReady(producedBlock state.StateDraft) gpa.OutMessages { +func (c *Consensus) uponStateMgrSaveProducedBlockInputsReady(producedBlock state.StateDraft) []gpa.MessageOut { if producedBlock == nil { // Don't have a block to save in the case of self-governed rotation. // So mark it as saved immediately. @@ -390,7 +391,7 @@ func (c *Consensus) uponStateMgrSaveProducedBlockInputsReady(producedBlock state return nil } -func (c *Consensus) uponStateMgrSaveProducedBlockDone(block state.Block) gpa.OutMessages { +func (c *Consensus) uponStateMgrSaveProducedBlockDone(block state.Block) []gpa.MessageOut { c.output.NeedStateMgrSaveBlock = nil return c.subTX.BlockSaved(block) } @@ -398,7 +399,7 @@ func (c *Consensus) uponStateMgrSaveProducedBlockDone(block state.Block) gpa.Out //////////////////////////////////////////////////////////////////////////////// // NC -func (c *Consensus) uponNodeconnInputsReady(anchor *isc.StateAnchor) gpa.OutMessages { +func (c *Consensus) uponNodeconnInputsReady(anchor *isc.StateAnchor) []gpa.MessageOut { if anchor == nil { c.log.LogDebugf("ACS got ⊥ as input, no L1 info can be fetched.") return c.subACS.L1InfoReceived([]*coin.CoinWithRef{}, nil) @@ -407,7 +408,7 @@ func (c *Consensus) uponNodeconnInputsReady(anchor *isc.StateAnchor) gpa.OutMess return nil } -func (c *Consensus) uponNodeconnOutputReady(gasCoins []*coin.CoinWithRef, l1params *parameters.L1Params) gpa.OutMessages { +func (c *Consensus) uponNodeconnOutputReady(gasCoins []*coin.CoinWithRef, l1params *parameters.L1Params) []gpa.MessageOut { c.log.LogDebugf("L1 info received, gasCoins=%v, l1Params=%v", gasCoins, l1params) c.output.NeedNodeConnL1Info = nil return c.subACS.L1InfoReceived(gasCoins, l1params) @@ -416,35 +417,31 @@ func (c *Consensus) uponNodeconnOutputReady(gasCoins []*coin.CoinWithRef, l1para //////////////////////////////////////////////////////////////////////////////// // DistributedSignature -func (c *Consensus) uponDistributedSignatureInitialInputsReady() gpa.OutMessages { +func (c *Consensus) uponDistributedSignatureInitialInputsReady() []gpa.MessageOut { c.log.LogDebugf("uponDistributedSignatureInitialInputsReady") sub, subMsgs, err := c.msgWrapper.DelegateInput(subsystemTypeDistributedSignature, 0, distsign.NewInputStart()) if err != nil { panic(fmt.Errorf("cannot provide input to DistributedSignature: %w", err)) } - return gpa.NoMessages(). - AddAll(subMsgs). - AddAll(c.subDistributedSignature.DistributedSignatureReady(sub.Output())) + return slices.Concat(subMsgs, c.subDistributedSignature.DistributedSignatureReady(sub.Output())) } -func (c *Consensus) uponDistributedSignatureIndexProposalReady(indexProposal []int) gpa.OutMessages { +func (c *Consensus) uponDistributedSignatureIndexProposalReady(indexProposal []int) []gpa.MessageOut { c.log.LogDebugf("uponDistributedSignatureIndexProposalReady") return c.subACS.DistributedSignatureIndexProposalReceived(indexProposal) } -func (c *Consensus) uponDistributedSignatureSigningInputsReceived(decidedIndexProposals map[gpa.NodeID][]int, messageToSign []byte) gpa.OutMessages { +func (c *Consensus) uponDistributedSignatureSigningInputsReceived(decidedIndexProposals map[gpa.NodeID][]int, messageToSign []byte) []gpa.MessageOut { c.log.LogDebugf("uponDistributedSignatureSigningInputsReceived(decidedIndexProposals=%+v, H(messageToSign)=%v)", decidedIndexProposals, hashing.HashDataBlake2b(messageToSign)) distributedSignatureDecidedInput := distsign.NewInputDecided(decidedIndexProposals, messageToSign) subDistributedSignature, subMsgs, err := c.msgWrapper.DelegateInput(subsystemTypeDistributedSignature, 0, distributedSignatureDecidedInput) if err != nil { panic(fmt.Errorf("cannot provide inputs for signing: %w", err)) } - return gpa.NoMessages(). - AddAll(subMsgs). - AddAll(c.subDistributedSignature.DistributedSignatureReady(subDistributedSignature.Output())) + return slices.Concat(subMsgs, c.subDistributedSignature.DistributedSignatureReady(subDistributedSignature.Output())) } -func (c *Consensus) uponDistributedSignatureOutputReady(signature []byte) gpa.OutMessages { +func (c *Consensus) uponDistributedSignatureOutputReady(signature []byte) []gpa.MessageOut { c.log.LogDebugf("uponDistributedSignatureOutputReady") return c.subTX.SignatureReceived(signature) } @@ -459,7 +456,7 @@ func (c *Consensus) uponACSInputsReceived( timeData time.Time, gasCoins []*coin.CoinWithRef, // Can be nil. l1params *parameters.L1Params, // Can be nil. -) gpa.OutMessages { +) []gpa.MessageOut { rotateTo := c.rotateTo if rotateTo != nil && rotateTo.Equals(*c.dkShare.GetAddress().AsIotaAddress()) { // Do not propose to rotate to the existing committee. @@ -480,12 +477,10 @@ func (c *Consensus) uponACSInputsReceived( if err != nil { panic(fmt.Errorf("cannot provide input to the ACS: %w", err)) } - return gpa.NoMessages(). - AddAll(subMsgs). - AddAll(c.subACS.ACSOutputReceived(subACS.Output())) + return slices.Concat(subMsgs, c.subACS.ACSOutputReceived(subACS.Output())) } -func (c *Consensus) uponACSOutputReceived(outputValues map[gpa.NodeID][]byte) gpa.OutMessages { +func (c *Consensus) uponACSOutputReceived(outputValues map[gpa.NodeID][]byte) []gpa.MessageOut { aggr := batchproposal.AggregateBatchProposals(outputValues, c.nodeIDs, c.f, c.log) if aggr.ShouldBeSkipped() { // Cannot proceed with such proposals. @@ -507,19 +502,21 @@ func (c *Consensus) uponACSOutputReceived(outputValues map[gpa.NodeID][]byte) gp rotationTXD := c.makeTransactionData(&rotationPTX, aggr) rotationTXB := c.makeTransactionSigningBytes(rotationTXD) c.log.LogDebugf("Rotation TxDataBytes=%s", hex.EncodeToString(c.makeTransactionDataBytes(rotationTXD))) - return gpa.NoMessages(). - AddAll(c.subTX.UnsignedTXReceived(rotationTXD)). - AddAll(c.subTX.BlockSaved(nil)). - AddAll(c.subTX.AnchorDecided(bao)). - AddAll(c.subDistributedSignature.MessageToSignReceived(rotationTXB)). - AddAll(c.subDistributedSignature.DecidedIndexProposalsReceived(aggr.DecidedDistributedSignatureIndexProposals())) + return slices.Concat( + c.subTX.UnsignedTXReceived(rotationTXD), + c.subTX.BlockSaved(nil), + c.subTX.AnchorDecided(bao), + c.subDistributedSignature.MessageToSignReceived(rotationTXB), + c.subDistributedSignature.DecidedIndexProposalsReceived(aggr.DecidedDistributedSignatureIndexProposals()), + ) } - return gpa.NoMessages(). - AddAll(c.subMempool.RequestsNeeded(reqs)). - AddAll(c.subStateMgr.DecidedVirtualStateNeeded(bao)). - AddAll(c.subVM.DecidedBatchProposalsReceived(aggr)). - AddAll(c.subRND.CanProceed(baoID.Bytes())). - AddAll(c.subDistributedSignature.DecidedIndexProposalsReceived(aggr.DecidedDistributedSignatureIndexProposals())) + return slices.Concat( + c.subMempool.RequestsNeeded(reqs), + c.subStateMgr.DecidedVirtualStateNeeded(bao), + c.subVM.DecidedBatchProposalsReceived(aggr), + c.subRND.CanProceed(baoID.Bytes()), + c.subDistributedSignature.DecidedIndexProposalsReceived(aggr.DecidedDistributedSignatureIndexProposals()), + ) } func (c *Consensus) uponACSTerminated() { @@ -529,19 +526,17 @@ func (c *Consensus) uponACSTerminated() { //////////////////////////////////////////////////////////////////////////////// // RND -func (c *Consensus) uponRNDInputsReady(dataToSign []byte) gpa.OutMessages { +func (c *Consensus) uponRNDInputsReady(dataToSign []byte) []gpa.MessageOut { sigShare, err := c.dkShare.BLSSignShare(dataToSign) if err != nil { panic(fmt.Errorf("cannot sign share for randomness: %w", err)) } - msgs := gpa.NoMessages() - for _, nid := range c.nodeIDs { - msgs.Add(newMsgBLSPartialSig(c.blsSuite, nid, sigShare)) - } - return msgs + return lo.Map(c.nodeIDs, func(nid gpa.NodeID, _ int) gpa.MessageOut { + return newMsgBLSPartialSig(c.blsSuite, nid, sigShare) + }) } -func (c *Consensus) uponRNDSigSharesReady(dataToSign []byte, partialSigs map[gpa.NodeID][]byte) (bool, gpa.OutMessages) { +func (c *Consensus) uponRNDSigSharesReady(dataToSign []byte, partialSigs map[gpa.NodeID][]byte) (bool, []gpa.MessageOut) { partialSigArray := make([][]byte, 0, len(partialSigs)) for nid := range partialSigs { partialSigArray = append(partialSigArray, partialSigs[nid]) @@ -557,7 +552,7 @@ func (c *Consensus) uponRNDSigSharesReady(dataToSign []byte, partialSigs map[gpa //////////////////////////////////////////////////////////////////////////////// // VM -func (c *Consensus) uponVMInputsReceived(aggregatedProposals *batchproposal.AggregatedBatchProposals, randomness *hashing.HashValue, requests []isc.Request) gpa.OutMessages { +func (c *Consensus) uponVMInputsReceived(aggregatedProposals *batchproposal.AggregatedBatchProposals, randomness *hashing.HashValue, requests []isc.Request) []gpa.MessageOut { decidedBaseAnchor := aggregatedProposals.DecidedBaseAnchor() stateAnchor := isc.NewStateAnchor(decidedBaseAnchor.Anchor(), decidedBaseAnchor.ISCPackage()) gasCoins := aggregatedProposals.AggregatedGasCoins() @@ -585,7 +580,7 @@ func (c *Consensus) uponVMInputsReceived(aggregatedProposals *batchproposal.Aggr return c.subTX.AnchorDecided(decidedBaseAnchor) } -func (c *Consensus) uponVMOutputReceived(vmResult *vm.VMTaskResult, aggregatedProposals *batchproposal.AggregatedBatchProposals) gpa.OutMessages { +func (c *Consensus) uponVMOutputReceived(vmResult *vm.VMTaskResult, aggregatedProposals *batchproposal.AggregatedBatchProposals) []gpa.MessageOut { c.output.NeedVMResult = nil if len(vmResult.RequestResults) == 0 { // No requests were processed, don't have what to do. @@ -601,10 +596,11 @@ func (c *Consensus) uponVMOutputReceived(vmResult *vm.VMTaskResult, aggregatedPr txData := c.makeTransactionData(&unsignedTX, aggregatedProposals) txBytes := c.makeTransactionSigningBytes(txData) c.log.LogDebugf("VM produced TxDataBytes=%s", hex.EncodeToString(c.makeTransactionDataBytes(txData))) - return gpa.NoMessages(). - AddAll(c.subStateMgr.BlockProduced(vmResult.StateDraft)). - AddAll(c.subTX.UnsignedTXReceived(txData)). - AddAll(c.subDistributedSignature.MessageToSignReceived(txBytes)) + return slices.Concat( + c.subStateMgr.BlockProduced(vmResult.StateDraft), + c.subTX.UnsignedTXReceived(txData), + c.subDistributedSignature.MessageToSignReceived(txBytes), + ) } //////////////////////////////////////////////////////////////////////////////// @@ -641,7 +637,7 @@ func (c *Consensus) makeTransactionSigningBytes(txData *iotago.TransactionData) } // Everything is ready for the output TX, produce it. -func (c *Consensus) uponTXInputsReady(decidedAnchor *isc.StateAnchor, unsignedTX *iotago.TransactionData, block state.Block, signature []byte) gpa.OutMessages { +func (c *Consensus) uponTXInputsReady(decidedAnchor *isc.StateAnchor, unsignedTX *iotago.TransactionData, block state.Block, signature []byte) []gpa.MessageOut { suiSignature := cryptolib.NewSignature(c.dkShare.GetSharedPublic(), signature).AsIotaSignature() signedTX := iotasigner.NewSignedTransaction(unsignedTX, suiSignature) c.output.Result = &Result{ diff --git a/packages/chain/consensus/consensusrunner/gr.go b/packages/chain/consensus/consensusrunner/gr.go index 93a50a5e92..fd7978c542 100644 --- a/packages/chain/consensus/consensusrunner/gr.go +++ b/packages/chain/consensus/consensusrunner/gr.go @@ -10,12 +10,10 @@ import ( "fmt" "time" - "go.uber.org/atomic" - "github.com/samber/lo" + "go.uber.org/atomic" "github.com/iotaledger/hive.go/log" - "github.com/iotaledger/wasp/v2/clients/iota-go/iotago" "github.com/iotaledger/wasp/v2/packages/chain/committeelog" "github.com/iotaledger/wasp/v2/packages/chain/consensus" @@ -400,13 +398,12 @@ func (r *ConsensusRunner) handleRedeliveryTick(t time.Time) { } func (r *ConsensusRunner) handleNetMessage(recv *peering.PeerMessageIn) { - msg, err := r.consInst.UnmarshalMessage(recv.MsgData) + msg, err := r.consInst.UnmarshalPayload(recv.MsgData) if err != nil { r.log.LogWarnf("cannot parse message: %v", err) return } - msg.SetSender(gpa.NodeIDFromPublicKey(recv.SenderPubKey)) - outMsgs := r.consInst.Message(msg) + outMsgs := r.consInst.Message(gpa.NewMessageIn(gpa.NodeIDFromPublicKey(recv.SenderPubKey), msg)) r.sendMessages(outMsgs) r.tryHandleOutput() } @@ -463,13 +460,13 @@ func (r *ConsensusRunner) provideOutput(output *consensus.Output) { } } -func (r *ConsensusRunner) sendMessages(outMsgs gpa.OutMessages) { +func (r *ConsensusRunner) sendMessages(outMsgs []gpa.MessageOut) { if outMsgs == nil { return } - outMsgs.MustIterate(func(msg gpa.Message) { - msgBytes := lo.Must(gpa.MarshalMessage(msg)) + for _, msg := range outMsgs { + msgBytes := lo.Must(gpa.MarshalPayload(msg.Payload)) pm := peering.NewPeerMessageData(r.netPeeringID, peering.ReceiverChainCons, msgTypeCons, msgBytes) - r.net.SendMsgByPubKey(r.netPeerPubs[msg.Recipient()], pm) - }) + r.net.SendMsgByPubKey(r.netPeerPubs[msg.Recipient], pm) + } } diff --git a/packages/chain/consensus/msg.go b/packages/chain/consensus/msg.go index af90acca40..37704954d2 100644 --- a/packages/chain/consensus/msg.go +++ b/packages/chain/consensus/msg.go @@ -12,10 +12,10 @@ const ( msgTypeWrapped ) -func (c *Consensus) UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{ - msgTypeBLSShare: func() gpa.Message { return &msgBLSPartialSig{blsSuite: c.blsSuite} }, - }, gpa.Fallback{ - msgTypeWrapped: c.msgWrapper.UnmarshalMessage, +func (c *Consensus) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{ + msgTypeBLSShare: func() gpa.MessagePayload { return &msgBLSPartialSig{blsSuite: c.blsSuite} }, + }, gpa.PayloadFallback{ + msgTypeWrapped: c.msgWrapper.UnmarshalPayload, }) } diff --git a/packages/chain/consensus/msg_bls_partial_sig.go b/packages/chain/consensus/msg_bls_partial_sig.go index 694a9e5913..035c2cb995 100644 --- a/packages/chain/consensus/msg_bls_partial_sig.go +++ b/packages/chain/consensus/msg_bls_partial_sig.go @@ -10,19 +10,17 @@ import ( ) type msgBLSPartialSig struct { - gpa.BasicMessage blsSuite suites.Suite partialSig []byte `bcs:"export"` } -var _ gpa.Message = new(msgBLSPartialSig) +var _ gpa.MessagePayload = new(msgBLSPartialSig) -func newMsgBLSPartialSig(blsSuite suites.Suite, recipient gpa.NodeID, partialSig []byte) *msgBLSPartialSig { - return &msgBLSPartialSig{ - BasicMessage: gpa.NewBasicMessage(recipient), - blsSuite: blsSuite, - partialSig: partialSig, - } +func newMsgBLSPartialSig(blsSuite suites.Suite, recipient gpa.NodeID, partialSig []byte) gpa.MessageOut { + return gpa.NewMessageOut(recipient, &msgBLSPartialSig{ + blsSuite: blsSuite, + partialSig: partialSig, + }) } func (msg *msgBLSPartialSig) MsgType() gpa.MessageType { diff --git a/packages/chain/consensus/msg_bls_partial_sig_test.go b/packages/chain/consensus/msg_bls_partial_sig_test.go index 461b8f8e69..f36ff4411d 100644 --- a/packages/chain/consensus/msg_bls_partial_sig_test.go +++ b/packages/chain/consensus/msg_bls_partial_sig_test.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/require" bcs "github.com/iotaledger/bcs-go" - "github.com/iotaledger/wasp/v2/packages/gpa" ) func TestMsgBLSPartialSigSerialization(t *testing.T) { @@ -18,14 +17,12 @@ func TestMsgBLSPartialSigSerialization(t *testing.T) { _, err := rand.Read(b) require.NoError(t, err) msg := &msgBLSPartialSig{ - gpa.BasicMessage{}, nil, b, } bcs.TestCodec(t, msg) msg = &msgBLSPartialSig{ - gpa.BasicMessage{}, nil, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, } diff --git a/packages/chain/consensus/sync_acs.go b/packages/chain/consensus/sync_acs.go index 126f91f8e9..106fe7ebaf 100644 --- a/packages/chain/consensus/sync_acs.go +++ b/packages/chain/consensus/sync_acs.go @@ -44,7 +44,7 @@ func NewSyncACS( } } -func (sub *SyncACS) StateProposalReceived(proposedBaseAnchor *isc.StateAnchor) gpa.OutMessages { +func (sub *SyncACS) StateProposalReceived(proposedBaseAnchor *isc.StateAnchor) []gpa.MessageOut { if sub.baseStateAnchorReceived { return nil } @@ -53,7 +53,7 @@ func (sub *SyncACS) StateProposalReceived(proposedBaseAnchor *isc.StateAnchor) g return sub.tryCompleteInput() } -func (sub *SyncACS) MempoolRequestsReceived(requestRefs []*isc.RequestRef) gpa.OutMessages { +func (sub *SyncACS) MempoolRequestsReceived(requestRefs []*isc.RequestRef) []gpa.MessageOut { if sub.RequestRefs != nil { return nil } @@ -61,7 +61,7 @@ func (sub *SyncACS) MempoolRequestsReceived(requestRefs []*isc.RequestRef) gpa.O return sub.tryCompleteInput() } -func (sub *SyncACS) DistributedSignatureIndexProposalReceived(distSignIndexProposal []int) gpa.OutMessages { +func (sub *SyncACS) DistributedSignatureIndexProposalReceived(distSignIndexProposal []int) []gpa.MessageOut { if sub.DistributedSignatureIndexProposal != nil { return nil } @@ -69,7 +69,7 @@ func (sub *SyncACS) DistributedSignatureIndexProposalReceived(distSignIndexPropo return sub.tryCompleteInput() } -func (sub *SyncACS) TimeDataReceived(timeData time.Time) gpa.OutMessages { +func (sub *SyncACS) TimeDataReceived(timeData time.Time) []gpa.MessageOut { if timeData.After(sub.TimeData) { sub.TimeData = timeData return sub.tryCompleteInput() @@ -77,7 +77,7 @@ func (sub *SyncACS) TimeDataReceived(timeData time.Time) gpa.OutMessages { return nil } -func (sub *SyncACS) L1InfoReceived(gasCoins []*coin.CoinWithRef, l1params *parameters.L1Params) gpa.OutMessages { +func (sub *SyncACS) L1InfoReceived(gasCoins []*coin.CoinWithRef, l1params *parameters.L1Params) []gpa.MessageOut { if sub.l1InfoReceived { return nil } @@ -87,7 +87,7 @@ func (sub *SyncACS) L1InfoReceived(gasCoins []*coin.CoinWithRef, l1params *param return sub.tryCompleteInput() } -func (sub *SyncACS) tryCompleteInput() gpa.OutMessages { +func (sub *SyncACS) tryCompleteInput() []gpa.MessageOut { if sub.inputsReady || !sub.baseStateAnchorReceived { return nil } @@ -98,7 +98,7 @@ func (sub *SyncACS) tryCompleteInput() gpa.OutMessages { return sub.c.uponACSInputsReceived(sub.baseStateAnchor, sub.RequestRefs, sub.DistributedSignatureIndexProposal, sub.TimeData, sub.gasCoins, sub.l1params) } -func (sub *SyncACS) ACSOutputReceived(output gpa.Output) gpa.OutMessages { +func (sub *SyncACS) ACSOutputReceived(output gpa.Output) []gpa.MessageOut { if output == nil { return nil } diff --git a/packages/chain/consensus/sync_dss.go b/packages/chain/consensus/sync_dss.go index 5dd6ee1b5a..4647ec3b30 100644 --- a/packages/chain/consensus/sync_dss.go +++ b/packages/chain/consensus/sync_dss.go @@ -5,6 +5,7 @@ package consensus import ( "fmt" + "slices" "strings" "github.com/iotaledger/wasp/v2/packages/chain/distsign" @@ -25,7 +26,7 @@ func NewSyncDistributedSignature(c *Consensus) *SyncDistributedSignature { return &SyncDistributedSignature{c: c} } -func (sub *SyncDistributedSignature) InitialInputReceived() gpa.OutMessages { +func (sub *SyncDistributedSignature) InitialInputReceived() []gpa.MessageOut { if sub.initialInputsReady { return nil } @@ -33,24 +34,24 @@ func (sub *SyncDistributedSignature) InitialInputReceived() gpa.OutMessages { return sub.c.uponDistributedSignatureInitialInputsReady() } -func (sub *SyncDistributedSignature) DistributedSignatureReady(output gpa.Output) gpa.OutMessages { +func (sub *SyncDistributedSignature) DistributedSignatureReady(output gpa.Output) []gpa.MessageOut { if output == nil || (sub.indexProposalReady && sub.outputReady) { return nil } - msgs := gpa.NoMessages() + var msgs []gpa.MessageOut distSignOutput := output.(*distsign.Output) if !sub.indexProposalReady && distSignOutput.ProposedIndexes != nil { sub.indexProposalReady = true - msgs.AddAll(sub.c.uponDistributedSignatureIndexProposalReady(distSignOutput.ProposedIndexes)) + msgs = slices.Concat(msgs, sub.c.uponDistributedSignatureIndexProposalReady(distSignOutput.ProposedIndexes)) } if !sub.outputReady && distSignOutput.Signature != nil { sub.outputReady = true - msgs.AddAll(sub.c.uponDistributedSignatureOutputReady(distSignOutput.Signature)) + msgs = slices.Concat(msgs, sub.c.uponDistributedSignatureOutputReady(distSignOutput.Signature)) } return msgs } -func (sub *SyncDistributedSignature) DecidedIndexProposalsReceived(decidedIndexProposals map[gpa.NodeID][]int) gpa.OutMessages { +func (sub *SyncDistributedSignature) DecidedIndexProposalsReceived(decidedIndexProposals map[gpa.NodeID][]int) []gpa.MessageOut { if sub.DecidedIndexProposals != nil || decidedIndexProposals == nil { return nil } @@ -58,7 +59,7 @@ func (sub *SyncDistributedSignature) DecidedIndexProposalsReceived(decidedIndexP return sub.tryCompleteSigning() } -func (sub *SyncDistributedSignature) MessageToSignReceived(messageToSign []byte) gpa.OutMessages { +func (sub *SyncDistributedSignature) MessageToSignReceived(messageToSign []byte) []gpa.MessageOut { if sub.MessageToSign != nil || messageToSign == nil { return nil } @@ -66,7 +67,7 @@ func (sub *SyncDistributedSignature) MessageToSignReceived(messageToSign []byte) return sub.tryCompleteSigning() } -func (sub *SyncDistributedSignature) tryCompleteSigning() gpa.OutMessages { +func (sub *SyncDistributedSignature) tryCompleteSigning() []gpa.MessageOut { if sub.signingInputsReady || sub.MessageToSign == nil || sub.DecidedIndexProposals == nil { return nil } diff --git a/packages/chain/consensus/sync_mp.go b/packages/chain/consensus/sync_mp.go index 0942d56a11..b8a546bdcf 100644 --- a/packages/chain/consensus/sync_mp.go +++ b/packages/chain/consensus/sync_mp.go @@ -25,7 +25,7 @@ func NewSyncMempool( } } -func (s *SyncMempool) BaseAnchorReceived(baseAnchor *isc.StateAnchor) gpa.OutMessages { +func (s *SyncMempool) BaseAnchorReceived(baseAnchor *isc.StateAnchor) []gpa.MessageOut { if s.baseAnchorReceived { return nil } @@ -34,7 +34,7 @@ func (s *SyncMempool) BaseAnchorReceived(baseAnchor *isc.StateAnchor) gpa.OutMes return s.c.uponMempoolProposalInputsReady(s.baseAnchor) } -func (s *SyncMempool) ProposalReceived(requestRefs []*isc.RequestRef) gpa.OutMessages { +func (s *SyncMempool) ProposalReceived(requestRefs []*isc.RequestRef) []gpa.MessageOut { if s.proposalReceived { return nil } @@ -42,7 +42,7 @@ func (s *SyncMempool) ProposalReceived(requestRefs []*isc.RequestRef) gpa.OutMes return s.c.uponMempoolProposalReceived(requestRefs) } -func (s *SyncMempool) RequestsNeeded(requestRefs []*isc.RequestRef) gpa.OutMessages { +func (s *SyncMempool) RequestsNeeded(requestRefs []*isc.RequestRef) []gpa.MessageOut { if s.requestsNeeded { return nil } @@ -50,7 +50,7 @@ func (s *SyncMempool) RequestsNeeded(requestRefs []*isc.RequestRef) gpa.OutMessa return s.c.uponMempoolRequestsNeeded(requestRefs) } -func (s *SyncMempool) RequestsReceived(requests []isc.Request) gpa.OutMessages { +func (s *SyncMempool) RequestsReceived(requests []isc.Request) []gpa.MessageOut { if s.requestsReceived { return nil } diff --git a/packages/chain/consensus/sync_nc.go b/packages/chain/consensus/sync_nc.go index 9d8377784b..e38edcf8ff 100644 --- a/packages/chain/consensus/sync_nc.go +++ b/packages/chain/consensus/sync_nc.go @@ -50,7 +50,7 @@ func (s *SyncNodeconn) String() string { return str } -func (s *SyncNodeconn) HaveInputAnchor(anchor *isc.StateAnchor) gpa.OutMessages { +func (s *SyncNodeconn) HaveInputAnchor(anchor *isc.StateAnchor) []gpa.MessageOut { if s.inputAnchorReceived { return nil } @@ -59,7 +59,7 @@ func (s *SyncNodeconn) HaveInputAnchor(anchor *isc.StateAnchor) gpa.OutMessages return s.tryCompleteInputs() } -func (s *SyncNodeconn) HaveState() gpa.OutMessages { +func (s *SyncNodeconn) HaveState() []gpa.MessageOut { if s.stateReceived { return nil } @@ -67,7 +67,7 @@ func (s *SyncNodeconn) HaveState() gpa.OutMessages { return s.tryCompleteInputs() } -func (s *SyncNodeconn) HaveRequests() gpa.OutMessages { +func (s *SyncNodeconn) HaveRequests() []gpa.MessageOut { if s.requestsReceived { return nil } @@ -75,7 +75,7 @@ func (s *SyncNodeconn) HaveRequests() gpa.OutMessages { return s.tryCompleteInputs() } -func (s *SyncNodeconn) tryCompleteInputs() gpa.OutMessages { +func (s *SyncNodeconn) tryCompleteInputs() []gpa.MessageOut { if !s.inputAnchorReceived || !s.stateReceived || !s.requestsReceived || s.inputProcessed { return nil } @@ -83,7 +83,7 @@ func (s *SyncNodeconn) tryCompleteInputs() gpa.OutMessages { return s.c.uponNodeconnInputsReady(s.inputAnchor) } -func (s *SyncNodeconn) HaveL1Info(gasCoins []*coin.CoinWithRef, l1params *parameters.L1Params) gpa.OutMessages { +func (s *SyncNodeconn) HaveL1Info(gasCoins []*coin.CoinWithRef, l1params *parameters.L1Params) []gpa.MessageOut { if s.gasCoins == nil && gasCoins != nil { s.gasCoins = gasCoins } @@ -93,7 +93,7 @@ func (s *SyncNodeconn) HaveL1Info(gasCoins []*coin.CoinWithRef, l1params *parame return s.tryCompleteOutput() } -func (s *SyncNodeconn) tryCompleteOutput() gpa.OutMessages { +func (s *SyncNodeconn) tryCompleteOutput() []gpa.MessageOut { if s.outputProcessed || s.gasCoins == nil || s.l1params == nil { return nil } diff --git a/packages/chain/consensus/sync_rnd.go b/packages/chain/consensus/sync_rnd.go index 27b3b3107e..e573451df6 100644 --- a/packages/chain/consensus/sync_rnd.go +++ b/packages/chain/consensus/sync_rnd.go @@ -4,6 +4,8 @@ package consensus import ( + "slices" + "github.com/iotaledger/wasp/v2/packages/gpa" ) @@ -26,17 +28,18 @@ func NewSyncRND( } } -func (sub *SyncRND) CanProceed(dataToSign []byte) gpa.OutMessages { +func (sub *SyncRND) CanProceed(dataToSign []byte) []gpa.MessageOut { if sub.dataToSign != nil || dataToSign == nil { return nil } sub.dataToSign = dataToSign - return gpa.NoMessages(). - AddAll(sub.c.uponRNDInputsReady(sub.dataToSign)). - AddAll(sub.tryComplete()) + return slices.Concat( + sub.c.uponRNDInputsReady(sub.dataToSign), + sub.tryComplete(), + ) } -func (sub *SyncRND) BLSPartialSigReceived(sender gpa.NodeID, partialSig []byte) gpa.OutMessages { +func (sub *SyncRND) BLSPartialSigReceived(sender gpa.NodeID, partialSig []byte) []gpa.MessageOut { if _, ok := sub.blsPartialSigs[sender]; ok { return nil // Duplicate, ignore it. } @@ -44,7 +47,7 @@ func (sub *SyncRND) BLSPartialSigReceived(sender gpa.NodeID, partialSig []byte) return sub.tryComplete() } -func (sub *SyncRND) tryComplete() gpa.OutMessages { +func (sub *SyncRND) tryComplete() []gpa.MessageOut { if sub.sigSharesReady || sub.dataToSign == nil || len(sub.blsPartialSigs) < sub.blsThreshold { return nil } diff --git a/packages/chain/consensus/sync_sm.go b/packages/chain/consensus/sync_sm.go index 3463b86976..9b22e81f98 100644 --- a/packages/chain/consensus/sync_sm.go +++ b/packages/chain/consensus/sync_sm.go @@ -33,7 +33,7 @@ func NewSyncStateMgr( return &SyncStateMgr{c: c} } -func (s *SyncStateMgr) ProposedBaseAnchorReceived(baseAnchor *isc.StateAnchor) gpa.OutMessages { +func (s *SyncStateMgr) ProposedBaseAnchorReceived(baseAnchor *isc.StateAnchor) []gpa.MessageOut { if s.proposedBaseAnchorReceived { return nil } @@ -42,7 +42,7 @@ func (s *SyncStateMgr) ProposedBaseAnchorReceived(baseAnchor *isc.StateAnchor) g return s.c.uponStateMgrStateProposalQueryInputsReady(s.proposedBaseAnchor) } -func (s *SyncStateMgr) StateProposalConfirmedByStateMgr() gpa.OutMessages { +func (s *SyncStateMgr) StateProposalConfirmedByStateMgr() []gpa.MessageOut { if s.stateProposalReceived { return nil } @@ -50,7 +50,7 @@ func (s *SyncStateMgr) StateProposalConfirmedByStateMgr() gpa.OutMessages { return s.c.uponStateMgrStateProposalReceived(s.proposedBaseAnchor) } -func (s *SyncStateMgr) DecidedVirtualStateNeeded(decidedBaseAnchor *isc.StateAnchor) gpa.OutMessages { +func (s *SyncStateMgr) DecidedVirtualStateNeeded(decidedBaseAnchor *isc.StateAnchor) []gpa.MessageOut { if s.decidedBaseAnchor != nil { return nil } @@ -60,7 +60,7 @@ func (s *SyncStateMgr) DecidedVirtualStateNeeded(decidedBaseAnchor *isc.StateAnc func (s *SyncStateMgr) DecidedVirtualStateReceived( chainState state.State, -) gpa.OutMessages { +) []gpa.MessageOut { if s.decidedStateReceived { return nil } @@ -68,7 +68,7 @@ func (s *SyncStateMgr) DecidedVirtualStateReceived( return s.c.uponStateMgrDecidedStateReceived(chainState) } -func (s *SyncStateMgr) BlockProduced(block state.StateDraft) gpa.OutMessages { +func (s *SyncStateMgr) BlockProduced(block state.StateDraft) []gpa.MessageOut { if s.producedBlockReceived { return nil } @@ -77,7 +77,7 @@ func (s *SyncStateMgr) BlockProduced(block state.StateDraft) gpa.OutMessages { return s.c.uponStateMgrSaveProducedBlockInputsReady(s.producedBlock) } -func (s *SyncStateMgr) BlockSaved(block state.Block) gpa.OutMessages { +func (s *SyncStateMgr) BlockSaved(block state.Block) []gpa.MessageOut { if s.saveProducedBlockDone { return nil } diff --git a/packages/chain/consensus/sync_tx.go b/packages/chain/consensus/sync_tx.go index abecc6cd99..73562c2e29 100644 --- a/packages/chain/consensus/sync_tx.go +++ b/packages/chain/consensus/sync_tx.go @@ -29,7 +29,7 @@ func NewSyncTX(c *Consensus) *SyncTX { return &SyncTX{c: c} } -func (sub *SyncTX) AnchorDecided(ao *isc.StateAnchor) gpa.OutMessages { +func (sub *SyncTX) AnchorDecided(ao *isc.StateAnchor) []gpa.MessageOut { if sub.decidedAnchor != nil || ao == nil { return nil } @@ -37,7 +37,7 @@ func (sub *SyncTX) AnchorDecided(ao *isc.StateAnchor) gpa.OutMessages { return sub.tryCompleteInputs() } -func (sub *SyncTX) UnsignedTXReceived(unsignedTX *iotago.TransactionData) gpa.OutMessages { +func (sub *SyncTX) UnsignedTXReceived(unsignedTX *iotago.TransactionData) []gpa.MessageOut { if sub.unsignedTX != nil || unsignedTX == nil { return nil } @@ -45,7 +45,7 @@ func (sub *SyncTX) UnsignedTXReceived(unsignedTX *iotago.TransactionData) gpa.Ou return sub.tryCompleteInputs() } -func (sub *SyncTX) SignatureReceived(signature []byte) gpa.OutMessages { +func (sub *SyncTX) SignatureReceived(signature []byte) []gpa.MessageOut { if sub.signature != nil || signature == nil { return nil } @@ -53,7 +53,7 @@ func (sub *SyncTX) SignatureReceived(signature []byte) gpa.OutMessages { return sub.tryCompleteInputs() } -func (sub *SyncTX) BlockSaved(block state.Block) gpa.OutMessages { +func (sub *SyncTX) BlockSaved(block state.Block) []gpa.MessageOut { if sub.blockSaved { return nil } @@ -62,7 +62,7 @@ func (sub *SyncTX) BlockSaved(block state.Block) gpa.OutMessages { return sub.tryCompleteInputs() } -func (sub *SyncTX) tryCompleteInputs() gpa.OutMessages { +func (sub *SyncTX) tryCompleteInputs() []gpa.MessageOut { if sub.inputsReady || sub.decidedAnchor == nil || sub.unsignedTX == nil || sub.signature == nil || !sub.blockSaved { return nil } diff --git a/packages/chain/consensus/sync_vm.go b/packages/chain/consensus/sync_vm.go index 025972cb32..4c2dd3977e 100644 --- a/packages/chain/consensus/sync_vm.go +++ b/packages/chain/consensus/sync_vm.go @@ -5,6 +5,7 @@ package consensus import ( "fmt" + "slices" "strings" "github.com/iotaledger/wasp/v2/packages/chain/consensus/batchproposal" @@ -32,18 +33,18 @@ func NewSyncVM( return &SyncVM{c: c} } -func (sub *SyncVM) DecidedBatchProposalsReceived(aggregatedProposals *batchproposal.AggregatedBatchProposals) gpa.OutMessages { +func (sub *SyncVM) DecidedBatchProposalsReceived(aggregatedProposals *batchproposal.AggregatedBatchProposals) []gpa.MessageOut { if sub.aggregatedProposals != nil || aggregatedProposals == nil { return nil } sub.aggregatedProposals = aggregatedProposals - msgs := gpa.NoMessages() - msgs.AddAll(sub.tryCompleteInputs()) - msgs.AddAll(sub.tryCompleteOutputs()) - return msgs + return slices.Concat( + sub.tryCompleteInputs(), + sub.tryCompleteOutputs(), + ) } -func (sub *SyncVM) DecidedStateReceived(chainState state.State) gpa.OutMessages { +func (sub *SyncVM) DecidedStateReceived(chainState state.State) []gpa.MessageOut { if sub.chainState != nil { return nil } @@ -51,7 +52,7 @@ func (sub *SyncVM) DecidedStateReceived(chainState state.State) gpa.OutMessages return sub.tryCompleteInputs() } -func (sub *SyncVM) RandomnessReceived(randomness hashing.HashValue) gpa.OutMessages { +func (sub *SyncVM) RandomnessReceived(randomness hashing.HashValue) []gpa.MessageOut { if sub.randomness != nil { return nil } @@ -59,7 +60,7 @@ func (sub *SyncVM) RandomnessReceived(randomness hashing.HashValue) gpa.OutMessa return sub.tryCompleteInputs() } -func (sub *SyncVM) RequestsReceived(requests []isc.Request) gpa.OutMessages { +func (sub *SyncVM) RequestsReceived(requests []isc.Request) []gpa.MessageOut { if sub.requests != nil || requests == nil { return nil } @@ -67,7 +68,7 @@ func (sub *SyncVM) RequestsReceived(requests []isc.Request) gpa.OutMessages { return sub.tryCompleteInputs() } -func (sub *SyncVM) tryCompleteInputs() gpa.OutMessages { +func (sub *SyncVM) tryCompleteInputs() []gpa.MessageOut { if sub.inputsReady || sub.aggregatedProposals == nil || sub.chainState == nil || sub.randomness == nil || sub.requests == nil { return nil } @@ -75,7 +76,7 @@ func (sub *SyncVM) tryCompleteInputs() gpa.OutMessages { return sub.c.uponVMInputsReceived(sub.aggregatedProposals, sub.randomness, sub.requests) } -func (sub *SyncVM) tryCompleteOutputs() gpa.OutMessages { +func (sub *SyncVM) tryCompleteOutputs() []gpa.MessageOut { if sub.vmResult == nil || sub.aggregatedProposals == nil { return nil } @@ -86,7 +87,7 @@ func (sub *SyncVM) tryCompleteOutputs() gpa.OutMessages { return sub.c.uponVMOutputReceived(sub.vmResult, sub.aggregatedProposals) } -func (sub *SyncVM) VMResultReceived(vmResult *vm.VMTaskResult) gpa.OutMessages { +func (sub *SyncVM) VMResultReceived(vmResult *vm.VMTaskResult) []gpa.MessageOut { if sub.vmResult != nil || vmResult == nil { return nil } diff --git a/packages/chain/distsign/dss.go b/packages/chain/distsign/dss.go index 1b6c58ea83..848140e105 100644 --- a/packages/chain/distsign/dss.go +++ b/packages/chain/distsign/dss.go @@ -24,6 +24,7 @@ package distsign import ( "fmt" + "slices" "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/sign/dss" @@ -106,12 +107,12 @@ func (d *DistributedSignature) AsGPA() gpa.GPA { } // Input handles the input to the protocol. -func (d *DistributedSignature) Input(input gpa.Input) gpa.OutMessages { +func (d *DistributedSignature) Input(input gpa.Input) []gpa.MessageOut { d.log.LogDebugf("Input %+v", input) switch input := input.(type) { case *inputStart: - msgs := d.msgWrapper.WrapMessages(subsystemDistributedKeyGeneration, 0, d.distributedKeyGen.Input(nonce.NewInputStart())) - return d.tryHandleDistributedKeyGenerationOutput(msgs) + msgs := d.msgWrapper.WrapMessagesOut(subsystemDistributedKeyGeneration, 0, d.distributedKeyGen.Input(nonce.NewInputStart())) + return slices.Concat(msgs, d.tryHandleDistributedKeyGenerationOutput()) case *inputDecided: return d.handleDecided(input) } @@ -119,17 +120,17 @@ func (d *DistributedSignature) Input(input gpa.Input) gpa.OutMessages { } // Message handles the messages. -func (d *DistributedSignature) Message(msg gpa.Message) gpa.OutMessages { - switch msgT := msg.(type) { +func (d *DistributedSignature) Message(msg gpa.MessageIn) []gpa.MessageOut { + switch msgT := msg.Payload.(type) { case *msgPartialSig: d.log.LogDebugf("Message %+v", msg) - return d.handlePartialSig(msgT) + return d.handlePartialSig(gpa.AsTypedMessageIn[*msgPartialSig](msg)) case *gpa.WrappingMsg: if msgT.Subsystem() == subsystemDistributedKeyGeneration && msgT.Index() == 0 { - msgs := d.msgWrapper.WrapMessages(subsystemDistributedKeyGeneration, 0, d.distributedKeyGen.Message(msgT.Wrapped())) - return d.tryHandleDistributedKeyGenerationOutput(msgs) + msgs := d.msgWrapper.WrapMessagesOut(subsystemDistributedKeyGeneration, 0, d.distributedKeyGen.Message(msgT.WrappedIn(msg.Sender))) + return slices.Concat(msgs, d.tryHandleDistributedKeyGenerationOutput()) } - d.log.LogWarnf("unknown wrapped message %+v, wrapped %T: %v", msgT, msgT.Wrapped(), msgT.Wrapped()) + d.log.LogWarnf("unknown wrapped message %T: %+v", msgT, msgT) return nil default: panic(fmt.Errorf("unknown message %T: %v", msg, msg)) @@ -147,11 +148,12 @@ func (d *DistributedSignature) Output() gpa.Output { } } -func (d *DistributedSignature) tryHandleDistributedKeyGenerationOutput(msgs gpa.OutMessages) gpa.OutMessages { +func (d *DistributedSignature) tryHandleDistributedKeyGenerationOutput() []gpa.MessageOut { distKeyGenOut := d.distributedKeyGen.Output() if d.distKeyGenOutIndexes == nil && distKeyGenOut != nil && distKeyGenOut.(*nonce.Output).Indexes != nil { d.distKeyGenOutIndexes = distKeyGenOut.(*nonce.Output).Indexes } + var msgs []gpa.MessageOut if d.distKeyGenOutNonce == nil && distKeyGenOut != nil && distKeyGenOut.(*nonce.Output).PriShare != nil { d.distKeyGenOutNonce = tcrypto.NewDistKeyShare( distKeyGenOut.(*nonce.Output).PriShare, @@ -191,13 +193,10 @@ func (d *DistributedSignature) tryHandleDistributedKeyGenerationOutput(msgs gpa. if d.nodeIDs[i] == d.me { continue } - msg := &msgPartialSig{ - BasicMessage: gpa.NewBasicMessage(d.nodeIDs[i]), - suite: d.suite, - partialSig: partialSig, - } - msg.SetSender(d.me) - msgs.Add(msg) + msgs = append(msgs, gpa.NewMessageOut(d.nodeIDs[i], &msgPartialSig{ + suite: d.suite, + partialSig: partialSig, + })) } // // Maybe we have everything for the signature already? @@ -213,23 +212,23 @@ func (d *DistributedSignature) tryHandleDistributedKeyGenerationOutput(msgs gpa. return msgs } -func (d *DistributedSignature) handlePartialSig(msg *msgPartialSig) gpa.OutMessages { +func (d *DistributedSignature) handlePartialSig(msg gpa.TypedMessageIn[*msgPartialSig]) []gpa.MessageOut { if d.signature != nil { // Signature already aggregated, ignore the remaining shares. return nil } if d.distributedSignatureSigner == nil { - if d.distSignPartialSigBuffer.Has(msg.Sender()) { - d.log.LogWarn("duplicate partial signature from %v", msg.Sender()) + if d.distSignPartialSigBuffer.Has(msg.Sender) { + d.log.LogWarn("duplicate partial signature from %v", msg.Sender) return nil } - d.distSignPartialSigBuffer.Set(msg.Sender(), msg.partialSig) + d.distSignPartialSigBuffer.Set(msg.Sender, msg.Payload.partialSig) return nil } // // Then process the one received with the current message. - err := d.distributedSignatureSigner.ProcessPartialSig(msg.partialSig) + err := d.distributedSignatureSigner.ProcessPartialSig(msg.Payload.partialSig) if err != nil { d.log.LogWarnf("Failed to process a partial signature: %v", err) return nil @@ -247,7 +246,7 @@ func (d *DistributedSignature) handlePartialSig(msg *msgPartialSig) gpa.OutMessa return nil } -func (d *DistributedSignature) handleDecided(input *inputDecided) gpa.OutMessages { +func (d *DistributedSignature) handleDecided(input *inputDecided) []gpa.MessageOut { if d.distKeyGenDecidedIndexProposals != nil { d.log.LogWarn("Duplicate will be dropped: DecidedIndexes=%+v", input.decidedIndexProposals) return nil @@ -256,8 +255,8 @@ func (d *DistributedSignature) handleDecided(input *inputDecided) gpa.OutMessage d.messageToSign = input.messageToSign decisionInput := nonce.NewInputAgreementResult(input.decidedIndexProposals) - msgs := d.msgWrapper.WrapMessages(subsystemDistributedKeyGeneration, 0, d.distributedKeyGen.Input(decisionInput)) - return d.tryHandleDistributedKeyGenerationOutput(msgs) + msgs := d.msgWrapper.WrapMessagesOut(subsystemDistributedKeyGeneration, 0, d.distributedKeyGen.Input(decisionInput)) + return slices.Concat(msgs, d.tryHandleDistributedKeyGenerationOutput()) } func (d *DistributedSignature) nodePKArray() []kyber.Point { diff --git a/packages/chain/distsign/msg.go b/packages/chain/distsign/msg.go index 2def7d54cd..20ec2c8fb4 100644 --- a/packages/chain/distsign/msg.go +++ b/packages/chain/distsign/msg.go @@ -24,10 +24,10 @@ func (d *DistributedSignature) msgWrapperFunc(subsystem byte, index int) (gpa.GP return nil, fmt.Errorf("unexpected subsystem: %v", subsystem) } -func (d *DistributedSignature) UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{ - msgTypePartialSig: func() gpa.Message { return &msgPartialSig{suite: d.suite} }, - }, gpa.Fallback{ - msgTypeWrapped: d.msgWrapper.UnmarshalMessage, +func (d *DistributedSignature) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{ + msgTypePartialSig: func() gpa.MessagePayload { return &msgPartialSig{suite: d.suite} }, + }, gpa.PayloadFallback{ + msgTypeWrapped: d.msgWrapper.UnmarshalPayload, }) } diff --git a/packages/chain/distsign/msg_partial_sig.go b/packages/chain/distsign/msg_partial_sig.go index da9bad5151..56eb624fde 100644 --- a/packages/chain/distsign/msg_partial_sig.go +++ b/packages/chain/distsign/msg_partial_sig.go @@ -17,12 +17,11 @@ import ( ) type msgPartialSig struct { - gpa.BasicMessage suite suites.Suite // Transient, for un-marshaling only. partialSig *dss.PartialSig } -var _ gpa.Message = new(msgPartialSig) +var _ gpa.MessagePayload = new(msgPartialSig) func (m *msgPartialSig) MsgType() gpa.MessageType { return msgTypePartialSig diff --git a/packages/chain/distsign/msg_partial_sig_test.go b/packages/chain/distsign/msg_partial_sig_test.go index 2eb7730dcd..5117e95652 100644 --- a/packages/chain/distsign/msg_partial_sig_test.go +++ b/packages/chain/distsign/msg_partial_sig_test.go @@ -11,7 +11,6 @@ import ( "go.dedis.ch/kyber/v3/sign/dss" bcs "github.com/iotaledger/bcs-go" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/tcrypto" ) @@ -33,7 +32,6 @@ func TestMsgPartialSigSerialization(t *testing.T) { require.NoError(t, err) msg := &msgPartialSig{ - gpa.BasicMessage{}, s, partialSig, } diff --git a/packages/chain/mempool/distsync/dist_sync.go b/packages/chain/mempool/distsync/dist_sync.go index 16d5d231d7..c6ede6c5a6 100644 --- a/packages/chain/mempool/distsync/dist_sync.go +++ b/packages/chain/mempool/distsync/dist_sync.go @@ -14,7 +14,6 @@ import ( "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/log" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/isc" "github.com/iotaledger/wasp/v2/packages/util" @@ -81,7 +80,7 @@ func New( } } -func (dsi *distSyncImpl) Input(input gpa.Input) gpa.OutMessages { +func (dsi *distSyncImpl) Input(input gpa.Input) []gpa.MessageOut { dsi.log.LogDebugf("Input %T: %+v", input, input) switch input := input.(type) { case *inputServerNodes: @@ -98,12 +97,12 @@ func (dsi *distSyncImpl) Input(input gpa.Input) gpa.OutMessages { panic(fmt.Errorf("unexpected input type %T: %+v", input, input)) } -func (dsi *distSyncImpl) Message(msg gpa.Message) gpa.OutMessages { - switch msg := msg.(type) { +func (dsi *distSyncImpl) Message(msg gpa.MessageIn) []gpa.MessageOut { + switch msg.Payload.(type) { case *msgMissingRequest: - return dsi.handleMsgMissingRequest(msg) + return dsi.handleMsgMissingRequest(gpa.AsTypedMessageIn[*msgMissingRequest](msg)) case *msgShareRequest: - return dsi.handleMsgShareRequest(msg) + return dsi.handleMsgShareRequest(gpa.AsTypedMessageIn[*msgShareRequest](msg)) } dsi.log.LogWarnf("unexpected message %T: %+v", msg, msg) return nil @@ -117,7 +116,7 @@ func (dsi *distSyncImpl) StatusString() string { return fmt.Sprintf("{MP, neededReqs=%v, nodeCountToShare=%v}", dsi.needed.Size(), dsi.nodeCountToShare) } -func (dsi *distSyncImpl) handleInputServerNodes(input *inputServerNodes) gpa.OutMessages { +func (dsi *distSyncImpl) handleInputServerNodes(input *inputServerNodes) []gpa.MessageOut { dsi.log.LogDebugf("handleInputServerNodes: %v", input) dsi.handleCommitteeNodes(input.committeeNodes) dsi.serverNodes = input.serverNodes @@ -129,7 +128,7 @@ func (dsi *distSyncImpl) handleInputServerNodes(input *inputServerNodes) gpa.Out return dsi.handleInputTimeTick() // Re-send requests if node set has changed. } -func (dsi *distSyncImpl) handleInputAccessNodes(input *inputAccessNodes) gpa.OutMessages { +func (dsi *distSyncImpl) handleInputAccessNodes(input *inputAccessNodes) []gpa.MessageOut { dsi.log.LogDebugf("handleInputAccessNodes: %v", input) dsi.handleCommitteeNodes(input.committeeNodes) dsi.accessNodes = input.accessNodes @@ -154,7 +153,7 @@ func (dsi *distSyncImpl) handleCommitteeNodes(committeeNodes []gpa.NodeID) { // In the current algorithm, for sharing a message: // - Just send a message to all the committee nodes (or server nodes, if committee is not known). -func (dsi *distSyncImpl) handleInputPublishRequest(input *inputPublishRequest) gpa.OutMessages { +func (dsi *distSyncImpl) handleInputPublishRequest(input *inputPublishRequest) []gpa.MessageOut { msgs := dsi.propagateRequest(input.request) // // Delete the it from the "needed" list, if any. @@ -166,8 +165,7 @@ func (dsi *distSyncImpl) handleInputPublishRequest(input *inputPublishRequest) g return msgs } -func (dsi *distSyncImpl) propagateRequest(request isc.Request) gpa.OutMessages { - msgs := gpa.NoMessages() +func (dsi *distSyncImpl) propagateRequest(request isc.Request) []gpa.MessageOut { var publishToNodes []gpa.NodeID if len(dsi.committeeNodes) > 0 { publishToNodes = dsi.committeeNodes @@ -176,8 +174,9 @@ func (dsi *distSyncImpl) propagateRequest(request isc.Request) gpa.OutMessages { dsi.log.LogDebugf("Forwarding request %v to server nodes: %v", request.ID(), dsi.serverNodes) publishToNodes = dsi.serverNodes } + var msgs []gpa.MessageOut for i := range publishToNodes { - msgs.Add(newMsgShareRequest(request, 0, publishToNodes[i])) + msgs = append(msgs, newMsgShareRequest(request, 0, publishToNodes[i])) } return msgs } @@ -185,7 +184,7 @@ func (dsi *distSyncImpl) propagateRequest(request isc.Request) gpa.OutMessages { // For querying a message: // - First ask all the committee for the message. // - ... -func (dsi *distSyncImpl) handleInputRequestNeeded(input *inputRequestNeeded) gpa.OutMessages { +func (dsi *distSyncImpl) handleInputRequestNeeded(input *inputRequestNeeded) []gpa.MessageOut { reqRefKey := input.requestRef.AsKey() reqNeeded, have := dsi.needed.Get(reqRefKey) if have { @@ -202,9 +201,9 @@ func (dsi *distSyncImpl) handleInputRequestNeeded(input *inputRequestNeeded) gpa if dsi.needed.Set(reqRefKey, reqNeeded) { dsi.missingReqsMetric(dsi.needed.Size()) } - msgs := gpa.NoMessages() + var msgs []gpa.MessageOut for _, nid := range dsi.committeeNodes { - msgs.Add(newMsgMissingRequest(input.requestRef, nid)) + msgs = append(msgs, newMsgMissingRequest(input.requestRef, nid)) } return msgs } @@ -212,7 +211,7 @@ func (dsi *distSyncImpl) handleInputRequestNeeded(input *inputRequestNeeded) gpa // For querying a message: // - ... // - If response not received, ask random subsets of server nodes. -func (dsi *distSyncImpl) handleInputTimeTick() gpa.OutMessages { +func (dsi *distSyncImpl) handleInputTimeTick() []gpa.MessageOut { if dsi.needed.Size() == 0 { return nil } @@ -220,7 +219,7 @@ func (dsi *distSyncImpl) handleInputTimeTick() gpa.OutMessages { if nodeCount == 0 { return nil } - msgs := gpa.NoMessages() + var msgs []gpa.MessageOut nodePerm := dsi.rnd.Perm(nodeCount) counter := 0 dsi.needed.ForEach(func(reqRefKey isc.RequestRefKey, reqNeeded *distSyncReqNeeded) bool { // Access is randomized. @@ -232,7 +231,7 @@ func (dsi *distSyncImpl) handleInputTimeTick() gpa.OutMessages { } recipient := dsi.serverNodes[nodePerm[counter%nodeCount]] dsi.log.LogDebugf("Sending MsgMissingRequest for %v to %v", reqNeeded.reqRef, recipient) - msgs.Add(newMsgMissingRequest(reqNeeded.reqRef, recipient)) + msgs = append(msgs, newMsgMissingRequest(reqNeeded.reqRef, recipient)) counter++ return counter <= dsi.maxMsgsPerTick }) @@ -240,20 +239,18 @@ func (dsi *distSyncImpl) handleInputTimeTick() gpa.OutMessages { return msgs } -func (dsi *distSyncImpl) handleMsgMissingRequest(msg *msgMissingRequest) gpa.OutMessages { - req := dsi.requestNeededCB(msg.requestRef) +func (dsi *distSyncImpl) handleMsgMissingRequest(msg gpa.TypedMessageIn[*msgMissingRequest]) []gpa.MessageOut { + req := dsi.requestNeededCB(msg.Payload.requestRef) if req != nil { - msgs := gpa.NoMessages() - msgs.Add(newMsgShareRequest(req, 0, msg.Sender())) - return msgs + return []gpa.MessageOut{newMsgShareRequest(req, 0, msg.Sender)} } return nil } -func (dsi *distSyncImpl) handleMsgShareRequest(msg *msgShareRequest) gpa.OutMessages { - msgs := gpa.NoMessages() - reqRefKey := isc.RequestRefFromRequest(msg.request).AsKey() - added := dsi.requestReceivedCB(msg.request) +func (dsi *distSyncImpl) handleMsgShareRequest(msg gpa.TypedMessageIn[*msgShareRequest]) []gpa.MessageOut { + var msgs []gpa.MessageOut + reqRefKey := isc.RequestRefFromRequest(msg.Payload.request).AsKey() + added := dsi.requestReceivedCB(msg.Payload.request) if dsi.needed.Delete(reqRefKey) { dsi.missingReqsMetric(dsi.needed.Size()) } @@ -262,19 +259,19 @@ func (dsi *distSyncImpl) handleMsgShareRequest(msg *msgShareRequest) gpa.OutMess // The "outside of the committee" condition is used here to decrease echo-factor of the synchronization. // Each fair committee will send the request to all the committee nodes, thus we can avoid repeating it. // Follow the logic as if the message is received via the API. - if added && !lo.Contains(dsi.committeeNodes, msg.Sender()) { - msgs.AddAll(dsi.propagateRequest(msg.request)) + if added && !lo.Contains(dsi.committeeNodes, msg.Sender) { + msgs = slices.Concat(msgs, dsi.propagateRequest(msg.Payload.request)) } // // The following is de-factor unused, as TTL is always 0 currently. - if msg.ttl > 0 { - ttl := msg.ttl + if msg.Payload.ttl > 0 { + ttl := msg.Payload.ttl if ttl > maxTTL { ttl = maxTTL } perm := dsi.rnd.Perm(len(dsi.committeeNodes)) for i := 0; i < dsi.nodeCountToShare; i++ { - msgs.Add(newMsgShareRequest(msg.request, ttl-1, dsi.committeeNodes[perm[i]])) + msgs = append(msgs, newMsgShareRequest(msg.Payload.request, ttl-1, dsi.committeeNodes[perm[i]])) } return msgs } diff --git a/packages/chain/mempool/distsync/msg.go b/packages/chain/mempool/distsync/msg.go index b9086c4316..bd8a722510 100644 --- a/packages/chain/mempool/distsync/msg.go +++ b/packages/chain/mempool/distsync/msg.go @@ -12,9 +12,9 @@ const ( msgTypeMissingRequest ) -func (dsi *distSyncImpl) UnmarshalMessage(data []byte) (msg gpa.Message, err error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{ - msgTypeMissingRequest: func() gpa.Message { return new(msgMissingRequest) }, - msgTypeShareRequest: func() gpa.Message { return new(msgShareRequest) }, +func (dsi *distSyncImpl) UnmarshalPayload(data []byte) (msg gpa.MessagePayload, err error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{ + msgTypeMissingRequest: func() gpa.MessagePayload { return new(msgMissingRequest) }, + msgTypeShareRequest: func() gpa.MessagePayload { return new(msgShareRequest) }, }) } diff --git a/packages/chain/mempool/distsync/msg_missing_request.go b/packages/chain/mempool/distsync/msg_missing_request.go index 2c710221ee..bd00e53feb 100644 --- a/packages/chain/mempool/distsync/msg_missing_request.go +++ b/packages/chain/mempool/distsync/msg_missing_request.go @@ -9,17 +9,15 @@ import ( ) type msgMissingRequest struct { - gpa.BasicMessage requestRef *isc.RequestRef `bcs:"export"` } -var _ gpa.Message = new(msgMissingRequest) +var _ gpa.MessagePayload = new(msgMissingRequest) -func newMsgMissingRequest(requestRef *isc.RequestRef, recipient gpa.NodeID) gpa.Message { - return &msgMissingRequest{ - BasicMessage: gpa.NewBasicMessage(recipient), - requestRef: requestRef, - } +func newMsgMissingRequest(requestRef *isc.RequestRef, recipient gpa.NodeID) gpa.MessageOut { + return gpa.NewMessageOut(recipient, &msgMissingRequest{ + requestRef: requestRef, + }) } func (msg *msgMissingRequest) MsgType() gpa.MessageType { diff --git a/packages/chain/mempool/distsync/msg_missing_request_test.go b/packages/chain/mempool/distsync/msg_missing_request_test.go index 21145cdb00..dbca314ca1 100644 --- a/packages/chain/mempool/distsync/msg_missing_request_test.go +++ b/packages/chain/mempool/distsync/msg_missing_request_test.go @@ -8,7 +8,6 @@ import ( bcs "github.com/iotaledger/bcs-go" "github.com/iotaledger/wasp/v2/packages/cryptolib" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/isc" "github.com/iotaledger/wasp/v2/packages/isc/isctest" "github.com/iotaledger/wasp/v2/packages/vm/core/governance" @@ -23,14 +22,12 @@ func TestMsgMissingRequestSerialization(t *testing.T) { req := isc.NewOffLedgerRequest(isctest.RandomChainID(), isc.NewMessage(contract, entryPoint, nil), 0, gasBudget).Sign(senderKP) msg := &msgMissingRequest{ - gpa.BasicMessage{}, isc.RequestRefFromRequest(req), } bcs.TestCodec(t, msg) msg = &msgMissingRequest{ - gpa.BasicMessage{}, isc.RequestRefFromRequest( isc.NewOffLedgerRequest( isctest.TestChainID, diff --git a/packages/chain/mempool/distsync/msg_share_request.go b/packages/chain/mempool/distsync/msg_share_request.go index 30bdaec1e0..bbd208d390 100644 --- a/packages/chain/mempool/distsync/msg_share_request.go +++ b/packages/chain/mempool/distsync/msg_share_request.go @@ -9,19 +9,17 @@ import ( ) type msgShareRequest struct { - gpa.BasicMessage ttl byte `bcs:"export"` request isc.Request `bcs:"export"` } -var _ gpa.Message = new(msgShareRequest) +var _ gpa.MessagePayload = new(msgShareRequest) -func newMsgShareRequest(request isc.Request, ttl byte, recipient gpa.NodeID) gpa.Message { - return &msgShareRequest{ - BasicMessage: gpa.NewBasicMessage(recipient), - request: request, - ttl: ttl, - } +func newMsgShareRequest(request isc.Request, ttl byte, recipient gpa.NodeID) gpa.MessageOut { + return gpa.NewMessageOut(recipient, &msgShareRequest{ + request: request, + ttl: ttl, + }) } func (msg *msgShareRequest) MsgType() gpa.MessageType { diff --git a/packages/chain/mempool/distsync/msg_share_request_test.go b/packages/chain/mempool/distsync/msg_share_request_test.go index 3644841afb..e0b1264f20 100644 --- a/packages/chain/mempool/distsync/msg_share_request_test.go +++ b/packages/chain/mempool/distsync/msg_share_request_test.go @@ -12,7 +12,6 @@ import ( bcs "github.com/iotaledger/bcs-go" "github.com/iotaledger/wasp/v2/packages/cryptolib" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/isc" "github.com/iotaledger/wasp/v2/packages/isc/isctest" ) @@ -21,7 +20,6 @@ func TestMsgShareRequestSerialization(t *testing.T) { { req := isc.NewOffLedgerRequest(isctest.RandomChainID(), isc.NewMessage(3, 14, isc.NewCallArguments([]byte{1, 2, 3})), 1337, 100).Sign(cryptolib.NewKeyPair()) msg := &msgShareRequest{ - gpa.BasicMessage{}, byte(rand.Intn(math.MaxUint8)), req, } @@ -31,7 +29,6 @@ func TestMsgShareRequestSerialization(t *testing.T) { { req := isc.NewOffLedgerRequest(isctest.TestChainID, isc.NewMessage(3, 14, isc.NewCallArguments([]byte{1, 2, 3})), 1337, 100).Sign(cryptolib.TestKeyPair) msg := &msgShareRequest{ - gpa.BasicMessage{}, 123, req, } @@ -44,7 +41,6 @@ func TestMsgShareRequestSerialization(t *testing.T) { require.NoError(t, err) msg := &msgShareRequest{ - gpa.BasicMessage{}, byte(rand.Intn(math.MaxUint8)), req, } @@ -57,7 +53,6 @@ func TestMsgShareRequestSerialization(t *testing.T) { require.NoError(t, err) msg := &msgShareRequest{ - gpa.BasicMessage{}, 123, req, } diff --git a/packages/chain/mempool/mempool.go b/packages/chain/mempool/mempool.go index 4ce27f0dae..a9020a29f6 100644 --- a/packages/chain/mempool/mempool.go +++ b/packages/chain/mempool/mempool.go @@ -873,13 +873,13 @@ func (mpi *mempoolImpl) handleTrackNewChainHead(req *reqTrackNewChainHead) { } func (mpi *mempoolImpl) handleNetMessage(recv *peering.PeerMessageIn) { - msg, err := mpi.distSync.UnmarshalMessage(recv.MsgData) + msg, err := mpi.distSync.UnmarshalPayload(recv.MsgData) if err != nil { mpi.log.LogWarnf("cannot parse message: %v", err) return } - msg.SetSender(mpi.pubKeyAsNodeID(recv.SenderPubKey)) - outMsgs := mpi.distSync.Message(msg) // Output is handled via callbacks in this case. + // Output is handled via callbacks in this case. + outMsgs := mpi.distSync.Message(gpa.NewMessageIn(mpi.pubKeyAsNodeID(recv.SenderPubKey), msg)) mpi.sendMessages(outMsgs) } @@ -968,15 +968,15 @@ func (mpi *mempoolImpl) tryCleanupProcessed(chainState state.State) { mpi.offLedgerPool.Cleanup(unprocessedPredicate[isc.OffLedgerRequest](chainState, mpi.log)) } -func (mpi *mempoolImpl) sendMessages(outMsgs gpa.OutMessages) { +func (mpi *mempoolImpl) sendMessages(outMsgs []gpa.MessageOut) { if outMsgs == nil { return } - outMsgs.MustIterate(func(msg gpa.Message) { - msgBytes := lo.Must(gpa.MarshalMessage(msg)) + for _, msg := range outMsgs { + msgBytes := lo.Must(gpa.MarshalPayload(msg.Payload)) pm := peering.NewPeerMessageData(mpi.netPeeringID, peering.ReceiverMempool, msgTypeMempool, msgBytes) - mpi.net.SendMsgByPubKey(mpi.netPeerPubs[msg.Recipient()], pm) - }) + mpi.net.SendMsgByPubKey(mpi.netPeerPubs[msg.Recipient], pm) + } } func (mpi *mempoolImpl) pubKeyAsNodeID(pubKey *cryptolib.PublicKey) gpa.NodeID { diff --git a/packages/chain/node.go b/packages/chain/node.go index 8fa0d2a28f..030bab5f5a 100644 --- a/packages/chain/node.go +++ b/packages/chain/node.go @@ -662,13 +662,12 @@ func (cni *chainNodeImpl) handleMilestoneTimestamp(timestamp time.Time) { } func (cni *chainNodeImpl) handleNetMessage(recv *peering.PeerMessageIn) { - msg, err := cni.chainMgr.UnmarshalMessage(recv.MsgData) + msg, err := cni.chainMgr.UnmarshalPayload(recv.MsgData) if err != nil { cni.log.LogWarnf("cannot parse message: %v", err) return } - msg.SetSender(cni.pubKeyAsNodeID(recv.SenderPubKey)) - cni.sendMessages(cni.chainMgr.Message(msg)) + cni.sendMessages(cni.chainMgr.Message(gpa.NewMessageIn(cni.pubKeyAsNodeID(recv.SenderPubKey), msg))) } func (cni *chainNodeImpl) handleNeedConsensus(ctx context.Context, upd *chainmanager.NeedConsensusMap) { @@ -835,20 +834,17 @@ func (cni *chainNodeImpl) cleanupPublishingTXes(neededPostTXes *shrinkingmap.Shr }) } -func (cni *chainNodeImpl) sendMessages(outMsgs gpa.OutMessages) { - if outMsgs == nil { - return - } - outMsgs.MustIterate(func(msg gpa.Message) { - recipientPubKey, ok := cni.netPeerPubs[msg.Recipient()] +func (cni *chainNodeImpl) sendMessages(outMsgs []gpa.MessageOut) { + for _, msg := range outMsgs { + recipientPubKey, ok := cni.netPeerPubs[msg.Recipient] if !ok { - cni.log.LogWarnf("Pub key for the recipient not found: %v", msg.Recipient()) + cni.log.LogWarnf("Pub key for the recipient not found: %v", msg.Recipient) return } - msgBytes := lo.Must(gpa.MarshalMessage(msg)) + msgBytes := lo.Must(gpa.MarshalPayload(msg.Payload)) pm := peering.NewPeerMessageData(cni.netPeeringID, peering.ReceiverChain, msgTypeChainMgr, msgBytes) cni.net.SendMsgByPubKey(recipientPubKey, pm) - }) + } } // activeAccessNodes = ∪{{Self}, accessNodesFromNode, accessNodesFromACT, accessNodesFromCNF, activeCommitteeNodes} diff --git a/packages/chain/statemanager/gpa/messages/block_message.go b/packages/chain/statemanager/gpa/messages/block_message.go index c7ed0e4f35..0d63a3e5c4 100644 --- a/packages/chain/statemanager/gpa/messages/block_message.go +++ b/packages/chain/statemanager/gpa/messages/block_message.go @@ -8,21 +8,19 @@ import ( ) type BlockMessage struct { - gpa.BasicMessage block state.Block } -var _ gpa.Message = new(BlockMessage) +var _ gpa.MessagePayload = new(BlockMessage) -func NewBlockMessage(block state.Block, to gpa.NodeID) *BlockMessage { +func NewBlockMessage(block state.Block) *BlockMessage { return &BlockMessage{ - BasicMessage: gpa.NewBasicMessage(to), - block: block, + block: block, } } func NewEmptyBlockMessage() *BlockMessage { - return NewBlockMessage(nil, gpa.NodeID{}) + return NewBlockMessage(nil) } func (msg *BlockMessage) GetBlock() state.Block { @@ -32,7 +30,6 @@ func (msg *BlockMessage) GetBlock() state.Block { func (msg *BlockMessage) UnmarshalBCS(d *bcs.Decoder) error { msg.block = state.NewBlock() d.Decode(msg.block) - return nil } diff --git a/packages/chain/statemanager/gpa/messages/block_message_test.go b/packages/chain/statemanager/gpa/messages/block_message_test.go index 58ee878dfd..dc37cb2e28 100644 --- a/packages/chain/statemanager/gpa/messages/block_message_test.go +++ b/packages/chain/statemanager/gpa/messages/block_message_test.go @@ -5,7 +5,6 @@ import ( bcs "github.com/iotaledger/bcs-go" "github.com/iotaledger/wasp/v2/packages/chain/statemanager/gpa/utils" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/state/statetest" ) @@ -14,21 +13,19 @@ func TestBlockMessageSerialization(t *testing.T) { for i := range blocks { // note that sender/receiver node IDs are transient // so don't use a random non-null node id here - msg := NewBlockMessage(blocks[i], gpa.NodeID{}) + msg := NewBlockMessage(blocks[i]) bcs.TestCodec(t, msg) } } func TestSerializationBlockMessage(t *testing.T) { msg := &BlockMessage{ - gpa.BasicMessage{}, statetest.RandomBlock(), } bcs.TestCodec(t, msg) bcs.TestCodecAndHash(t, &BlockMessage{ - gpa.BasicMessage{}, statetest.TestBlock(), }, "453dabc9e5e2") } diff --git a/packages/chain/statemanager/gpa/messages/get_block_message.go b/packages/chain/statemanager/gpa/messages/get_block_message.go index 5fceda7f99..523bc1fef6 100644 --- a/packages/chain/statemanager/gpa/messages/get_block_message.go +++ b/packages/chain/statemanager/gpa/messages/get_block_message.go @@ -6,21 +6,19 @@ import ( ) type GetBlockMessage struct { - gpa.BasicMessage commitment *state.L1Commitment `bcs:"export"` } -var _ gpa.Message = new(GetBlockMessage) +var _ gpa.MessagePayload = new(GetBlockMessage) -func NewGetBlockMessage(commitment *state.L1Commitment, to gpa.NodeID) *GetBlockMessage { +func NewGetBlockMessage(commitment *state.L1Commitment) *GetBlockMessage { return &GetBlockMessage{ - BasicMessage: gpa.NewBasicMessage(to), - commitment: commitment, + commitment: commitment, } } func NewEmptyGetBlockMessage() *GetBlockMessage { - return NewGetBlockMessage(&state.L1Commitment{}, gpa.NodeID{}) + return NewGetBlockMessage(&state.L1Commitment{}) } func (msg *GetBlockMessage) GetL1Commitment() *state.L1Commitment { diff --git a/packages/chain/statemanager/gpa/messages/get_block_message_test.go b/packages/chain/statemanager/gpa/messages/get_block_message_test.go index f202cd6469..956d46578f 100644 --- a/packages/chain/statemanager/gpa/messages/get_block_message_test.go +++ b/packages/chain/statemanager/gpa/messages/get_block_message_test.go @@ -5,7 +5,6 @@ import ( bcs "github.com/iotaledger/bcs-go" "github.com/iotaledger/wasp/v2/packages/chain/statemanager/gpa/utils" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/state/statetest" ) @@ -15,21 +14,19 @@ func TestMarshalUnmarshalGetBlockMessage(t *testing.T) { // note that sender/receiver node IDs are transient // so don't use a random non-null node id here commitment := blocks[i].L1Commitment() - msg := NewGetBlockMessage(commitment, gpa.NodeID{}) + msg := NewGetBlockMessage(commitment) bcs.TestCodec(t, msg) } } func TestGetBlockMessageSerialization(t *testing.T) { msg := &GetBlockMessage{ - gpa.BasicMessage{}, statetest.NewRandL1Commitment(), } bcs.TestCodec(t, msg) bcs.TestCodecAndHash(t, &GetBlockMessage{ - gpa.BasicMessage{}, statetest.TestL1Commitment, }, "30dd892c3980") } diff --git a/packages/chain/statemanager/gpa/state_manager_gpa.go b/packages/chain/statemanager/gpa/state_manager_gpa.go index 2de2901d8b..251a12f1f7 100644 --- a/packages/chain/statemanager/gpa/state_manager_gpa.go +++ b/packages/chain/statemanager/gpa/state_manager_gpa.go @@ -3,13 +3,13 @@ package gpa import ( "fmt" + "slices" "time" "github.com/samber/lo" "github.com/samber/lo/mutable" "github.com/iotaledger/hive.go/log" - "github.com/iotaledger/wasp/v2/packages/chain/statemanager/gpa/inputs" "github.com/iotaledger/wasp/v2/packages/chain/statemanager/gpa/messages" gpautils "github.com/iotaledger/wasp/v2/packages/chain/statemanager/gpa/utils" @@ -92,7 +92,7 @@ func New( // Implementation for gpa.GPA interface // ------------------------------------- -func (smT *stateManagerGPA) Input(input gpa.Input) gpa.OutMessages { +func (smT *stateManagerGPA) Input(input gpa.Input) []gpa.MessageOut { switch inputCasted := input.(type) { case *inputs.ConsensusStateProposal: // From consensus return smT.handleConsensusStateProposal(inputCasted) @@ -112,12 +112,12 @@ func (smT *stateManagerGPA) Input(input gpa.Input) gpa.OutMessages { } } -func (smT *stateManagerGPA) Message(msg gpa.Message) gpa.OutMessages { - switch msgCasted := msg.(type) { +func (smT *stateManagerGPA) Message(msg gpa.MessageIn) []gpa.MessageOut { + switch msgCasted := msg.Payload.(type) { case *messages.GetBlockMessage: - return smT.handlePeerGetBlock(msgCasted.Sender(), msgCasted.GetL1Commitment()) + return smT.handlePeerGetBlock(msg.Sender, msgCasted.GetL1Commitment()) case *messages.BlockMessage: - return smT.handlePeerBlock(msgCasted.Sender(), msgCasted.GetBlock()) + return smT.handlePeerBlock(msg.Sender, msgCasted.GetBlock()) default: smT.log.LogWarnf("Unknown message received, ignoring it: type=%T, message=%v", msg, msg) return nil // No messages to send @@ -145,10 +145,10 @@ func (smT *stateManagerGPA) StatusString() string { ) } -func (smT *stateManagerGPA) UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{ - messages.MsgTypeBlockMessage: func() gpa.Message { return messages.NewEmptyBlockMessage() }, - messages.MsgTypeGetBlockMessage: func() gpa.Message { return messages.NewEmptyGetBlockMessage() }, +func (smT *stateManagerGPA) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{ + messages.MsgTypeBlockMessage: func() gpa.MessagePayload { return messages.NewEmptyBlockMessage() }, + messages.MsgTypeGetBlockMessage: func() gpa.MessagePayload { return messages.NewEmptyGetBlockMessage() }, }) } @@ -156,7 +156,7 @@ func (smT *stateManagerGPA) UnmarshalMessage(data []byte) (gpa.Message, error) { // Internal functions // ------------------------------------- -func (smT *stateManagerGPA) handlePeerGetBlock(from gpa.NodeID, commitment *state.L1Commitment) gpa.OutMessages { +func (smT *stateManagerGPA) handlePeerGetBlock(from gpa.NodeID, commitment *state.L1Commitment) []gpa.MessageOut { // TODO: [KP] Only accept queries from access nodes. fromLog := from.ShortString() smT.log.LogDebugf("Message GetBlock %s received from peer %s", commitment, fromLog) @@ -166,10 +166,10 @@ func (smT *stateManagerGPA) handlePeerGetBlock(from gpa.NodeID, commitment *stat return nil // No messages to send } smT.log.LogDebugf("Message GetBlock %s: block index %v found, sending it to peer %s", commitment, block.StateIndex(), fromLog) - return gpa.NoMessages().Add(messages.NewBlockMessage(block, from)) + return []gpa.MessageOut{gpa.NewMessageOut(from, messages.NewBlockMessage(block))} } -func (smT *stateManagerGPA) handlePeerBlock(from gpa.NodeID, block state.Block) gpa.OutMessages { +func (smT *stateManagerGPA) handlePeerBlock(from gpa.NodeID, block state.Block) []gpa.MessageOut { blockIndex := block.StateIndex() blockCommitment := block.L1Commitment() fromLog := from.ShortString() @@ -185,7 +185,7 @@ func (smT *stateManagerGPA) handlePeerBlock(from gpa.NodeID, block state.Block) return messages } -func (smT *stateManagerGPA) handleConsensusStateProposal(csp *inputs.ConsensusStateProposal) gpa.OutMessages { +func (smT *stateManagerGPA) handleConsensusStateProposal(csp *inputs.ConsensusStateProposal) []gpa.MessageOut { start := time.Now() smT.log.LogDebugf("Input consensus state proposal index %v %s received...", csp.GetStateIndex(), csp.GetL1Commitment()) callback := newBlockRequestCallback( @@ -203,7 +203,7 @@ func (smT *stateManagerGPA) handleConsensusStateProposal(csp *inputs.ConsensusSt return messages } -func (smT *stateManagerGPA) handleConsensusDecidedState(cds *inputs.ConsensusDecidedState) gpa.OutMessages { +func (smT *stateManagerGPA) handleConsensusDecidedState(cds *inputs.ConsensusDecidedState) []gpa.MessageOut { start := time.Now() smT.log.LogDebugf("Input consensus decided state index %v %s received...", cds.GetStateIndex(), cds.GetL1Commitment()) callback := newBlockRequestCallback( @@ -227,7 +227,7 @@ func (smT *stateManagerGPA) handleConsensusDecidedState(cds *inputs.ConsensusDec return messages } -func (smT *stateManagerGPA) handleConsensusBlockProduced(input *inputs.ConsensusBlockProduced) gpa.OutMessages { +func (smT *stateManagerGPA) handleConsensusBlockProduced(input *inputs.ConsensusBlockProduced) []gpa.MessageOut { start := time.Now() stateIndex := input.GetStateDraft().BlockIndex() - 1 // NOTE: as this state draft is complete, the returned index is the one of the next state (which will be obtained, once this state draft is committed); to get the index of the base state, we need to subtract one commitment := input.GetStateDraft().BaseL1Commitment() @@ -243,7 +243,7 @@ func (smT *stateManagerGPA) handleConsensusBlockProduced(input *inputs.Consensus smT.log.LogDebugf("Input block produced on state index %v %s: state draft has been committed to the store, responded to consensus with resulting block index %v %s", stateIndex, commitment, block.StateIndex(), blockCommitment) fetcher := smT.blocksToFetch.takeFetcher(blockCommitment) - var result gpa.OutMessages + var result []gpa.MessageOut if fetcher != nil { result = smT.markFetched(fetcher, false) } @@ -252,7 +252,7 @@ func (smT *stateManagerGPA) handleConsensusBlockProduced(input *inputs.Consensus return result // No messages to send } -func (smT *stateManagerGPA) handleChainFetchStateDiff(input *inputs.ChainFetchStateDiff) gpa.OutMessages { +func (smT *stateManagerGPA) handleChainFetchStateDiff(input *inputs.ChainFetchStateDiff) []gpa.MessageOut { start := time.Now() smT.log.LogDebugf("Input mempool state request for state index %v %s is received compared to state index %v %s...", input.GetNewStateIndex(), input.GetNewL1Commitment(), input.GetOldStateIndex(), input.GetOldL1Commitment()) @@ -276,9 +276,10 @@ func (smT *stateManagerGPA) handleChainFetchStateDiff(input *inputs.ChainFetchSt input.GetNewStateIndex(), input.GetNewL1Commitment()) respondIfNeededFun() }) - result := gpa.NoMessages() - result.AddAll(smT.traceBlockChainWithCallback(input.GetOldStateIndex(), input.GetOldL1Commitment(), oldRequestCallback)) - result.AddAll(smT.traceBlockChainWithCallback(input.GetNewStateIndex(), input.GetNewL1Commitment(), newRequestCallback)) + result := slices.Concat( + smT.traceBlockChainWithCallback(input.GetOldStateIndex(), input.GetOldL1Commitment(), oldRequestCallback), + smT.traceBlockChainWithCallback(input.GetNewStateIndex(), input.GetNewL1Commitment(), newRequestCallback), + ) smT.log.LogDebugf("Input mempool state request for state index %v %s handled", input.GetNewStateIndex(), input.GetNewL1Commitment()) return result @@ -375,17 +376,17 @@ func (smT *stateManagerGPA) handleChainFetchStateDiffRespond(input *inputs.Chain smT.metrics.ChainFetchStateDiffHandled(time.Since(start)) } -func (smT *stateManagerGPA) handleStateManagerBlocksToCommit(commitments []*state.L1Commitment) gpa.OutMessages { +func (smT *stateManagerGPA) handleStateManagerBlocksToCommit(commitments []*state.L1Commitment) []gpa.MessageOut { start := time.Now() smT.log.LogDebugf("Input state manager blocks to commit %s is received", commitments) - result := gpa.NoMessages() + var result []gpa.MessageOut for _, commitment := range commitments { fetcher := smT.blocksFetched.takeFetcher(commitment) if fetcher == nil { smT.log.LogWarnf("Input state manager blocks to commit %s: blocks waiting to be committed does not contain block %s; probably it is has already been committed", commitments, commitment) } else { - result.AddAll(smT.markFetched(fetcher, true)) + result = slices.Concat(result, smT.markFetched(fetcher, true)) } } smT.log.LogDebugf("Input state manager blocks to commit %s handled", commitments) @@ -424,7 +425,7 @@ func (smT *stateManagerGPA) getBlock(commitment *state.L1Commitment) state.Block return block } -func (smT *stateManagerGPA) traceBlockChainWithCallback(index uint32, lastCommitment *state.L1Commitment, callback blockRequestCallback) gpa.OutMessages { +func (smT *stateManagerGPA) traceBlockChainWithCallback(index uint32, lastCommitment *state.L1Commitment, callback blockRequestCallback) []gpa.MessageOut { if smT.store.HasTrieRoot(lastCommitment.TrieRoot()) { smT.log.LogDebugf("Tracing block index %v %s chain: the block is already in the store, calling back", index, lastCommitment) callback.requestCompleted() @@ -449,7 +450,7 @@ func (smT *stateManagerGPA) traceBlockChainWithCallback(index uint32, lastCommit // formulated as "give me blocks from some commitment till some index". If the // requested node has the required block committed into the store, it certainly // has all the blocks before it. -func (smT *stateManagerGPA) traceBlockChain(initFetcher blockFetcher) gpa.OutMessages { +func (smT *stateManagerGPA) traceBlockChain(initFetcher blockFetcher) []gpa.MessageOut { var fetcher blockFetcher var previousCommitment *state.L1Commitment for fetcher = initFetcher; !smT.store.HasTrieRoot(fetcher.getCommitment().TrieRoot()); fetcher = newBlockFetcherWithRelatedFetcher(previousCommitment, fetcher) { @@ -502,7 +503,7 @@ func (smT *stateManagerGPA) traceBlockChain(initFetcher blockFetcher) gpa.OutMes return result } -func (smT *stateManagerGPA) markFetched(fetcher blockFetcher, doCommit bool) gpa.OutMessages { +func (smT *stateManagerGPA) markFetched(fetcher blockFetcher, doCommit bool) []gpa.MessageOut { if doCommit { commitment := fetcher.getCommitment() block := smT.blockCache.GetBlock(commitment) @@ -511,7 +512,7 @@ func (smT *stateManagerGPA) markFetched(fetcher blockFetcher, doCommit bool) gpa // for some unexpected reasons it is not in WAL: rerequest it smT.log.LogWarnf("Block %s was previously obtained, but it can neither be found in cache nor in WAL. Rerequesting it.", commitment) smT.blocksToFetch.addFetcher(fetcher) - return gpa.NoMessages().AddAll(smT.makeGetBlockRequestMessages(commitment)) + return smT.makeGetBlockRequestMessages(commitment) } blockIndex := block.StateIndex() // Commit block @@ -548,18 +549,16 @@ func (smT *stateManagerGPA) markFetched(fetcher blockFetcher, doCommit bool) gpa } // Make `numberOfNodesToRequestBlockFromConst` messages to random peers -func (smT *stateManagerGPA) makeGetBlockRequestMessages(commitment *state.L1Commitment) gpa.OutMessages { +func (smT *stateManagerGPA) makeGetBlockRequestMessages(commitment *state.L1Commitment) []gpa.MessageOut { nodeIDs := smT.nodeRandomiser.GetRandomOtherNodeIDs(smT.parameters.StateManagerGetBlockNodeCount) - response := gpa.NoMessages() - for _, nodeID := range nodeIDs { - response.Add(messages.NewGetBlockMessage(commitment, nodeID)) - } - return response + return lo.Map(nodeIDs, func(nodeID gpa.NodeID, _ int) gpa.MessageOut { + return gpa.NewMessageOut(nodeID, messages.NewGetBlockMessage(commitment)) + }) } -func (smT *stateManagerGPA) handleStateManagerTimerTick(now time.Time) gpa.OutMessages { +func (smT *stateManagerGPA) handleStateManagerTimerTick(now time.Time) []gpa.MessageOut { start := time.Now() - result := gpa.NoMessages() + var result []gpa.MessageOut nextStatusLogTime := smT.lastStatusLogTime.Add(smT.parameters.StateManagerStatusLogPeriod) if now.After(nextStatusLogTime) { smT.log.LogDebugf("State manager gpa status: %s", smT.StatusString()) @@ -569,7 +568,7 @@ func (smT *stateManagerGPA) handleStateManagerTimerTick(now time.Time) gpa.OutMe if now.After(nextGetBlocksTime) { commitments := smT.blocksToFetch.getCommitments() for _, commitment := range commitments { - result.AddAll(smT.makeGetBlockRequestMessages(commitment)) + result = slices.Concat(result, smT.makeGetBlockRequestMessages(commitment)) } smT.lastGetBlocksTime = now smT.log.LogDebugf("Resent getBlock messages for blocks %s, next resend not earlier than %v", diff --git a/packages/chain/statemanager/state_manager.go b/packages/chain/statemanager/state_manager.go index ad6af24822..6839417014 100644 --- a/packages/chain/statemanager/state_manager.go +++ b/packages/chain/statemanager/state_manager.go @@ -311,13 +311,12 @@ func (smT *stateManager) handleInput(input gpa.Input) { } func (smT *stateManager) handleMessage(peerMsg *peering.PeerMessageIn) { - msg, err := smT.stateManagerGPA.UnmarshalMessage(peerMsg.MsgData) + msg, err := smT.stateManagerGPA.UnmarshalPayload(peerMsg.MsgData) if err != nil { smT.log.LogWarnf("Parsing message failed: %v", err) return } - msg.SetSender(gpa.NodeIDFromPublicKey(peerMsg.SenderPubKey)) - outMsgs := smT.stateManagerGPA.Message(msg) + outMsgs := smT.stateManagerGPA.Message(gpa.NewMessageIn(gpa.NodeIDFromPublicKey(peerMsg.SenderPubKey), msg)) smT.sendMessages(outMsgs) smT.handleOutput() } @@ -386,18 +385,15 @@ func (smT *stateManager) handleTimerTick(now time.Time) { smT.handleInput(inputs.NewStateManagerTimerTick(now)) } -func (smT *stateManager) sendMessages(outMsgs gpa.OutMessages) { - if outMsgs == nil { - return - } - outMsgs.MustIterate(func(msg gpa.Message) { - msgBytes := lo.Must(gpa.MarshalMessage(msg)) +func (smT *stateManager) sendMessages(outMsgs []gpa.MessageOut) { + for _, msg := range outMsgs { + msgBytes := lo.Must(gpa.MarshalPayload(msg.Payload)) pm := peering.NewPeerMessageData(smT.netPeeringID, peering.ReceiverStateManager, constMsgTypeStm, msgBytes) - recipientPubKey, ok := smT.nodeIDToPubKey[msg.Recipient()] + recipientPubKey, ok := smT.nodeIDToPubKey[msg.Recipient] if !ok { - smT.log.LogDebugf("Dropping outgoing message, because NodeID=%s it is not in the NodeList.", msg.Recipient().ShortString()) + smT.log.LogDebugf("Dropping outgoing message, because NodeID=%s it is not in the NodeList.", msg.Recipient.ShortString()) return } smT.net.SendMsgByPubKey(recipientPubKey, pm) - }) + } } diff --git a/packages/chains/accessmanager/access_manager.go b/packages/chains/accessmanager/access_manager.go index 02ba32bda6..96c326ee56 100644 --- a/packages/chains/accessmanager/access_manager.go +++ b/packages/chains/accessmanager/access_manager.go @@ -192,31 +192,28 @@ func (ami *AccessMgr) handleDistTimeTick(timestamp time.Time) { } func (ami *AccessMgr) handleNetMessage(recv *peering.PeerMessageIn) { - msg, err := ami.dist.UnmarshalMessage(recv.MsgData) + msg, err := ami.dist.UnmarshalPayload(recv.MsgData) if err != nil { ami.log.LogWarnf("cannot parse message: %v", err) return } - msg.SetSender(ami.pubKeyAsNodeID(recv.SenderPubKey)) - outMsgs := ami.dist.Message(msg) // Output is handled via callbacks in this case. + // Output is handled via callbacks in this case. + outMsgs := ami.dist.Message(gpa.NewMessageIn(ami.pubKeyAsNodeID(recv.SenderPubKey), msg)) ami.sendMessages(outMsgs) } -func (ami *AccessMgr) sendMessages(outMsgs gpa.OutMessages) { +func (ami *AccessMgr) sendMessages(outMsgs []gpa.MessageOut) { if len(ami.dismissPeerBuf) != 0 { for _, dismissPeerPub := range ami.dismissPeerBuf { ami.dist.DismissPeer(ami.pubKeyAsNodeID(dismissPeerPub)) } ami.dismissPeerBuf = []*cryptolib.PublicKey{} } - if outMsgs == nil { - return - } - outMsgs.MustIterate(func(msg gpa.Message) { - msgBytes := lo.Must(gpa.MarshalMessage(msg)) + for _, msg := range outMsgs { + msgBytes := lo.Must(gpa.MarshalPayload(msg.Payload)) pm := peering.NewPeerMessageData(ami.netPeeringID, peering.ReceiverAccessMgr, msgTypeAccessMgr, msgBytes) - ami.net.SendMsgByPubKey(ami.netPeerPubs[msg.Recipient()], pm) - }) + ami.net.SendMsgByPubKey(ami.netPeerPubs[msg.Recipient], pm) + } } func (ami *AccessMgr) pubKeyAsNodeID(pubKey *cryptolib.PublicKey) gpa.NodeID { diff --git a/packages/chains/accessmanager/dist/access_manager_dist.go b/packages/chains/accessmanager/dist/access_manager_dist.go index bbaa6ceeb5..c732170e19 100644 --- a/packages/chains/accessmanager/dist/access_manager_dist.go +++ b/packages/chains/accessmanager/dist/access_manager_dist.go @@ -13,6 +13,7 @@ package dist import ( "fmt" + "slices" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/log" @@ -72,7 +73,7 @@ func (amd *accessMgrDist) ChainServerNodes(chainID isc.ChainID) []*cryptolib.Pub } // Implements the gpa.GPA interface. -func (amd *accessMgrDist) Input(input gpa.Input) gpa.OutMessages { +func (amd *accessMgrDist) Input(input gpa.Input) []gpa.MessageOut { switch input := input.(type) { case *inputChainDisabled: return amd.handleInputChainDisabled(input) @@ -85,9 +86,9 @@ func (amd *accessMgrDist) Input(input gpa.Input) gpa.OutMessages { } // Implements the gpa.GPA interface. -func (amd *accessMgrDist) Message(msg gpa.Message) gpa.OutMessages { - if msg, ok := msg.(*msgAccess); ok { - return amd.handleMsgAccess(msg) +func (amd *accessMgrDist) Message(msg gpa.MessageIn) []gpa.MessageOut { + if _, ok := msg.Payload.(*msgAccess); ok { + return amd.handleMsgAccess(gpa.AsTypedMessageIn[*msgAccess](msg)) } panic(fmt.Errorf("unexpected message %T: %+v", msg, msg)) } @@ -103,16 +104,16 @@ func (amd *accessMgrDist) StatusString() string { } // > Notify all the trusted access nodes, that we will not serve the requests anymore. -func (amd *accessMgrDist) handleInputChainDisabled(input *inputChainDisabled) gpa.OutMessages { +func (amd *accessMgrDist) handleInputChainDisabled(input *inputChainDisabled) []gpa.MessageOut { chain, exists := amd.chains.Get(input.chainID) if !exists { return nil // Already disabled. } chain.Disabled() amd.chains.Delete(input.chainID) - msgs := gpa.NoMessages() + var msgs []gpa.MessageOut amd.nodes.ForEach(func(_ gpa.NodeID, node *accessMgrNode) bool { - msgs.AddAll(node.SetChainAccess(input.chainID, false)) + msgs = slices.Concat(msgs, node.SetChainAccess(input.chainID, false)) return true }) return msgs @@ -122,7 +123,7 @@ func (amd *accessMgrDist) handleInputChainDisabled(input *inputChainDisabled) gp // // > Send disabled for nodes not in the access list anymore. // > Send enabled for new access nodes. -func (amd *accessMgrDist) handleInputAccessNodes(input *inputAccessNodes) gpa.OutMessages { +func (amd *accessMgrDist) handleInputAccessNodes(input *inputAccessNodes) []gpa.MessageOut { // // Update the info from the chain perspective. chain, exists := amd.chains.Get(input.chainID) @@ -140,16 +141,16 @@ func (amd *accessMgrDist) handleInputAccessNodes(input *inputAccessNodes) gpa.Ou chain.AccessGrantedFor(input.accessNodes) // // Update the info for each node. - msgs := gpa.NoMessages() + var msgs []gpa.MessageOut amd.nodes.ForEach(func(nodeID gpa.NodeID, node *accessMgrNode) bool { - msgs.AddAll(node.SetChainAccess(input.chainID, chain.IsAccessGrantedFor(nodeID))) + msgs = slices.Concat(msgs, node.SetChainAccess(input.chainID, chain.IsAccessGrantedFor(nodeID))) return true }) return msgs } -func (amd *accessMgrDist) handleInputTrustedNodes(input *inputTrustedNodes) gpa.OutMessages { - msgs := gpa.NoMessages() +func (amd *accessMgrDist) handleInputTrustedNodes(input *inputTrustedNodes) []gpa.MessageOut { + var msgs []gpa.MessageOut // // Setup new nodes. trustedIndex := map[gpa.NodeID]bool{} @@ -167,7 +168,7 @@ func (amd *accessMgrDist) handleInputTrustedNodes(input *inputTrustedNodes) gpa. return true }) trustedNode, trustedNodeMsgs := newAccessMgrNode(trustedNodeID, trustedNodePubKey, accessFor) - msgs.AddAll(trustedNodeMsgs) + msgs = slices.Concat(msgs, trustedNodeMsgs) amd.nodes.Set(trustedNodeID, trustedNode) } // @@ -178,7 +179,7 @@ func (amd *accessMgrDist) handleInputTrustedNodes(input *inputTrustedNodes) gpa. } amd.chains.ForEach(func(_ isc.ChainID, chain *accessMgrChain) bool { chain.MarkAsServerFor(node.pubKey, false) - msgs.AddAll(node.SetChainAccess(chain.chainID, false)) + msgs = slices.Concat(msgs, node.SetChainAccess(chain.chainID, false)) return true }) amd.nodes.Delete(nodeID) @@ -188,8 +189,8 @@ func (amd *accessMgrDist) handleInputTrustedNodes(input *inputTrustedNodes) gpa. return msgs } -func (amd *accessMgrDist) handleMsgAccess(msg *msgAccess) gpa.OutMessages { - node, exists := amd.nodes.Get(msg.Sender()) +func (amd *accessMgrDist) handleMsgAccess(msg gpa.TypedMessageIn[*msgAccess]) []gpa.MessageOut { + node, exists := amd.nodes.Get(msg.Sender) if !exists { return nil } @@ -291,7 +292,7 @@ func newAccessMgrNode( nodeID gpa.NodeID, pubKey *cryptolib.PublicKey, accessFor *chainSet, -) (*accessMgrNode, gpa.OutMessages) { +) (*accessMgrNode, []gpa.MessageOut) { amn := &accessMgrNode{ nodeID: nodeID, pubKey: pubKey, @@ -300,41 +301,42 @@ func newAccessMgrNode( accessFor: accessFor, serverFor: newChainSet(), } - msgs := gpa.NoMessages() - msgs.Add(newMsgAccess(amn.nodeID, amn.ourLC, amn.peerLC, amn.accessFor.AsSlice(), amn.serverFor.AsSlice())) + msgs := []gpa.MessageOut{ + newMsgAccess(amn.nodeID, amn.ourLC, amn.peerLC, amn.accessFor.AsSlice(), amn.serverFor.AsSlice()), + } return amn, msgs } -func (amn *accessMgrNode) SetChainAccess(chainID isc.ChainID, access bool) gpa.OutMessages { +func (amn *accessMgrNode) SetChainAccess(chainID isc.ChainID, access bool) []gpa.MessageOut { if access { return amn.grantAccess(chainID) } return amn.revokeAccess(chainID) } -func (amn *accessMgrNode) grantAccess(chainID isc.ChainID) gpa.OutMessages { +func (amn *accessMgrNode) grantAccess(chainID isc.ChainID) []gpa.MessageOut { if amn.accessFor.Has(chainID) { return nil } amn.accessFor.Add(chainID) amn.ourLC++ - msgs := gpa.NoMessages() - msgs.Add(newMsgAccess(amn.nodeID, amn.ourLC, amn.peerLC, amn.accessFor.AsSlice(), amn.serverFor.AsSlice())) - return msgs + return []gpa.MessageOut{ + newMsgAccess(amn.nodeID, amn.ourLC, amn.peerLC, amn.accessFor.AsSlice(), amn.serverFor.AsSlice()), + } } -func (amn *accessMgrNode) revokeAccess(chainID isc.ChainID) gpa.OutMessages { +func (amn *accessMgrNode) revokeAccess(chainID isc.ChainID) []gpa.MessageOut { if !amn.accessFor.Has(chainID) { return nil } amn.accessFor.Delete(chainID) amn.ourLC++ - msgs := gpa.NoMessages() - msgs.Add(newMsgAccess(amn.nodeID, amn.ourLC, amn.peerLC, amn.accessFor.AsSlice(), amn.serverFor.AsSlice())) - return msgs + return []gpa.MessageOut{ + newMsgAccess(amn.nodeID, amn.ourLC, amn.peerLC, amn.accessFor.AsSlice(), amn.serverFor.AsSlice()), + } } -func (amn *accessMgrNode) handleMsgAccess(msg *msgAccess) gpa.OutMessages { +func (amn *accessMgrNode) handleMsgAccess(msg gpa.TypedMessageIn[*msgAccess]) []gpa.MessageOut { // This has to be checked before updating the state. // > IF /\ m.access = serverForChains(n, m.src) \* Peer's info hasn't changed, so we don't need to ack it. // > /\ m.server = H(accessForChains(n, m.src)) \* Our info echoed, so that was an ack. @@ -343,22 +345,22 @@ func (amn *accessMgrNode) handleMsgAccess(msg *msgAccess) gpa.OutMessages { // > THEN sendAndAck(m, {}) // > ELSE sendAndAck(m, accessMsgs(n)) sendDone := true && - util.Same(msg.accessForChains, amn.serverFor.AsSlice()) && - util.Same(msg.serverForChains, amn.accessFor.AsSlice()) && - msg.senderLClock >= amn.peerLC && - msg.receiverLClock <= amn.ourLC + util.Same(msg.Payload.accessForChains, amn.serverFor.AsSlice()) && + util.Same(msg.Payload.serverForChains, amn.accessFor.AsSlice()) && + msg.Payload.senderLClock >= amn.peerLC && + msg.Payload.receiverLClock <= amn.ourLC // // Update serverFor and peerLC. - if msg.senderLClock > amn.peerLC { - amn.serverFor.FromSlice(msg.accessForChains) - amn.peerLC = msg.senderLClock + if msg.Payload.senderLClock > amn.peerLC { + amn.serverFor.FromSlice(msg.Payload.accessForChains) + amn.peerLC = msg.Payload.senderLClock } // // Update ourLC. - if amn.ourLC <= msg.receiverLClock { - amn.ourLC = msg.receiverLClock + if amn.ourLC <= msg.Payload.receiverLClock { + amn.ourLC = msg.Payload.receiverLClock msgServerFor := newChainSet() - msgServerFor.FromSlice(msg.serverForChains) + msgServerFor.FromSlice(msg.Payload.serverForChains) if !amn.accessFor.Equals(msgServerFor) { amn.ourLC++ } @@ -366,9 +368,9 @@ func (amn *accessMgrNode) handleMsgAccess(msg *msgAccess) gpa.OutMessages { // // Send message back, if needed. if !sendDone { - return gpa.NoMessages().Add( - newMsgAccess(msg.Sender(), amn.ourLC, amn.peerLC, amn.accessFor.AsSlice(), amn.serverFor.AsSlice()), - ) + return []gpa.MessageOut{ + newMsgAccess(msg.Sender, amn.ourLC, amn.peerLC, amn.accessFor.AsSlice(), amn.serverFor.AsSlice()), + } } return nil } diff --git a/packages/chains/accessmanager/dist/msg.go b/packages/chains/accessmanager/dist/msg.go index d528098dcd..132a760d48 100644 --- a/packages/chains/accessmanager/dist/msg.go +++ b/packages/chains/accessmanager/dist/msg.go @@ -11,8 +11,8 @@ const ( msgTypeAccess gpa.MessageType = iota ) -func (amd *accessMgrDist) UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{ - msgTypeAccess: func() gpa.Message { return new(msgAccess) }, +func (amd *accessMgrDist) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{ + msgTypeAccess: func() gpa.MessagePayload { return new(msgAccess) }, }) } diff --git a/packages/chains/accessmanager/dist/msg_access.go b/packages/chains/accessmanager/dist/msg_access.go index e68274e437..43eb04dbde 100644 --- a/packages/chains/accessmanager/dist/msg_access.go +++ b/packages/chains/accessmanager/dist/msg_access.go @@ -10,28 +10,26 @@ import ( // Send by a node which has a chain enabled to a node it considers an access node. type msgAccess struct { - gpa.BasicMessage senderLClock int `bcs:"export,type=u32"` receiverLClock int `bcs:"export,type=u32"` accessForChains []isc.ChainID `bcs:"export,len_bytes=2"` serverForChains []isc.ChainID `bcs:"export,len_bytes=2"` } -var _ gpa.Message = new(msgAccess) +var _ gpa.MessagePayload = new(msgAccess) func newMsgAccess( recipient gpa.NodeID, senderLClock, receiverLClock int, accessForChains []isc.ChainID, serverForChains []isc.ChainID, -) gpa.Message { - return &msgAccess{ - BasicMessage: gpa.NewBasicMessage(recipient), +) gpa.MessageOut { + return gpa.NewMessageOut(recipient, &msgAccess{ senderLClock: senderLClock, receiverLClock: receiverLClock, accessForChains: accessForChains, serverForChains: serverForChains, - } + }) } func (msg *msgAccess) MsgType() gpa.MessageType { diff --git a/packages/chains/accessmanager/dist/msg_access_test.go b/packages/chains/accessmanager/dist/msg_access_test.go index e1d0318528..fe8087a8d0 100644 --- a/packages/chains/accessmanager/dist/msg_access_test.go +++ b/packages/chains/accessmanager/dist/msg_access_test.go @@ -9,14 +9,12 @@ import ( "testing" bcs "github.com/iotaledger/bcs-go" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/isc" "github.com/iotaledger/wasp/v2/packages/isc/isctest" ) func TestMsgAccessSerialization(t *testing.T) { msg := &msgAccess{ - gpa.BasicMessage{}, rand.Intn(math.MaxUint32 + 1), rand.Intn(math.MaxUint32 + 1), []isc.ChainID{isctest.RandomChainID(), isctest.RandomChainID()}, @@ -26,7 +24,6 @@ func TestMsgAccessSerialization(t *testing.T) { bcs.TestCodec(t, msg) msg = &msgAccess{ - gpa.BasicMessage{}, math.MaxUint32, math.MaxUint32, []isc.ChainID{isctest.RandomChainID(), isctest.RandomChainID()}, diff --git a/packages/gpa/aba/craig/aba.go b/packages/gpa/aba/craig/aba.go deleted file mode 100644 index 57987fba33..0000000000 --- a/packages/gpa/aba/craig/aba.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// Package craig implements Craig's "Good-Case-Coin-Free" ABA consensus. -package craig - -import ( - "errors" - - "github.com/iotaledger/wasp/v2/packages/gpa" -) - -type abaImpl struct{} - -var _ gpa.GPA = &abaImpl{} - -func New() gpa.GPA { - return nil -} - -func (a *abaImpl) Input(input gpa.Input) gpa.OutMessages { - return nil -} - -func (a *abaImpl) Message(msg gpa.Message) gpa.OutMessages { - return nil -} - -func (a *abaImpl) Output() gpa.Output { - return nil -} - -func (a *abaImpl) StatusString() string { - return "{ABA:Craig, TBD}" -} - -func (a *abaImpl) UnmarshalMessage(data []byte) (gpa.Message, error) { - return nil, errors.New("not implemented") // TODO: XXX: Impl. -} diff --git a/packages/gpa/aba/mostefaoui/mostefaoui.go b/packages/gpa/aba/mostefaoui/mostefaoui.go index c112184f4a..63f60a177f 100644 --- a/packages/gpa/aba/mostefaoui/mostefaoui.go +++ b/packages/gpa/aba/mostefaoui/mostefaoui.go @@ -72,6 +72,7 @@ package mostefaoui import ( "fmt" + "slices" "github.com/iotaledger/hive.go/log" @@ -93,20 +94,20 @@ const ( ) type ABA struct { - nodeIDs []gpa.NodeID // Nodes in the consensus. - nodeIdx map[gpa.NodeID]bool // For a fast check, if peer is known. - round int // The current round. - varBinVals *varBinVals // The `binValues` variable (based on BVAL msgs). - varAuxVals *varAuxVals // The `vals` variable (based on AUX msgs). - varDone *varDone // Termination condition. - uponDecisionInputs *uponDecisionInputs // Decision condition. - ccInsts []gpa.GPA // Common coin instances for all the rounds. - ccCreateFun func(round int) gpa.GPA // Function to create CC instances. - output *Output // The current output of the algorithm. - postponedMsgs []*msgVote // Buffer for future round messages. - msgWrapper *gpa.MsgWrapper // Helper to wrap messages for sub-components. - asGPA gpa.GPA // This object, but with required wrappers. - log log.Logger // A logger. + nodeIDs []gpa.NodeID // Nodes in the consensus. + nodeIdx map[gpa.NodeID]bool // For a fast check, if peer is known. + round int // The current round. + varBinVals *varBinVals // The `binValues` variable (based on BVAL msgs). + varAuxVals *varAuxVals // The `vals` variable (based on AUX msgs). + varDone *varDone // Termination condition. + uponDecisionInputs *uponDecisionInputs // Decision condition. + ccInsts []gpa.GPA // Common coin instances for all the rounds. + ccCreateFun func(round int) gpa.GPA // Function to create CC instances. + output *Output // The current output of the algorithm. + postponedMsgs []gpa.TypedMessageIn[*msgVote] // Buffer for future round messages. + msgWrapper *gpa.MsgWrapper // Helper to wrap messages for sub-components. + asGPA gpa.GPA // This object, but with required wrappers. + log log.Logger // A logger. } var _ gpa.GPA = &ABA{} @@ -128,7 +129,7 @@ func New(nodeIDs []gpa.NodeID, me gpa.NodeID, f int, ccCreateFun func(round int) ccInsts: []gpa.GPA{}, ccCreateFun: ccCreateFun, output: nil, - postponedMsgs: []*msgVote{}, + postponedMsgs: []gpa.TypedMessageIn[*msgVote]{}, log: log, } a.varBinVals = newBinVals(nodeIDs, f, a.uponBinValuesUpdated) @@ -173,7 +174,7 @@ func (a *ABA) AsGPA() gpa.GPA { // // > • upon receiving input b_input, set est_0 := b_input and proceed as // > follows in consecutive epochs, with increasing labels r: -func (a *ABA) Input(input gpa.Input) gpa.OutMessages { +func (a *ABA) Input(input gpa.Input) []gpa.MessageOut { if a.round != -1 { panic(fmt.Errorf("duplicate input to BBA: %v", input)) } @@ -187,7 +188,7 @@ func (a *ABA) Input(input gpa.Input) gpa.OutMessages { // // > – multicast BVAL_r(est_r) // > – bin_values_r := {} -func (a *ABA) startRound(round int, est bool) gpa.OutMessages { +func (a *ABA) startRound(round int, est bool) []gpa.MessageOut { if a.output != nil && a.output.Terminated { // Don't start the next round if the algorithm is already terminated. return nil @@ -195,29 +196,28 @@ func (a *ABA) startRound(round int, est bool) gpa.OutMessages { if round != a.round+1 { panic(fmt.Errorf("non-sequential rounds %v->%v", a.round, round)) } - msgs := gpa.NoMessages() a.round = round a.varAuxVals.startRound(a.round) a.varDone.startRound(round) a.uponDecisionInputs.startRound() - msgs.AddAll(a.varBinVals.startRound(a.round, est)) + msgs := a.varBinVals.startRound(a.round, est) // // Start the CC. subGPA, subMsgs, err := a.msgWrapper.DelegateInput(subsystemCC, round, nil) if err != nil { panic(fmt.Errorf("failed to provide input to CC: %v", err)) } - msgs.AddAll(subMsgs) + msgs = slices.Concat(msgs, subMsgs) if out := subGPA.Output(); out != nil { - msgs.AddAll(a.uponDecisionInputs.ccOutputReceived(*out.(*bool))) + msgs = slices.Concat(msgs, a.uponDecisionInputs.ccOutputReceived(*out.(*bool))) } // // Resend postponed messages, if any. if len(a.postponedMsgs) > 0 { oldPostponedMsgs := a.postponedMsgs - a.postponedMsgs = []*msgVote{} + a.postponedMsgs = []gpa.TypedMessageIn[*msgVote]{} for _, m := range oldPostponedMsgs { - msgs.AddAll(a.handleMsgVote(m)) + msgs = slices.Concat(msgs, a.handleMsgVote(m)) } } return msgs @@ -225,32 +225,32 @@ func (a *ABA) startRound(round int, est bool) gpa.OutMessages { // Message implements the gpa.GPA interface. // Here we only route the messages to appropriate objects. -func (a *ABA) Message(msg gpa.Message) gpa.OutMessages { - switch msgT := msg.(type) { +func (a *ABA) Message(msg gpa.MessageIn) []gpa.MessageOut { + switch msg.Payload.(type) { case *msgVote: // The BVAL and AUX messages. - return a.handleMsgVote(msgT) + return a.handleMsgVote(gpa.AsTypedMessageIn[*msgVote](msg)) case *msgDone: // The DONE messages for the termination. - return a.handleMsgDone(msgT) + return a.handleMsgDone(gpa.AsTypedMessageIn[*msgDone](msg)) case *gpa.WrappingMsg: // The CC messages. - return a.handleMsgWrapped(msgT) + return a.handleMsgWrapped(gpa.AsTypedMessageIn[*gpa.WrappingMsg](msg)) } a.log.LogWarnf("unexpected message of type %T: %+v", msg, msg) return nil } -func (a *ABA) handleMsgVote(msgT *msgVote) gpa.OutMessages { - if _, ok := a.nodeIdx[msgT.Sender()]; !ok { +func (a *ABA) handleMsgVote(msgT gpa.TypedMessageIn[*msgVote]) []gpa.MessageOut { + if _, ok := a.nodeIdx[msgT.Sender]; !ok { a.log.LogWarnf("unknown sender: %+v", msgT) return nil // Unknown sender. } - if msgT.round < a.round || (a.output != nil && a.output.Terminated) { + if msgT.Payload.round < a.round || (a.output != nil && a.output.Terminated) { return nil // Outdated message. } - if msgT.round > a.round { + if msgT.Payload.round > a.round { a.postponedMsgs = append(a.postponedMsgs, msgT) return nil // Will be processed later. } - switch msgT.voteType { + switch msgT.Payload.voteType { case BVAL: return a.varBinVals.msgVoteBVALReceived(msgT) case AUX: @@ -260,25 +260,24 @@ func (a *ABA) handleMsgVote(msgT *msgVote) gpa.OutMessages { return nil } -func (a *ABA) handleMsgDone(msgT *msgDone) gpa.OutMessages { - if _, ok := a.nodeIdx[msgT.Sender()]; !ok { +func (a *ABA) handleMsgDone(msgT gpa.TypedMessageIn[*msgDone]) []gpa.MessageOut { + if _, ok := a.nodeIdx[msgT.Sender]; !ok { return nil // Unknown sender. } return a.varDone.msgDoneReceived(msgT) } -func (a *ABA) handleMsgWrapped(msgT *gpa.WrappingMsg) gpa.OutMessages { - msgs := gpa.NoMessages() +func (a *ABA) handleMsgWrapped(msgT gpa.TypedMessageIn[*gpa.WrappingMsg]) []gpa.MessageOut { subGPA, subMsgs, err := a.msgWrapper.DelegateMessage(msgT) if err != nil { a.log.LogWarnf("cannot select subsystem: %v", err) return nil } - msgs.AddAll(subMsgs) - if msgT.Subsystem() == subsystemCC && msgT.Index() == a.round && !a.uponDecisionInputs.haveCC() { + msgs := subMsgs + if msgT.Payload.Subsystem() == subsystemCC && msgT.Payload.Index() == a.round && !a.uponDecisionInputs.haveCC() { ccOut := subGPA.Output() if ccOut != nil { - msgs.AddAll(a.uponDecisionInputs.ccOutputReceived(*ccOut.(*bool))) + msgs = slices.Concat(msgs, a.uponDecisionInputs.ccOutputReceived(*ccOut.(*bool))) } } return msgs @@ -292,7 +291,7 @@ func (a *ABA) handleMsgWrapped(msgT *gpa.WrappingMsg) gpa.OutMessages { // > bin_values_r may continue to change as BVAL_r messages // > are received, thus this condition may be triggered upon // > arrival of either an AUX_r or a BVAL_r message) -func (a *ABA) uponBinValuesUpdated(binValues []bool) gpa.OutMessages { +func (a *ABA) uponBinValuesUpdated(binValues []bool) []gpa.MessageOut { return a.varAuxVals.binValuesUpdated(binValues) } @@ -302,7 +301,7 @@ func (a *ABA) uponBinValuesUpdated(binValues []bool) gpa.OutMessages { // > bin_values_r may continue to change as BVAL_r messages // > are received, thus this condition may be triggered upon // > arrival of either an AUX_r or a BVAL_r message) -func (a *ABA) uponAuxValsReady(auxVals []bool) gpa.OutMessages { +func (a *ABA) uponAuxValsReady(auxVals []bool) []gpa.MessageOut { return a.uponDecisionInputs.auxValsReady(auxVals) } @@ -310,16 +309,17 @@ func (a *ABA) uponAuxValsReady(auxVals []bool) gpa.OutMessages { // > · est_r+1 := b // > · if (b = s%2) then output b // > ∗ else est_r+1 := s%2 -func (a *ABA) uponDecisionInputsReceived(cc bool, auxVals []bool) gpa.OutMessages { +func (a *ABA) uponDecisionInputsReceived(cc bool, auxVals []bool) []gpa.MessageOut { if len(auxVals) == 1 { nextEst := auxVals[0] if nextEst == cc { if a.output == nil { a.output = &Output{Value: nextEst, Terminated: a.varDone.isDone()} } - msgs := gpa.NoMessages() - msgs.AddAll(a.varDone.outputProduced()) - return msgs.AddAll(a.startRound(a.round+1, nextEst)) + return slices.Concat( + a.varDone.outputProduced(), + a.startRound(a.round+1, nextEst), + ) } return a.startRound(a.round+1, nextEst) } @@ -327,11 +327,10 @@ func (a *ABA) uponDecisionInputsReceived(cc bool, auxVals []bool) gpa.OutMessage } // Here we get notification from `varDone` on the termination. -func (a *ABA) uponTerminationCondition() gpa.OutMessages { +func (a *ABA) uponTerminationCondition() { if a.output != nil { a.output.Terminated = true } - return nil } // Output implements the gpa.GPA interface. diff --git a/packages/gpa/aba/mostefaoui/msg.go b/packages/gpa/aba/mostefaoui/msg.go index 83cd9c359a..4fdb79c071 100644 --- a/packages/gpa/aba/mostefaoui/msg.go +++ b/packages/gpa/aba/mostefaoui/msg.go @@ -13,12 +13,12 @@ const ( msgTypeWrapped ) -// UnmarshalMessage implements the gpa.GPA interface. -func (a *ABA) UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{ - msgTypeVote: func() gpa.Message { return new(msgVote) }, - msgTypeDone: func() gpa.Message { return new(msgDone) }, - }, gpa.Fallback{ - msgTypeWrapped: a.msgWrapper.UnmarshalMessage, +// UnmarshalPayload implements the gpa.GPA interface. +func (a *ABA) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{ + msgTypeVote: func() gpa.MessagePayload { return new(msgVote) }, + msgTypeDone: func() gpa.MessagePayload { return new(msgDone) }, + }, gpa.PayloadFallback{ + msgTypeWrapped: a.msgWrapper.UnmarshalPayload, }) } diff --git a/packages/gpa/aba/mostefaoui/msg_done.go b/packages/gpa/aba/mostefaoui/msg_done.go index f01d386ac8..1e25693130 100644 --- a/packages/gpa/aba/mostefaoui/msg_done.go +++ b/packages/gpa/aba/mostefaoui/msg_done.go @@ -4,24 +4,24 @@ package mostefaoui import ( + "fmt" + "github.com/iotaledger/wasp/v2/packages/gpa" ) type msgDone struct { - gpa.BasicMessage round int `bcs:"type=u16,export"` } -var _ gpa.Message = new(msgDone) +var _ gpa.MessagePayload = new(msgDone) -func multicastMsgDone(recipients []gpa.NodeID, me gpa.NodeID, round int) gpa.OutMessages { - msgs := gpa.NoMessages() +func multicastMsgDone(recipients []gpa.NodeID, me gpa.NodeID, round int) []gpa.MessageOut { + var msgs []gpa.MessageOut for _, recipient := range recipients { if recipient != me { - msgs.Add(&msgDone{ - BasicMessage: gpa.NewBasicMessage(recipient), - round: round, - }) + msgs = append(msgs, gpa.NewMessageOut(recipient, &msgDone{ + round: round, + })) } } return msgs @@ -30,3 +30,7 @@ func multicastMsgDone(recipients []gpa.NodeID, me gpa.NodeID, round int) gpa.Out func (msg *msgDone) MsgType() gpa.MessageType { return msgTypeDone } + +func (msg *msgDone) String() string { + return fmt.Sprintf("mostefaoui/Done(round=%d)", msg.round) +} diff --git a/packages/gpa/aba/mostefaoui/msg_done_test.go b/packages/gpa/aba/mostefaoui/msg_done_test.go index 40deaec5d9..761ee9c972 100644 --- a/packages/gpa/aba/mostefaoui/msg_done_test.go +++ b/packages/gpa/aba/mostefaoui/msg_done_test.go @@ -9,19 +9,16 @@ import ( "testing" bcs "github.com/iotaledger/bcs-go" - "github.com/iotaledger/wasp/v2/packages/gpa" ) func TestMsgDoneSerialization(t *testing.T) { msg := &msgDone{ - gpa.BasicMessage{}, int(uint16(rand.Intn(math.MaxUint16 + 1))), } bcs.TestCodec(t, msg) msg = &msgDone{ - gpa.BasicMessage{}, math.MaxUint16, } diff --git a/packages/gpa/aba/mostefaoui/msg_vote.go b/packages/gpa/aba/mostefaoui/msg_vote.go index 63076899a5..965a5f123b 100644 --- a/packages/gpa/aba/mostefaoui/msg_vote.go +++ b/packages/gpa/aba/mostefaoui/msg_vote.go @@ -4,6 +4,10 @@ package mostefaoui import ( + "fmt" + + "github.com/samber/lo" + "github.com/iotaledger/wasp/v2/packages/gpa" ) @@ -14,28 +18,39 @@ const ( AUX ) +func (v msgVoteType) String() string { + switch v { + case BVAL: + return "BVAL" + case AUX: + return "AUX" + default: + return "Unknown" + } +} + type msgVote struct { - gpa.BasicMessage round int `bcs:"export,type=u16"` voteType msgVoteType `bcs:"export"` value bool `bcs:"export"` } -var _ gpa.Message = new(msgVote) +var _ gpa.MessagePayload = new(msgVote) -func multicastMsgVote(recipients []gpa.NodeID, round int, voteType msgVoteType, value bool) gpa.OutMessages { - msgs := gpa.NoMessages() - for _, recipient := range recipients { - msgs.Add(&msgVote{ - BasicMessage: gpa.NewBasicMessage(recipient), - round: round, - voteType: voteType, - value: value, +func multicastMsgVote(recipients []gpa.NodeID, round int, voteType msgVoteType, value bool) []gpa.MessageOut { + return lo.Map(recipients, func(recipient gpa.NodeID, _ int) gpa.MessageOut { + return gpa.NewMessageOut(recipient, &msgVote{ + round: round, + voteType: voteType, + value: value, }) - } - return msgs + }) } func (msg *msgVote) MsgType() gpa.MessageType { return msgTypeVote } + +func (msg *msgVote) String() string { + return fmt.Sprintf("mostefaoui/Vote(round=%d, type=%s, value=%t)", msg.round, msg.voteType.String(), msg.value) +} diff --git a/packages/gpa/aba/mostefaoui/msg_vote_test.go b/packages/gpa/aba/mostefaoui/msg_vote_test.go index 187c963d6f..c8c9bbd298 100644 --- a/packages/gpa/aba/mostefaoui/msg_vote_test.go +++ b/packages/gpa/aba/mostefaoui/msg_vote_test.go @@ -5,12 +5,10 @@ import ( "testing" bcs "github.com/iotaledger/bcs-go" - "github.com/iotaledger/wasp/v2/packages/gpa" ) func TestMsgVoteCodec(t *testing.T) { msg := &msgVote{ - gpa.BasicMessage{}, math.MaxUint16, AUX, true, diff --git a/packages/gpa/aba/mostefaoui/upon_decision_inputs.go b/packages/gpa/aba/mostefaoui/upon_decision_inputs.go index 44eaea82fc..ee52d19c5d 100644 --- a/packages/gpa/aba/mostefaoui/upon_decision_inputs.go +++ b/packages/gpa/aba/mostefaoui/upon_decision_inputs.go @@ -25,10 +25,10 @@ type uponDecisionInputs struct { ccValue bool auxVals []bool done bool - doneCB func(cc bool, auxVals []bool) gpa.OutMessages + doneCB func(cc bool, auxVals []bool) []gpa.MessageOut } -func newUponDecisionInputs(doneCB func(cc bool, auxVals []bool) gpa.OutMessages) *uponDecisionInputs { +func newUponDecisionInputs(doneCB func(cc bool, auxVals []bool) []gpa.MessageOut) *uponDecisionInputs { u := &uponDecisionInputs{doneCB: doneCB} u.startRound() return u @@ -41,7 +41,7 @@ func (u *uponDecisionInputs) startRound() { u.done = false } -func (u *uponDecisionInputs) ccOutputReceived(cc bool) gpa.OutMessages { +func (u *uponDecisionInputs) ccOutputReceived(cc bool) []gpa.MessageOut { if u.ccReceived { return nil } @@ -50,7 +50,7 @@ func (u *uponDecisionInputs) ccOutputReceived(cc bool) gpa.OutMessages { return u.tryOutput() } -func (u *uponDecisionInputs) auxValsReady(auxVals []bool) gpa.OutMessages { +func (u *uponDecisionInputs) auxValsReady(auxVals []bool) []gpa.MessageOut { if u.auxVals != nil { return nil } @@ -58,7 +58,7 @@ func (u *uponDecisionInputs) auxValsReady(auxVals []bool) gpa.OutMessages { return u.tryOutput() } -func (u *uponDecisionInputs) tryOutput() gpa.OutMessages { +func (u *uponDecisionInputs) tryOutput() []gpa.MessageOut { if u.done || !u.ccReceived || u.auxVals == nil { return nil } diff --git a/packages/gpa/aba/mostefaoui/var_aux_vals.go b/packages/gpa/aba/mostefaoui/var_aux_vals.go index 9118639430..de953fbad3 100644 --- a/packages/gpa/aba/mostefaoui/var_aux_vals.go +++ b/packages/gpa/aba/mostefaoui/var_aux_vals.go @@ -5,6 +5,7 @@ package mostefaoui import ( "fmt" + "slices" "github.com/iotaledger/wasp/v2/packages/gpa" ) @@ -24,14 +25,14 @@ type varAuxVals struct { f int nodeIDs []gpa.NodeID recv map[gpa.NodeID]bool - readyCB func(auxVals []bool) gpa.OutMessages + readyCB func(auxVals []bool) []gpa.MessageOut ready bool round int sent bool binValues []bool } -func newAuxVals(nodeIDs []gpa.NodeID, f int, readyCB func(auxVals []bool) gpa.OutMessages) *varAuxVals { +func newAuxVals(nodeIDs []gpa.NodeID, f int, readyCB func(auxVals []bool) []gpa.MessageOut) *varAuxVals { v := &varAuxVals{ n: len(nodeIDs), f: f, @@ -61,23 +62,23 @@ func (v *varAuxVals) startRound(round int) { // > bin_values_r may continue to change as BVAL_r messages // > are received, thus this condition may be triggered upon // > arrival of either an AUX_r or a BVAL_r message) -func (v *varAuxVals) binValuesUpdated(binValues []bool) gpa.OutMessages { - msgs := gpa.NoMessages() +func (v *varAuxVals) binValuesUpdated(binValues []bool) []gpa.MessageOut { + var msgs []gpa.MessageOut if len(binValues) == 1 { - msgs.AddAll(v.multicast(binValues[0])) + msgs = slices.Concat(msgs, v.multicast(binValues[0])) } v.binValues = binValues - return msgs.AddAll(v.tryOutput()) + return slices.Concat(msgs, v.tryOutput()) } // > ∗ wait until at least (N − f) AUX_r messages have been // > received, such that the set of values carried by these // > messages, vals are a subset of bin_values_r ... -func (v *varAuxVals) msgVoteAUXReceived(msg *msgVote) gpa.OutMessages { - if _, ok := v.recv[msg.Sender()]; ok { +func (v *varAuxVals) msgVoteAUXReceived(msg gpa.TypedMessageIn[*msgVote]) []gpa.MessageOut { + if _, ok := v.recv[msg.Sender]; ok { return nil // Duplicate. } - v.recv[msg.Sender()] = msg.value + v.recv[msg.Sender] = msg.Payload.value return v.tryOutput() } @@ -87,7 +88,7 @@ func (v *varAuxVals) msgVoteAUXReceived(msg *msgVote) gpa.OutMessages { // > bin_values_r may continue to change as BVAL_r messages // > are received, thus this condition may be triggered upon // > arrival of either an AUX_r or a BVAL_r message) -func (v *varAuxVals) tryOutput() gpa.OutMessages { +func (v *varAuxVals) tryOutput() []gpa.MessageOut { if v.ready || len(v.recv) < v.n-v.f || v.binValues == nil { return nil } @@ -128,7 +129,7 @@ func (v *varAuxVals) tryOutput() gpa.OutMessages { return nil } -func (v *varAuxVals) multicast(value bool) gpa.OutMessages { +func (v *varAuxVals) multicast(value bool) []gpa.MessageOut { if v.sent { return nil } diff --git a/packages/gpa/aba/mostefaoui/var_bin_vals.go b/packages/gpa/aba/mostefaoui/var_bin_vals.go index 5c84e362d7..d12e218e17 100644 --- a/packages/gpa/aba/mostefaoui/var_bin_vals.go +++ b/packages/gpa/aba/mostefaoui/var_bin_vals.go @@ -5,6 +5,7 @@ package mostefaoui import ( "fmt" + "slices" "github.com/iotaledger/wasp/v2/packages/gpa" ) @@ -23,7 +24,7 @@ type varBinVals struct { n int f int nodeIDs []gpa.NodeID - updateCB func(binVals []bool) gpa.OutMessages + updateCB func(binVals []bool) []gpa.MessageOut round int est bool recvT map[gpa.NodeID]bool @@ -33,7 +34,7 @@ type varBinVals struct { binValues []bool } -func newBinVals(nodeIDs []gpa.NodeID, f int, updateCB func(binVals []bool) gpa.OutMessages) *varBinVals { +func newBinVals(nodeIDs []gpa.NodeID, f int, updateCB func(binVals []bool) []gpa.MessageOut) *varBinVals { v := &varBinVals{ n: len(nodeIDs), f: f, @@ -45,7 +46,7 @@ func newBinVals(nodeIDs []gpa.NodeID, f int, updateCB func(binVals []bool) gpa.O // > – multicast BVAL_r(est_r) // > – bin_values_r := {} -func (v *varBinVals) startRound(round int, est bool) gpa.OutMessages { +func (v *varBinVals) startRound(round int, est bool) []gpa.MessageOut { v.round = round v.est = est v.recvT = map[gpa.NodeID]bool{} @@ -61,22 +62,22 @@ func (v *varBinVals) startRound(round int, est bool) gpa.OutMessages { // > – upon receiving BVAL_r(b) messages from 2f + 1 nodes, // > bin_values_r := bin_values_r ∪ {b} // > – wait until bin_values_r != {}, then -func (v *varBinVals) msgVoteBVALReceived(msg *msgVote) gpa.OutMessages { - recv := v.recv(msg.value) // NOTE: A reference to a field. +func (v *varBinVals) msgVoteBVALReceived(msg gpa.TypedMessageIn[*msgVote]) []gpa.MessageOut { + recv := v.recv(msg.Payload.value) // NOTE: A reference to a field. - if ok := recv[msg.Sender()]; ok { + if ok := recv[msg.Sender]; ok { return nil // Duplicate. } - recv[msg.Sender()] = true + recv[msg.Sender] = true - msgs := gpa.NoMessages() + var msgs []gpa.MessageOut if len(recv) == v.f+1 { - msgs.AddAll(v.multicast(msg.value)) // This checks, if already sent. + msgs = v.multicast(msg.Payload.value) // This checks, if already sent. } if len(recv) == 2*v.f+1 { - v.binValues = append(v.binValues, msg.value) - return msgs.AddAll(v.updateCB(v.binValues)) + v.binValues = append(v.binValues, msg.Payload.value) + return slices.Concat(msgs, v.updateCB(v.binValues)) } return msgs } @@ -97,7 +98,7 @@ func (v *varBinVals) recv(value bool) map[gpa.NodeID]bool { return v.recvF } -func (v *varBinVals) multicast(value bool) gpa.OutMessages { +func (v *varBinVals) multicast(value bool) []gpa.MessageOut { sent := v.sent(value) if *sent { return nil diff --git a/packages/gpa/aba/mostefaoui/var_done.go b/packages/gpa/aba/mostefaoui/var_done.go index 07ced01d2b..6a040b5e5a 100644 --- a/packages/gpa/aba/mostefaoui/var_done.go +++ b/packages/gpa/aba/mostefaoui/var_done.go @@ -5,6 +5,7 @@ package mostefaoui import ( "fmt" + "slices" "github.com/iotaledger/hive.go/log" "github.com/iotaledger/wasp/v2/packages/gpa" @@ -24,12 +25,12 @@ type varDone struct { f int round int recv map[gpa.NodeID]int // All the received DONE messages and last our decision. - doneCB func() gpa.OutMessages + doneCB func() done bool log log.Logger } -func newVarDone(nodeIDs []gpa.NodeID, me gpa.NodeID, f int, doneCB func() gpa.OutMessages, log log.Logger) *varDone { +func newVarDone(nodeIDs []gpa.NodeID, me gpa.NodeID, f int, doneCB func(), log log.Logger) *varDone { return &varDone{ nodeIDs: nodeIDs, me: me, @@ -46,28 +47,32 @@ func (v *varDone) startRound(round int) { v.round = round } -func (v *varDone) outputProduced() gpa.OutMessages { +func (v *varDone) setDone() { + if !v.done { + v.done = true + v.doneCB() + } +} + +func (v *varDone) outputProduced() []gpa.MessageOut { if firstDoneRound, ok := v.recv[v.me]; ok && firstDoneRound < v.round { // We have decided for the second time. That's enough. - if !v.done { - v.done = true - return v.doneCB() - } + v.setDone() return nil } v.recv[v.me] = v.round - msgs := gpa.NoMessages() - msgs.AddAll(multicastMsgDone(v.nodeIDs, v.me, v.round)) - msgs.AddAll(v.tryComplete()) - return msgs + return slices.Concat( + multicastMsgDone(v.nodeIDs, v.me, v.round), + v.tryComplete(), + ) } -func (v *varDone) msgDoneReceived(msg *msgDone) gpa.OutMessages { - if _, ok := v.recv[msg.Sender()]; ok { +func (v *varDone) msgDoneReceived(msg gpa.TypedMessageIn[*msgDone]) []gpa.MessageOut { + if _, ok := v.recv[msg.Sender]; ok { return nil // Duplicate } - v.recv[msg.Sender()] = msg.round + v.recv[msg.Sender] = msg.Payload.round return v.tryComplete() } @@ -78,7 +83,7 @@ func (v *varDone) isDone() bool { // If others (more than F) have decided in previous epochs, then we are // among the others, who decided in a subsequent round, therefore we don't // need to wait for more epochs to close the process. -func (v *varDone) tryComplete() gpa.OutMessages { +func (v *varDone) tryComplete() []gpa.MessageOut { if v.done || len(v.recv) <= v.f { return nil } @@ -94,8 +99,7 @@ func (v *varDone) tryComplete() gpa.OutMessages { } } if count > v.f { - v.done = true - return v.doneCB() + v.setDone() } return nil } diff --git a/packages/gpa/ack_handler.go b/packages/gpa/ack_handler.go index e2610a9605..47303b5391 100644 --- a/packages/gpa/ack_handler.go +++ b/packages/gpa/ack_handler.go @@ -5,8 +5,12 @@ package gpa import ( "fmt" + "slices" "time" + "fortio.org/safecast" + "github.com/samber/lo" + bcs "github.com/iotaledger/bcs-go" "github.com/iotaledger/hive.go/ds/shrinkingmap" ) @@ -26,7 +30,7 @@ type ackHandler struct { nested GPA resendPeriod time.Duration initialized *shrinkingmap.ShrinkingMap[NodeID, bool] - initPending *shrinkingmap.ShrinkingMap[NodeID, []Message] + initPending *shrinkingmap.ShrinkingMap[NodeID, []MessagePayload] counters *shrinkingmap.ShrinkingMap[NodeID, int] // For numbering the outgoing messages. sentUnacked *shrinkingmap.ShrinkingMap[NodeID, *shrinkingmap.ShrinkingMap[int, *ackHandlerBatch]] recvAcksIn *shrinkingmap.ShrinkingMap[NodeID, map[int]*int] @@ -36,8 +40,8 @@ type AckHandler interface { GPA DismissPeer(peerID NodeID) // To avoid resending messages to dead peers. MakeTickInput(time.Time) Input - NestedMessage(msg Message) OutMessages - NestedCall(c func(GPA) OutMessages) OutMessages + NestedMessage(msg MessageIn) []MessageOut + NestedCall(c func(GPA) []MessageOut) []MessageOut } var _ AckHandler = &ackHandler{} @@ -48,7 +52,7 @@ func NewAckHandler(me NodeID, nested GPA, resendPeriod time.Duration) AckHandler nested: nested, resendPeriod: resendPeriod, initialized: shrinkingmap.New[NodeID, bool](), - initPending: shrinkingmap.New[NodeID, []Message](), + initPending: shrinkingmap.New[NodeID, []MessagePayload](), counters: shrinkingmap.New[NodeID, int](), sentUnacked: shrinkingmap.New[NodeID, *shrinkingmap.ShrinkingMap[int, *ackHandlerBatch]](), recvAcksIn: shrinkingmap.New[NodeID, map[int]*int](), @@ -67,7 +71,7 @@ func (a *ackHandler) MakeTickInput(timestamp time.Time) Input { return &ackHandlerTick{timestamp: timestamp} } -func (a *ackHandler) Input(input Input) OutMessages { +func (a *ackHandler) Input(input Input) []MessageOut { switch input := input.(type) { case *ackHandlerTick: return a.handleTickMsg(input) @@ -76,22 +80,22 @@ func (a *ackHandler) Input(input Input) OutMessages { } } -func (a *ackHandler) Message(msg Message) OutMessages { - switch msg := msg.(type) { +func (a *ackHandler) Message(msg MessageIn) []MessageOut { + switch msg.Payload.(type) { case *ackHandlerReset: - return a.handleResetMsg(msg) + return a.handleResetMsg(AsTypedMessageIn[*ackHandlerReset](msg)) case *ackHandlerBatch: - return a.handleBatchMsg(msg) + return a.handleBatchMsg(AsTypedMessageIn[*ackHandlerBatch](msg)) default: panic(fmt.Errorf("unexpected message type: %+v", msg)) } } -func (a *ackHandler) NestedMessage(msg Message) OutMessages { +func (a *ackHandler) NestedMessage(msg MessageIn) []MessageOut { return a.makeBatches(a.nested.Message(msg)) } -func (a *ackHandler) NestedCall(c func(GPA) OutMessages) OutMessages { +func (a *ackHandler) NestedCall(c func(GPA) []MessageOut) []MessageOut { return a.makeBatches(c(a.nested)) } @@ -103,10 +107,10 @@ func (a *ackHandler) StatusString() string { return fmt.Sprintf("{ACK:%s}", a.nested.StatusString()) } -func (a *ackHandler) UnmarshalMessage(data []byte) (Message, error) { - msg, err := UnmarshalMessage(data, Mapper{ - msgTypeAckHandlerReset: func() Message { return &ackHandlerReset{} }, - msgTypeAckHandlerBatch: func() Message { return &ackHandlerBatch{nestedGPA: a.nested} }, +func (a *ackHandler) UnmarshalPayload(data []byte) (MessagePayload, error) { + msg, err := UnmarshalPayload(data, PayloadAllocator{ + msgTypeAckHandlerReset: func() MessagePayload { return &ackHandlerReset{} }, + msgTypeAckHandlerBatch: func() MessagePayload { return &ackHandlerBatch{nestedGPA: a.nested} }, }) if err != nil { fmt.Printf("ack, err=%v\n", err) // TODO: Clean this up. @@ -114,135 +118,130 @@ func (a *ackHandler) UnmarshalMessage(data []byte) (Message, error) { return msg, err } -func (a *ackHandler) handleTickMsg(msg *ackHandlerTick) OutMessages { +func (a *ackHandler) handleTickMsg(msg *ackHandlerTick) []MessageOut { resendOlderThan := msg.timestamp.Add(-a.resendPeriod) - resendMsgs := NoMessages() - a.sentUnacked.ForEach(func(_ NodeID, nodeSentUnacked *shrinkingmap.ShrinkingMap[int, *ackHandlerBatch]) bool { + var resendMsgs []MessageOut + a.sentUnacked.ForEach(func(nodeID NodeID, nodeSentUnacked *shrinkingmap.ShrinkingMap[int, *ackHandlerBatch]) bool { nodeSentUnacked.ForEach(func(batchID int, batch *ackHandlerBatch) bool { if batch.sent == nil { - // Don't resent, just mark the current timestamp. + // Don't resend, just mark the current timestamp. // We have sent it after the previous tick. batch.sent = &msg.timestamp } else if batch.sent.Before(resendOlderThan) { - // Resent it, timeout is already passed. + // Resend it, timeout is already passed. batch.sent = &msg.timestamp - resendMsgs.Add(batch) + resendMsgs = append(resendMsgs, NewMessageOut(nodeID, batch)) } - return true }) return true }) a.initPending.ForEachKey(func(nodeID NodeID) bool { - resendMsgs.Add(&ackHandlerReset{BasicMessage: NewBasicMessage(nodeID), response: false, latestID: 0}) + resendMsgs = append(resendMsgs, NewMessageOut(nodeID, &ackHandlerReset{ + response: false, + latestID: 0, + })) return true }) return resendMsgs } -func (a *ackHandler) handleResetMsg(msg *ackHandlerReset) OutMessages { - from := msg.sender - if !msg.response { +func (a *ackHandler) handleResetMsg(msg TypedMessageIn[*ackHandlerReset]) []MessageOut { + from := msg.Sender + if !msg.Payload.response { maxID := 0 - - if recvAcksIn, exists := a.recvAcksIn.Get(msg.sender); exists { + if recvAcksIn, exists := a.recvAcksIn.Get(msg.Sender); exists { for id := range recvAcksIn { if id > maxID { maxID = id } } } - return NoMessages().Add(&ackHandlerReset{ - BasicMessage: NewBasicMessage(msg.sender), - response: true, - latestID: maxID, - }) + return []MessageOut{NewMessageOut(msg.Sender, &ackHandlerReset{ + response: true, + latestID: maxID, + })} } if ini, exists := a.initialized.Get(from); exists && ini { return nil } - a.counters.Set(msg.sender, msg.latestID+1) - a.initialized.Set(msg.sender, true) - return a.makeBatches(NoMessages()) + a.counters.Set(msg.Sender, msg.Payload.latestID+1) + a.initialized.Set(msg.Sender, true) + return a.makeBatches(nil) } -func (a *ackHandler) handleBatchMsg(msgBatch *ackHandlerBatch) OutMessages { +func (a *ackHandler) handleBatchMsg(msgBatch TypedMessageIn[*ackHandlerBatch]) []MessageOut { // // Process the received acknowledgements. // Drop all the outgoing batches, that are now acknowledged. - for _, ackedBatchID := range msgBatch.acks { - if unacked, exists := a.sentUnacked.Get(msgBatch.sender); exists { + for _, ackedBatchID := range msgBatch.Payload.acks { + if unacked, exists := a.sentUnacked.Get(msgBatch.Sender); exists { unacked.Delete(ackedBatchID) } } // // Was that ack-only message? - if msgBatch.id == nil { + if msgBatch.Payload.id == nil { // That was ack-only batch, nothing more to do with it. - return NoMessages() + return nil } - peerRecvAcksIn, _ := a.recvAcksIn.GetOrCreate(msgBatch.sender, func() map[int]*int { return make(map[int]*int) }) + peerRecvAcksIn, _ := a.recvAcksIn.GetOrCreate(msgBatch.Sender, func() map[int]*int { return make(map[int]*int) }) - batchAckedIn, exists := peerRecvAcksIn[*msgBatch.id] + batchAckedIn, exists := peerRecvAcksIn[*msgBatch.Payload.id] if exists { // Was received already before. if batchAckedIn == nil { // Not acknowledged yet, just send an ack-only message for now. // The sender has already re-sent the message, so it waits for the ack. - return NoMessages().Add(&ackHandlerBatch{ - recipient: msgBatch.sender, - id: nil, // That's ack-only. - msgs: []Message{}, // No payload. - acks: []int{*msgBatch.id}, // Ack single message. - sent: nil, // We will not track this message, it has no payload. - }) + return []MessageOut{NewMessageOut(msgBatch.Sender, &ackHandlerBatch{ + id: nil, // That's ack-only. + msgs: nil, // No payload. + acks: []int{*msgBatch.Payload.id}, // Ack single message. + sent: nil, // We will not track this message, it has no payload. + })} } // // We have acked it already. If we have the batch with an ack, we // resent it. Otherwise the ack was already acked and this message // is outdated and can be ignored. - peerSentUnacked, exists := a.sentUnacked.Get(msgBatch.sender) + peerSentUnacked, exists := a.sentUnacked.Get(msgBatch.Sender) if !exists { - return NoMessages() + return nil } ackedBatch, exists := peerSentUnacked.Get(*batchAckedIn) if !exists { - return NoMessages() + return nil } now := time.Now() ackedBatch.sent = &now - return NoMessages().Add(ackedBatch) + return []MessageOut{NewMessageOut(msgBatch.Sender, ackedBatch)} } // - // That's new batch, we have to process it. - nestedMsgs := NoMessages() - for i := range msgBatch.msgs { - nestedMsgs.AddAll(a.nested.Message(msgBatch.msgs[i])) + // That's a new batch, we have to process it. + var nestedMsgs []MessageOut + for _, p := range msgBatch.Payload.msgs { + nestedMsgs = slices.Concat(nestedMsgs, a.nested.Message(NewMessageIn(msgBatch.Sender, p))) } - sender, _ := a.recvAcksIn.GetOrCreate(msgBatch.sender, func() map[int]*int { return make(map[int]*int) }) - sender[*msgBatch.id] = nil + sender, _ := a.recvAcksIn.GetOrCreate(msgBatch.Sender, func() map[int]*int { return make(map[int]*int) }) + sender[*msgBatch.Payload.id] = nil return a.makeBatches(nestedMsgs) } -func (a *ackHandler) makeBatches(msgs OutMessages) OutMessages { - if msgs == nil { - return nil - } - groupedMsgs := map[NodeID][]Message{} - msgs.MustIterate(func(msg Message) { - msgRecipient := msg.Recipient() - if recipientMsgs, ok := groupedMsgs[msgRecipient]; ok { - groupedMsgs[msgRecipient] = append(recipientMsgs, msg) - } else { - groupedMsgs[msgRecipient] = []Message{msg} - } - }) +func (a *ackHandler) makeBatches(msgs []MessageOut) []MessageOut { + groupedMsgs := lo.MapEntries( + lo.GroupBy(msgs, func(msg MessageOut) NodeID { return msg.Recipient }), + func(nodeID NodeID, msgsForNode []MessageOut) (NodeID, []MessagePayload) { + return nodeID, lo.Map(msgsForNode, func(msg MessageOut, _ int) MessagePayload { + return msg.Payload + }) + }, + ) - a.initPending.ForEach(func(nodeID NodeID, pending []Message) bool { + a.initPending.ForEach(func(nodeID NodeID, pending []MessagePayload) bool { if gr, ok := groupedMsgs[nodeID]; ok { groupedMsgs[nodeID] = append(gr, pending...) } else { @@ -252,12 +251,15 @@ func (a *ackHandler) makeBatches(msgs OutMessages) OutMessages { }) a.initPending.Clear() - batches := NoMessages() + var batches []MessageOut for nodeID, batchMsgs := range groupedMsgs { if initialized, exists := a.initialized.Get(nodeID); !exists || !initialized { - pending, _ := a.initPending.GetOrCreate(nodeID, func() []Message { return make([]Message, 0, 1) }) + pending, _ := a.initPending.GetOrCreate(nodeID, func() []MessagePayload { return make([]MessagePayload, 0, 1) }) a.initPending.Set(nodeID, append(pending, batchMsgs...)) - batches.Add(&ackHandlerReset{BasicMessage: NewBasicMessage(nodeID), response: false, latestID: 0}) + batches = append(batches, NewMessageOut(nodeID, &ackHandlerReset{ + response: false, + latestID: 0, + })) continue } // @@ -279,18 +281,16 @@ func (a *ackHandler) makeBatches(msgs OutMessages) OutMessages { // // Produce the batch and register it as unacked. batch := &ackHandlerBatch{ - sender: a.me, - recipient: nodeID, - id: &batchID, - acks: acks, - msgs: batchMsgs, - sent: nil, // Will be set after first resend, to avoid resend to early. + id: &batchID, + acks: acks, + msgs: batchMsgs, + sent: nil, // Will be set after first resend, to avoid resend too early. } unackedMap, _ := a.sentUnacked.GetOrCreate(nodeID, func() *shrinkingmap.ShrinkingMap[int, *ackHandlerBatch] { return shrinkingmap.New[int, *ackHandlerBatch]() }) unackedMap.Set(*batch.id, batch) - batches.Add(batch) + batches = append(batches, NewMessageOut(nodeID, batch)) } return batches } @@ -299,12 +299,11 @@ func (a *ackHandler) makeBatches(msgs OutMessages) OutMessages { // ackHandlerReset type ackHandlerReset struct { - BasicMessage response bool `bcs:"export"` latestID int `bcs:"export"` } -var _ Message = new(ackHandlerReset) +var _ MessagePayload = new(ackHandlerReset) func (msg *ackHandlerReset) MsgType() MessageType { return msgTypeAckHandlerReset @@ -315,60 +314,53 @@ func (msg *ackHandlerReset) MsgType() MessageType { // Message conveying the message batches and acknowledgements. type ackHandlerBatch struct { - sender NodeID - recipient NodeID - id *int // That's ACK only, if nil. - msgs []Message // Messages in the batch. - acks []int // Acknowledged batches. - sent *time.Time // Transient, only used for outgoing messages, not sent to the outside. - nestedGPA GPA // Transient, for un-marshaling only. + id *int // That's ACK only, if nil. + msgs []MessagePayload // Messages in the batch. + acks []int // Acknowledged batches. + sent *time.Time // Transient, only used for outgoing messages, not sent to the outside. + nestedGPA GPA // Transient, for un-marshaling only. } -var _ Message = new(ackHandlerBatch) +var _ MessagePayload = new(ackHandlerBatch) func (msg *ackHandlerBatch) MsgType() MessageType { return msgTypeAckHandlerBatch } -func (msg *ackHandlerBatch) Recipient() NodeID { - return msg.recipient -} - -func (msg *ackHandlerBatch) SetSender(sender NodeID) { - msg.sender = sender - for _, msg := range msg.msgs { - msg.SetSender(sender) - } -} - func (msg *ackHandlerBatch) MarshalBCS(e *bcs.Encoder) error { e.EncodeOptional(msg.id) - msgsBytes, err := MarshalMessages(msg.msgs) + n, err := safecast.Convert[uint16](len(msg.msgs)) if err != nil { - return fmt.Errorf("msgs: %w", err) + return fmt.Errorf("too many nested messages to marshal: %w", err) + } + e.Encode(n) + for _, p := range msg.msgs { + msgBytes, err := MarshalPayload(p) + if err != nil { + return fmt.Errorf("marshaling nested payload: %w", err) + } + e.Encode(msgBytes) } - e.Encode(msgsBytes) e.Encode(msg.acks) - return nil } func (msg *ackHandlerBatch) UnmarshalBCS(d *bcs.Decoder) error { msg.id = nil - d.DecodeOptional(&msg.id) - msgsBytes := bcs.Decode[[][]byte](d) - msg.msgs = make([]Message, len(msgsBytes)) - - for i := range msgsBytes { - var err error - msg.msgs[i], err = msg.nestedGPA.UnmarshalMessage(msgsBytes[i]) + var n uint16 + d.Decode(&n) + msg.msgs = make([]MessagePayload, n) + for i := uint16(0); i < n; i++ { + msgBytes := bcs.Decode[[]byte](d) + payload, err := msg.nestedGPA.UnmarshalPayload(msgBytes) if err != nil { - return err + return fmt.Errorf("msgs[%d]: %w", i, err) } + msg.msgs[i] = payload } msg.acks = bcs.Decode[[]int](d) diff --git a/packages/gpa/ack_handler_test.go b/packages/gpa/ack_handler_test.go index 5b59eeea15..0b589cec97 100644 --- a/packages/gpa/ack_handler_test.go +++ b/packages/gpa/ack_handler_test.go @@ -58,7 +58,7 @@ func TestAckHandlerBatchCodec(t *testing.T) { testMsgs := []ackHandlerBatch{ { id: lo.ToPtr(42), - msgs: []Message{ + msgs: []MessagePayload{ &TestMessage{ID: 50}, &TestMessage{ID: 100}, }, @@ -67,7 +67,7 @@ func TestAckHandlerBatchCodec(t *testing.T) { }, { id: lo.ToPtr(42), - msgs: []Message{}, + msgs: []MessagePayload{}, acks: []int{1, 2, 3}, nestedGPA: &testGPA{}, }, @@ -105,9 +105,9 @@ type testGPA struct { var _ GPA = &testGPA{} -func (g *testGPA) UnmarshalMessage(data []byte) (Message, error) { - return UnmarshalMessage(data, Mapper{ - msgTypeTest: func() Message { return &TestMessage{} }, +func (g *testGPA) UnmarshalPayload(data []byte) (MessagePayload, error) { + return UnmarshalPayload(data, PayloadAllocator{ + msgTypeTest: func() MessagePayload { return &TestMessage{} }, }, nil) } diff --git a/packages/gpa/acs/acs.go b/packages/gpa/acs/acs.go index 9028a460c3..263e1efc83 100644 --- a/packages/gpa/acs/acs.go +++ b/packages/gpa/acs/acs.go @@ -30,6 +30,7 @@ package acs import ( "fmt" "math" + "slices" "github.com/iotaledger/hive.go/log" @@ -130,7 +131,7 @@ func (a *ACS) AsGPA() gpa.GPA { // Input implements the gpa.GPA interface: // > • upon receiving input v_i, input v_i to RBC_i -func (a *ACS) Input(input gpa.Input) gpa.OutMessages { +func (a *ACS) Input(input gpa.Input) []gpa.MessageOut { if _, ok := input.([]byte); !ok { panic("input has to be []byte") } @@ -138,36 +139,32 @@ func (a *ACS) Input(input gpa.Input) gpa.OutMessages { return nil // Duplicate input. } a.rbcInput = true - msgs := gpa.NoMessages() sub, subMsgs, err := a.msgWrapper.DelegateInput(subsystemRBC, a.nodeIdx[a.me], input) if err != nil { panic(fmt.Errorf("cannot provide input to RBC: %w", err)) } - msgs.AddAll(subMsgs) - msgs.AddAll(a.tryHandleRBCOutput(a.me, sub)) - return msgs + return slices.Concat( + subMsgs, + a.tryHandleRBCOutput(a.me, sub), + ) } -func (a *ACS) Message(msg gpa.Message) gpa.OutMessages { - msgT, ok := msg.(*gpa.WrappingMsg) +func (a *ACS) Message(msg gpa.MessageIn) []gpa.MessageOut { + msgT, ok := msg.Payload.(*gpa.WrappingMsg) if !ok { a.log.LogWarnf("unexpected message of type %T: %+v", msg, msg) return nil } - msgs := gpa.NoMessages() - sub, subMsgs, err := a.msgWrapper.DelegateMessage(msgT) + sub, subMsgs, err := a.msgWrapper.DelegateMessage(gpa.AsTypedMessageIn[*gpa.WrappingMsg](msg)) if err != nil { a.log.LogWarnf("cannot delegate a message: %v", err) return nil } - msgs.AddAll(subMsgs) switch msgT.Subsystem() { case subsystemRBC: - msgs.AddAll(a.tryHandleRBCOutput(a.nodeIDs[msgT.Index()], sub)) - return msgs + return slices.Concat(subMsgs, a.tryHandleRBCOutput(a.nodeIDs[msgT.Index()], sub)) case subsystemABA: - msgs.AddAll(a.tryHandleABAOutput(a.nodeIDs[msgT.Index()], sub)) - return msgs + return slices.Concat(subMsgs, a.tryHandleABAOutput(a.nodeIDs[msgT.Index()], sub)) default: a.log.LogWarnf("unexpected subsystem: %v", msgT.Subsystem()) return nil @@ -176,7 +173,7 @@ func (a *ACS) Message(msg gpa.Message) gpa.OutMessages { // > • upon delivery of v_j from RBC_j, if input has not yet been // > provided to BA_j, then provide input 1 to BA_j. -func (a *ACS) tryHandleRBCOutput(nodeID gpa.NodeID, rbcInst gpa.GPA) gpa.OutMessages { +func (a *ACS) tryHandleRBCOutput(nodeID gpa.NodeID, rbcInst gpa.GPA) []gpa.MessageOut { out := rbcInst.Output() if out == nil { return nil // Output not ready yet. @@ -191,28 +188,28 @@ func (a *ACS) tryHandleRBCOutput(nodeID gpa.NodeID, rbcInst gpa.GPA) gpa.OutMess return nil // We already provided an input to the ABA. } a.abaInputs[nodeID] = true - msgs := gpa.NoMessages() sub, subMsgs, err := a.msgWrapper.DelegateInput(subsystemABA, a.nodeIdx[nodeID], true) if err != nil { panic(fmt.Errorf("cannot provide input to ABA: %w", err)) } - msgs.AddAll(subMsgs) - msgs.AddAll(a.tryHandleABAOutput(nodeID, sub)) - return msgs + return slices.Concat( + subMsgs, + a.tryHandleABAOutput(nodeID, sub), + ) } // > • upon delivery of value 1 from at least N − f instances of BA, // > provide input 0 to each instance of BA that has not yet been // > provided input. -func (a *ACS) tryHandleABAOutput(nodeID gpa.NodeID, abaInst gpa.GPA) gpa.OutMessages { +func (a *ACS) tryHandleABAOutput(nodeID gpa.NodeID, abaInst gpa.GPA) []gpa.MessageOut { out := abaInst.Output() if out == nil { return nil // Output not ready yet. } abaOut := out.(*mostefaoui.Output) - msgs := gpa.NoMessages() + var msgs []gpa.MessageOut if abaOut.Terminated { - msgs.AddAll(a.termCond.abaTerminated(nodeID)) + msgs = a.termCond.abaTerminated(nodeID) } if _, ok := a.abaOutputs[nodeID]; ok { @@ -241,8 +238,11 @@ func (a *ACS) tryHandleABAOutput(nodeID gpa.NodeID, abaInst gpa.GPA) gpa.OutMess if err != nil { panic(fmt.Errorf("cannot provide input to ABA: %w", err)) } - msgs.AddAll(subMsgs) - msgs.AddAll(a.tryHandleABAOutput(nid, sub)) + msgs = slices.Concat( + msgs, + subMsgs, + a.tryHandleABAOutput(nid, sub), + ) } } return msgs @@ -274,7 +274,7 @@ func (a *ACS) tryOutput() { } } -func (a *ACS) uponTermCondition() gpa.OutMessages { +func (a *ACS) uponTermCondition() []gpa.MessageOut { if a.output != nil { a.output.Terminated = true } diff --git a/packages/gpa/acs/msg.go b/packages/gpa/acs/msg.go index f1d3bac51a..861701d5e0 100644 --- a/packages/gpa/acs/msg.go +++ b/packages/gpa/acs/msg.go @@ -11,8 +11,8 @@ const ( msgTypeWrapped gpa.MessageType = iota ) -func (a *ACS) UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{}, gpa.Fallback{ - msgTypeWrapped: a.msgWrapper.UnmarshalMessage, +func (a *ACS) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{}, gpa.PayloadFallback{ + msgTypeWrapped: a.msgWrapper.UnmarshalPayload, }) } diff --git a/packages/gpa/acs/upon_term_condition.go b/packages/gpa/acs/upon_term_condition.go index 6dc89462dc..1c6581d85f 100644 --- a/packages/gpa/acs/upon_term_condition.go +++ b/packages/gpa/acs/upon_term_condition.go @@ -9,11 +9,11 @@ import "github.com/iotaledger/wasp/v2/packages/gpa" type uponTermCondition struct { n int term map[gpa.NodeID]bool - termCB func() gpa.OutMessages + termCB func() []gpa.MessageOut done bool } -func newUponTermCondition(n int, termCB func() gpa.OutMessages) *uponTermCondition { +func newUponTermCondition(n int, termCB func() []gpa.MessageOut) *uponTermCondition { return &uponTermCondition{ n: n, term: map[gpa.NodeID]bool{}, @@ -22,7 +22,7 @@ func newUponTermCondition(n int, termCB func() gpa.OutMessages) *uponTermConditi } } -func (u *uponTermCondition) abaTerminated(nodeID gpa.NodeID) gpa.OutMessages { +func (u *uponTermCondition) abaTerminated(nodeID gpa.NodeID) []gpa.MessageOut { if u.done { return nil } diff --git a/packages/gpa/acss/acss.go b/packages/gpa/acss/acss.go index dd0b6ae184..1e8fd9c9cb 100644 --- a/packages/gpa/acss/acss.go +++ b/packages/gpa/acss/acss.go @@ -86,11 +86,14 @@ import ( "errors" "fmt" "math" + "slices" "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/share" "go.dedis.ch/kyber/v3/suites" + "github.com/samber/lo" + bcs "github.com/iotaledger/bcs-go" "github.com/iotaledger/hive.go/log" @@ -116,19 +119,19 @@ type acssImpl struct { mySK kyber.Scalar myPK kyber.Point myIdx int - dealer gpa.NodeID // A node that is recognized as a dealer. - dealCB func(int, []byte) []byte // Callback to be called on the encrypted deals (for tests actually). - peerPKs map[gpa.NodeID]kyber.Point // Peer public keys. - peerIdx []gpa.NodeID // Particular order of the nodes (position in the polynomial). - rbc gpa.GPA // RBC to share `C||E`. - rbcOut *crypto.Deal // Deal broadcasted by the dealer. - voteOKRecv map[gpa.NodeID]bool // A set of received OK votes. - voteREADYRecv map[gpa.NodeID]bool // A set of received READY votes. - voteREADYSent bool // Have we sent our READY vote? - pendingIRMsgs []*msgImplicateRecover // I/R messages are buffered, if the RBC is not completed yet. - implicateRecv map[gpa.NodeID]bool // To check, that implicate only received once from a node. - recoverRecv map[gpa.NodeID]*share.PriShare // Private shares from the RECOVER messages. - outS *share.PriShare // Our share of the secret (decrypted from rbcOutE). + dealer gpa.NodeID // A node that is recognized as a dealer. + dealCB func(int, []byte) []byte // Callback to be called on the encrypted deals (for tests actually). + peerPKs map[gpa.NodeID]kyber.Point // Peer public keys. + peerIdx []gpa.NodeID // Particular order of the nodes (position in the polynomial). + rbc gpa.GPA // RBC to share `C||E`. + rbcOut *crypto.Deal // Deal broadcasted by the dealer. + voteOKRecv map[gpa.NodeID]bool // A set of received OK votes. + voteREADYRecv map[gpa.NodeID]bool // A set of received READY votes. + voteREADYSent bool // Have we sent our READY vote? + pendingIRMsgs []gpa.TypedMessageIn[*msgImplicateRecover] // I/R messages are buffered, if the RBC is not completed yet. + implicateRecv map[gpa.NodeID]bool // To check, that implicate only received once from a node. + recoverRecv map[gpa.NodeID]*share.PriShare // Private shares from the RECOVER messages. + outS *share.PriShare // Our share of the secret (decrypted from rbcOutE). output bool msgWrapper *gpa.MsgWrapper log log.Logger @@ -168,7 +171,7 @@ func New( voteOKRecv: map[gpa.NodeID]bool{}, voteREADYRecv: map[gpa.NodeID]bool{}, voteREADYSent: false, - pendingIRMsgs: []*msgImplicateRecover{}, + pendingIRMsgs: []gpa.TypedMessageIn[*msgImplicateRecover]{}, implicateRecv: map[gpa.NodeID]bool{}, recoverRecv: map[gpa.NodeID]*share.PriShare{}, outS: nil, @@ -192,7 +195,7 @@ func New( // Input for the algorithm is the secret to share. // It can be provided by the dealer only. -func (a *acssImpl) Input(input gpa.Input) gpa.OutMessages { +func (a *acssImpl) Input(input gpa.Input) []gpa.MessageOut { if a.me != a.dealer { panic(errors.New("only dealer can initiate the sharing")) } @@ -203,12 +206,12 @@ func (a *acssImpl) Input(input gpa.Input) gpa.OutMessages { } // Receive all the messages and route them to the appropriate handlers. -func (a *acssImpl) Message(msg gpa.Message) gpa.OutMessages { - switch m := msg.(type) { +func (a *acssImpl) Message(msg gpa.MessageIn) []gpa.MessageOut { + switch m := msg.Payload.(type) { case *gpa.WrappingMsg: switch m.Subsystem() { case subsystemRBC: - return a.handleRBCMessage(m) + return a.handleRBCMessage(gpa.AsTypedMessageIn[*gpa.WrappingMsg](msg)) default: a.log.LogWarnf("unexpected wrapped message subsystem: %+v", m) return nil @@ -216,15 +219,15 @@ func (a *acssImpl) Message(msg gpa.Message) gpa.OutMessages { case *msgVote: switch m.kind { case msgVoteOK: - return a.handleVoteOK(m) + return a.handleVoteOK(gpa.AsTypedMessageIn[*msgVote](msg)) case msgVoteREADY: - return a.handleVoteREADY(m) + return a.handleVoteREADY(gpa.AsTypedMessageIn[*msgVote](msg)) default: a.log.LogWarnf("unexpected vote message: %+v", m) return nil } case *msgImplicateRecover: - return a.handleImplicateRecoverReceived(m) + return a.handleImplicateRecoverReceived(gpa.AsTypedMessageIn[*msgImplicateRecover](msg)) default: panic(fmt.Errorf("unexpected message: %+v", msg)) } @@ -237,7 +240,7 @@ func (a *acssImpl) Message(msg gpa.Message) gpa.OutMessages { // > // > // party i (including the dealer) // > RBC(C||E) -func (a *acssImpl) handleInput(secretToShare kyber.Scalar) gpa.OutMessages { +func (a *acssImpl) handleInput(secretToShare kyber.Scalar) []gpa.MessageOut { pubKeys := make([]kyber.Point, 0) for _, peerID := range a.peerIdx { pubKeys = append(pubKeys, a.peerPKs[peerID]) @@ -250,30 +253,30 @@ func (a *acssImpl) handleInput(secretToShare kyber.Scalar) gpa.OutMessages { // > RBC(C||E) rbcCEPayloadBytes := bcs.MustMarshal(&msgRBCCEPayload{suite: a.suite, data: data}) - msgs := a.msgWrapper.WrapMessages(subsystemRBC, 0, a.rbc.Input(rbcCEPayloadBytes)) - return a.tryHandleRBCTermination(false, msgs) + msgs := a.msgWrapper.WrapMessagesOut(subsystemRBC, 0, a.rbc.Input(rbcCEPayloadBytes)) + return slices.Concat(msgs, a.tryHandleRBCTermination(false)) } // Delegate received messages to the RBC and handle its output. // // > // party i (including the dealer) // > RBC(C||E) -func (a *acssImpl) handleRBCMessage(m *gpa.WrappingMsg) gpa.OutMessages { +func (a *acssImpl) handleRBCMessage(m gpa.TypedMessageIn[*gpa.WrappingMsg]) []gpa.MessageOut { wasOut := a.rbc.Output() != nil // To send the msgRBCCEOutput message once (for perf reasons). - msgs := a.msgWrapper.WrapMessages(subsystemRBC, 0, a.rbc.Message(m.Wrapped())) - return a.tryHandleRBCTermination(wasOut, msgs) + msgs := a.msgWrapper.WrapMessagesOut(subsystemRBC, 0, a.rbc.Message(m.Payload.WrappedIn(m.Sender))) + return slices.Concat(msgs, a.tryHandleRBCTermination(wasOut)) } -func (a *acssImpl) tryHandleRBCTermination(wasOut bool, msgs gpa.OutMessages) gpa.OutMessages { +func (a *acssImpl) tryHandleRBCTermination(wasOut bool) []gpa.MessageOut { if out := a.rbc.Output(); !wasOut && out != nil { // Send the result for self as a message (maybe the code will look nicer this way). outParsed, err := bcs.UnmarshalInto(out.([]byte), &msgRBCCEPayload{suite: a.suite}) if err != nil { outParsed = &msgRBCCEPayload{err: err} } - msgs.AddAll(a.handleRBCOutput(outParsed)) + return a.handleRBCOutput(outParsed) } - return msgs + return nil } // Upon receiving the RBC output... @@ -283,43 +286,46 @@ func (a *acssImpl) tryHandleRBCTermination(wasOut bool, msgs gpa.OutMessages) gp // > send to all parties // > else: // > send -func (a *acssImpl) handleRBCOutput(rbcOutput *msgRBCCEPayload) gpa.OutMessages { +func (a *acssImpl) handleRBCOutput(rbcOutput *msgRBCCEPayload) []gpa.MessageOut { if a.outS != nil || a.rbcOut != nil { // Take the first RBC output only. return nil } - msgs := gpa.NoMessages() // // Store the broadcast result and process pending IMPLICATE/RECOVER messages, if any. if rbcOutput.err != nil { - return a.broadcastImplicate(rbcOutput.err, msgs) + return a.broadcastImplicate(rbcOutput.err) } deal, err := crypto.DealUnmarshalBinary(a.suite, a.n, rbcOutput.data) if err != nil { - return a.broadcastImplicate(errors.New("cannot unmarshal msgRBCCEPayload.data"), msgs) + return a.broadcastImplicate(errors.New("cannot unmarshal msgRBCCEPayload.data")) } a.rbcOut = deal - msgs = a.handleImplicateRecoverPending(msgs) + msgs := a.handleImplicateRecoverPending() // // Process the RBC output, as described above. secret := crypto.Secret(a.suite, a.rbcOut.PubKey, a.mySK) myShare, err := crypto.DecryptShare(a.suite, a.rbcOut, a.myIdx, secret) if err != nil { - return a.broadcastImplicate(err, msgs) + return slices.Concat(msgs, a.broadcastImplicate(err)) } a.outS = myShare a.tryOutput() // Maybe the READY messages are already received. - return a.handleImplicateRecoverPending(a.broadcastVote(msgVoteOK, msgs)) + return slices.Concat( + msgs, + a.broadcastVote(msgVoteOK), + a.handleImplicateRecoverPending(), + ) } // > on receiving from n-f parties: // > send to all parties -func (a *acssImpl) handleVoteOK(msg *msgVote) gpa.OutMessages { - a.voteOKRecv[msg.Sender()] = true +func (a *acssImpl) handleVoteOK(msg gpa.TypedMessageIn[*msgVote]) []gpa.MessageOut { + a.voteOKRecv[msg.Sender] = true count := len(a.voteOKRecv) if !a.voteREADYSent && count >= (a.n-a.f) { a.voteREADYSent = true - return a.broadcastVote(msgVoteREADY, gpa.NoMessages()) + return a.broadcastVote(msgVoteREADY) } return nil } @@ -331,45 +337,46 @@ func (a *acssImpl) handleVoteOK(msg *msgVote) gpa.OutMessages { // > if sᵢ is valid: // > out = true // > output sᵢ -func (a *acssImpl) handleVoteREADY(msg *msgVote) gpa.OutMessages { - a.voteREADYRecv[msg.Sender()] = true +func (a *acssImpl) handleVoteREADY(msg gpa.TypedMessageIn[*msgVote]) []gpa.MessageOut { + a.voteREADYRecv[msg.Sender] = true count := len(a.voteREADYRecv) - msgs := gpa.NoMessages() + var msgs []gpa.MessageOut if !a.voteREADYSent && count >= (a.f+1) { - msgs = a.broadcastVote(msgVoteREADY, msgs) + msgs = a.broadcastVote(msgVoteREADY) a.voteREADYSent = true } a.tryOutput() - return a.handleImplicateRecoverPending(msgs) + return slices.Concat(msgs, a.handleImplicateRecoverPending()) } // It is possible that we are receiving IMPLICATE/RECOVER messages before our RBC is completed. // We store these messages for processing after that, if RBC is not done and process it otherwise. -func (a *acssImpl) handleImplicateRecoverReceived(msg *msgImplicateRecover) gpa.OutMessages { +func (a *acssImpl) handleImplicateRecoverReceived(msg gpa.TypedMessageIn[*msgImplicateRecover]) []gpa.MessageOut { if a.rbcOut == nil { a.pendingIRMsgs = append(a.pendingIRMsgs, msg) return nil } - switch msg.kind { + switch msg.Payload.kind { case msgImplicateRecoverKindIMPLICATE: return a.handleImplicate(msg) case msgImplicateRecoverKindRECOVER: return a.handleRecover(msg) default: - a.log.LogWarnf("handleImplicateRecoverReceived: unexpected msgImplicateRecover.kind=%v, message: %+v", msg.kind, msg) + a.log.LogWarnf("handleImplicateRecoverReceived: unexpected msgImplicateRecover.kind=%v, message: %+v", msg.Payload.kind, msg) return nil } } -func (a *acssImpl) handleImplicateRecoverPending(msgs gpa.OutMessages) gpa.OutMessages { +func (a *acssImpl) handleImplicateRecoverPending() []gpa.MessageOut { // // Only process the IMPLICATE/RECOVER messages, if this node has RBC completed. if a.rbcOut == nil { - return msgs + return nil } - postponedIRMsgs := []*msgImplicateRecover{} + postponedIRMsgs := []gpa.TypedMessageIn[*msgImplicateRecover]{} + var msgs []gpa.MessageOut for _, m := range a.pendingIRMsgs { - switch m.kind { + switch m.Payload.kind { case msgImplicateRecoverKindIMPLICATE: // Only handle the IMPLICATE messages when output is already produced to implement the following: // @@ -378,14 +385,14 @@ func (a *acssImpl) handleImplicateRecoverPending(msgs gpa.OutMessages) gpa.OutMe // > return // if a.output { - msgs.AddAll(a.handleImplicate(m)) + msgs = slices.Concat(msgs, a.handleImplicate(m)) } else { postponedIRMsgs = append(postponedIRMsgs, m) } case msgImplicateRecoverKindRECOVER: - msgs.AddAll(a.handleRecover(m)) + msgs = slices.Concat(msgs, a.handleRecover(m)) default: - a.log.LogWarnf("handleImplicateRecoverReceived: unexpected msgImplicateRecover.kind=%v, message: %+v", m.kind, m) + a.log.LogWarnf("handleImplicateRecoverReceived: unexpected msgImplicateRecover.kind=%v, message: %+v", m.Payload.kind, m) // Don't return here, we are just dropping incorrect message. } } @@ -403,22 +410,22 @@ func (a *acssImpl) handleImplicateRecoverPending(msgs gpa.OutMessages) gpa.OutMe // > return // // NOTE: We assume `if out == true:` stands for a wait for such condition. -func (a *acssImpl) handleImplicate(msg *msgImplicateRecover) gpa.OutMessages { - peerIndex := a.peerIndex(msg.sender) +func (a *acssImpl) handleImplicate(msg gpa.TypedMessageIn[*msgImplicateRecover]) []gpa.MessageOut { + peerIndex := a.peerIndex(msg.Sender) if peerIndex == -1 { - a.log.LogWarnf("implicate received from unknown peer: %v", msg.sender) + a.log.LogWarnf("implicate received from unknown peer: %v", msg.Sender) return nil } // // Check message duplicates. - if _, ok := a.implicateRecv[msg.sender]; ok { + if _, ok := a.implicateRecv[msg.Sender]; ok { // Received the implicate before, just ignore it. return nil } - a.implicateRecv[msg.sender] = true + a.implicateRecv[msg.Sender] = true // // Check implicate. - secret, err := crypto.CheckImplicate(a.suite, a.rbcOut.PubKey, a.peerPKs[msg.sender], msg.data) + secret, err := crypto.CheckImplicate(a.suite, a.rbcOut.PubKey, a.peerPKs[msg.Sender], msg.Payload.data) if err != nil { a.log.LogWarnf("Invalid implication received: %v", err) return nil @@ -431,7 +438,7 @@ func (a *acssImpl) handleImplicate(msg *msgImplicateRecover) gpa.OutMessages { } // // Create the reveal message. - return a.broadcastRecover(gpa.NoMessages()) + return a.broadcastRecover() } // Here the RBC is assumed to be completed already and the private key is checked. @@ -444,27 +451,27 @@ func (a *acssImpl) handleImplicate(msg *msgImplicateRecover) gpa.OutMessages { // > sᵢ = SSS.Recover(T, f+1, n)(i) // > out = true // > output sᵢ -func (a *acssImpl) handleRecover(msg *msgImplicateRecover) gpa.OutMessages { +func (a *acssImpl) handleRecover(msg gpa.TypedMessageIn[*msgImplicateRecover]) []gpa.MessageOut { if a.output { // Ignore the RECOVER messages, if we are done with the output. return nil } - peerIndex := a.peerIndex(msg.sender) + peerIndex := a.peerIndex(msg.Sender) if peerIndex == -1 { - a.log.LogWarnf("Recover received from unexpected sender: %v", msg.sender) + a.log.LogWarnf("Recover received from unexpected sender: %v", msg.Sender) return nil } - if _, ok := a.recoverRecv[msg.sender]; ok { - a.log.LogWarnf("Recover was already received from %v", msg.sender) + if _, ok := a.recoverRecv[msg.Sender]; ok { + a.log.LogWarnf("Recover was already received from %v", msg.Sender) return nil } - peerSecret, err := crypto.DecryptShare(a.suite, a.rbcOut, peerIndex, msg.data) + peerSecret, err := crypto.DecryptShare(a.suite, a.rbcOut, peerIndex, msg.Payload.data) if err != nil { a.log.LogWarn("invalid secret revealed") return nil } - a.recoverRecv[msg.sender] = peerSecret + a.recoverRecv[msg.Sender] = peerSecret // > wait until len(T) >= f+1: // > sᵢ = SSS.Recover(T, f+1, n)(i) @@ -488,34 +495,33 @@ func (a *acssImpl) handleRecover(msg *msgImplicateRecover) gpa.OutMessages { return nil } -func (a *acssImpl) broadcastVote(voteKind msgVoteKind, msgs gpa.OutMessages) gpa.OutMessages { - for i := range a.peerIdx { - msg := &msgVote{ - BasicMessage: gpa.NewBasicMessage(a.peerIdx[i]), - kind: voteKind, - } - msg.SetSender(a.me) - msgs.Add(msg) - } - return msgs +func (a *acssImpl) broadcastVote(voteKind msgVoteKind) []gpa.MessageOut { + return lo.Map(a.peerIdx, func(peer gpa.NodeID, _ int) gpa.MessageOut { + return gpa.NewMessageOut(peer, &msgVote{ + kind: voteKind, + }) + }) } -func (a *acssImpl) broadcastImplicate(reason error, msgs gpa.OutMessages) gpa.OutMessages { +func (a *acssImpl) broadcastImplicate(reason error) []gpa.MessageOut { a.log.LogWarnf("Sending implicate because of: %v", reason) implicate := crypto.Implicate(a.suite, a.rbcOut.PubKey, a.mySK) - return a.broadcastImplicateRecover(msgImplicateRecoverKindIMPLICATE, implicate, msgs) + return a.broadcastImplicateRecover(msgImplicateRecoverKindIMPLICATE, implicate) } -func (a *acssImpl) broadcastRecover(msgs gpa.OutMessages) gpa.OutMessages { +func (a *acssImpl) broadcastRecover() []gpa.MessageOut { secret := crypto.Secret(a.suite, a.rbcOut.PubKey, a.mySK) - return a.broadcastImplicateRecover(msgImplicateRecoverKindRECOVER, secret, msgs) + return a.broadcastImplicateRecover(msgImplicateRecoverKindRECOVER, secret) } -func (a *acssImpl) broadcastImplicateRecover(kind msgImplicateKind, data []byte, msgs gpa.OutMessages) gpa.OutMessages { - for i := range a.peerIdx { - msgs.Add(&msgImplicateRecover{kind: kind, recipient: a.peerIdx[i], i: a.myIdx, data: data}) - } - return msgs +func (a *acssImpl) broadcastImplicateRecover(kind msgImplicateKind, data []byte) []gpa.MessageOut { + return lo.Map(a.peerIdx, func(peer gpa.NodeID, _ int) gpa.MessageOut { + return gpa.NewMessageOut(peer, &msgImplicateRecover{ + kind: kind, + i: a.myIdx, + data: data, + }) + }) } func (a *acssImpl) tryOutput() { diff --git a/packages/gpa/acss/acss_test.go b/packages/gpa/acss/acss_test.go index 1007de0e89..b76f66fe28 100644 --- a/packages/gpa/acss/acss_test.go +++ b/packages/gpa/acss/acss_test.go @@ -115,12 +115,12 @@ type silentNode struct { var _ gpa.GPA = &silentNode{} -func (s *silentNode) Input(input gpa.Input) gpa.OutMessages { +func (s *silentNode) Input(input gpa.Input) []gpa.MessageOut { // Return the messages, if that's a dealer, otherwise the execution is not meaningful. return s.nested.Input(input) } -func (s *silentNode) Message(msg gpa.Message) gpa.OutMessages { +func (s *silentNode) Message(msg gpa.MessageIn) []gpa.MessageOut { // Just drop all the received messages. return nil } @@ -133,6 +133,6 @@ func (s *silentNode) StatusString() string { return "{silentNode}" } -func (s *silentNode) UnmarshalMessage(data []byte) (gpa.Message, error) { - return s.nested.UnmarshalMessage(data) +func (s *silentNode) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return s.nested.UnmarshalPayload(data) } diff --git a/packages/gpa/acss/msg.go b/packages/gpa/acss/msg.go index f84cf42f37..24088e3e3a 100644 --- a/packages/gpa/acss/msg.go +++ b/packages/gpa/acss/msg.go @@ -11,11 +11,11 @@ const ( msgTypeRBCCEPayload ) -func (a *acssImpl) UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{ - msgTypeImplicateRecover: func() gpa.Message { return new(msgImplicateRecover) }, - msgTypeVote: func() gpa.Message { return new(msgVote) }, - }, gpa.Fallback{ - msgTypeWrapped: a.msgWrapper.UnmarshalMessage, +func (a *acssImpl) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{ + msgTypeImplicateRecover: func() gpa.MessagePayload { return new(msgImplicateRecover) }, + msgTypeVote: func() gpa.MessagePayload { return new(msgVote) }, + }, gpa.PayloadFallback{ + msgTypeWrapped: a.msgWrapper.UnmarshalPayload, }) } diff --git a/packages/gpa/acss/msg_implicate_recover.go b/packages/gpa/acss/msg_implicate_recover.go index 396f1dfcf6..0bfacc61f9 100644 --- a/packages/gpa/acss/msg_implicate_recover.go +++ b/packages/gpa/acss/msg_implicate_recover.go @@ -16,22 +16,12 @@ const ( // The and messages. type msgImplicateRecover struct { - sender gpa.NodeID - recipient gpa.NodeID - kind msgImplicateKind `bcs:"export"` - i int `bcs:"export,type=u16"` - data []byte `bcs:"export"` // Either implication or the recovered secret. + kind msgImplicateKind `bcs:"export"` + i int `bcs:"export,type=u16"` + data []byte `bcs:"export"` // Either implication or the recovered secret. } -var _ gpa.Message = new(msgImplicateRecover) - -func (msg *msgImplicateRecover) Recipient() gpa.NodeID { - return msg.recipient -} - -func (msg *msgImplicateRecover) SetSender(sender gpa.NodeID) { - msg.sender = sender -} +var _ gpa.MessagePayload = new(msgImplicateRecover) func (msg *msgImplicateRecover) MsgType() gpa.MessageType { return msgTypeImplicateRecover diff --git a/packages/gpa/acss/msg_implicate_recover_test.go b/packages/gpa/acss/msg_implicate_recover_test.go index c7e91b75f8..3bf9b585bd 100644 --- a/packages/gpa/acss/msg_implicate_recover_test.go +++ b/packages/gpa/acss/msg_implicate_recover_test.go @@ -12,7 +12,6 @@ import ( "github.com/stretchr/testify/require" bcs "github.com/iotaledger/bcs-go" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/testutil/testval" ) @@ -22,8 +21,6 @@ func TestMsgImplicateRecoverSerialization(t *testing.T) { _, err := cryptorand.Read(b) require.NoError(t, err) msg := &msgImplicateRecover{ - gpa.NodeID{}, - gpa.NodeID{}, msgImplicateRecoverKindIMPLICATE, int(uint16(rand.Intn(math.MaxUint16 + 1))), b, @@ -33,8 +30,6 @@ func TestMsgImplicateRecoverSerialization(t *testing.T) { } { msg := &msgImplicateRecover{ - gpa.NodeID{}, - gpa.NodeID{}, msgImplicateRecoverKindIMPLICATE, int(math.MaxUint16), testval.TestBytes(10), @@ -47,8 +42,6 @@ func TestMsgImplicateRecoverSerialization(t *testing.T) { _, err := cryptorand.Read(b) require.NoError(t, err) msg := &msgImplicateRecover{ - gpa.NodeID{}, - gpa.NodeID{}, msgImplicateRecoverKindRECOVER, int(uint16(rand.Intn(math.MaxUint16 + 1))), b, @@ -58,8 +51,6 @@ func TestMsgImplicateRecoverSerialization(t *testing.T) { } { msg := &msgImplicateRecover{ - gpa.NodeID{}, - gpa.NodeID{}, msgImplicateRecoverKindRECOVER, int(math.MaxUint16), testval.TestBytes(10), diff --git a/packages/gpa/acss/msg_rbc_ce.go b/packages/gpa/acss/msg_rbc_ce.go index 59d0d56eee..f0e58643cb 100644 --- a/packages/gpa/acss/msg_rbc_ce.go +++ b/packages/gpa/acss/msg_rbc_ce.go @@ -13,13 +13,12 @@ import ( // // > RBC(C||E) type msgRBCCEPayload struct { - gpa.BasicMessage suite suites.Suite data []byte `bcs:"export"` err error // Transient field, should not be serialized. } -var _ gpa.Message = new(msgRBCCEPayload) +var _ gpa.MessagePayload = new(msgRBCCEPayload) func (m *msgRBCCEPayload) MsgType() gpa.MessageType { return msgTypeRBCCEPayload diff --git a/packages/gpa/acss/msg_rbc_ce_test.go b/packages/gpa/acss/msg_rbc_ce_test.go index 4cc4305e2d..c2d68fd3f5 100644 --- a/packages/gpa/acss/msg_rbc_ce_test.go +++ b/packages/gpa/acss/msg_rbc_ce_test.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/require" bcs "github.com/iotaledger/bcs-go" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/testutil/testval" ) @@ -19,7 +18,6 @@ func TestMsgRBCCEPayloadSerialization(t *testing.T) { _, err := rand.Read(b) require.NoError(t, err) msg := &msgRBCCEPayload{ - gpa.BasicMessage{}, nil, b, nil, @@ -28,7 +26,6 @@ func TestMsgRBCCEPayloadSerialization(t *testing.T) { bcs.TestCodec(t, msg) msg = &msgRBCCEPayload{ - gpa.BasicMessage{}, nil, testval.TestBytes(10), nil, diff --git a/packages/gpa/acss/msg_vote.go b/packages/gpa/acss/msg_vote.go index 60c1f7ec9e..4d473bb263 100644 --- a/packages/gpa/acss/msg_vote.go +++ b/packages/gpa/acss/msg_vote.go @@ -16,11 +16,10 @@ const ( // This message is used a vote for the "Bracha-style totality" agreement. type msgVote struct { - gpa.BasicMessage kind msgVoteKind `bcs:"export"` } -var _ gpa.Message = new(msgVote) +var _ gpa.MessagePayload = new(msgVote) func (m *msgVote) MsgType() gpa.MessageType { return msgTypeVote diff --git a/packages/gpa/acss/msg_vote_test.go b/packages/gpa/acss/msg_vote_test.go index e83e898c18..810121cc68 100644 --- a/packages/gpa/acss/msg_vote_test.go +++ b/packages/gpa/acss/msg_vote_test.go @@ -7,13 +7,11 @@ import ( "testing" bcs "github.com/iotaledger/bcs-go" - "github.com/iotaledger/wasp/v2/packages/gpa" ) func TestMsgVoteSerialization(t *testing.T) { { msg := &msgVote{ - gpa.BasicMessage{}, msgVoteOK, } @@ -21,7 +19,6 @@ func TestMsgVoteSerialization(t *testing.T) { } { msg := &msgVote{ - gpa.BasicMessage{}, msgVoteREADY, } diff --git a/packages/gpa/asyncdistkeygen/nonce/msg.go b/packages/gpa/asyncdistkeygen/nonce/msg.go index cd4d08e733..71e61ce7c9 100644 --- a/packages/gpa/asyncdistkeygen/nonce/msg.go +++ b/packages/gpa/asyncdistkeygen/nonce/msg.go @@ -19,8 +19,8 @@ func (n *nonceDistributedKeyGenerationImpl) subsystemFunc(subsystem byte, index return nil, fmt.Errorf("unexpected subsystem: %v", subsystem) } -func (n *nonceDistributedKeyGenerationImpl) UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{}, gpa.Fallback{ - msgTypeWrapped: n.wrapper.UnmarshalMessage, +func (n *nonceDistributedKeyGenerationImpl) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{}, gpa.PayloadFallback{ + msgTypeWrapped: n.wrapper.UnmarshalPayload, }) } diff --git a/packages/gpa/asyncdistkeygen/nonce/nonce.go b/packages/gpa/asyncdistkeygen/nonce/nonce.go index a7da9879d3..9d25a2b5d3 100644 --- a/packages/gpa/asyncdistkeygen/nonce/nonce.go +++ b/packages/gpa/asyncdistkeygen/nonce/nonce.go @@ -33,6 +33,7 @@ package nonce import ( "fmt" + "slices" "sort" "github.com/samber/lo" @@ -121,24 +122,24 @@ func New( return gpa.NewOwnHandler(me, n) } -func (n *nonceDistributedKeyGenerationImpl) Input(input gpa.Input) gpa.OutMessages { +func (n *nonceDistributedKeyGenerationImpl) Input(input gpa.Input) []gpa.MessageOut { switch input := input.(type) { case *inputStart: secret := n.suite.Scalar().Pick(n.suite.RandomStream()) - msgs := n.wrapper.WrapMessages(msgWrapperACSS, n.myIdx, n.acss[n.myIdx].Input(secret)) - return n.tryHandleACSSTermination(n.myIdx, msgs) + msgs := n.wrapper.WrapMessagesOut(msgWrapperACSS, n.myIdx, n.acss[n.myIdx].Input(secret)) + return slices.Concat(msgs, n.tryHandleACSSTermination(n.myIdx)) case *inputAgreementResult: return n.handleAgreementResult(input) } panic(fmt.Errorf("unexpected input %T: %+v", input, input)) } -func (n *nonceDistributedKeyGenerationImpl) Message(msg gpa.Message) gpa.OutMessages { - switch msgT := msg.(type) { +func (n *nonceDistributedKeyGenerationImpl) Message(msg gpa.MessageIn) []gpa.MessageOut { + switch msgT := msg.Payload.(type) { case *gpa.WrappingMsg: switch msgT.Subsystem() { case msgWrapperACSS: - return n.handleACSSMessage(msgT) + return n.handleACSSMessage(gpa.AsTypedMessageIn[*gpa.WrappingMsg](msg)) default: n.log.LogWarnf("unexpected message subsystem: %+v", msg) return nil @@ -160,25 +161,26 @@ func (n *nonceDistributedKeyGenerationImpl) StatusString() string { return fmt.Sprintf("{ADKG:Nonce, acss: %s}", acssStats) } -func (n *nonceDistributedKeyGenerationImpl) handleACSSMessage(msg *gpa.WrappingMsg) gpa.OutMessages { - msgIndex := msg.Index() - msgs := n.wrapper.WrapMessages(msgWrapperACSS, msgIndex, n.acss[msgIndex].Message(msg.Wrapped())) - return n.tryHandleACSSTermination(msgIndex, msgs) +func (n *nonceDistributedKeyGenerationImpl) handleACSSMessage(msg gpa.TypedMessageIn[*gpa.WrappingMsg]) []gpa.MessageOut { + msgIndex := msg.Payload.Index() + msgsOut := n.acss[msgIndex].Message(msg.Payload.WrappedIn(msg.Sender)) + wrappedMsgsOut := n.wrapper.WrapMessagesOut(msgWrapperACSS, msgIndex, msgsOut) + return slices.Concat(wrappedMsgsOut, n.tryHandleACSSTermination(msgIndex)) } -func (n *nonceDistributedKeyGenerationImpl) tryHandleACSSTermination(acssIndex int, msgs gpa.OutMessages) gpa.OutMessages { +func (n *nonceDistributedKeyGenerationImpl) tryHandleACSSTermination(acssIndex int) []gpa.MessageOut { out := n.acss[acssIndex].Output() if out != nil && n.st[acssIndex] == nil { acssOutput, ok := out.(*acss.Output) if !ok { panic(fmt.Errorf("acss output wrong type: %+v", out)) } - msgs.AddAll(n.handleACSSOutput(acssIndex, acssOutput.PriShare, acssOutput.Commits)) + return n.handleACSSOutput(acssIndex, acssOutput.PriShare, acssOutput.Commits) } - return msgs + return nil } -func (n *nonceDistributedKeyGenerationImpl) handleACSSOutput(index int, priShare *share.PriShare, commits []kyber.Point) gpa.OutMessages { +func (n *nonceDistributedKeyGenerationImpl) handleACSSOutput(index int, priShare *share.PriShare, commits []kyber.Point) []gpa.MessageOut { j := index if _, ok := n.st[j]; ok { // Already set. Ignore the duplicate messages. @@ -200,7 +202,7 @@ func (n *nonceDistributedKeyGenerationImpl) handleACSSOutput(index int, priShare return n.tryMakeFinalOutput() } -func (n *nonceDistributedKeyGenerationImpl) handleAgreementResult(input *inputAgreementResult) gpa.OutMessages { +func (n *nonceDistributedKeyGenerationImpl) handleAgreementResult(input *inputAgreementResult) []gpa.MessageOut { if n.agreedT != nil { return nil } @@ -240,7 +242,7 @@ func (n *nonceDistributedKeyGenerationImpl) handleAgreementResult(input *inputAg return n.tryMakeFinalOutput() } -func (n *nonceDistributedKeyGenerationImpl) tryMakeFinalOutput() gpa.OutMessages { +func (n *nonceDistributedKeyGenerationImpl) tryMakeFinalOutput() []gpa.MessageOut { if n.agreedT == nil { return nil } diff --git a/packages/gpa/cc/blssig/blssig.go b/packages/gpa/cc/blssig/blssig.go index 777044c46c..400f6a6e7e 100644 --- a/packages/gpa/cc/blssig/blssig.go +++ b/packages/gpa/cc/blssig/blssig.go @@ -70,7 +70,7 @@ func New( return cc } -func (cc *ccImpl) Input(input gpa.Input) gpa.OutMessages { +func (cc *ccImpl) Input(input gpa.Input) []gpa.MessageOut { if input != nil { panic(errors.New("input must be nil")) } @@ -89,32 +89,31 @@ func (cc *ccImpl) Input(input gpa.Input) gpa.OutMessages { return nil } cc.tryOutput() - msgs := gpa.NoMessages() + var msgs []gpa.MessageOut for _, nodeID := range cc.nodeIDs { if nodeID != cc.me { - msgs.Add(&msgSigShare{ - BasicMessage: gpa.NewBasicMessage(nodeID), - sigShare: sigShare, - }) + msgs = append(msgs, gpa.NewMessageOut(nodeID, &msgSigShare{ + sigShare: sigShare, + })) } } return msgs } -func (cc *ccImpl) Message(msg gpa.Message) gpa.OutMessages { +func (cc *ccImpl) Message(msg gpa.MessageIn) []gpa.MessageOut { if cc.output != nil { // Decided, don't need to process messages anymore. return nil } - shareMsg, ok := msg.(*msgSigShare) + shareMsg, ok := msg.Payload.(*msgSigShare) if !ok { panic(fmt.Errorf("unexpected message: %+v", msg)) } - if _, ok := cc.sigShares[shareMsg.Sender()]; ok { + if _, ok := cc.sigShares[msg.Sender]; ok { // Drop a duplicate. return nil } - cc.sigShares[shareMsg.Sender()] = shareMsg.sigShare + cc.sigShares[msg.Sender] = shareMsg.sigShare cc.tryOutput() return nil } diff --git a/packages/gpa/cc/blssig/msg.go b/packages/gpa/cc/blssig/msg.go index a1a5a7ddf9..ca24e92aef 100644 --- a/packages/gpa/cc/blssig/msg.go +++ b/packages/gpa/cc/blssig/msg.go @@ -8,8 +8,8 @@ const ( msgTypeSigShare gpa.MessageType = iota ) -func (cc *ccImpl) UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{ - msgTypeSigShare: func() gpa.Message { return new(msgSigShare) }, +func (cc *ccImpl) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{ + msgTypeSigShare: func() gpa.MessagePayload { return new(msgSigShare) }, }) } diff --git a/packages/gpa/cc/blssig/msg_sig_share.go b/packages/gpa/cc/blssig/msg_sig_share.go index a9452747a0..c7b9888693 100644 --- a/packages/gpa/cc/blssig/msg_sig_share.go +++ b/packages/gpa/cc/blssig/msg_sig_share.go @@ -8,11 +8,10 @@ import ( ) type msgSigShare struct { - gpa.BasicMessage sigShare []byte `bcs:"export"` } -var _ gpa.Message = new(msgSigShare) +var _ gpa.MessagePayload = new(msgSigShare) func (msg *msgSigShare) MsgType() gpa.MessageType { return msgTypeSigShare diff --git a/packages/gpa/cc/blssig/msg_sig_share_test.go b/packages/gpa/cc/blssig/msg_sig_share_test.go index b78d14ef80..3fb19655a3 100644 --- a/packages/gpa/cc/blssig/msg_sig_share_test.go +++ b/packages/gpa/cc/blssig/msg_sig_share_test.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/require" bcs "github.com/iotaledger/bcs-go" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/testutil/testval" ) @@ -19,13 +18,11 @@ func TestMsgSigShareSerialization(t *testing.T) { _, err := rand.Read(b) require.NoError(t, err) msg := &msgSigShare{ - gpa.BasicMessage{}, b, } bcs.TestCodec(t, msg) msg = &msgSigShare{ - gpa.BasicMessage{}, testval.TestBytes(10), } bcs.TestCodecAndHash(t, msg, "9a5a2e001fcf") diff --git a/packages/gpa/cc/semi/semi.go b/packages/gpa/cc/semi/semi.go index f9cc57f283..b91a10d52f 100644 --- a/packages/gpa/cc/semi/semi.go +++ b/packages/gpa/cc/semi/semi.go @@ -25,7 +25,7 @@ func New(index int, target gpa.GPA) gpa.GPA { return &ccSemi{index: index, target: target} } -func (cc *ccSemi) Input(input gpa.Input) gpa.OutMessages { +func (cc *ccSemi) Input(input gpa.Input) []gpa.MessageOut { if input != nil { panic(errors.New("input must be nil")) } @@ -40,25 +40,27 @@ func (cc *ccSemi) Input(input gpa.Input) gpa.OutMessages { cc.output = &coin return nil } - return cc.checkOutput(cc.target.Input(input)) + msgs := cc.target.Input(input) + cc.checkOutput() + return msgs } -func (cc *ccSemi) Message(msg gpa.Message) gpa.OutMessages { +func (cc *ccSemi) Message(msg gpa.MessageIn) []gpa.MessageOut { if cc.output != nil { return nil } - return cc.checkOutput(cc.target.Message(msg)) + msgs := cc.target.Message(msg) + cc.checkOutput() + return msgs } -func (cc *ccSemi) checkOutput(msgs gpa.OutMessages) gpa.OutMessages { +func (cc *ccSemi) checkOutput() { if cc.output != nil { - return msgs + return } - out := cc.target.Output() - if out != nil { + if out := cc.target.Output(); out != nil { cc.output = out.(*bool) } - return msgs } func (cc *ccSemi) Output() gpa.Output { @@ -76,6 +78,6 @@ func (cc *ccSemi) StatusString() string { return fmt.Sprintf("{CC:semi, index=%v, output=%v, target=%v}", cc.index, cc.output, cc.target.StatusString()) } -func (cc *ccSemi) UnmarshalMessage(data []byte) (gpa.Message, error) { - return cc.target.UnmarshalMessage(data) +func (cc *ccSemi) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return cc.target.UnmarshalPayload(data) } diff --git a/packages/gpa/interface.go b/packages/gpa/interface.go index de90addd63..b32a6657e6 100644 --- a/packages/gpa/interface.go +++ b/packages/gpa/interface.go @@ -11,7 +11,6 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" bcs "github.com/iotaledger/bcs-go" - "github.com/iotaledger/wasp/v2/packages/cryptolib" "github.com/iotaledger/wasp/v2/packages/util" ) @@ -48,95 +47,74 @@ func (niT NodeID) ShortString() string { return hexutil.Encode(niT[:4]) // 4 bytes - 8 hexadecimal digits } -type Message interface { - Recipient() NodeID // The sender should indicate the recipient. - SetSender(NodeID) // The transport later will set a validated sender for a message. +type MessagePayload interface { MsgType() MessageType } -type BasicMessage struct { - sender NodeID - recipient NodeID +type TypedMessageIn[T MessagePayload] struct { + Sender NodeID + Payload T } -func NewBasicMessage(recipient NodeID) BasicMessage { - return BasicMessage{recipient: recipient} +func NewMessageIn(sender NodeID, payload MessagePayload) TypedMessageIn[MessagePayload] { + return TypedMessageIn[MessagePayload]{ + Sender: sender, + Payload: payload, + } } -func (msg *BasicMessage) Recipient() NodeID { - return msg.recipient +type TypedMessageOut[T MessagePayload] struct { + Recipient NodeID + Payload MessagePayload } -func (msg *BasicMessage) Sender() NodeID { - return msg.sender +func NewMessageOut(recipient NodeID, payload MessagePayload) TypedMessageOut[MessagePayload] { + return TypedMessageOut[MessagePayload]{ + Recipient: recipient, + Payload: payload, + } } -func (msg *BasicMessage) SetSender(sender NodeID) { - msg.sender = sender -} +type ( + MessageIn = TypedMessageIn[MessagePayload] + MessageOut = TypedMessageOut[MessagePayload] +) -type Input interface{} - -type Output interface{} - -// OutMessages is a buffer for collecting out messages. -// It is used to decrease array reallocations, if a slice would be used directly. -// Additionally, you can safely append to the OutMessages while you iterate over it. -// It should be implemented as a deep-list, allowing efficient appends and iterations. -type OutMessages interface { - // - // Add single message to the out messages. - Add(msg Message) OutMessages - // - // Add several messages. - AddMany(msgs []Message) OutMessages - // - // Add all the messages collected to other OutMessages. - // The added OutMsgs object is marked done here. - AddAll(msgs OutMessages) OutMessages - // - // Mark this instance as freezed, after this it cannot be appended. - Done() OutMessages - // - // Returns a number of elements in the collection. - Count() int - // - // Iterates over the collection, stops on first error. - // Collection can be appended while iterating. - Iterate(callback func(msg Message) error) error - // - // Iterated over the collection. - // Collection can be appended while iterating. - MustIterate(callback func(msg Message)) - // - // Returns contents of the collection as an array of messages. - AsArray() []Message +func AsTypedMessageIn[T MessagePayload](msg MessageIn) TypedMessageIn[T] { + return TypedMessageIn[T]{ + Sender: msg.Sender, + Payload: msg.Payload.(T), + } } +type ( + Input any + Output any +) + // GPA is a generic interface for functional style distributed algorithms. // GPA stands for Generic Pure Algorithm. type GPA interface { - Input(inp Input) OutMessages // Can return nil for NoMessages. - Message(msg Message) OutMessages // Can return nil for NoMessages. + Input(inp Input) []MessageOut + Message(msg MessageIn) []MessageOut Output() Output StatusString() string // Status of the protocol as a string. - UnmarshalMessage(data []byte) (Message, error) + UnmarshalPayload(data []byte) (MessagePayload, error) } type ( - Mapper map[MessageType]func() Message - Fallback map[MessageType]func(data []byte) (Message, error) + PayloadAllocator map[MessageType]func() MessagePayload + PayloadFallback map[MessageType]func(data []byte) (MessagePayload, error) ) -func MarshalMessage(msg Message) ([]byte, error) { +func MarshalPayload(p MessagePayload) ([]byte, error) { e := bcs.NewBytesEncoder() - e.WriteByte(msg.MsgType()) - e.Encode(msg) - + e.WriteByte(p.MsgType()) + e.Encode(p) return e.Bytes(), e.Err() } -func UnmarshalMessage(data []byte, mapper Mapper, fallback ...Fallback) (Message, error) { +func UnmarshalPayload(data []byte, mapper PayloadAllocator, fallback ...PayloadFallback) (MessagePayload, error) { r := bytes.NewReader(data) msgType, err := bcs.UnmarshalStream[MessageType](r) @@ -148,7 +126,6 @@ func UnmarshalMessage(data []byte, mapper Mapper, fallback ...Fallback) (Message if allocator != nil { msg := allocator() _, err := bcs.UnmarshalStreamInto(r, &msg) - return msg, err } @@ -163,24 +140,9 @@ func UnmarshalMessage(data []byte, mapper Mapper, fallback ...Fallback) (Message if unmarshaler == nil { return nil, fmt.Errorf("unexpected message type %d", msgType) } - return unmarshaler(data[1:]) } -func MarshalMessages(msgs []Message) ([][]byte, error) { - msgsBytes := make([][]byte, len(msgs)) - var err error - - for i := range msgs { - msgsBytes[i], err = MarshalMessage(msgs[i]) - if err != nil { - return nil, fmt.Errorf("msgs[%d]: %w", i, err) - } - } - - return msgsBytes, nil -} - type Logger interface { LogWarnf(msg string, args ...any) } diff --git a/packages/gpa/interface_test.go b/packages/gpa/interface_test.go index ed0dfa38ef..f711db7083 100644 --- a/packages/gpa/interface_test.go +++ b/packages/gpa/interface_test.go @@ -24,13 +24,6 @@ func (m *TestMsg) MsgType() gpa.MessageType { return TestMsgID1 } -func (m *TestMsg) Recipient() gpa.NodeID { - return gpa.NodeID{} -} - -func (m *TestMsg) SetSender(gpa.NodeID) { -} - type WrappedMsg struct { C []bool } @@ -39,22 +32,15 @@ func (m *WrappedMsg) MsgType() gpa.MessageType { return TestMsgWrapped } -func (m *WrappedMsg) Recipient() gpa.NodeID { - return gpa.NodeID{} -} - -func (m *WrappedMsg) SetSender(gpa.NodeID) { -} - -func TestUnmarshalMessage(t *testing.T) { - decodeWrapped := func(b []byte) (gpa.Message, error) { +func TestUnmarshalPayload(t *testing.T) { + decodeWrapped := func(b []byte) (gpa.MessagePayload, error) { return bcs.Unmarshal[*WrappedMsg](b) } - unmarshal := func(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{ - TestMsgID1: func() gpa.Message { return &TestMsg{} }, - }, gpa.Fallback{ + unmarshal := func(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{ + TestMsgID1: func() gpa.MessagePayload { return &TestMsg{} }, + }, gpa.PayloadFallback{ TestMsgWrapped: decodeWrapped, }) } diff --git a/packages/gpa/msg_wrapper.go b/packages/gpa/msg_wrapper.go index 3b2b9f44f7..4851916d37 100644 --- a/packages/gpa/msg_wrapper.go +++ b/packages/gpa/msg_wrapper.go @@ -6,6 +6,8 @@ package gpa import ( "fmt" + "github.com/samber/lo" + bcs "github.com/iotaledger/bcs-go" ) @@ -20,38 +22,50 @@ func NewMsgWrapper(msgType MessageType, subsystemFunc func(subsystem byte, index return &MsgWrapper{msgType, subsystemFunc} } -func (w *MsgWrapper) WrapMessage(subsystem byte, index int, msg Message) Message { - return &WrappingMsg{w.msgType, subsystem, index, msg} +func (w *MsgWrapper) WrapMessageOut(subsystem byte, index int, msg MessageOut) MessageOut { + return NewMessageOut( + msg.Recipient, + &WrappingMsg{w.msgType, subsystem, index, msg.Payload}, + ) } -func (w *MsgWrapper) WrapMessages(subsystem byte, index int, msgs OutMessages) OutMessages { - if msgs == nil { - return nil - } - wrapped := NoMessages() - msgs.MustIterate(func(msg Message) { - wrapped.Add(w.WrapMessage(subsystem, index, msg)) +func (w *MsgWrapper) WrapMessagesOut(subsystem byte, index int, msgs []MessageOut) []MessageOut { + return lo.Map(msgs, func(msg MessageOut, _ int) MessageOut { + return w.WrapMessageOut(subsystem, index, msg) + }) +} + +func (w *MsgWrapper) WrapMessageIn(subsystem byte, index int, msg MessageIn) MessageIn { + return NewMessageIn( + msg.Sender, + &WrappingMsg{w.msgType, subsystem, index, msg.Payload}, + ) +} + +func (w *MsgWrapper) WrapMessagesIn(subsystem byte, index int, msgs []MessageIn) []MessageIn { + return lo.Map(msgs, func(msg MessageIn, _ int) MessageIn { + return w.WrapMessageIn(subsystem, index, msg) }) - return wrapped } -func (w *MsgWrapper) DelegateInput(subsystem byte, index int, input Input) (GPA, OutMessages, error) { +func (w *MsgWrapper) DelegateInput(subsystem byte, index int, input Input) (GPA, []MessageOut, error) { sub, err := w.subsystemFunc(subsystem, index) if err != nil { return nil, nil, err } - return sub, w.WrapMessages(subsystem, index, sub.Input(input)), nil + return sub, w.WrapMessagesOut(subsystem, index, sub.Input(input)), nil } -func (w *MsgWrapper) DelegateMessage(msg *WrappingMsg) (GPA, OutMessages, error) { - sub, err := w.subsystemFunc(msg.Subsystem(), msg.Index()) +func (w *MsgWrapper) DelegateMessage(msg TypedMessageIn[*WrappingMsg]) (GPA, []MessageOut, error) { + sub, err := w.subsystemFunc(msg.Payload.subsystem, msg.Payload.index) if err != nil { return nil, nil, err } - return sub, w.WrapMessages(msg.Subsystem(), msg.Index(), sub.Message(msg.Wrapped())), nil + subOut := sub.Message(NewMessageIn(msg.Sender, msg.Payload.wrapped)) + return sub, w.WrapMessagesOut(msg.Payload.subsystem, msg.Payload.index, subOut), nil } -func (w *MsgWrapper) UnmarshalMessage(data []byte) (Message, error) { +func (w *MsgWrapper) UnmarshalPayload(data []byte) (MessagePayload, error) { rawMsg, err := bcs.Unmarshal[rawWrappingMsg](data) if err != nil { return nil, fmt.Errorf("unmarshaling wrapping msg: %w", err) @@ -62,11 +76,10 @@ func (w *MsgWrapper) UnmarshalMessage(data []byte) (Message, error) { return nil, fmt.Errorf("retrieving subsystem GPA %v/%v: %w", rawMsg.Subsystem, rawMsg.Index, err) } - wrapped, err := subGPA.UnmarshalMessage(rawMsg.WrappedMsgBytes) + wrapped, err := subGPA.UnmarshalPayload(rawMsg.WrappedMsgBytes) if err != nil { return nil, fmt.Errorf("unmarshalling wrapped message: subsystem %v index %v: %w", rawMsg.Subsystem, rawMsg.Index, err) } - return &WrappingMsg{ msgType: w.msgType, subsystem: rawMsg.Subsystem, @@ -75,15 +88,15 @@ func (w *MsgWrapper) UnmarshalMessage(data []byte) (Message, error) { }, nil } -// WrappingMsg is the message that contains another, and its routing info. +// WrappingMsg is a message that contains another, and its routing info. type WrappingMsg struct { msgType MessageType subsystem byte index int - wrapped Message + wrapped MessagePayload } -var _ Message = new(WrappingMsg) +var _ MessagePayload = new(WrappingMsg) func (msg *WrappingMsg) MsgType() MessageType { return msg.msgType @@ -97,33 +110,31 @@ func (msg *WrappingMsg) Index() int { return msg.index } -func (msg *WrappingMsg) Wrapped() Message { - return msg.wrapped -} - -func (msg *WrappingMsg) Recipient() NodeID { - return msg.wrapped.Recipient() +func (msg *WrappingMsg) WrappedIn(sender NodeID) MessageIn { + return NewMessageIn(sender, msg.wrapped) } -func (msg *WrappingMsg) SetSender(sender NodeID) { - msg.wrapped.SetSender(sender) +func (msg *WrappingMsg) WrappedOut(receipient NodeID) MessageOut { + return NewMessageOut(receipient, msg.wrapped) } func (msg *WrappingMsg) MarshalBCS(e *bcs.Encoder) error { - wrappedMsgBytes, err := MarshalMessage(msg.wrapped) + wrappedMsgBytes, err := MarshalPayload(msg.wrapped) if err != nil { return fmt.Errorf("marshaling wrapped message: %w", err) } - e.Encode(rawWrappingMsg{ Subsystem: msg.subsystem, Index: msg.index, WrappedMsgBytes: wrappedMsgBytes, }) - return nil } +func (msg *WrappingMsg) String() string { + return fmt.Sprintf("WrappingMsg{subsystem=%v, index=%v, wrapped=%s}", msg.subsystem, msg.index, msg.wrapped) +} + type rawWrappingMsg struct { Subsystem byte Index int `bcs:"type=u16"` diff --git a/packages/gpa/msg_wrapper_test.go b/packages/gpa/msg_wrapper_test.go index c6a1066001..326c93cb2c 100644 --- a/packages/gpa/msg_wrapper_test.go +++ b/packages/gpa/msg_wrapper_test.go @@ -29,31 +29,35 @@ func TestMsgWrapper(t *testing.T) { return nil, fmt.Errorf("unknown subsystem %d index %d", subsystem, index) }) - msg1 := &TestWrappedMessage1{V: 42} - msg2 := &TestWrappedMessage2{V: "hello"} - wrapped1 := wrapper.WrapMessage(2, 3, msg1) - wrapped2 := wrapper.WrapMessage(4, 5, msg2) + sender := gpa.NodeID{1} + recipient := gpa.NodeID{2} - wrapped1Enc := bcs.MustMarshal(lo.ToPtr[any](wrapped1)) - wrapped2Enc := bcs.MustMarshal(lo.ToPtr[any](wrapped2)) + msg1 := gpa.NewMessageIn(sender, &TestWrappedMessage1{V: 42}) + msg2 := gpa.NewMessageOut(recipient, &TestWrappedMessage2{V: "hello"}) - unwrapped1, err := wrapper.UnmarshalMessage(wrapped1Enc) + wrapped1 := wrapper.WrapMessageIn(2, 3, msg1) + wrapped2 := wrapper.WrapMessageOut(4, 5, msg2) + + wrapped1Enc := bcs.MustMarshal(lo.ToPtr[any](wrapped1.Payload)) + wrapped2Enc := bcs.MustMarshal(lo.ToPtr[any](wrapped2.Payload)) + + unwrapped1, err := wrapper.UnmarshalPayload(wrapped1Enc) require.NoError(t, err) - require.Equal(t, msg1, unwrapped1.(*gpa.WrappingMsg).Wrapped()) + require.Equal(t, msg1, unwrapped1.(*gpa.WrappingMsg).WrappedIn(msg1.Sender)) - unwrapped2, err := wrapper.UnmarshalMessage(wrapped2Enc) + unwrapped2, err := wrapper.UnmarshalPayload(wrapped2Enc) require.NoError(t, err) - require.Equal(t, msg2, unwrapped2.(*gpa.WrappingMsg).Wrapped()) + require.Equal(t, msg2, unwrapped2.(*gpa.WrappingMsg).WrappedOut(msg2.Recipient)) - unknownSubsystem := wrapper.WrapMessage(2, 4, msg1) - wrongSubsystem := wrapper.WrapMessage(2, 3, msg2) + unknownSubsystem := wrapper.WrapMessageIn(2, 4, msg1) + wrongSubsystem := wrapper.WrapMessageOut(2, 3, msg2) - unknownSubsystemEnc := bcs.MustMarshal(lo.ToPtr[any](unknownSubsystem)) - wrongSubsystemEnc := bcs.MustMarshal(lo.ToPtr[any](wrongSubsystem)) + unknownSubsystemEnc := bcs.MustMarshal(lo.ToPtr[any](unknownSubsystem.Payload)) + wrongSubsystemEnc := bcs.MustMarshal(lo.ToPtr[any](wrongSubsystem.Payload)) - _, err = wrapper.UnmarshalMessage(unknownSubsystemEnc) + _, err = wrapper.UnmarshalPayload(unknownSubsystemEnc) require.Error(t, err) - _, err = wrapper.UnmarshalMessage(wrongSubsystemEnc) + _, err = wrapper.UnmarshalPayload(wrongSubsystemEnc) require.Error(t, err) } @@ -61,16 +65,15 @@ type subsystemGPA1 struct { testGPABase[*TestWrappedMessage1] } -func (g *subsystemGPA1) UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, - gpa.Mapper{ - 1: func() gpa.Message { return new(TestWrappedMessage1) }, +func (g *subsystemGPA1) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, + gpa.PayloadAllocator{ + 1: func() gpa.MessagePayload { return &TestWrappedMessage1{} }, }, ) } type TestWrappedMessage1 struct { - gpa.BasicMessage V int } @@ -82,16 +85,15 @@ type subsystemGPA2 struct { testGPABase[*TestWrappedMessage2] } -func (g *subsystemGPA2) UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, - gpa.Mapper{ - 2: func() gpa.Message { return new(TestWrappedMessage2) }, +func (g *subsystemGPA2) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, + gpa.PayloadAllocator{ + 2: func() gpa.MessagePayload { return &TestWrappedMessage2{} }, }, ) } type TestWrappedMessage2 struct { - gpa.BasicMessage V string } @@ -99,9 +101,9 @@ func (m *TestWrappedMessage2) MsgType() gpa.MessageType { return 2 } -type testGPABase[MsgType gpa.Message] struct{} +type testGPABase[MsgType gpa.MessagePayload] struct{} -func (testGPABase[_]) Input(inp gpa.Input) gpa.OutMessages { return nil } -func (testGPABase[_]) Message(msg gpa.Message) gpa.OutMessages { return nil } -func (testGPABase[_]) Output() gpa.Output { return nil } -func (testGPABase[_]) StatusString() string { return "" } +func (testGPABase[_]) Input(inp gpa.Input) []gpa.MessageOut { return nil } +func (testGPABase[_]) Message(msg gpa.MessageIn) []gpa.MessageOut { return nil } +func (testGPABase[_]) Output() gpa.Output { return nil } +func (testGPABase[_]) StatusString() string { return "" } diff --git a/packages/gpa/out_messages.go b/packages/gpa/out_messages.go deleted file mode 100644 index f78f240ab0..0000000000 --- a/packages/gpa/out_messages.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -package gpa - -type outMessagesElem struct { - msg Message // / One - msgs []Message // { of these - out OutMessages // \ can be present. - next *outMessagesElem -} - -type outMessagesImpl struct { - count int - done bool - head *outMessagesElem - tail *outMessagesElem -} - -var _ OutMessages = &outMessagesImpl{} - -// NoMessages is a convenience function to return from the Input or Message functions in GPA. -func NoMessages() OutMessages { - return &outMessagesImpl{count: 0, head: nil, tail: nil} -} - -// Implements the OutMessages interface. -func (omi *outMessagesImpl) Add(msg Message) OutMessages { - if msg == nil { - panic("trying to add nil message, is that a mistake?") - } - if omi.done { - panic("out messages marked as done") - } - omi.addElem(&outMessagesElem{msg: msg, next: nil}) - omi.count++ - return omi -} - -// Implements the OutMessages interface. -func (omi *outMessagesImpl) AddMany(msgs []Message) OutMessages { - if omi.done { - panic("out messages marked as done") - } - if len(msgs) == 0 { - return omi - } - omi.addElem(&outMessagesElem{msgs: msgs, next: nil}) - omi.count += len(msgs) - return omi -} - -// Implements the OutMessages interface. -func (omi *outMessagesImpl) AddAll(msgs OutMessages) OutMessages { - if omi.done { - panic("out messages marked as done") - } - if omi == msgs { - panic("cannot append self to itself") - } - if msgs == nil || msgs.Count() == 0 { - return omi - } - omi.addElem(&outMessagesElem{out: msgs.Done(), next: nil}) - omi.count += msgs.Count() - return omi -} - -func (omi *outMessagesImpl) addElem(elem *outMessagesElem) { - if omi.head == nil { - omi.head = elem - omi.tail = omi.head - return - } - omi.tail.next = elem - omi.tail = elem -} - -// Implements the OutMessages interface. -func (omi *outMessagesImpl) Done() OutMessages { - if omi == nil { - panic("trying to close nil OutMessages") - } - omi.done = true - return omi -} - -// Implements the OutMessages interface. -func (omi *outMessagesImpl) Count() int { - if omi == nil { - return 0 - } - return omi.count -} - -// Implements the OutMessages interface. -func (omi *outMessagesImpl) Iterate(callback func(msg Message) error) error { - if omi == nil { - return nil - } - for elem := omi.head; elem != nil; elem = elem.next { - if elem.msg != nil { - if err := callback(elem.msg); err != nil { - return err - } - continue - } - if elem.msgs != nil { - for i := range elem.msgs { - if err := callback(elem.msgs[i]); err != nil { - return err - } - } - continue - } - if err := elem.out.Iterate(callback); err != nil { - return err - } - } - return nil -} - -// Implements the OutMessages interface. -func (omi *outMessagesImpl) MustIterate(callback func(msg Message)) { - if omi == nil { - return - } - for elem := omi.head; elem != nil; elem = elem.next { - if elem.msg != nil { - callback(elem.msg) - continue - } - if elem.msgs != nil { - for i := range elem.msgs { - callback(elem.msgs[i]) - } - continue - } - elem.out.MustIterate(callback) - } -} - -// Implements the OutMessages interface. -func (omi *outMessagesImpl) AsArray() []Message { - if omi == nil { - return nil - } - out := make([]Message, omi.count) - pos := 0 - omi.MustIterate(func(msg Message) { - out[pos] = msg - pos++ - }) - return out -} diff --git a/packages/gpa/out_messages_test.go b/packages/gpa/out_messages_test.go deleted file mode 100644 index d67b5fde1e..0000000000 --- a/packages/gpa/out_messages_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -package gpa_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/iotaledger/wasp/v2/packages/gpa" -) - -func TestOutMessages(t *testing.T) { - // Create a fresh one. - msgs := gpa.NoMessages() - require.Equal(t, 0, msgs.Count()) - require.Equal(t, []gpa.Message{}, msgs.AsArray()) - // - // Add some messages. - m1 := &gpa.TestMessage{ID: 1} - m2 := &gpa.TestMessage{ID: 2} - m3 := &gpa.TestMessage{ID: 3} - m4 := &gpa.TestMessage{ID: 4} - m5 := &gpa.TestMessage{ID: 5} - msgs.Add(m1) - msgs.AddMany([]gpa.Message{m2, m3, m4}) - msgs.Add(m5) - require.Equal(t, 5, msgs.Count()) - require.Equal(t, []gpa.Message{m1, m2, m3, m4, m5}, msgs.AsArray()) - // - // Add one to other. - m0 := &gpa.TestMessage{ID: 0} - moreMsgs := gpa.NoMessages().Add(m0).AddAll(msgs) - require.Equal(t, 6, moreMsgs.Count()) - require.Equal(t, []gpa.Message{m0, m1, m2, m3, m4, m5}, moreMsgs.AsArray()) -} - -// Check if appending works while iterating. -func TestOutMessagesIterate(t *testing.T) { - m1 := &gpa.TestMessage{ID: 1} - m2 := &gpa.TestMessage{ID: 2} - m3 := &gpa.TestMessage{ID: 3} - m4 := &gpa.TestMessage{ID: 4} - - out := []gpa.Message{} - msgs := gpa.NoMessages().Add(m1) - msgs.MustIterate(func(msg gpa.Message) { - out = append(out, msg) - if msg.(*gpa.TestMessage).ID == 1 { - msgs.AddMany([]gpa.Message{m2, m3, m4}) - } - }) - require.Equal(t, 4, msgs.Count()) - require.Equal(t, []gpa.Message{m1, m2, m3, m4}, msgs.AsArray()) -} diff --git a/packages/gpa/own_handler.go b/packages/gpa/own_handler.go index bf8b128b3c..30363c3c94 100644 --- a/packages/gpa/own_handler.go +++ b/packages/gpa/own_handler.go @@ -3,7 +3,10 @@ package gpa -import "fmt" +import ( + "fmt" + "slices" +) // OwnHandler is a GPA instance handling own messages immediately. // @@ -11,31 +14,28 @@ import "fmt" // protocols, one just send a message, and this handler passes it back // as an ordinary message. type OwnHandler struct { - me NodeID - target GPA - outPredicate func(msg Message) bool + me NodeID + target GPA } var _ GPA = &OwnHandler{} -func NewOwnHandlerWithOutPredicate(me NodeID, target GPA, outPredicate func(Message) bool) GPA { - return &OwnHandler{me: me, target: target, outPredicate: outPredicate} +func NewOwnHandlerWithOutPredicate(me NodeID, target GPA) GPA { + return &OwnHandler{me: me, target: target} } func NewOwnHandler(me NodeID, target GPA) GPA { - return NewOwnHandlerWithOutPredicate(me, target, func(msg Message) bool { return false }) + return NewOwnHandlerWithOutPredicate(me, target) } -func (o *OwnHandler) Input(input Input) OutMessages { +func (o *OwnHandler) Input(input Input) []MessageOut { msgs := o.target.Input(input) - outMsgs := NoMessages() - return o.handleMsgs(msgs, outMsgs) + return o.handleMsgs(msgs) } -func (o *OwnHandler) Message(msg Message) OutMessages { +func (o *OwnHandler) Message(msg MessageIn) []MessageOut { msgs := o.target.Message(msg) - outMsgs := NoMessages() - return o.handleMsgs(msgs, outMsgs) + return o.handleMsgs(msgs) } func (o *OwnHandler) Output() Output { @@ -46,21 +46,20 @@ func (o *OwnHandler) StatusString() string { return fmt.Sprintf("{OWN%s}", o.target.StatusString()) } -func (o *OwnHandler) UnmarshalMessage(data []byte) (Message, error) { - return o.target.UnmarshalMessage(data) +func (o *OwnHandler) UnmarshalPayload(data []byte) (MessagePayload, error) { + return o.target.UnmarshalPayload(data) } -func (o *OwnHandler) handleMsgs(msgs, outMsgs OutMessages) OutMessages { - if msgs == nil { - return outMsgs - } - msgs.MustIterate(func(msg Message) { - if msg.Recipient() == o.me && !o.outPredicate(msg) { - msg.SetSender(o.me) - msgs.AddAll(o.target.Message(msg)) - } else { - outMsgs.Add(msg) +func (o *OwnHandler) handleMsgs(msgs []MessageOut) []MessageOut { + var outMsgs []MessageOut + for len(msgs) > 0 { + var msg MessageOut + msg, msgs = msgs[0], msgs[1:] + if msg.Recipient == o.me { + msgs = slices.Concat(msgs, o.target.Message(NewMessageIn(o.me, msg.Payload))) + continue } - }) + outMsgs = append(outMsgs, msg) + } return outMsgs } diff --git a/packages/gpa/rbc/bracha/bracha.go b/packages/gpa/rbc/bracha/bracha.go index 5a6589ed27..58deabedf0 100644 --- a/packages/gpa/rbc/bracha/bracha.go +++ b/packages/gpa/rbc/bracha/bracha.go @@ -46,6 +46,8 @@ import ( "errors" "fmt" + "github.com/samber/lo" + "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/hashing" ) @@ -99,7 +101,7 @@ func New(peers []gpa.NodeID, f int, me, broadcaster gpa.NodeID, maxMsgSize int, // 01: // only broadcaster node // 02: input 𝑀 // 03: send ⟨PROPOSE, 𝑀⟩ to all -func (r *rbc) Input(input gpa.Input) gpa.OutMessages { +func (r *rbc) Input(input gpa.Input) []gpa.MessageOut { if r.broadcaster != r.me { panic(errors.New("only broadcaster is allowed to take an input")) } @@ -113,13 +115,14 @@ func (r *rbc) Input(input gpa.Input) gpa.OutMessages { } // Implements the GPA interface. -func (r *rbc) Message(msg gpa.Message) gpa.OutMessages { - switch msgT := msg.(type) { +func (r *rbc) Message(msg gpa.MessageIn) []gpa.MessageOut { + switch msg.Payload.(type) { case *msgBracha: + msgT := gpa.AsTypedMessageIn[*msgBracha](msg) if !r.checkMsgRecv(msgT) { return nil } - switch msgT.brachaType { + switch msgT.Payload.brachaType { case msgBrachaTypePropose: return r.handlePropose(msgT) case msgBrachaTypeEcho: @@ -127,7 +130,7 @@ func (r *rbc) Message(msg gpa.Message) gpa.OutMessages { case msgBrachaTypeReady: return r.handleReady(msgT) default: - r.log.LogWarnf("unexpected brachaType=%v in message: %+v", msgT.brachaType, msgT) + r.log.LogWarnf("unexpected brachaType=%v in message: %+v", msgT.Payload.brachaType, msgT) return nil } default: @@ -140,16 +143,16 @@ func (r *rbc) Message(msg gpa.Message) gpa.OutMessages { // 06: upon receiving ⟨PROPOSE, 𝑀⟩ from the broadcaster do // 07: if 𝑃(𝑀) then // 08: send ⟨ECHO, 𝑀⟩ to all -func (r *rbc) handlePropose(msg *msgBracha) gpa.OutMessages { - if msg.Sender() != r.broadcaster { +func (r *rbc) handlePropose(msg gpa.TypedMessageIn[*msgBracha]) []gpa.MessageOut { + if msg.Sender != r.broadcaster { // PROPOSE messages can only be sent by the broadcaster process. // Ignore all the rest. return nil } - if !r.predicate(msg.value) { + if !r.predicate(msg.Payload.value) { return nil } - msgs := r.sendToAll(msgBrachaTypeEcho, msg.value) + msgs := r.sendToAll(msgBrachaTypeEcho, msg.Payload.value) r.echoSent = true return msgs } @@ -158,7 +161,7 @@ func (r *rbc) handlePropose(msg *msgBracha) gpa.OutMessages { // // 09: upon receiving 2𝑡 + 1 ⟨ECHO, 𝑀⟩ messages and not having sent a READY message do // 10: send ⟨READY, 𝑀⟩ to all -func (r *rbc) handleEcho(msg *msgBracha) gpa.OutMessages { +func (r *rbc) handleEcho(msg gpa.TypedMessageIn[*msgBracha]) []gpa.MessageOut { // // Mark the message as received. h := r.valueHash(msg) @@ -168,7 +171,7 @@ func (r *rbc) handleEcho(msg *msgBracha) gpa.OutMessages { // As there are only n distinct peers, every two Byzantine quorums overlap in at least one correct peer. // |echoRecv| ≥ ⌈(n+f+1)/2⌉ ⟺ |echoRecv| > ⌊(n+f)/2⌋ if len(r.echoRecv[h]) > (r.n+r.f)/2 { - return r.maybeSendReady(msg.value) + return r.maybeSendReady(msg.Payload.value) } return nil } @@ -179,7 +182,7 @@ func (r *rbc) handleEcho(msg *msgBracha) gpa.OutMessages { // 12: send ⟨READY, 𝑀⟩ to all // 13: upon receiving 2𝑡 + 1 ⟨READY, 𝑀⟩ messages do // 14: output 𝑀 -func (r *rbc) handleReady(msg *msgBracha) gpa.OutMessages { +func (r *rbc) handleReady(msg gpa.TypedMessageIn[*msgBracha]) []gpa.MessageOut { // // Mark the message as received. h := r.valueHash(msg) @@ -188,24 +191,24 @@ func (r *rbc) handleReady(msg *msgBracha) gpa.OutMessages { // // Decide, if quorum is enough. if count > 2*r.f && r.output == nil { - r.output = msg.value + r.output = msg.Payload.value } // // Send the READY message, when a READY message was received from at least one honest peer. // This amplification assures totality. if count > r.f { - return r.maybeSendReady(msg.value) + return r.maybeSendReady(msg.Payload.value) } return nil } -func (r *rbc) checkMsgRecv(msg *msgBracha) bool { - if msg.value == nil || len(msg.value) > r.maxMsgSize { +func (r *rbc) checkMsgRecv(msg gpa.TypedMessageIn[*msgBracha]) bool { + if msg.Payload.value == nil || len(msg.Payload.value) > r.maxMsgSize { return false // Value not set, or is to big. } - if mt, ok := r.msgRecv[msg.Sender()]; ok { - if _, ok := mt[msg.brachaType]; !ok { - mt[msg.brachaType] = true + if mt, ok := r.msgRecv[msg.Sender]; ok { + if _, ok := mt[msg.Payload.brachaType]; !ok { + mt[msg.Payload.brachaType] = true return true // OK, that was the first such message. } return false // Was already received before, ignore it. @@ -213,21 +216,21 @@ func (r *rbc) checkMsgRecv(msg *msgBracha) bool { return false // Unknown peer has sent it. } -func (r *rbc) markEchoRecv(h hashing.HashValue, msg *msgBracha) { +func (r *rbc) markEchoRecv(h hashing.HashValue, msg gpa.TypedMessageIn[*msgBracha]) { if _, ok := r.echoRecv[h]; !ok { r.echoRecv[h] = map[gpa.NodeID]bool{} } - r.echoRecv[h][msg.Sender()] = true + r.echoRecv[h][msg.Sender] = true } -func (r *rbc) markReadyRecv(h hashing.HashValue, msg *msgBracha) { +func (r *rbc) markReadyRecv(h hashing.HashValue, msg gpa.TypedMessageIn[*msgBracha]) { if _, ok := r.readyRecv[h]; !ok { r.readyRecv[h] = map[gpa.NodeID]bool{} } - r.readyRecv[h][msg.Sender()] = true + r.readyRecv[h][msg.Sender] = true } -func (r *rbc) maybeSendReady(v []byte) gpa.OutMessages { +func (r *rbc) maybeSendReady(v []byte) []gpa.MessageOut { if r.readySent { return nil } @@ -236,20 +239,17 @@ func (r *rbc) maybeSendReady(v []byte) gpa.OutMessages { return msgs } -func (r *rbc) sendToAll(brachaType msgBrachaType, value []byte) gpa.OutMessages { - msgs := make([]gpa.Message, len(r.peers)) - for i := range r.peers { - msgs[i] = &msgBracha{ - BasicMessage: gpa.NewBasicMessage(r.peers[i]), - brachaType: brachaType, - value: value, - } - } - return gpa.NoMessages().AddMany(msgs) +func (r *rbc) sendToAll(brachaType msgBrachaType, value []byte) []gpa.MessageOut { + return lo.Map(r.peers, func(peer gpa.NodeID, _ int) gpa.MessageOut { + return gpa.NewMessageOut(peer, &msgBracha{ + brachaType: brachaType, + value: value, + }) + }) } -func (r *rbc) valueHash(msg *msgBracha) hashing.HashValue { - return hashing.HashData(msg.value) +func (r *rbc) valueHash(msg gpa.TypedMessageIn[*msgBracha]) hashing.HashValue { + return hashing.HashData(msg.Payload.value) } // Implements the GPA interface. @@ -269,8 +269,8 @@ func (r *rbc) StatusString() string { } // Implements the GPA interface. -func (r *rbc) UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{ - msgType: func() gpa.Message { return new(msgBracha) }, +func (r *rbc) UnmarshalPayload(data []byte) (gpa.MessagePayload, error) { + return gpa.UnmarshalPayload(data, gpa.PayloadAllocator{ + msgType: func() gpa.MessagePayload { return new(msgBracha) }, }) } diff --git a/packages/gpa/rbc/bracha/msg_bracha.go b/packages/gpa/rbc/bracha/msg_bracha.go index cc9e8fd21c..cf7d1feac4 100644 --- a/packages/gpa/rbc/bracha/msg_bracha.go +++ b/packages/gpa/rbc/bracha/msg_bracha.go @@ -4,28 +4,46 @@ package bracha import ( + "fmt" + "github.com/iotaledger/wasp/v2/packages/gpa" ) +const msgType gpa.MessageType = iota + +// The type for message kinds (only one of these in this case). type msgBrachaType byte const ( - // The type for message kinds (only one of these in this case). - msgType gpa.MessageType = iota - msgBrachaTypePropose msgBrachaType = iota msgBrachaTypeEcho msgBrachaTypeReady ) +func (msgBrachaType msgBrachaType) String() string { + switch msgBrachaType { + case msgBrachaTypePropose: + return "Propose" + case msgBrachaTypeEcho: + return "Echo" + case msgBrachaTypeReady: + return "Ready" + default: + return "UnknownBrachaMsgType" + } +} + type msgBracha struct { - gpa.BasicMessage brachaType msgBrachaType `bcs:"export"` // Type value []byte `bcs:"export"` // Value } -var _ gpa.Message = new(msgBracha) +var _ gpa.MessagePayload = new(msgBracha) func (msg *msgBracha) MsgType() gpa.MessageType { return msgType } + +func (msg *msgBracha) String() string { + return fmt.Sprintf("Bracha.%s(%q)", msg.brachaType.String(), msg.value) +} diff --git a/packages/gpa/rbc/bracha/msg_bracha_test.go b/packages/gpa/rbc/bracha/msg_bracha_test.go index f3aa574ac8..53de846557 100644 --- a/packages/gpa/rbc/bracha/msg_bracha_test.go +++ b/packages/gpa/rbc/bracha/msg_bracha_test.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/require" bcs "github.com/iotaledger/bcs-go" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/testutil/testval" ) @@ -20,7 +19,6 @@ func TestMsgBrachaSerialization(t *testing.T) { _, err := rand.Read(b) require.NoError(t, err) msg := &msgBracha{ - gpa.BasicMessage{}, msgBrachaTypePropose, b, } @@ -29,19 +27,17 @@ func TestMsgBrachaSerialization(t *testing.T) { } { msg := &msgBracha{ - gpa.BasicMessage{}, msgBrachaTypePropose, testval.TestBytes(10), } - bcs.TestCodecAndHash(t, msg, "46ca7766e199") + bcs.TestCodecAndHash(t, msg, "fafb2a25ad65") } { b := make([]byte, 10) _, err := rand.Read(b) require.NoError(t, err) msg := &msgBracha{ - gpa.BasicMessage{}, msgBrachaTypeEcho, b, } @@ -50,19 +46,17 @@ func TestMsgBrachaSerialization(t *testing.T) { } { msg := &msgBracha{ - gpa.BasicMessage{}, msgBrachaTypeEcho, testval.TestBytes(10), } - bcs.TestCodecAndHash(t, msg, "13fb21f67718") + bcs.TestCodecAndHash(t, msg, "46ca7766e199") } { b := make([]byte, 10) _, err := rand.Read(b) require.NoError(t, err) msg := &msgBracha{ - gpa.BasicMessage{}, msgBrachaTypeReady, b, } @@ -71,11 +65,10 @@ func TestMsgBrachaSerialization(t *testing.T) { } { msg := &msgBracha{ - gpa.BasicMessage{}, msgBrachaTypeReady, testval.TestBytes(10), } - bcs.TestCodecAndHash(t, msg, "131d4ae6fdab") + bcs.TestCodecAndHash(t, msg, "13fb21f67718") } } diff --git a/packages/gpa/test_context.go b/packages/gpa/test_context.go index 592fa919d6..53566a5b54 100644 --- a/packages/gpa/test_context.go +++ b/packages/gpa/test_context.go @@ -11,6 +11,11 @@ import ( "github.com/samber/lo" ) +type PendingMessage = struct { + recipient NodeID + msg MessageIn +} + // TestContext imitates a cluster of nodes and the medium performing the message exchange. // Inputs are processes in-order for each node individually. type TestContext struct { @@ -22,10 +27,10 @@ type TestContext struct { outputHandler func(nodeID NodeID, output Output) // User can check outputs w/o synchronizing other parts. msgDeliveryProb float64 // A probability to deliver a message (to not discard/loose it). msgSerialize bool // Use serialization/deserialization when delivering the messages? - msgCh <-chan SenderMessage // A way to provide additional messages w/o synchronizing other parts. - msgs []SenderMessage // Not yet delivered messages. + msgs []PendingMessage // Not yet delivered messages. msgsSent int // Stats. msgsRecv int // Stats. + bytesRecv int } func NewTestContext(nodes map[NodeID]GPA) *TestContext { @@ -40,7 +45,7 @@ func NewTestContext(nodes map[NodeID]GPA) *TestContext { inputProb: 1.0, inputCount: 0, msgDeliveryProb: 1.0, - msgs: []SenderMessage{}, + msgs: []PendingMessage{}, } return &tc } @@ -88,20 +93,21 @@ func (tc *TestContext) WithMessageDeliveryProbability(msgDeliveryProb float64) * return tc } -func (tc *TestContext) WithMessages(sender NodeID, msgs []Message) *TestContext { - tc.msgsSent += len(msgs) - tc.msgs = append(tc.msgs, tc.setMessageSender(sender, NoMessages().AddMany(msgs))...) +func (tc *TestContext) WithMessages(recipient NodeID, msgs []MessageIn) *TestContext { + tc.addMessages(lo.Map(msgs, func(m MessageIn, _ int) PendingMessage { + return PendingMessage{recipient: recipient, msg: m} + })) return tc } -func (tc *TestContext) WithMessage(sender NodeID, msg Message) *TestContext { - tc.msgsSent++ - tc.msgs = append(tc.msgs, tc.setMessageSender(sender, NoMessages().Add(msg))...) - return tc +func (tc *TestContext) addMessages(msgs []PendingMessage) { + tc.msgsSent += len(msgs) + tc.msgs = append(tc.msgs, msgs...) } -func (tc *TestContext) WithMessageChannel(msgCh <-chan SenderMessage) *TestContext { - tc.msgCh = msgCh +func (tc *TestContext) WithMessage(recipient NodeID, msg MessageIn) *TestContext { + tc.msgsSent++ + tc.msgs = append(tc.msgs, PendingMessage{recipient: recipient, msg: msg}) return tc } @@ -110,11 +116,6 @@ func (tc *TestContext) WithOutputHandler(outputHandler func(nodeID NodeID, outpu return tc } -func (tc *TestContext) WithCall(sender NodeID, call func() []Message) *TestContext { - msgs := call() - return tc.WithMessages(sender, msgs) -} - func (tc *TestContext) RunUntil(predicate func() bool) { loop := make(chan bool, 1) loop <- true @@ -138,13 +139,6 @@ func (tc *TestContext) RunUntil(predicate func() bool) { tc.inputs[nid] = append(tc.inputs[nid], input) } tc.inputCount += len(inputs) - case msg, ok := <-tc.msgCh: - keepLooping() - if !ok { - tc.msgCh = nil - continue - } - tc.msgs = append(tc.msgs, msg) case <-loop: if predicate() { return @@ -156,7 +150,7 @@ func (tc *TestContext) RunUntil(predicate func() bool) { loop <- true continue } - if tc.inputCh == nil && tc.msgCh == nil { + if tc.inputCh == nil { // Channels are closed and there is no more inputs or messages. Stop it. return } @@ -182,11 +176,11 @@ func (tc *TestContext) tryProcessInput() { } tc.inputCount-- - newMsgs := tc.setMessageSender(rndNID, tc.nodes[rndNID].Input(rndInp)) - if newMsgs != nil { - tc.msgsSent += len(newMsgs) - tc.msgs = append(tc.msgs, newMsgs...) - } + // fmt.Printf("-> %s :: INPUT %s\n", rndNID.ShortString(), rndInp) + msgs := tc.nodes[rndNID].Input(rndInp) + tc.addMessages(lo.Map(msgs, func(m MessageOut, _ int) PendingMessage { + return PendingMessage{recipient: m.Recipient, msg: NewMessageIn(rndNID, m.Payload)} + })) tc.tryCallOutputHandler(rndNID) } } @@ -195,32 +189,37 @@ func (tc *TestContext) tryProcessMessage() { if len(tc.msgs) == 0 { return } - msgIdx := rand.Intn(len(tc.msgs)) - msg := tc.msgs[msgIdx] - nid := msg.Message.Recipient() - tc.msgs = append(tc.msgs[:msgIdx], tc.msgs[msgIdx+1:]...) + + // select a random message, swap it with the last one and decrease the slice length + rnd := rand.Intn(len(tc.msgs)) + pendingMsg := tc.msgs[rnd] + tc.msgs[rnd] = tc.msgs[len(tc.msgs)-1] + tc.msgs = tc.msgs[:len(tc.msgs)-1] + tc.msgsRecv++ - if rand.Float64() <= tc.msgDeliveryProb { // Deliver some messages. - gpaMsg := msg.Message - if tc.msgSerialize { - msgBytes := lo.Must(MarshalMessage(msg.Message)) - if m, err := tc.nodes[nid].UnmarshalMessage(msgBytes); err == nil { - gpaMsg = m - gpaMsg.SetSender(msg.Sender) - } else { - // E.g. silent node cannot decode messages. - gpaMsg = nil - } - } - if gpaMsg != nil { - newMsgs := tc.setMessageSender(nid, tc.nodes[nid].Message(gpaMsg)) - if newMsgs != nil { - tc.msgsSent += len(newMsgs) - tc.msgs = append(tc.msgs, newMsgs...) - } - tc.tryCallOutputHandler(nid) + if rand.Float64() > tc.msgDeliveryProb { + // message dropped + return + } + + nid := pendingMsg.recipient + msg := pendingMsg.msg + if tc.msgSerialize { + msgBytes := lo.Must(MarshalPayload(msg.Payload)) + tc.bytesRecv += len(msgBytes) + m, err := tc.nodes[nid].UnmarshalPayload(msgBytes) + if err != nil { + // E.g. silent node cannot decode messages. + return } + msg = NewMessageIn(msg.Sender, m) } + // fmt.Printf("%s -> %s :: %s (count: %d / %d bytes)\n", msg.Sender.ShortString(), nid.ShortString(), msg.Payload, tc.msgsRecv, tc.bytesRecv) + msgs := tc.nodes[nid].Message(msg) + tc.addMessages(lo.Map(msgs, func(m MessageOut, _ int) PendingMessage { + return PendingMessage{recipient: m.Recipient, msg: NewMessageIn(nid, m.Payload)} + })) + tc.tryCallOutputHandler(nid) } func (tc *TestContext) tryCallOutputHandler(nid NodeID) { @@ -257,21 +256,8 @@ func (tc *TestContext) OutOfMessagesPredicate() func() bool { return func() bool { return false } } -func (tc *TestContext) setMessageSender(sender NodeID, msgs OutMessages) []SenderMessage { - if msgs == nil { - return nil - } - msgArray := msgs.AsArray() - result := make([]SenderMessage, len(msgArray)) - for i := range msgArray { - msgArray[i].SetSender(sender) - result[i] = SenderMessage{Sender: sender, Message: msgArray[i]} - } - return result -} - func (tc *TestContext) PrintAllStatusStrings(prefix string, logFunc func(format string, args ...any)) { - logFunc("TC[%p] Status, |inputs|=%v, inputsCh=%v, |msgs|=%v, msgsCh=%v", tc, tc.inputCount, tc.inputCh != nil, len(tc.msgs), tc.msgCh != nil) + logFunc("TC[%p] Status, |inputs|=%v, inputsCh=%v, |msgs|=%v", tc, tc.inputCount, tc.inputCh != nil, len(tc.msgs)) keys := []NodeID{} for nid := range tc.nodes { keys = append(keys, nid) @@ -284,8 +270,3 @@ func (tc *TestContext) PrintAllStatusStrings(prefix string, logFunc func(format logFunc("TC[%p] %v [node=%v]: %v", tc, prefix, nidStr, tc.nodes[nidStr].StatusString()) } } - -type SenderMessage struct { - Sender NodeID - Message Message -} diff --git a/packages/gpa/test_message.go b/packages/gpa/test_message.go index c3cd9799c2..e0e40aedb2 100644 --- a/packages/gpa/test_message.go +++ b/packages/gpa/test_message.go @@ -7,21 +7,11 @@ const msgTypeTest MessageType = 0xff // TestMessage is just a message for test cases. type TestMessage struct { - recipient NodeID - sender NodeID - ID int + ID int } -var _ Message = new(TestMessage) +var _ MessagePayload = new(TestMessage) func (msg *TestMessage) MsgType() MessageType { return msgTypeTest } - -func (msg *TestMessage) Recipient() NodeID { - return msg.recipient -} - -func (msg *TestMessage) SetSender(sender NodeID) { - msg.sender = sender -} diff --git a/packages/gpa/test_round.go b/packages/gpa/test_round.go index a2dbca68cc..ec6924cf37 100644 --- a/packages/gpa/test_round.go +++ b/packages/gpa/test_round.go @@ -26,16 +26,16 @@ func NewTestRound(nodeIDs []NodeID, me NodeID) GPA { return NewOwnHandler(me, &testRound{me: me, nodeIDs: nodeIDs, received: map[NodeID]bool{}}) } -func (tr *testRound) Input(input Input) OutMessages { - msgs := make([]Message, len(tr.nodeIDs)) +func (tr *testRound) Input(input Input) []MessageOut { + msgs := make([]MessageOut, len(tr.nodeIDs)) for i := range msgs { - msgs[i] = &testRoundMsg{BasicMessage: NewBasicMessage(tr.nodeIDs[i])} + msgs[i] = NewMessageOut(tr.nodeIDs[i], &testRoundMsg{}) } - return NoMessages().AddMany(msgs) + return msgs } -func (tr *testRound) Message(msg Message) OutMessages { - from := msg.(*testRoundMsg).sender +func (tr *testRound) Message(msg MessageIn) []MessageOut { + from := msg.Sender if tr.received[from] { panic(errors.New("duplicate message")) } @@ -55,17 +55,15 @@ func (tr *testRound) StatusString() string { return fmt.Sprintf("{testRound, received=%v}", tr.received) } -func (tr *testRound) UnmarshalMessage(data []byte) (Message, error) { - return UnmarshalMessage(data, Mapper{ - msgTypeTestRound: func() Message { return new(testRoundMsg) }, +func (tr *testRound) UnmarshalPayload(data []byte) (MessagePayload, error) { + return UnmarshalPayload(data, PayloadAllocator{ + msgTypeTestRound: func() MessagePayload { return &testRoundMsg{} }, }) } -type testRoundMsg struct { - BasicMessage -} +type testRoundMsg struct{} -var _ Message = new(testRoundMsg) +var _ MessagePayload = new(testRoundMsg) func (msg *testRoundMsg) MsgType() MessageType { return msgTypeTestRound diff --git a/packages/gpa/test_silent.go b/packages/gpa/test_silent.go index 1f0691f467..a6839615bc 100644 --- a/packages/gpa/test_silent.go +++ b/packages/gpa/test_silent.go @@ -15,11 +15,11 @@ func MakeTestSilentNode() GPA { return &silentNode{} } -func (s *silentNode) Input(input Input) OutMessages { +func (s *silentNode) Input(input Input) []MessageOut { return nil } -func (s *silentNode) Message(msg Message) OutMessages { +func (s *silentNode) Message(msg MessageIn) []MessageOut { return nil } @@ -31,6 +31,6 @@ func (s *silentNode) StatusString() string { return "{silentNode}" } -func (s *silentNode) UnmarshalMessage(data []byte) (Message, error) { +func (s *silentNode) UnmarshalPayload(data []byte) (MessagePayload, error) { return nil, errors.New("not implemented") }