From 5f9c4b3a25a76e26d18b1f273d4d2ffa8d61d861 Mon Sep 17 00:00:00 2001 From: wanwiset25 Date: Sun, 14 Sep 2025 14:50:47 +0400 Subject: [PATCH 1/3] engine_v2: save reward file during Seal for block miner --- consensus/XDPoS/engines/engine_v2/engine.go | 81 +++++++++++++++++++-- 1 file changed, 73 insertions(+), 8 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 9de918d1dc..7fa4aea61e 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -7,6 +7,7 @@ import ( "math/big" "os" "path/filepath" + "strconv" "sync" "time" @@ -66,6 +67,10 @@ type XDPoS_v2 struct { highestTimeoutCert *types.TimeoutCert highestCommitBlock *types.BlockInfo + latestReward map[string]interface{} + latestRewardBlocknum uint64 + latestRewardLock sync.RWMutex + HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) HookPenalty func(chain consensus.ChainReader, number *big.Int, parentHash common.Hash, candidates []common.Address) ([]common.Address, error) @@ -402,14 +407,23 @@ func (x *XDPoS_v2) Finalize(chain consensus.ChainReader, header *types.Header, s if err != nil { return nil, err } - if len(common.StoreRewardFolder) > 0 { - data, err := json.Marshal(rewards) - if err == nil { - err = os.WriteFile(filepath.Join(common.StoreRewardFolder, header.Number.String()+"."+header.Hash().Hex()), data, 0644) - } - if err != nil { - log.Error("Error when save reward info ", "number", header.Number, "hash", header.Hash().Hex(), "err", err) - } + + x.signLock.RLock() + signer := x.signer + x.signLock.RUnlock() + x.latestRewardLock.Lock() + x.latestReward = rewards + x.latestRewardBlocknum = header.Number.Uint64() + x.latestRewardLock.Unlock() + + parentHeader := chain.GetHeaderByHash(header.ParentHash) + isMyTurn, err := x.YourTurn(chain, parentHeader, signer) + if err != nil { + log.Error("[Finalize] error checking myturn", "err", err) + return nil, err + } + if !isMyTurn { // if myturn use Seal to save file + x.saveRewardToFile(header.Hash(), header.Number.Uint64()) } } @@ -470,6 +484,23 @@ func (x *XDPoS_v2) Seal(chain consensus.ChainReader, block *types.Block, stop <- log.Error("[Seal] Error when decode extra field to get the round number from v2 block during sealing", "Hash", header.Hash().Hex(), "Number", header.Number.Uint64(), "Error", err) return nil, err } + + isEpochSwitch, _, err := x.IsEpochSwitch(header) + if err != nil { + log.Error("[Seal] IsEpochSwitch bug!", "err", err) + return nil, err + } + if isEpochSwitch { + parentHeader := chain.GetHeaderByHash(header.ParentHash) + isMyTurn, err := x.YourTurn(chain, parentHeader, signer) + if err != nil { + log.Error("[Seal] error checking myturn", "err", err) + } + if isMyTurn { // if not myturn use Finalize to save file + x.saveRewardToFile(header.Hash(), header.Number.Uint64()) + } + } + x.highestSelfMinedRound = decodedExtraField.Round return block.WithSeal(header), nil @@ -1017,3 +1048,37 @@ func (x *XDPoS_v2) periodicJob() { func (x *XDPoS_v2) GetLatestCommittedBlockInfo() *types.BlockInfo { return x.highestCommitBlock } + +func (x *XDPoS_v2) saveRewardToFile(blockHash common.Hash, blockNumber uint64) error { + if len(common.StoreRewardFolder) == 0 { + return nil + } + + x.latestRewardLock.RLock() + rewards := x.latestReward + rewardsBlocknum := x.latestRewardBlocknum + x.latestRewardLock.RUnlock() + + if rewardsBlocknum != blockNumber { + log.Error("[saveRewardToFile] error blocknumber mismatch with latest reward state, this should not happen!!", "rewardsBlocknum", rewardsBlocknum, "blockNumber", blockNumber) + return errors.New("reward block number mismatch") + } + + data, err := json.Marshal(rewards) + if err != nil { + log.Error("[saveRewardToFile] error Marshalling rewards", "err", err) + return err + } + + filename := strconv.FormatUint(blockNumber, 10) + "." + blockHash.Hex() + err = os.WriteFile(filepath.Join(common.StoreRewardFolder, filename), data, 0644) + if err != nil { + log.Error("[saveRewardToFile] Error when writing reward info", "filename", filename, "rewards", string(data), "err", err) + return err + } + + log.Debug("[saveRewardToFile] saved rewards", "filename", filename) + log.Debug("[saveRewardToFile] saved rewards", "rewards", string(data)) + + return nil +} From 8ba050e99a2b46c11463539bc60849a1027c63b1 Mon Sep 17 00:00:00 2001 From: wanwiset25 Date: Tue, 16 Sep 2025 03:12:34 +0400 Subject: [PATCH 2/3] PR fixes --- consensus/XDPoS/engines/engine_v2/engine.go | 78 ++++++++++++++------- 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 7fa4aea61e..7bdc3dbfab 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -69,7 +69,6 @@ type XDPoS_v2 struct { latestReward map[string]interface{} latestRewardBlocknum uint64 - latestRewardLock sync.RWMutex HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) HookPenalty func(chain consensus.ChainReader, number *big.Int, parentHash common.Hash, candidates []common.Address) ([]common.Address, error) @@ -411,17 +410,28 @@ func (x *XDPoS_v2) Finalize(chain consensus.ChainReader, header *types.Header, s x.signLock.RLock() signer := x.signer x.signLock.RUnlock() - x.latestRewardLock.Lock() - x.latestReward = rewards + x.lock.Lock() x.latestRewardBlocknum = header.Number.Uint64() - x.latestRewardLock.Unlock() - - parentHeader := chain.GetHeaderByHash(header.ParentHash) - isMyTurn, err := x.YourTurn(chain, parentHeader, signer) + x.latestReward, err = deepCloneJSON(rewards) + x.lock.Unlock() if err != nil { - log.Error("[Finalize] error checking myturn", "err", err) + log.Error("[Finalize] Error deep cloning latest reward", "err", err) return nil, err } + + var decodedExtraField types.ExtraFields_v2 + err = utils.DecodeBytesExtraFields(header.Extra, &decodedExtraField) + if err != nil { + log.Error("[Finalize] Error when decode extra field to get the round number", "Hash", header.Hash().Hex(), "Number", header.Number.Uint64(), "Error", err) + } + + parentHeader := chain.GetHeaderByHash(header.ParentHash) + isMyTurn, err := x.yourturn(chain, decodedExtraField.Round, parentHeader, signer) + if err != nil { + log.Error("[Finalize] Error checking myturn", "err", err) + return nil, err + } + if !isMyTurn { // if myturn use Seal to save file x.saveRewardToFile(header.Hash(), header.Number.Uint64()) } @@ -492,15 +502,15 @@ func (x *XDPoS_v2) Seal(chain consensus.ChainReader, block *types.Block, stop <- } if isEpochSwitch { parentHeader := chain.GetHeaderByHash(header.ParentHash) - isMyTurn, err := x.YourTurn(chain, parentHeader, signer) + isMyTurn, err := x.yourturn(chain, decodedExtraField.Round, parentHeader, signer) + if err != nil { - log.Error("[Seal] error checking myturn", "err", err) + log.Error("[Seal] Error checking myturn", "err", err) } if isMyTurn { // if not myturn use Finalize to save file x.saveRewardToFile(header.Hash(), header.Number.Uint64()) } } - x.highestSelfMinedRound = decodedExtraField.Round return block.WithSeal(header), nil @@ -1049,36 +1059,56 @@ func (x *XDPoS_v2) GetLatestCommittedBlockInfo() *types.BlockInfo { return x.highestCommitBlock } -func (x *XDPoS_v2) saveRewardToFile(blockHash common.Hash, blockNumber uint64) error { +func (x *XDPoS_v2) saveRewardToFile(blockHash common.Hash, blockNumber uint64) { if len(common.StoreRewardFolder) == 0 { - return nil + log.Info("[saveRewardToFile] Skip saving reward to file", "len(common.StoreRewardFolder)", len(common.StoreRewardFolder)) + return } - x.latestRewardLock.RLock() - rewards := x.latestReward + x.lock.RLock() rewardsBlocknum := x.latestRewardBlocknum - x.latestRewardLock.RUnlock() + rewards, err := deepCloneJSON(x.latestReward) + x.lock.RUnlock() + if err != nil { + log.Error("[saveRewardToFile] Error deep cloning latest reward", "err", err) + return + } if rewardsBlocknum != blockNumber { - log.Error("[saveRewardToFile] error blocknumber mismatch with latest reward state, this should not happen!!", "rewardsBlocknum", rewardsBlocknum, "blockNumber", blockNumber) - return errors.New("reward block number mismatch") + log.Error("[saveRewardToFile] Error blocknumber mismatch with latest reward state, this should not happen!!", "rewardsBlocknum", rewardsBlocknum, "blockNumber", blockNumber) + return } data, err := json.Marshal(rewards) if err != nil { - log.Error("[saveRewardToFile] error Marshalling rewards", "err", err) - return err + log.Error("[saveRewardToFile] Error Marshalling rewards", "err", err) + return } filename := strconv.FormatUint(blockNumber, 10) + "." + blockHash.Hex() err = os.WriteFile(filepath.Join(common.StoreRewardFolder, filename), data, 0644) if err != nil { log.Error("[saveRewardToFile] Error when writing reward info", "filename", filename, "rewards", string(data), "err", err) - return err + return } - log.Debug("[saveRewardToFile] saved rewards", "filename", filename) - log.Debug("[saveRewardToFile] saved rewards", "rewards", string(data)) + log.Debug("[saveRewardToFile] Saved rewards", "filename", filename) + log.Debug("[saveRewardToFile] Saved rewards", "rewards", string(data)) - return nil + return +} + +func deepCloneJSON(original map[string]interface{}) (map[string]interface{}, error) { + data, err := json.Marshal(original) + if err != nil { + return nil, fmt.Errorf("failed to marshal: %w", err) + } + + var cloned map[string]interface{} + err = json.Unmarshal(data, &cloned) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal: %w", err) + } + + return cloned, nil } From 1935cb7f253a3cc272cddfabd179abdd66166ff0 Mon Sep 17 00:00:00 2001 From: wanwiset25 Date: Tue, 23 Sep 2025 11:55:17 +0400 Subject: [PATCH 3/3] add more details in logging --- consensus/XDPoS/engines/engine_v2/engine.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 7bdc3dbfab..8538d415da 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -415,7 +415,7 @@ func (x *XDPoS_v2) Finalize(chain consensus.ChainReader, header *types.Header, s x.latestReward, err = deepCloneJSON(rewards) x.lock.Unlock() if err != nil { - log.Error("[Finalize] Error deep cloning latest reward", "err", err) + log.Error("[Finalize] Error deep cloning latest reward", "err", err, "rewards", rewards) return nil, err } @@ -428,7 +428,7 @@ func (x *XDPoS_v2) Finalize(chain consensus.ChainReader, header *types.Header, s parentHeader := chain.GetHeaderByHash(header.ParentHash) isMyTurn, err := x.yourturn(chain, decodedExtraField.Round, parentHeader, signer) if err != nil { - log.Error("[Finalize] Error checking myturn", "err", err) + log.Error("[Finalize] Error checking myturn", "err", err, "round", decodedExtraField.Round, "signer", signer, "parentHeader", parentHeader) return nil, err } @@ -505,7 +505,8 @@ func (x *XDPoS_v2) Seal(chain consensus.ChainReader, block *types.Block, stop <- isMyTurn, err := x.yourturn(chain, decodedExtraField.Round, parentHeader, signer) if err != nil { - log.Error("[Seal] Error checking myturn", "err", err) + log.Error("[Seal] Error checking myturn", "err", err, "round", decodedExtraField.Round, "signer", signer, "parentHeader", parentHeader) + return nil, err } if isMyTurn { // if not myturn use Finalize to save file x.saveRewardToFile(header.Hash(), header.Number.Uint64()) @@ -1081,7 +1082,7 @@ func (x *XDPoS_v2) saveRewardToFile(blockHash common.Hash, blockNumber uint64) { data, err := json.Marshal(rewards) if err != nil { - log.Error("[saveRewardToFile] Error Marshalling rewards", "err", err) + log.Error("[saveRewardToFile] Error Marshalling rewards", "err", err, "rewards", rewards) return }