// Copyright 2018 The dexon-consensus Authors // This file is part of the dexon-consensus library. // // The dexon-consensus library is free software: you can redistribute it // and/or modify it under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation, either version 3 of the License, // or (at your option) any later version. // // The dexon-consensus library is distributed in the hope that it will be // useful, but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser // General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the dexon-consensus library. If not, see // . package test import ( "fmt" "time" "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core/crypto" "github.com/dexon-foundation/dexon-consensus/core/types" typesDKG "github.com/dexon-foundation/dexon-consensus/core/types/dkg" "github.com/dexon-foundation/dexon/rlp" ) // StateChangeType is the type of state change request. type StateChangeType uint8 // Types of state change. const ( StateChangeNothing StateChangeType = iota // DKG & CRS StateAddCRS StateAddDKGComplaint StateAddDKGMasterPublicKey StateAddDKGMPKReady StateAddDKGFinal StateResetDKG // Configuration related. StateChangeLambdaBA StateChangeLambdaDKG StateChangeRoundLength StateChangeMinBlockInterval StateChangeNotarySetSize // Node set related. StateAddNode ) func (t StateChangeType) String() string { switch t { case StateChangeNothing: return "Nothing" case StateAddCRS: return "AddCRS" case StateAddDKGComplaint: return "AddDKGComplaint" case StateAddDKGMasterPublicKey: return "AddDKGMasterPublicKey" case StateAddDKGMPKReady: return "AddDKGMPKReady" case StateAddDKGFinal: return "AddDKGFinal" case StateChangeLambdaBA: return "ChangeLambdaBA" case StateChangeLambdaDKG: return "ChangeLambdaDKG" case StateChangeRoundLength: return "ChangeRoundLength" case StateChangeMinBlockInterval: return "ChangeMinBlockInterval" case StateChangeNotarySetSize: return "ChangeNotarySetSize" case StateAddNode: return "AddNode" } panic(fmt.Errorf("attempting to dump unknown type of state change: %d", t)) } // StateChangeRequest carries information of state change request. type StateChangeRequest struct { Type StateChangeType `json:"type"` Payload interface{} `json:"payload"` // The purpose of these fields are aiming to provide an unique ID for each // change request. Hash common.Hash Timestamp uint64 } // this structure is mainly for marshalling for StateChangeRequest. type rawStateChangeRequest struct { Type StateChangeType Payload rlp.RawValue Hash common.Hash Timestamp uint64 } // NewStateChangeRequest constructs an StateChangeRequest instance. func NewStateChangeRequest( t StateChangeType, payload interface{}) *StateChangeRequest { now := uint64(time.Now().UTC().UnixNano()) b, err := rlp.EncodeToBytes(struct { Type StateChangeType Payload interface{} Timestamp uint64 }{t, payload, now}) if err != nil { panic(err) } return &StateChangeRequest{ Hash: crypto.Keccak256Hash(b), Type: t, Payload: payload, Timestamp: now, } } // Clone a StateChangeRequest instance. func (req *StateChangeRequest) Clone() (copied *StateChangeRequest) { copied = &StateChangeRequest{ Type: req.Type, Hash: req.Hash, Timestamp: req.Timestamp, } // NOTE: The cloned DKGx structs would be different from sources in binary // level, thus would produce different hash from the source. // I don't want different hash for source/copied requests thus would // copy the hash from source directly. switch req.Type { case StateAddNode: srcBytes := req.Payload.([]byte) copiedBytes := make([]byte, len(srcBytes)) copy(copiedBytes, srcBytes) req.Payload = copiedBytes case StateAddCRS: crsReq := req.Payload.(*crsAdditionRequest) copied.Payload = &crsAdditionRequest{ Round: crsReq.Round, CRS: crsReq.CRS, } case StateAddDKGMPKReady: copied.Payload = CloneDKGMPKReady(req.Payload.(*typesDKG.MPKReady)) case StateAddDKGFinal: copied.Payload = CloneDKGFinalize(req.Payload.(*typesDKG.Finalize)) case StateAddDKGMasterPublicKey: copied.Payload = CloneDKGMasterPublicKey( req.Payload.(*typesDKG.MasterPublicKey)) case StateAddDKGComplaint: copied.Payload = CloneDKGComplaint(req.Payload.(*typesDKG.Complaint)) default: copied.Payload = req.Payload } return } // Equal checks equality between two StateChangeRequest. func (req *StateChangeRequest) Equal(other *StateChangeRequest) error { if req.Hash == other.Hash { return nil } return ErrStatePendingChangesNotEqual } // String dump the state change request into string form. func (req *StateChangeRequest) String() (ret string) { ret = fmt.Sprintf("stateChangeRequest{Type:%s", req.Type) switch req.Type { case StateChangeNothing: case StateAddCRS: crsReq := req.Payload.(*crsAdditionRequest) ret += fmt.Sprintf( "Round:%v CRS:%s", crsReq.Round, crsReq.CRS.String()[:6]) case StateAddDKGComplaint: ret += fmt.Sprintf("%s", req.Payload.(*typesDKG.Complaint)) case StateAddDKGMasterPublicKey: ret += fmt.Sprintf("%s", req.Payload.(*typesDKG.MasterPublicKey)) case StateAddDKGMPKReady: ret += fmt.Sprintf("%s", req.Payload.(*typesDKG.MPKReady)) case StateAddDKGFinal: ret += fmt.Sprintf("%s", req.Payload.(*typesDKG.Finalize)) case StateChangeLambdaBA: ret += fmt.Sprintf("%v", time.Duration(req.Payload.(uint64))) case StateChangeLambdaDKG: ret += fmt.Sprintf("%v", time.Duration(req.Payload.(uint64))) case StateChangeRoundLength: ret += fmt.Sprintf("%v", time.Duration(req.Payload.(uint64))) case StateChangeMinBlockInterval: ret += fmt.Sprintf("%v", time.Duration(req.Payload.(uint64))) case StateChangeNotarySetSize: ret += fmt.Sprintf("%v", req.Payload.(uint32)) case StateAddNode: ret += fmt.Sprintf( "%s", types.NewNodeID(req.Payload.(crypto.PublicKey)).String()[:6]) default: panic(fmt.Errorf( "attempting to dump unknown type of state change request: %v", req.Type)) } ret += "}" return }