diff --git a/go.mod b/go.mod index 420ba02195..a1232fc197 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/gorilla/websocket v1.5.3 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/holiman/uint256 v1.3.2 - github.com/iotaledger/bcs-go v0.0.0-20250716100925-71f848cac593 + github.com/iotaledger/bcs-go v0.0.0-20251107161402-980e31f7d5f7 github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 github.com/iotaledger/hive.go/app v0.0.0-20251001162450-d572d7955f11 github.com/iotaledger/hive.go/constraints v0.0.0-20251001162450-d572d7955f11 diff --git a/go.sum b/go.sum index 3761394dfc..5f5f46f88f 100644 --- a/go.sum +++ b/go.sum @@ -259,6 +259,10 @@ github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJ github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/iotaledger/bcs-go v0.0.0-20250716100925-71f848cac593 h1:gfl7sPlhXPd+9YBb3V+C5U8bRvOozsOaubKwgQZWZ1w= github.com/iotaledger/bcs-go v0.0.0-20250716100925-71f848cac593/go.mod h1:yTxBDTSAbTPf9Xz0JAiBTVRM9RlJCCZd6amEA85L6ac= +github.com/iotaledger/bcs-go v0.0.0-20251107153435-2b6e4bbdcb0a h1:XG1WN7nlq0PQppuCEIZPeeFW6JPh1ijKYt1PLKX6p44= +github.com/iotaledger/bcs-go v0.0.0-20251107153435-2b6e4bbdcb0a/go.mod h1:yTxBDTSAbTPf9Xz0JAiBTVRM9RlJCCZd6amEA85L6ac= +github.com/iotaledger/bcs-go v0.0.0-20251107161402-980e31f7d5f7 h1:8hBuGWWZOyyfpeo3v7FFVig8kA8xQN9kCjzwyTB8YX8= +github.com/iotaledger/bcs-go v0.0.0-20251107161402-980e31f7d5f7/go.mod h1:yTxBDTSAbTPf9Xz0JAiBTVRM9RlJCCZd6amEA85L6ac= github.com/iotaledger/go-ethereum v1.16.2-wasp h1:cjVwedrUNXnFor77tDJOlTN8NY+SxxW68vbq3tmhMhs= github.com/iotaledger/go-ethereum v1.16.2-wasp/go.mod h1:X5CIOyo8SuK1Q5GnaEizQVLHT/DfsiGWuNeVdQcEMNA= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= diff --git a/go.work.sum b/go.work.sum index c54dcecbb0..feb591f1e5 100644 --- a/go.work.sum +++ b/go.work.sum @@ -135,6 +135,7 @@ github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+h github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/iotaledger/hive.go/constraints v0.0.0-20240520064018-c635e5900894/go.mod h1:JF7jjkL6tSUOXm23SWadBzBrl7eJk1DQRLc/fNoVZ+o= github.com/iotaledger/hive.go/kvstore v0.0.0-20230829151000-a4416d2e9c93/go.mod h1:DeP4JF4N995LteD0+/o7NsW1bO5IXURIJ27A69Ca5+Y= github.com/iotaledger/iota.go v1.0.0/go.mod h1:RiKYwDyY7aCD1L0YRzHSjOsJ5mUR9yvQpvhZncNcGQI= github.com/ipfs/go-datastore v0.8.2/go.mod h1:W+pI1NsUsz3tcsAACMtfC+IZdnQTnC/7VfPoJBQuts0= @@ -240,6 +241,7 @@ go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08 go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -249,6 +251,8 @@ golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -259,6 +263,7 @@ golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/packages/chain/chainmanager/chain_manager.go b/packages/chain/chainmanager/chain_manager.go index 8c26c2a3be..113fc2539e 100644 --- a/packages/chain/chainmanager/chain_manager.go +++ b/packages/chain/chainmanager/chain_manager.go @@ -185,8 +185,8 @@ func (npt *NeedPublishTX) String() string { type committeeLogInst struct { committeeAddr cryptolib.Address dkShare tcrypto.DKShare - gpaInstance gpa.GPA pendingMsgs []gpa.Message + gpaInstance *committeelog.CommitteeLog } type ChainMgr struct { @@ -293,8 +293,8 @@ 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) { - case *msgCommitteeLog: - return cmi.handleMsgCommitteeLog(msg) + case *msgNextLogIndex: + return cmi.handleMsgNextLogIndex(msg) case *msgBlockProduced: return cmi.handleMsgBlockProduced(msg) } @@ -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 *committeelog.CommitteeLog) committeelog.OutMessages { 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 *committeelog.CommitteeLog) committeelog.OutMessages { return cl.Input(committeelog.NewInputConsensusOutputRejected(input.anchor, input.logIndex)) }) } @@ -433,7 +433,7 @@ 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 { + return cmi.withCommitteeLog(input.committeeAddr, func(cl *committeelog.CommitteeLog) committeelog.OutMessages { return cl.Input(committeelog.NewInputConsensusOutputSkip(input.logIndex)) }) } @@ -442,24 +442,24 @@ func (cmi *ChainMgr) handleInputConsensusOutputSkip(input *inputConsensusOutputS // > Forward the message to the corresponding CommitteeLog; HandleCommitteeLogOutput. func (cmi *ChainMgr) handleInputConsensusTimeout(input *inputConsensusTimeout) gpa.OutMessages { cmi.log.LogDebugf("handleInputConsensusTimeout: %+v", input) - return cmi.withCommitteeLog(input.committeeAddr, func(cl gpa.GPA) gpa.OutMessages { + return cmi.withCommitteeLog(input.committeeAddr, func(cl *committeelog.CommitteeLog) committeelog.OutMessages { return cl.Input(committeelog.NewInputConsensusTimeout(input.logIndex)) }) } func (cmi *ChainMgr) handleInputCanPropose() gpa.OutMessages { cmi.log.LogDebugf("handleInputCanPropose") - return cmi.withAllCommitteeLogs(func(cl gpa.GPA) gpa.OutMessages { + return cmi.withAllCommitteeLogs(func(cl *committeelog.CommitteeLog) committeelog.OutMessages { 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 { - cmi.log.LogDebugf("handleMsgCommitteeLog: %+v", msg) - return cmi.withCommitteeLog(msg.committeeAddr, func(cl gpa.GPA) gpa.OutMessages { - return cl.Message(msg.wrapped) +func (cmi *ChainMgr) handleMsgNextLogIndex(msg *msgNextLogIndex) gpa.OutMessages { + cmi.log.LogDebugf("handleMsgNextLogIndex: %+v", msg) + return cmi.withCommitteeLog(msg.CommitteeAddr, func(cl *committeelog.CommitteeLog) committeelog.OutMessages { + return cl.HandleMsgNextLogIndex(&msg.MsgForCommitteeLog) }) } @@ -497,7 +497,7 @@ 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 committeelog.OutMessages) gpa.OutMessages { // // > Wrap out messages. msgs := gpa.NoMessages() @@ -605,11 +605,16 @@ func (cmi *ChainMgr) StatusString() string { // TODO: Call it periodically. Show //////////////////////////////////////////////////////////////////////////////// // Helper functions. -func (cmi *ChainMgr) wrapCommitteeLogMsgs(cli *committeeLogInst, outMsgs gpa.OutMessages) gpa.OutMessages { +func (cmi *ChainMgr) wrapCommitteeLogMsgs(cli *committeeLogInst, outMsgs committeelog.OutMessages) gpa.OutMessages { wrappedMsgs := gpa.NoMessages() - outMsgs.MustIterate(func(msg gpa.Message) { - wrappedMsgs.Add(NewMsgCommitteeLog(cli.committeeAddr, msg)) - }) + if outMsgs == nil { + return wrappedMsgs + } + + for _, msg := range outMsgs.NextLogIndex { + wrappedMsgs.Add(NewMsgNextLogIndex(cli.committeeAddr, msg)) + } + return wrappedMsgs } @@ -623,7 +628,7 @@ 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 *committeelog.CommitteeLog) committeelog.OutMessages) gpa.OutMessages { cli, err := cmi.ensureCommitteeLog(committeeAddr) if err != nil { cmi.log.LogWarnf("cannot find committee: %v", committeeAddr) @@ -632,7 +637,7 @@ func (cmi *ChainMgr) withCommitteeLog(committeeAddr cryptolib.Address, handler f return gpa.NoMessages().AddAll(cmi.handleCommitteeLogOutput(cli, handler(cli.gpaInstance))) } -func (cmi *ChainMgr) withAllCommitteeLogs(handler func(cl gpa.GPA) gpa.OutMessages) gpa.OutMessages { +func (cmi *ChainMgr) withAllCommitteeLogs(handler func(cl *committeelog.CommitteeLog) committeelog.OutMessages) gpa.OutMessages { msgs := gpa.NoMessages() for _, cli := range cmi.committeeLogs { msgs.AddAll(cmi.handleCommitteeLogOutput(cli, handler(cli.gpaInstance))) @@ -682,11 +687,10 @@ func (cmi *ChainMgr) ensureCommitteeLog(committeeAddr cryptolib.Address) (*commi if err != nil { return nil, fmt.Errorf("cannot create committeeLog for committeeAddress=%v: %w", committeeAddr, err) } - clGPA := clInst.AsGPA() cli := &committeeLogInst{ committeeAddr: committeeAddr, dkShare: dkShare, - gpaInstance: clGPA, + gpaInstance: clInst, pendingMsgs: []gpa.Message{}, } cmi.committeeLogs[committeeAddr.Key()] = cli diff --git a/packages/chain/chainmanager/msg.go b/packages/chain/chainmanager/msg.go index f2b43c1f61..650c56453b 100644 --- a/packages/chain/chainmanager/msg.go +++ b/packages/chain/chainmanager/msg.go @@ -5,24 +5,16 @@ package chainmanager import ( "github.com/iotaledger/wasp/v2/packages/gpa" - "github.com/iotaledger/wasp/v2/packages/state" ) const ( - msgTypeCommitteeLog gpa.MessageType = iota + msgTypeMsgNextLogIndex gpa.MessageType = iota 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 { - msgBlock := new(msgBlockProduced) - - // TODO: Validate if we ever have different block implementations. - msgBlock.block = state.NewBlock() - - return msgBlock - }, + msgTypeMsgNextLogIndex: func() gpa.Message { return new(msgNextLogIndex) }, + msgTypeBlockProduced: func() gpa.Message { return new(msgBlockProduced) }, }) } diff --git a/packages/chain/chainmanager/msg_cmt_log.go b/packages/chain/chainmanager/msg_cmt_log.go index 01d5962f95..c7964597f1 100644 --- a/packages/chain/chainmanager/msg_cmt_log.go +++ b/packages/chain/chainmanager/msg_cmt_log.go @@ -3,65 +3,35 @@ package chainmanager import ( "fmt" - 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" ) -// gpa.Wrapper is not applicable here, because here the addressing -// is by CommitteeID, not by integer index. -type msgCommitteeLog struct { - committeeAddr cryptolib.Address - wrapped gpa.Message +type msgNextLogIndex struct { + CommitteeAddr cryptolib.Address + MsgForCommitteeLog committeelog.MsgNextLogIndex } -var _ gpa.Message = new(msgCommitteeLog) - -func NewMsgCommitteeLog(committeeAddr cryptolib.Address, wrapped gpa.Message) gpa.Message { - return &msgCommitteeLog{ - committeeAddr: committeeAddr, - wrapped: wrapped, +func NewMsgNextLogIndex(committeeAddr cryptolib.Address, msg committeelog.MsgNextLogIndex) *msgNextLogIndex { + return &msgNextLogIndex{ + CommitteeAddr: committeeAddr, + MsgForCommitteeLog: msg, } } -func (msg *msgCommitteeLog) MsgType() gpa.MessageType { - return msgTypeCommitteeLog -} - -func (msg *msgCommitteeLog) String() string { - return fmt.Sprintf("{chainMgr.msgCommitteeLog, committeeAddr=%v, wrapped=%+v}", msg.committeeAddr.String(), msg.wrapped) +func (msg *msgNextLogIndex) MsgType() gpa.MessageType { + return msgTypeMsgNextLogIndex } -func (msg *msgCommitteeLog) Recipient() gpa.NodeID { - return msg.wrapped.Recipient() +func (msg *msgNextLogIndex) String() string { + return fmt.Sprintf("{chainMgr.msgNextLogIndex, committeeAddr=%v, MsgNextLogIndex=%+v}", msg.CommitteeAddr.String(), msg.MsgForCommitteeLog) } -func (msg *msgCommitteeLog) SetSender(sender gpa.NodeID) { - msg.wrapped.SetSender(sender) +func (msg *msgNextLogIndex) Recipient() gpa.NodeID { + return msg.MsgForCommitteeLog.Recipient() } -func (msg *msgCommitteeLog) MarshalBCS(e *bcs.Encoder) error { - wrappedBytes, err := gpa.MarshalMessage(msg.wrapped) - if err != nil { - return fmt.Errorf("marshaling wrapped message: %w", err) - } - - e.Encode(msg.committeeAddr) - e.Encode(wrappedBytes) - - return nil -} - -func (msg *msgCommitteeLog) UnmarshalBCS(d *bcs.Decoder) error { - d.Decode(&msg.committeeAddr) - wrappedBytes := bcs.Decode[[]byte](d) - - var err error - msg.wrapped, err = committeelog.UnmarshalMessage(wrappedBytes) - if err != nil { - return fmt.Errorf("unmarshaling wrapped message: %w", err) - } - - return nil +func (msg *msgNextLogIndex) SetSender(sender gpa.NodeID) { + msg.MsgForCommitteeLog.SetSender(sender) } diff --git a/packages/chain/chainmanager/msg_cmt_log_test.go b/packages/chain/chainmanager/msg_cmt_log_test.go index f9f9958c8e..528f56bdd1 100644 --- a/packages/chain/chainmanager/msg_cmt_log_test.go +++ b/packages/chain/chainmanager/msg_cmt_log_test.go @@ -12,9 +12,9 @@ import ( func TestMsgCommitteeLogSerialization(t *testing.T) { address := cryptolib.NewRandomAddress() - msg := &msgCommitteeLog{ + msg := &msgNextLogIndex{ *address, - &committeelog.MsgNextLogIndex{ + committeelog.MsgNextLogIndex{ BasicMessage: gpa.BasicMessage{}, NextLogIndex: committeelog.LogIndex(rand.Int31()), PleaseRepeat: false, @@ -23,9 +23,9 @@ func TestMsgCommitteeLogSerialization(t *testing.T) { bcs.TestCodec(t, msg) - msg = &msgCommitteeLog{ + msg = &msgNextLogIndex{ *cryptolib.TestAddress, - &committeelog.MsgNextLogIndex{ + 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..c0be4cad43 100644 --- a/packages/chain/committeelog/cmt_log.go +++ b/packages/chain/committeelog/cmt_log.go @@ -63,12 +63,9 @@ type CommitteeLog struct { suspended bool // Is this committee currently suspended? output Output // The current output. first bool // A workaround to senf the first nextLI messages. - asGPA gpa.GPA // This object, just with all the needed wrappers. log log.Logger } -var _ gpa.GPA = &CommitteeLog{} - // New constructs a new node instance for this protocol. // // > ON Startup: @@ -136,25 +133,19 @@ 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) OutMessages { 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) OutMessages { log.LogDebugf("VarLocalView: Output received, %v", ao) return cl.varConsInsts.LatestL1Anchor(ao, cl.varLogIndex.ConsensusStarted) }, log.NewChildLogger("VLV")) - cl.asGPA = gpa.NewOwnHandler(me, cl) return cl, nil } -// AsGPA implements the CommitteeLog interface. -func (cl *CommitteeLog) AsGPA() gpa.GPA { - return cl.asGPA -} - // Input implements the gpa.GPA interface. -func (cl *CommitteeLog) Input(input gpa.Input) gpa.OutMessages { +func (cl *CommitteeLog) Input(input gpa.Input) OutMessages { switch input.(type) { case *inputCanPropose: break // Don't log, its periodic. @@ -162,11 +153,11 @@ func (cl *CommitteeLog) Input(input gpa.Input) gpa.OutMessages { cl.log.LogDebugf("Input %T: %+v", input, input) } switch input := input.(type) { - case *inputAnchorConfirmed: + case *InputAnchorConfirmed: return cl.handleInputAnchorConfirmed(input) case *inputConsensusOutputSkip: return cl.handleInputConsensusOutputSkip(input) - case *inputConsensusOutputConfirmed: + case *InputConsensusOutputConfirmed: return cl.handleInputConsensusOutputConfirmed(input) case *inputConsensusOutputRejected: return cl.handleInputConsensusOutputRejected(input) @@ -181,44 +172,34 @@ func (cl *CommitteeLog) Input(input gpa.Input) gpa.OutMessages { panic(fmt.Errorf("unexpected input %T: %+v", input, input)) } -// Message implements the gpa.GPA interface. -func (cl *CommitteeLog) Message(msg gpa.Message) gpa.OutMessages { - msgNLI, ok := msg.(*MsgNextLogIndex) - if !ok { - cl.log.LogWarnf("dropping unexpected message %T: %+v", msg, msg) - return nil - } - return cl.handleMsgNextLogIndex(msgNLI) -} - // The latest anchor object's version confirmed at the L1. -func (cl *CommitteeLog) handleInputAnchorConfirmed(input *inputAnchorConfirmed) gpa.OutMessages { +func (cl *CommitteeLog) handleInputAnchorConfirmed(input *InputAnchorConfirmed) OutMessages { 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) OutMessages { 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) OutMessages { 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) OutMessages { 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) OutMessages { return cl.varConsInsts.ConsOutputTimeout(input.logIndex, cl.varLogIndex.ConsensusStarted) } -func (cl *CommitteeLog) handleInputCanPropose() gpa.OutMessages { - msgs := gpa.NoMessages() +func (cl *CommitteeLog) handleInputCanPropose() OutMessages { + msgs := NoMessages() msgs.AddAll(cl.varConsInsts.Tick(cl.varLogIndex.ConsensusStarted)) if cl.first && cl.output != nil && len(cl.output) > 0 { @@ -239,7 +220,7 @@ func (cl *CommitteeLog) handleInputSuspend() { // > ON Reception of ⟨NextLI, •⟩ message: // > ... -func (cl *CommitteeLog) handleMsgNextLogIndex(msg *MsgNextLogIndex) gpa.OutMessages { +func (cl *CommitteeLog) HandleMsgNextLogIndex(msg *MsgNextLogIndex) OutMessages { return cl.varLogIndex.MsgNextLogIndexReceived(msg) } diff --git a/packages/chain/committeelog/cmt_log_test.go b/packages/chain/committeelog/cmt_log_test.go index d205c04707..69ba533d94 100644 --- a/packages/chain/committeelog/cmt_log_test.go +++ b/packages/chain/committeelog/cmt_log_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/wasp/v2/clients/iota-go/iotago" "github.com/iotaledger/wasp/v2/clients/iota-go/iotago/iotatest" "github.com/iotaledger/wasp/v2/clients/iscmove" @@ -56,7 +57,7 @@ func testCommitteeLogBasic(t *testing.T, n, f int) { // // Construct the algorithm nodes. gpaNodeIDs := gpa.NodeIDsFromPublicKeys(peerPubKeys) - gpaNodes := map[gpa.NodeID]gpa.GPA{} + gpaNodes := map[gpa.NodeID]*committeelog.CommitteeLog{} for i := range gpaNodeIDs { dkShare, err := committeeKeyShares[i].LoadDKShare(committeeAddress) require.NoError(t, err) @@ -83,9 +84,34 @@ func testCommitteeLogBasic(t *testing.T, n, f int) { log.NewChildLogger(fmt.Sprintf("N%v", i)), ) require.NoError(t, err) - gpaNodes[gpaNodeIDs[i]] = committeeLogInst.AsGPA() + gpaNodes[gpaNodeIDs[i]] = committeeLogInst } - gpaTC := gpa.NewTestContext(gpaNodes) + + gpaTC := gpa.NewTestContextNew(gpaNodes, gpa.TestContextNewFunctors[*committeelog.CommitteeLog, any, gpa.MessageNew]{ + ApplyInput: func(obj *committeelog.CommitteeLog, input any) []gpa.MessageNew { + outMsgs := obj.Input(input.(gpa.Input)) + if outMsgs == nil { + return nil + } + return lo.Map(outMsgs.NextLogIndex, func(li committeelog.MsgNextLogIndex) gpa.MessageNew { return &li }) + }, + ApplyMessage: func(obj *committeelog.CommitteeLog, msg gpa.MessageNew) []gpa.MessageNew { + switch m := msg.(type) { + case *committeelog.MsgNextLogIndex: + outMsgs := obj.HandleMsgNextLogIndex(m) + if outMsgs == nil { + return nil + } + return lo.Map(outMsgs.NextLogIndex, func(li committeelog.MsgNextLogIndex) gpa.MessageNew { return &li }) + default: + panic(fmt.Sprintf("unexpected message type %T", msg)) + } + }, + Output: func(obj *committeelog.CommitteeLog) any { return obj.Output() }, + StatusString: func(obj *committeelog.CommitteeLog) string { return obj.StatusString() }, + }) + gpaTC.WithoutSerialization() + // // Start the algorithms. gpaTC.RunAll() @@ -95,7 +121,7 @@ func testCommitteeLogBasic(t *testing.T, n, f int) { // FIXME is should be anchor state transition, instead of random anchor ao1 := randomAnchorWithID(*aliasRef.ObjectID, committeeAddress, 1) t.Logf("Anchor1=%v", ao1) - gpaTC.WithInputs(inputAnchorConfirmed(gpaNodes, ao1)).RunAll() + gpaTC.WithInputs(inputAnchorConfirmedNew(gpaNodes, ao1)).RunAll() gpaTC.PrintAllStatusStrings("After Anchor1Recv", t.Logf) cons1 := gpaNodes[gpaNodeIDs[0]].Output().(committeelog.Output) cons1Outs := map[gpa.NodeID]committeelog.Output{} @@ -111,7 +137,7 @@ func testCommitteeLogBasic(t *testing.T, n, f int) { // FIXME is should be anchor state transition, instead of random anchor ao2 := randomAnchorWithID(*aliasRef.ObjectID, committeeAddress, 2) t.Logf("Anchor2=%v", ao2) - gpaTC.WithInputs(inputConsensusOutput(cons1Outs, ao2)).RunAll() + gpaTC.WithInputs(inputConsensusOutputNew(cons1Outs, ao2)).RunAll() gpaTC.PrintAllStatusStrings("After gpaMsgsAnchor2Cons", t.Logf) cons2 := gpaNodes[gpaNodeIDs[0]].Output().(committeelog.Output) t.Logf("cons2=%v", cons2) @@ -125,7 +151,7 @@ func testCommitteeLogBasic(t *testing.T, n, f int) { } // // Anchor Confirmed received (nothing changes, we are ahead of it) - gpaTC.WithInputs(inputAnchorConfirmed(gpaNodes, ao2)).RunAll() + gpaTC.WithInputs(inputAnchorConfirmedNew(gpaNodes, ao2)).RunAll() gpaTC.PrintAllStatusStrings("After gpaMsgsAnchor2Recv", t.Logf) for _, n := range gpaNodes { require.NotNil(t, n.Output()) @@ -136,6 +162,22 @@ func testCommitteeLogBasic(t *testing.T, n, f int) { //////////////////////////////////////////////////////////////////////////////// // Helper functions. +func inputAnchorConfirmedNew(gpaNodes map[gpa.NodeID]*committeelog.CommitteeLog, ao *isc.StateAnchor) map[gpa.NodeID]any { + inputs := map[gpa.NodeID]any{} + for n := range gpaNodes { + inputs[n] = committeelog.NewInputAnchorConfirmed(ao) + } + return inputs +} + +func inputAnchorConfirme(gpaNodes map[gpa.NodeID]gpa.GPA, ao *isc.StateAnchor) map[gpa.NodeID]any { + inputs := map[gpa.NodeID]any{} + for n := range gpaNodes { + inputs[n] = committeelog.NewInputAnchorConfirmed(ao) + } + return inputs +} + func inputAnchorConfirmed(gpaNodes map[gpa.NodeID]gpa.GPA, ao *isc.StateAnchor) map[gpa.NodeID]gpa.Input { inputs := map[gpa.NodeID]gpa.Input{} for n := range gpaNodes { @@ -144,6 +186,21 @@ func inputAnchorConfirmed(gpaNodes map[gpa.NodeID]gpa.GPA, ao *isc.StateAnchor) return inputs } +func inputConsensusOutputNew(consReq map[gpa.NodeID]committeelog.Output, nextAnchor *isc.StateAnchor) map[gpa.NodeID]any { + inputs := map[gpa.NodeID]any{} + for nid, outs := range consReq { + maxLI := committeelog.NilLogIndex() + for li := range outs { + if li <= maxLI { + break + } + maxLI = li + inputs[nid] = committeelog.NewInputConsensusOutputConfirmed(nextAnchor, li) + } + } + return inputs +} + func inputConsensusOutput(consReq map[gpa.NodeID]committeelog.Output, nextAnchor *isc.StateAnchor) map[gpa.NodeID]gpa.Input { inputs := map[gpa.NodeID]gpa.Input{} for nid, outs := range consReq { diff --git a/packages/chain/committeelog/input_alias_output_confirmed.go b/packages/chain/committeelog/input_alias_output_confirmed.go index 9ebf76b2c8..8bc2b112f6 100644 --- a/packages/chain/committeelog/input_alias_output_confirmed.go +++ b/packages/chain/committeelog/input_alias_output_confirmed.go @@ -6,20 +6,19 @@ package committeelog import ( "fmt" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/isc" ) -type inputAnchorConfirmed struct { +type InputAnchorConfirmed struct { anchor *isc.StateAnchor } -func NewInputAnchorConfirmed(anchor *isc.StateAnchor) gpa.Input { - return &inputAnchorConfirmed{ +func NewInputAnchorConfirmed(anchor *isc.StateAnchor) *InputAnchorConfirmed { + return &InputAnchorConfirmed{ anchor: anchor, } } -func (inp *inputAnchorConfirmed) String() string { +func (inp *InputAnchorConfirmed) String() string { return fmt.Sprintf("{committeeLog.inputAnchorConfirmed, %v}", inp.anchor) } diff --git a/packages/chain/committeelog/input_consensus_output_confirmed.go b/packages/chain/committeelog/input_consensus_output_confirmed.go index f91a878aa9..29124e655c 100644 --- a/packages/chain/committeelog/input_consensus_output_confirmed.go +++ b/packages/chain/committeelog/input_consensus_output_confirmed.go @@ -6,22 +6,21 @@ package committeelog import ( "fmt" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/isc" ) -type inputConsensusOutputConfirmed struct { +type InputConsensusOutputConfirmed struct { nextAnchor *isc.StateAnchor logIndex LogIndex } -func NewInputConsensusOutputConfirmed(nextAnchor *isc.StateAnchor, logIndex LogIndex) gpa.Input { - return &inputConsensusOutputConfirmed{ +func NewInputConsensusOutputConfirmed(nextAnchor *isc.StateAnchor, logIndex LogIndex) *InputConsensusOutputConfirmed { + return &InputConsensusOutputConfirmed{ nextAnchor: nextAnchor, logIndex: logIndex, } } -func (inp *inputConsensusOutputConfirmed) String() string { +func (inp *InputConsensusOutputConfirmed) String() string { return fmt.Sprintf("{committeeLog.inputConsensusOutputConfirmed, result=%v, li=%v}", inp.nextAnchor, inp.logIndex) } diff --git a/packages/chain/committeelog/msg.go b/packages/chain/committeelog/msg.go index 5480844c3c..d775c05b3e 100644 --- a/packages/chain/committeelog/msg.go +++ b/packages/chain/committeelog/msg.go @@ -3,20 +3,19 @@ package committeelog -import ( - "github.com/iotaledger/wasp/v2/packages/gpa" -) - -const ( - msgTypeNextLogIndex gpa.MessageType = iota -) +// This is done just to decrease number of diff lines in PR. We can change it after demo of idea. +type OutMessages *OutMessagesV +type OutMessagesV struct { + NextLogIndex []MsgNextLogIndex +} -func (cl *CommitteeLog) UnmarshalMessage(data []byte) (gpa.Message, error) { - return UnmarshalMessage(data) +func (m *OutMessagesV) AddAll(msgs OutMessages) OutMessages { + if msgs != nil { + m.NextLogIndex = append(m.NextLogIndex, msgs.NextLogIndex...) + } + return m } -func UnmarshalMessage(data []byte) (gpa.Message, error) { - return gpa.UnmarshalMessage(data, gpa.Mapper{ - msgTypeNextLogIndex: func() gpa.Message { return new(MsgNextLogIndex) }, - }) +func NoMessages() *OutMessagesV { + return &OutMessagesV{} } diff --git a/packages/chain/committeelog/msg_next_log_index.go b/packages/chain/committeelog/msg_next_log_index.go index a2befc5805..654bd28316 100644 --- a/packages/chain/committeelog/msg_next_log_index.go +++ b/packages/chain/committeelog/msg_next_log_index.go @@ -31,8 +31,6 @@ type MsgNextLogIndex struct { PleaseRepeat bool // If true, the receiver should resend its latest message back to the sender. } -var _ gpa.Message = new(MsgNextLogIndex) - func NewMsgNextLogIndex(recipient gpa.NodeID, nextLogIndex LogIndex, cause MsgNextLogIndexCause, pleaseRepeat bool) *MsgNextLogIndex { return &MsgNextLogIndex{ BasicMessage: gpa.NewBasicMessage(recipient), @@ -53,10 +51,6 @@ func (msg *MsgNextLogIndex) AsResent() *MsgNextLogIndex { } } -func (msg *MsgNextLogIndex) MsgType() gpa.MessageType { - return msgTypeNextLogIndex -} - func (msg *MsgNextLogIndex) String() string { return fmt.Sprintf( "{MsgNextLogIndex[%v], sender=%v, nextLogIndex=%v, pleaseRepeat=%v", diff --git a/packages/chain/committeelog/quorum_counter.go b/packages/chain/committeelog/quorum_counter.go index 51b307b86f..89e85d95d7 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) OutMessages { if li <= qc.myLastVoteLI { return nil } qc.myLastVoteLI = li - msgs := gpa.NoMessages() + msgs := NoMessages() 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) qc.lastSentMsgs[nodeID] = msg - msgs.Add(msg) + msgs.NextLogIndex = append(msgs.NextLogIndex, *msg) } return msgs } @@ -44,9 +44,9 @@ 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, msgs OutMessages) OutMessages { if msg, ok := qc.lastSentMsgs[peer]; ok { - msgs.Add(msg.AsResent()) + msgs.NextLogIndex = append(msgs.NextLogIndex, *msg.AsResent()) } return msgs } diff --git a/packages/chain/committeelog/var_cons_insts.go b/packages/chain/committeelog/var_cons_insts.go index a765bf87cb..f78a61f8cc 100644 --- a/packages/chain/committeelog/var_cons_insts.go +++ b/packages/chain/committeelog/var_cons_insts.go @@ -6,11 +6,10 @@ import ( "github.com/iotaledger/hive.go/log" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/isc" ) -type onLIInc = func(li LogIndex) gpa.OutMessages +type onLIInc = func(li LogIndex) OutMessages // VarConsInsts implements the algorithm modeled in WaspChainCommitteeLogSUI.tla type VarConsInsts struct { @@ -54,13 +53,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) OutMessages { 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) OutMessages { vci.haveConsOut = true if vci.lastAnchor == nil { vci.lastLI = li.Next() // Will be set in LatestL1Anchor. @@ -70,13 +69,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) OutMessages { 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() +func (vci *VarConsInsts) LatestSeenLI(seenLI LogIndex, cb onLIInc) OutMessages { + msgs := NoMessages() msgs.AddAll(vci.trySet(seenLI.Prev(), nil, cb)) if !vci.haveConsOut { // Still don't have the initial round succeeded, thus keep proposing the NIL. @@ -90,12 +89,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) OutMessages { 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) OutMessages { n := len(vci.delayed) last := vci.delayed[n-1] for i := n - 1; i > 0; i-- { @@ -108,7 +107,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) OutMessages { // // Is it outdated? if li < vci.minLI { @@ -124,7 +123,7 @@ func (vci *VarConsInsts) trySet(li LogIndex, ao *isc.StateAnchor, cb onLIInc) gp vci.lis[li] = ao // // Track the max. - msgs := gpa.NoMessages() + msgs := NoMessages() if li > vci.maxLI { vci.persistCB(li) vci.maxLI = li diff --git a/packages/chain/committeelog/var_localview.go b/packages/chain/committeelog/var_localview.go index 8fcba849ca..7a001b67c0 100644 --- a/packages/chain/committeelog/var_localview.go +++ b/packages/chain/committeelog/var_localview.go @@ -52,7 +52,6 @@ import ( "github.com/iotaledger/hive.go/log" "github.com/iotaledger/wasp/v2/clients/iota-go/iotasigner" - "github.com/iotaledger/wasp/v2/packages/gpa" "github.com/iotaledger/wasp/v2/packages/isc" ) @@ -70,12 +69,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) OutMessages // 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) OutMessages, log log.Logger) *VarLocalView { log.LogDebugf("NewVarLocalView, pipeliningLimit=%v", pipeliningLimit) return &VarLocalView{ latestTip: nil, @@ -86,12 +85,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) OutMessages { 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) OutMessages { stateIndex := consumedAnchor.GetStateIndex() stateIndexEntries, _ := lvi.pendingTXes.GetOrCreate(stateIndex, func() []*varLocalViewEntry { return []*varLocalViewEntry{} }) contains := lo.ContainsBy(stateIndexEntries, func(entry *varLocalViewEntry) bool { @@ -108,7 +107,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) OutMessages { 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 +126,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() OutMessages { if lvi.confirmedAnchor == nil { lvi.updateVal(nil) return nil @@ -151,7 +150,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) OutMessages { 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..76de8310f4 100644 --- a/packages/chain/committeelog/var_log_index.go +++ b/packages/chain/committeelog/var_log_index.go @@ -19,7 +19,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) OutMessages metrics *metrics.ChainCommitteeLogMetrics log log.Logger } @@ -29,7 +29,7 @@ func NewVarLogIndex( n int, f int, persistedLI LogIndex, - outputCB func(li LogIndex) gpa.OutMessages, + outputCB func(li LogIndex) OutMessages, metrics *metrics.ChainCommitteeLogMetrics, log log.Logger, ) *VarLogIndex { @@ -55,15 +55,15 @@ func (vli *VarLogIndex) StatusString() string { ) } -func (vli *VarLogIndex) ConsensusStarted(consensusLI LogIndex) gpa.OutMessages { +func (vli *VarLogIndex) ConsensusStarted(consensusLI LogIndex) OutMessages { vli.log.LogDebugf("ConsensusStarted: consensusLI=%v", consensusLI) - msgs := gpa.NoMessages() + msgs := NoMessages() msgs.AddAll(vli.qcStarted.MaybeSendVote(consensusLI)) msgs.AddAll(vli.tryOutputOnStarted()) return msgs } -func (vli *VarLogIndex) MsgNextLogIndexReceived(msg *MsgNextLogIndex) gpa.OutMessages { +func (vli *VarLogIndex) MsgNextLogIndexReceived(msg *MsgNextLogIndex) OutMessages { vli.log.LogDebugf("MsgNextLogIndexReceived, %v", msg) sender := msg.Sender() if !vli.knownNodeID(sender) { @@ -80,18 +80,18 @@ func (vli *VarLogIndex) MsgNextLogIndexReceived(msg *MsgNextLogIndex) gpa.OutMes } } -func (vli *VarLogIndex) msgNextLogIndexOnStarted(msg *MsgNextLogIndex) gpa.OutMessages { +func (vli *VarLogIndex) msgNextLogIndexOnStarted(msg *MsgNextLogIndex) OutMessages { vli.qcStarted.VoteReceived(msg) return vli.tryOutputOnStarted() } -func (vli *VarLogIndex) tryOutputOnStarted() gpa.OutMessages { +func (vli *VarLogIndex) tryOutputOnStarted() OutMessages { 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) OutMessages { 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..85e0c53483 100644 --- a/packages/chain/committeelog/var_log_index_test.go +++ b/packages/chain/committeelog/var_log_index_test.go @@ -23,7 +23,7 @@ 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) committeelog.OutMessages { vliOut = li return nil }, nil, log) @@ -48,7 +48,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) committeelog.OutMessages { vliOut = li return nil }, nil, log) diff --git a/packages/chain/distsign/dss.go b/packages/chain/distsign/dss.go index 1b6c58ea83..f55b0affee 100644 --- a/packages/chain/distsign/dss.go +++ b/packages/chain/distsign/dss.go @@ -96,7 +96,7 @@ func New( log: log, } d.msgWrapper = gpa.NewMsgWrapper(msgTypeWrapped, d.msgWrapperFunc) - d.withWrappers = gpa.NewOwnHandler(me, d) + d.withWrappers = d return d } diff --git a/packages/gpa/aba/mostefaoui/mostefaoui.go b/packages/gpa/aba/mostefaoui/mostefaoui.go index c112184f4a..5c482da20f 100644 --- a/packages/gpa/aba/mostefaoui/mostefaoui.go +++ b/packages/gpa/aba/mostefaoui/mostefaoui.go @@ -136,7 +136,7 @@ func New(nodeIDs []gpa.NodeID, me gpa.NodeID, f int, ccCreateFun func(round int) a.varDone = newVarDone(nodeIDs, me, f, a.uponTerminationCondition, log) a.uponDecisionInputs = newUponDecisionInputs(a.uponDecisionInputsReceived) a.msgWrapper = gpa.NewMsgWrapper(msgTypeWrapped, a.selectSubsystem) - a.asGPA = gpa.NewOwnHandler(me, a) + a.asGPA = a return a } diff --git a/packages/gpa/acs/acs.go b/packages/gpa/acs/acs.go index 9028a460c3..6819256f6e 100644 --- a/packages/gpa/acs/acs.go +++ b/packages/gpa/acs/acs.go @@ -105,7 +105,7 @@ func New(nodeIDs []gpa.NodeID, me gpa.NodeID, f int, ccCreateFun func(node gpa.N } a.termCond = newUponTermCondition(n, a.uponTermCondition) a.msgWrapper = gpa.NewMsgWrapper(msgTypeWrapped, a.selectSubsystem) - a.asGPA = gpa.NewOwnHandler(me, a) + a.asGPA = a return a } diff --git a/packages/gpa/acss/acss.go b/packages/gpa/acss/acss.go index dd0b6ae184..6b3d2992dc 100644 --- a/packages/gpa/acss/acss.go +++ b/packages/gpa/acss/acss.go @@ -187,7 +187,7 @@ func New( if a.myIdx = a.peerIndex(me); a.myIdx == -1 { panic("i'm not in the peer list") } - return gpa.NewOwnHandler(me, &a) + return &a } // Input for the algorithm is the secret to share. @@ -265,6 +265,10 @@ func (a *acssImpl) handleRBCMessage(m *gpa.WrappingMsg) gpa.OutMessages { } func (a *acssImpl) tryHandleRBCTermination(wasOut bool, msgs gpa.OutMessages) gpa.OutMessages { + if msgs == nil { + msgs = gpa.NoMessages() + } + 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}) diff --git a/packages/gpa/asyncdistkeygen/nonce/nonce.go b/packages/gpa/asyncdistkeygen/nonce/nonce.go index a7da9879d3..0dc942bd1d 100644 --- a/packages/gpa/asyncdistkeygen/nonce/nonce.go +++ b/packages/gpa/asyncdistkeygen/nonce/nonce.go @@ -118,7 +118,7 @@ func New( for i := range n.acss { n.acss[i] = acss.New(suite, nodeIDs, peerPKs, f, me, mySK, nodeIDs[i], nil, log) } - return gpa.NewOwnHandler(me, n) + return n } func (n *nonceDistributedKeyGenerationImpl) Input(input gpa.Input) gpa.OutMessages { diff --git a/packages/gpa/msg_wrapper.go b/packages/gpa/msg_wrapper.go index 3b2b9f44f7..6fc11638b0 100644 --- a/packages/gpa/msg_wrapper.go +++ b/packages/gpa/msg_wrapper.go @@ -25,13 +25,12 @@ func (w *MsgWrapper) WrapMessage(subsystem byte, index int, msg Message) Message } 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)) - }) + if msgs != nil { + msgs.MustIterate(func(msg Message) { + wrapped.Add(w.WrapMessage(subsystem, index, msg)) + }) + } return wrapped } diff --git a/packages/gpa/rbc/bracha/bracha.go b/packages/gpa/rbc/bracha/bracha.go index 5a6589ed27..00114b83ec 100644 --- a/packages/gpa/rbc/bracha/bracha.go +++ b/packages/gpa/rbc/bracha/bracha.go @@ -91,7 +91,7 @@ func New(peers []gpa.NodeID, f int, me, broadcaster gpa.NodeID, maxMsgSize int, for i := range peers { r.msgRecv[peers[i]] = map[msgBrachaType]bool{} } - return gpa.NewOwnHandler(me, r) + return r } // Input implements the GPA interface. diff --git a/packages/gpa/test_context_new.go b/packages/gpa/test_context_new.go new file mode 100644 index 0000000000..32a7661ff6 --- /dev/null +++ b/packages/gpa/test_context_new.go @@ -0,0 +1,312 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package gpa + +import ( + "bytes" + "math/rand" + "sort" + + "github.com/samber/lo" +) + +type MessageNew interface { + Recipient() NodeID // The sender should indicate the recipient. + SetSender(NodeID) // The transport later will set a validated sender for a message. +} + +type TestContextNewFunctors[Obj any, Input any, Msg MessageNew] struct { + ApplyInput func(obj Obj, input Input) []Msg + ApplyMessage func(obj Obj, msg Msg) []Msg + Output func(obj Obj) any + StatusString func(obj Obj) string + MarshalMessage func(msg Msg) ([]byte, error) + UnmarshalMessage func(obj Obj, data []byte) (Msg, error) +} + +// TestContextNew imitates a cluster of nodes and the medium performing the message exchange. +// Inputs are processes in-order for each node individually. +type TestContextNew[Obj any, Input any, Msg MessageNew] struct { + functors TestContextNewFunctors[Obj, Input, Msg] + nodes map[NodeID]Obj // Nodes to test. + inputs map[NodeID][]Input // Not yet provided inputs. + inputCh <-chan map[NodeID]Input // A way to provide additional inputs w/o synchronizing other parts. + inputProb float64 // A probability to process input, instead of a message (if any). + inputCount int // Number if inputs still not delivered. + 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 SenderMessageNew[Msg] // A way to provide additional messages w/o synchronizing other parts. + msgs []SenderMessageNew[Msg] // Not yet delivered messages. + msgsSent int // Stats. + msgsRecv int // Stats. +} + +func NewTestContextNew[Obj any, Input any, Msg MessageNew]( + nodes map[NodeID]Obj, + functors TestContextNewFunctors[Obj, Input, Msg], +) *TestContextNew[Obj, Input, Msg] { + inputs := map[NodeID][]Input{} + for n := range nodes { + inputs[n] = []Input{} + } + tc := TestContextNew[Obj, Input, Msg]{ + functors: functors, + msgSerialize: true, + nodes: nodes, + inputs: inputs, + inputProb: 1.0, + inputCount: 0, + msgDeliveryProb: 1.0, + msgs: []SenderMessageNew[Msg]{}, + } + return &tc +} + +func (tc *TestContextNew[Obj, Input, Message]) WithoutSerialization() *TestContextNew[Obj, Input, Message] { + tc.msgSerialize = false + return tc +} + +func (tc *TestContextNew[Obj, Input, Message]) MsgCounts() (int, int) { + return tc.msgsSent, tc.msgsRecv +} + +// AddInputs adds new inputs to the existing set. +// The inputs will be overridden, if exist for the same nodes. +func (tc *TestContextNew[Obj, Input, Message]) AddInputs(inputs map[NodeID]Input) { + for nid := range inputs { + tc.inputs[nid] = append(tc.inputs[nid], inputs[nid]) + } + tc.inputCount += len(inputs) +} + +func (tc *TestContextNew[Obj, Input, Message]) WithInput(nodeID NodeID, input Input) *TestContextNew[Obj, Input, Message] { + tc.AddInputs(map[NodeID]Input{nodeID: input}) + return tc +} + +func (tc *TestContextNew[Obj, Input, Message]) WithInputs(inputs map[NodeID]Input) *TestContextNew[Obj, Input, Message] { + tc.AddInputs(inputs) + return tc +} + +func (tc *TestContextNew[Obj, Input, Message]) WithInputChannel(inputCh <-chan map[NodeID]Input) *TestContextNew[Obj, Input, Message] { + tc.inputCh = inputCh + return tc +} + +func (tc *TestContextNew[Obj, Input, Message]) WithInputProbability(inputProb float64) *TestContextNew[Obj, Input, Message] { + tc.inputProb = inputProb + return tc +} + +func (tc *TestContextNew[Obj, Input, Message]) WithMessageDeliveryProbability(msgDeliveryProb float64) *TestContextNew[Obj, Input, Message] { + tc.msgDeliveryProb = msgDeliveryProb + return tc +} + +func (tc *TestContextNew[Obj, Input, Message]) WithMessages(sender NodeID, msgs []Message) *TestContextNew[Obj, Input, Message] { + tc.msgsSent += len(msgs) + tc.msgs = append(tc.msgs, tc.setMessageSender(sender, msgs)...) + return tc +} + +func (tc *TestContextNew[Obj, Input, Message]) WithMessage(sender NodeID, msg Message) *TestContextNew[Obj, Input, Message] { + tc.msgsSent++ + tc.msgs = append(tc.msgs, tc.setMessageSender(sender, []Message{msg})...) + return tc +} + +func (tc *TestContextNew[Obj, Input, Message]) WithMessageChannel(msgCh <-chan SenderMessageNew[Message]) *TestContextNew[Obj, Input, Message] { + tc.msgCh = msgCh + return tc +} + +func (tc *TestContextNew[Obj, Input, Message]) WithOutputHandler(outputHandler func(nodeID NodeID, output Output)) *TestContextNew[Obj, Input, Message] { + tc.outputHandler = outputHandler + return tc +} + +func (tc *TestContextNew[Obj, Input, Message]) WithCall(sender NodeID, call func() []Message) *TestContextNew[Obj, Input, Message] { + msgs := call() + return tc.WithMessages(sender, msgs) +} + +func (tc *TestContextNew[Obj, Input, Message]) RunUntil(predicate func() bool) { + loop := make(chan bool, 1) + loop <- true + keepLooping := func() { + if len(loop) == 0 { + loop <- true + } + } + for { + select { + case inputs, ok := <-tc.inputCh: + keepLooping() + if !ok { + tc.inputCh = nil + continue + } + if len(inputs) == 0 { + continue + } + for nid, input := range inputs { + 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 + } + tc.tryProcessInput() // Try provide an input, if any and we are lucky. + tc.tryProcessMessage() // Otherwise just process the messages. + if len(tc.msgs) > 0 || tc.inputCount > 0 { + // We can proceed with looping. + loop <- true + continue + } + if tc.inputCh == nil && tc.msgCh == nil { + // Channels are closed and there is no more inputs or messages. Stop it. + return + } + // Otherwise we have to wait for something from channels. + } + } +} + +func (tc *TestContextNew[Obj, Input, Message]) tryProcessInput() { + if tc.inputCount > 0 && (rand.Float64() <= tc.inputProb || len(tc.msgs) == 0) { + rnd := rand.Intn(tc.inputCount) + var rndNID NodeID + var rndInp Input + for nodeID, nodeInputs := range tc.inputs { + if rnd >= len(nodeInputs) { + rnd -= len(nodeInputs) + continue + } + rndNID = nodeID + rndInp = nodeInputs[0] + tc.inputs[nodeID] = nodeInputs[1:] // Take them in order. + break + } + tc.inputCount-- + + outMsgs := tc.functors.ApplyInput(tc.nodes[rndNID], rndInp) + newMsgs := tc.setMessageSender(rndNID, outMsgs) + if newMsgs != nil { + tc.msgsSent += len(newMsgs) + tc.msgs = append(tc.msgs, newMsgs...) + } + tc.tryCallOutputHandler(rndNID) + } +} + +func (tc *TestContextNew[Obj, Input, Message]) 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:]...) + tc.msgsRecv++ + if rand.Float64() <= tc.msgDeliveryProb { // Deliver some messages. + gpaMsg := msg.Message + if tc.msgSerialize { + msgBytes := lo.Must(tc.functors.MarshalMessage(*msg.Message)) + if m, err := tc.functors.UnmarshalMessage(tc.nodes[nid], msgBytes); err == nil { + gpaMsg = &m + (*gpaMsg).SetSender(msg.Sender) + } else { + // E.g. silent node cannot decode messages. + gpaMsg = nil + } + } + if gpaMsg != nil { + outMsgs := tc.functors.ApplyMessage(tc.nodes[nid], *gpaMsg) + newMsgs := tc.setMessageSender(nid, outMsgs) + if newMsgs != nil { + tc.msgsSent += len(newMsgs) + tc.msgs = append(tc.msgs, newMsgs...) + } + tc.tryCallOutputHandler(nid) + } + } +} + +func (tc *TestContextNew[Obj, Input, Message]) tryCallOutputHandler(nid NodeID) { + out := tc.functors.Output(tc.nodes[nid]) + if out != nil && tc.outputHandler != nil { + tc.outputHandler(nid, out) + } +} + +func (tc *TestContextNew[Obj, Input, Message]) RunAll() { + tc.RunUntil(tc.OutOfMessagesPredicate()) +} + +// NumberOfOutputs returns a number of non-nil outputs. +func (tc *TestContextNew[Obj, Input, Message]) NumberOfOutputs() int { + outNum := 0 + for _, node := range tc.nodes { + output := tc.functors.Output(node) + if output != nil { + outNum++ + } + } + return outNum +} + +// NumberOfOutputsPredicate runs until there will be at least outNum of non-nil outputs generated. +func (tc *TestContextNew[Obj, Input, Message]) NumberOfOutputsPredicate(outNum int) func() bool { + return func() bool { + return tc.NumberOfOutputs() >= outNum + } +} + +// OutOfMessagesPredicate runs until all the messages will be processed. +func (tc *TestContextNew[Obj, Input, Message]) OutOfMessagesPredicate() func() bool { + return func() bool { return false } +} + +func (tc *TestContextNew[Obj, Input, Message]) setMessageSender(sender NodeID, msgs []Message) []SenderMessageNew[Message] { + if msgs == nil { + return nil + } + result := make([]SenderMessageNew[Message], len(msgs)) + for i := range msgs { + msgs[i].SetSender(sender) + result[i] = SenderMessageNew[Message]{Sender: sender, Message: lo.ToPtr(msgs[i])} + } + return result +} + +func (tc *TestContextNew[Obj, Input, Message]) 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) + keys := []NodeID{} + for nid := range tc.nodes { + keys = append(keys, nid) + } + // Print them sorted. + sort.Slice(keys, func(i, j int) bool { + return bytes.Compare(keys[i][:], keys[j][:]) < 0 + }) + for _, nidStr := range keys { + logFunc("TC[%p] %v [node=%v]: %v", tc, prefix, nidStr, tc.functors.StatusString(tc.nodes[nidStr])) + } +} + +type SenderMessageNew[Msg MessageNew] struct { + Sender NodeID + Message *Msg +} diff --git a/packages/state/block_hash.go b/packages/state/block_hash.go index 6e56637ba6..4a3b6277b6 100644 --- a/packages/state/block_hash.go +++ b/packages/state/block_hash.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/iotaledger/wasp/v2/packages/cryptolib" + "github.com/iotaledger/wasp/v2/packages/testutil/testval" ) const BlockHashSize = 20 @@ -49,3 +50,5 @@ func RandomBlockHash() BlockHash { _, _ = rand.Read(b[:]) return b } + +var TestBlockHash = BlockHash(testval.TestBytes(BlockHashSize)) diff --git a/packages/state/types.go b/packages/state/types.go index 3bc49352b6..4b59fd7cbf 100644 --- a/packages/state/types.go +++ b/packages/state/types.go @@ -7,6 +7,7 @@ import ( "io" "time" + bcs "github.com/iotaledger/bcs-go" "github.com/iotaledger/wasp/v2/packages/isc" "github.com/iotaledger/wasp/v2/packages/kv" "github.com/iotaledger/wasp/v2/packages/kv/buffered" @@ -112,6 +113,17 @@ type Block interface { Bytes() []byte } +var ( + _ = bcs.AddCustomEncoder(func(e *bcs.Encoder, b Block) error { + e.Encode(b.(*block)) + return nil + }) + _ = bcs.AddCustomDecoder(func(d *bcs.Decoder, b *Block) error { + *b = bcs.Decode[*block](d) + return nil + }) +) + type StateCommonValues interface { BlockIndex() uint32 Timestamp() time.Time diff --git a/packages/state/types_test.go b/packages/state/types_test.go new file mode 100644 index 0000000000..1913718a7c --- /dev/null +++ b/packages/state/types_test.go @@ -0,0 +1,38 @@ +package state + +import ( + "testing" + + "github.com/iotaledger/bcs-go" + "github.com/iotaledger/wasp/v2/packages/kv" + "github.com/iotaledger/wasp/v2/packages/kv/buffered" + "github.com/iotaledger/wasp/v2/packages/testutil/testval" + "github.com/iotaledger/wasp/v2/packages/trie" +) + +func TestBlockCodec(t *testing.T) { + muts := buffered.NewMutations() + muts.Set(kv.Key(testval.TestBytes(10, 1)), testval.TestBytes(17, 2)) + muts.Set(kv.Key(testval.TestBytes(5, 3)), testval.TestBytes(34, 4)) + muts.Del(kv.Key(testval.TestBytes(9, 5)), true) + + var b Block = &block{ + trieRoot: trie.RandomHash(), + mutations: muts, + previousL1Commitment: &L1Commitment{ + trieRoot: trie.RandomHash(), + blockHash: RandomBlockHash(), + }, + } + bcs.TestCodec(t, b) + + b = &block{ + trieRoot: trie.TestHash, + mutations: muts, + previousL1Commitment: &L1Commitment{ + trieRoot: trie.Hash(testval.TestBytes(trie.HashSizeBytes)), + blockHash: TestBlockHash, + }, + } + bcs.TestCodecAndHash(t, b, "8533a81db889") +} diff --git a/packages/trie/hash.go b/packages/trie/hash.go index 780242f93a..bc08c7249e 100644 --- a/packages/trie/hash.go +++ b/packages/trie/hash.go @@ -5,6 +5,9 @@ import ( "encoding/hex" "io" + "github.com/samber/lo" + + "github.com/iotaledger/wasp/v2/packages/testutil/testval" "github.com/iotaledger/wasp/v2/packages/util/rwutil" ) @@ -83,3 +86,5 @@ func RandomHash() Hash { _, _ = rand.Read(h[:]) return h } + +var TestHash = lo.Must(HashFromBytes(testval.TestBytes(HashSizeBytes))) diff --git a/tools/wasp-cli/go.mod b/tools/wasp-cli/go.mod index 2ba3a4a50b..5f017438ad 100644 --- a/tools/wasp-cli/go.mod +++ b/tools/wasp-cli/go.mod @@ -19,7 +19,7 @@ require ( github.com/go-go-golems/glazed v0.6.10 github.com/google/uuid v1.6.0 github.com/hashicorp/go-version v1.7.0 - github.com/iotaledger/bcs-go v0.0.0-20250716100925-71f848cac593 + github.com/iotaledger/bcs-go v0.0.0-20251107161402-980e31f7d5f7 github.com/iotaledger/hive.go/crypto v0.0.0-20251001162450-d572d7955f11 github.com/iotaledger/hive.go/db v0.0.0-20251001162450-d572d7955f11 github.com/iotaledger/hive.go/log v0.0.0-20251001162450-d572d7955f11 diff --git a/tools/wasp-cli/go.sum b/tools/wasp-cli/go.sum index 6b6c1e77ba..fe72fb755e 100644 --- a/tools/wasp-cli/go.sum +++ b/tools/wasp-cli/go.sum @@ -273,8 +273,8 @@ github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJ github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/iotaledger/bcs-go v0.0.0-20250716100925-71f848cac593 h1:gfl7sPlhXPd+9YBb3V+C5U8bRvOozsOaubKwgQZWZ1w= -github.com/iotaledger/bcs-go v0.0.0-20250716100925-71f848cac593/go.mod h1:yTxBDTSAbTPf9Xz0JAiBTVRM9RlJCCZd6amEA85L6ac= +github.com/iotaledger/bcs-go v0.0.0-20251107161402-980e31f7d5f7 h1:8hBuGWWZOyyfpeo3v7FFVig8kA8xQN9kCjzwyTB8YX8= +github.com/iotaledger/bcs-go v0.0.0-20251107161402-980e31f7d5f7/go.mod h1:yTxBDTSAbTPf9Xz0JAiBTVRM9RlJCCZd6amEA85L6ac= github.com/iotaledger/go-ethereum v1.16.2-wasp h1:cjVwedrUNXnFor77tDJOlTN8NY+SxxW68vbq3tmhMhs= github.com/iotaledger/go-ethereum v1.16.2-wasp/go.mod h1:X5CIOyo8SuK1Q5GnaEizQVLHT/DfsiGWuNeVdQcEMNA= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys=