Skip to content

Merge 9.0.3.0 changes into master - #620

Merged
marcos-iov merged 37 commits into
masterfrom
9003-master-merge
Jul 8, 2026
Merged

marcos-iov merged 37 commits into
masterfrom
9003-master-merge

Conversation

@marcos-iov

@marcos-iov marcos-iov commented Jul 8, 2026 •

Copy link
Copy Markdown
Collaborator

rit:fix/inform-coinbases-of-informed-blocks

marcos-iov and others added 30 commits June 19, 2026 11:59
…d-blocks

Fix/inform coinbases of informed blocks
Obtain rskTxHash from releaseCreationInformation instead of setting the tx in the tx receipt

Refactor ReleaseCreationInformation and getter to obtain outpoint values from logs

Add missing outpoint values when creating release creation information in tests setup

Keep releaseCreationInformation altogether in PowHSMSignerMessageBuilder

Fix tests in PowHSMSigningClientBtcTest

Remove signer version check from getTxInfoToSign

Remove unnecessary isNull check. Reorder logs to be more accurate
Copilot AI review requested due to automatic review settings July 8, 2026 14:08
@marcos-iov
marcos-iov requested a review from a team as a code owner July 8, 2026 14:08
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR merges the 9.0.3.0 line into master, updating the powpeg-node codebase and tests to align with updated pegout-signing and BTC coinbase informing flows, alongside a dependency/version alignment to rskj-core 9.0.3-VETIVER.

Changes:

  • Refactors pegout release creation lookup (ReleaseCreationInformationGetter / ReleaseCreationInformation) to validate Bridge logs more strictly and to carry decoded UTXO outpoint values explicitly.
  • Reworks BTC coinbase informing in BtcToRskClient to send coinbase transactions based on Bridge state (block header informed / coinbase already informed / main-chain membership) rather than a “ready-to-inform” flag.
  • Updates tests and test utilities to match the new APIs/data shapes and adds additional coverage for restart/reorg scenarios.

Reviewed changes

Copilot reviewed 21 out of 22 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/test/java/co/rsk/federate/utils/ClientProofsAssertions.java Adds assertions for presence/absence of block hashes in the persisted coinbase map.
src/test/java/co/rsk/federate/signing/utils/TestUtils.java Adds receipt/log/txinfo builders and updates block creation helper to include activation mocking.
src/test/java/co/rsk/federate/signing/hsm/message/SignerMessageBuilderFactoryTest.java Updates ReleaseCreationInformation construction to include outpoint values list.
src/test/java/co/rsk/federate/signing/hsm/message/ReleaseCreationInformationGetterTest.java Reworks tests around the new log-validation and receipt/blockchain setup helpers.
src/test/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilderTest.java Updates tests for ReleaseCreationInformation’s new outpoint-values parameter.
src/test/java/co/rsk/federate/signing/hsm/client/PowHSMSigningClientBtcTest.java Updates signing client tests to pass explicit outpoint values into release information.
src/test/java/co/rsk/federate/FederatorSupportTest.java Adds tests for isBlockHashInformedToBridge behavior on empty/non-empty Bridge results.
src/test/java/co/rsk/federate/EventsTestUtils.java Adds helpers to build encoded topics/data and generic log builders for Bridge events.
src/test/java/co/rsk/federate/CoinbaseInformationTest.java Removes tests for the removed readyToInform flag.
src/test/java/co/rsk/federate/BtcToRskClientTest.java Refactors storage setup and adds new coinbase-informing tests including restart/reorg scenarios.
src/test/java/co/rsk/federate/btcreleaseclient/BtcReleaseClientTest.java Refactors and expands release-processing tests using new event/log utilities and receipt helpers.
src/test/java/co/rsk/federate/bitcoin/KitStub.java Initializes vChain when injecting a mocked BlockStore.
src/main/resources/version.properties Updates runtime version metadata to 9.0.3.0 / VETIVER.
src/main/java/co/rsk/federate/signing/hsm/message/ReleaseCreationInformationGetter.java Replaces version-dependent logic with strict Bridge-log validation and outpoint-values extraction.
src/main/java/co/rsk/federate/signing/hsm/message/ReleaseCreationInformation.java Makes outpoint-values an explicit constructor parameter and stores it immutably.
src/main/java/co/rsk/federate/signing/hsm/message/PowHSMSignerMessageBuilder.java Uses ReleaseCreationInformation as the single source of receipt/block/outpoint data.
src/main/java/co/rsk/federate/FederatorSupport.java Adds isBlockHashInformedToBridge helper calling Bridge GET_BTC_BLOCKCHAIN_BLOCK_HEADER_BY_HASH.
src/main/java/co/rsk/federate/CoinbaseInformation.java Removes readyToInform flag and related accessors.
src/main/java/co/rsk/federate/BtcToRskClient.java Removes “mark ready” flow and reworks coinbase informing to be state-based and reorg-aware.
src/main/java/co/rsk/federate/btcreleaseclient/BtcReleaseClient.java Updates to new getter signature and adjusts error handling/logging around release retrieval.
gradle/verification-metadata.xml Updates verification metadata for co.rsk:rskj-core:9.0.3-VETIVER.
build.gradle Pins rskj-core dependency to 9.0.3-VETIVER.

}
TransactionReceipt transactionReceipt = transactionInfo.getReceipt();
Block block = blockStore.getBlockByHash(transactionInfo.getBlockHash());
List<LogInfo> logs = pegoutCreationRskTxReceipt.getLogInfoList();
Comment on lines +142 to +149
public boolean isBlockHashInformedToBridge(Sha256Hash blockHash) {
byte[] blockHeaderBytes = this.bridgeTransactionSender.callTx(
federatorAddress,
Bridge.GET_BTC_BLOCKCHAIN_BLOCK_HEADER_BY_HASH,
new Object[]{ blockHash.getBytes() }
);
return blockHeaderBytes.length > 0;
}
Comment on lines +350 to 354
} catch (FederatorAlreadySignedException | FederationCantSignException | HSMReleaseCreationInformationException e) {
String message = String.format(
"[tryGetReleaseInformation] There was an error trying to process pegout with btcTxHash %s",
pegoutToSign.getHash()
pegoutBtcTxHash
);
Comment thread src/main/resources/version.properties Outdated
Comment on lines +1 to +2
versionNumber='9.0.3.0'
modifier="VETIVER"
Copilot AI review requested due to automatic review settings July 8, 2026 14:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 23 changed files in this pull request and generated 6 comments.

Comment on lines +142 to +149
public boolean isBlockHashInformedToBridge(Sha256Hash blockHash) {
byte[] blockHeaderBytes = this.bridgeTransactionSender.callTx(
federatorAddress,
Bridge.GET_BTC_BLOCKCHAIN_BLOCK_HEADER_BY_HASH,
new Object[]{ blockHash.getBytes() }
);
return blockHeaderBytes.length > 0;
}
Comment on lines +59 to 65
TransactionReceipt pegoutCreationRskTxReceipt = pegoutCreationRskTxInfo.getReceipt();
Block pegoutCreationRskBlock = blockStore.getBlockByHash(pegoutCreationRskTxInfo.getBlockHash());

protected ReleaseCreationInformation getBaseReleaseCreationInformation(
Keccak256 pegoutCreationRskTxHash,
BtcTransaction pegoutBtcTx
) throws HSMReleaseCreationInformationException {
TransactionInfo transactionInfo = receiptStore.getInMainChain(pegoutCreationRskTxHash.getBytes(), blockStore).orElse(null);
if (transactionInfo == null) {
String message = String.format(
"Rsk transaction %s where the pegout was created could not be found in best chain",
pegoutCreationRskTxHash
);
logger.error("[getTxInfoToSign] {}", message);
throw new HSMReleaseCreationInformationException(message);
}
TransactionReceipt transactionReceipt = transactionInfo.getReceipt();
Block block = blockStore.getBlockByHash(transactionInfo.getBlockHash());
List<LogInfo> logs = pegoutCreationRskTxReceipt.getLogInfoList();
ReleaseCreationLogs releaseCreationLogs = getReleaseCreationLogs(pegoutCreationRskTxHash, pegoutBtcTx, logs);
List<Coin> utxoOutpointsValues = getUtxoOutpointsValues(releaseCreationLogs.pegoutTransactionCreatedEvent());

Comment on lines +111 to +116
private List<Coin> getUtxoOutpointsValues(LogInfo logInfo) {
CallTransaction.Function pegoutTransactionCreatedEvent = BridgeEvents.PEGOUT_TRANSACTION_CREATED.getEvent();
byte[] pegoutTransactionCreatedEventData = logInfo.getData();
byte[] utxoOutpointsValuesEncoded = (byte[]) pegoutTransactionCreatedEvent.decodeEventData(pegoutTransactionCreatedEventData)[0];
return UtxoUtils.decodeOutpointValues(utxoOutpointsValuesEncoded);
}
Comment on lines +139 to 142
private boolean isLogFromBridge(LogInfo logInfo) {
RskAddress logsEmittedFrom = new RskAddress(logInfo.getAddress());
return logsEmittedFrom.equals(PrecompiledContracts.BRIDGE_ADDR);
}
Comment on lines 32 to 37
this.pegoutCreationBlock = pegoutCreationBlock;
this.transactionReceipt = transactionReceipt;
this.pegoutCreationRskTxHash = pegoutCreationRskTxHash;
this.pegoutBtcTx = pegoutBtcTx;

this.utxoOutpointValues = decodeUtxoOutpointValues();
}

private List<Coin> decodeUtxoOutpointValues() {
return transactionReceipt.getLogInfoList().stream()
.filter(this::isPegoutTransactionCreatedLog)
.findFirst()
.map(LogInfo::getData)
.map(this::decodePegoutTransactionEventData)
.map(UtxoUtils::decodeOutpointValues)
.orElse(Collections.emptyList());
}

private boolean isPegoutTransactionCreatedLog(LogInfo log) {
Function pegoutTransactionCreatedEvent = BridgeEvents.PEGOUT_TRANSACTION_CREATED.getEvent();
final byte[] pegoutTransactionCreatedSignatureTopic = pegoutTransactionCreatedEvent.encodeSignatureLong();

boolean logHasTopics = !log.getTopics().isEmpty();
return logHasTopics &&
// event signature topic is always the first one
Arrays.equals(log.getTopics().get(0).getData(), pegoutTransactionCreatedSignatureTopic);
}

private byte[] decodePegoutTransactionEventData(byte[] pegoutCreatedTransactionEventData) {
Function pegoutTransactionCreatedEvent = BridgeEvents.PEGOUT_TRANSACTION_CREATED.getEvent();
return (byte[]) pegoutTransactionCreatedEvent.decodeEventData(
pegoutCreatedTransactionEventData)[0];
this.utxoOutpointValues = Collections.unmodifiableList(utxoOutpointsValues);
}
Comment on lines +381 to 387
} catch (FederatorAlreadySignedException | FederationCantSignException | HSMReleaseCreationInformationException e) {
String message = String.format(
"[tryGetReleaseInformation] There was an error trying to process pegout with btcTxHash %s",
pegoutToSign.getHash()
pegoutBtcTxHash
);
logger.error(message, e);
} catch (FederatorAlreadySignedException e) {
logger.info("[tryGetReleaseInformation] {}", e.getMessage());
}
Copilot AI review requested due to automatic review settings July 8, 2026 14:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 23 changed files in this pull request and generated 2 comments.

Comment on lines +59 to +64
TransactionReceipt pegoutCreationRskTxReceipt = pegoutCreationRskTxInfo.getReceipt();
Block pegoutCreationRskBlock = blockStore.getBlockByHash(pegoutCreationRskTxInfo.getBlockHash());

protected ReleaseCreationInformation getBaseReleaseCreationInformation(
Keccak256 pegoutCreationRskTxHash,
BtcTransaction pegoutBtcTx
) throws HSMReleaseCreationInformationException {
TransactionInfo transactionInfo = receiptStore.getInMainChain(pegoutCreationRskTxHash.getBytes(), blockStore).orElse(null);
if (transactionInfo == null) {
String message = String.format(
"Rsk transaction %s where the pegout was created could not be found in best chain",
pegoutCreationRskTxHash
);
logger.error("[getTxInfoToSign] {}", message);
throw new HSMReleaseCreationInformationException(message);
}
TransactionReceipt transactionReceipt = transactionInfo.getReceipt();
Block block = blockStore.getBlockByHash(transactionInfo.getBlockHash());
List<LogInfo> logs = pegoutCreationRskTxReceipt.getLogInfoList();
ReleaseCreationLogs releaseCreationLogs = getReleaseCreationLogs(pegoutCreationRskTxHash, pegoutBtcTx, logs);
List<Coin> utxoOutpointsValues = getUtxoOutpointsValues(releaseCreationLogs.pegoutTransactionCreatedEvent());
public List<Coin> getUtxoOutpointValues() {
return Collections.unmodifiableList(utxoOutpointValues);
}
public List<Coin> getUtxoOutpointValues() { return utxoOutpointValues; }
Copilot AI review requested due to automatic review settings July 8, 2026 14:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 22 changed files in this pull request and generated 3 comments.

Comment on lines +142 to +149
public boolean isBlockHashInformedToBridge(Sha256Hash blockHash) {
byte[] blockHeaderBytes = this.bridgeTransactionSender.callTx(
federatorAddress,
Bridge.GET_BTC_BLOCKCHAIN_BLOCK_HEADER_BY_HASH,
new Object[]{ blockHash.getBytes() }
);
return blockHeaderBytes.length > 0;
}
Comment on lines +59 to +64
TransactionReceipt pegoutCreationRskTxReceipt = pegoutCreationRskTxInfo.getReceipt();
Block pegoutCreationRskBlock = blockStore.getBlockByHash(pegoutCreationRskTxInfo.getBlockHash());

protected ReleaseCreationInformation getBaseReleaseCreationInformation(
Keccak256 pegoutCreationRskTxHash,
BtcTransaction pegoutBtcTx
) throws HSMReleaseCreationInformationException {
TransactionInfo transactionInfo = receiptStore.getInMainChain(pegoutCreationRskTxHash.getBytes(), blockStore).orElse(null);
if (transactionInfo == null) {
String message = String.format(
"Rsk transaction %s where the pegout was created could not be found in best chain",
pegoutCreationRskTxHash
);
logger.error("[getTxInfoToSign] {}", message);
throw new HSMReleaseCreationInformationException(message);
}
TransactionReceipt transactionReceipt = transactionInfo.getReceipt();
Block block = blockStore.getBlockByHash(transactionInfo.getBlockHash());
List<LogInfo> logs = pegoutCreationRskTxReceipt.getLogInfoList();
ReleaseCreationLogs releaseCreationLogs = getReleaseCreationLogs(pegoutCreationRskTxHash, pegoutBtcTx, logs);
List<Coin> utxoOutpointsValues = getUtxoOutpointsValues(releaseCreationLogs.pegoutTransactionCreatedEvent());
Comment on lines 33 to 37
this.transactionReceipt = transactionReceipt;
this.pegoutCreationRskTxHash = pegoutCreationRskTxHash;
this.pegoutBtcTx = pegoutBtcTx;

this.utxoOutpointValues = decodeUtxoOutpointValues();
}

private List<Coin> decodeUtxoOutpointValues() {
return transactionReceipt.getLogInfoList().stream()
.filter(this::isPegoutTransactionCreatedLog)
.findFirst()
.map(LogInfo::getData)
.map(this::decodePegoutTransactionEventData)
.map(UtxoUtils::decodeOutpointValues)
.orElse(Collections.emptyList());
}

private boolean isPegoutTransactionCreatedLog(LogInfo log) {
Function pegoutTransactionCreatedEvent = BridgeEvents.PEGOUT_TRANSACTION_CREATED.getEvent();
final byte[] pegoutTransactionCreatedSignatureTopic = pegoutTransactionCreatedEvent.encodeSignatureLong();

boolean logHasTopics = !log.getTopics().isEmpty();
return logHasTopics &&
// event signature topic is always the first one
Arrays.equals(log.getTopics().get(0).getData(), pegoutTransactionCreatedSignatureTopic);
}

private byte[] decodePegoutTransactionEventData(byte[] pegoutCreatedTransactionEventData) {
Function pegoutTransactionCreatedEvent = BridgeEvents.PEGOUT_TRANSACTION_CREATED.getEvent();
return (byte[]) pegoutTransactionCreatedEvent.decodeEventData(
pegoutCreatedTransactionEventData)[0];
this.utxoOutpointValues = Collections.unmodifiableList(utxoOutpointsValues);
}
Copilot AI review requested due to automatic review settings July 8, 2026 14:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 22 changed files in this pull request and generated 4 comments.

Comment on lines +142 to +148
public boolean isBlockHashInformedToBridge(Sha256Hash blockHash) {
byte[] blockHeaderBytes = this.bridgeTransactionSender.callTx(
federatorAddress,
Bridge.GET_BTC_BLOCKCHAIN_BLOCK_HEADER_BY_HASH,
new Object[]{ blockHash.getBytes() }
);
return blockHeaderBytes.length > 0;
Comment on lines +59 to +63
TransactionReceipt pegoutCreationRskTxReceipt = pegoutCreationRskTxInfo.getReceipt();
Block pegoutCreationRskBlock = blockStore.getBlockByHash(pegoutCreationRskTxInfo.getBlockHash());

protected ReleaseCreationInformation getBaseReleaseCreationInformation(
Keccak256 pegoutCreationRskTxHash,
BtcTransaction pegoutBtcTx
) throws HSMReleaseCreationInformationException {
TransactionInfo transactionInfo = receiptStore.getInMainChain(pegoutCreationRskTxHash.getBytes(), blockStore).orElse(null);
if (transactionInfo == null) {
String message = String.format(
"Rsk transaction %s where the pegout was created could not be found in best chain",
pegoutCreationRskTxHash
);
logger.error("[getTxInfoToSign] {}", message);
throw new HSMReleaseCreationInformationException(message);
}
TransactionReceipt transactionReceipt = transactionInfo.getReceipt();
Block block = blockStore.getBlockByHash(transactionInfo.getBlockHash());
List<LogInfo> logs = pegoutCreationRskTxReceipt.getLogInfoList();
ReleaseCreationLogs releaseCreationLogs = getReleaseCreationLogs(pegoutCreationRskTxHash, pegoutBtcTx, logs);
Comment on lines +111 to +116
private List<Coin> getUtxoOutpointsValues(LogInfo logInfo) {
CallTransaction.Function pegoutTransactionCreatedEvent = BridgeEvents.PEGOUT_TRANSACTION_CREATED.getEvent();
byte[] pegoutTransactionCreatedEventData = logInfo.getData();
byte[] utxoOutpointsValuesEncoded = (byte[]) pegoutTransactionCreatedEvent.decodeEventData(pegoutTransactionCreatedEventData)[0];
return UtxoUtils.decodeOutpointValues(utxoOutpointsValuesEncoded);
}
Comment on lines 33 to 37
this.transactionReceipt = transactionReceipt;
this.pegoutCreationRskTxHash = pegoutCreationRskTxHash;
this.pegoutBtcTx = pegoutBtcTx;

this.utxoOutpointValues = decodeUtxoOutpointValues();
}

private List<Coin> decodeUtxoOutpointValues() {
return transactionReceipt.getLogInfoList().stream()
.filter(this::isPegoutTransactionCreatedLog)
.findFirst()
.map(LogInfo::getData)
.map(this::decodePegoutTransactionEventData)
.map(UtxoUtils::decodeOutpointValues)
.orElse(Collections.emptyList());
}

private boolean isPegoutTransactionCreatedLog(LogInfo log) {
Function pegoutTransactionCreatedEvent = BridgeEvents.PEGOUT_TRANSACTION_CREATED.getEvent();
final byte[] pegoutTransactionCreatedSignatureTopic = pegoutTransactionCreatedEvent.encodeSignatureLong();

boolean logHasTopics = !log.getTopics().isEmpty();
return logHasTopics &&
// event signature topic is always the first one
Arrays.equals(log.getTopics().get(0).getData(), pegoutTransactionCreatedSignatureTopic);
}

private byte[] decodePegoutTransactionEventData(byte[] pegoutCreatedTransactionEventData) {
Function pegoutTransactionCreatedEvent = BridgeEvents.PEGOUT_TRANSACTION_CREATED.getEvent();
return (byte[]) pegoutTransactionCreatedEvent.decodeEventData(
pegoutCreatedTransactionEventData)[0];
this.utxoOutpointValues = Collections.unmodifiableList(utxoOutpointsValues);
}
@sonarqubecloud

sonarqubecloud Bot commented Jul 8, 2026

Copy link
Copy Markdown

@marcos-iov
marcos-iov merged commit 2bbacb6 into master Jul 8, 2026
12 of 19 checks passed
@marcos-iov
marcos-iov deleted the 9003-master-merge branch July 8, 2026 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants