Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle null response #2262

Merged
merged 42 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
5c459e4
Added msg type propagation to precompiled contracts
Vovchyk Jan 11, 2024
ccaac41
WIP Bridge message type check
josedahlquist Jan 12, 2024
f17bc41
Refactor CallTypeHelper to use a Predicate instead of Function
marcos-iov Jan 15, 2024
60c7e5c
Rename CallTypeHelper predicates
marcos-iov Jan 15, 2024
6ac2e41
Allow static calls to getter methods
marcos-iov Jan 15, 2024
1ccaede
Extract Bridge call validations to their own methods
marcos-iov Jan 15, 2024
3d5fb05
Remove explicit array creation from Bridge tests
marcos-iov Jan 15, 2024
b7b0d1d
Remove unused config object from Bridge tests
marcos-iov Jan 15, 2024
04610aa
Add rskipArrowhead to the missing config files
marcos-iov Jan 15, 2024
f48437c
Remove empty constructor from PrecompiledContractArgs
marcos-iov Jan 15, 2024
1c88079
Make msg type CALL by default in PrecompiledContractArgsBuilder
marcos-iov Jan 15, 2024
9f6540c
Create BridgeBuilder class and refactor Bridge tests
marcos-iov Jan 15, 2024
d417ce7
Refactor remaining Bridge tests
marcos-iov Jan 16, 2024
4f83611
Use mainnet config by default to run Bridge tests
marcos-iov Jan 16, 2024
af64954
adding test for limiting precompiled call types
donequis Jan 15, 2024
4b7e4e2
Add MsgType parameter to BridgeBuilder
marcos-iov Jan 17, 2024
58f1c61
Add tests for addFederatorPublicKey Bridge method
marcos-iov Jan 17, 2024
0bb602c
Add tests for addFederatorPublicKeyMultikey Bridge method
marcos-iov Jan 17, 2024
c27c86a
Add tests for addLockWhitelistAddress Bridge method
marcos-iov Jan 17, 2024
84c9814
Add tests for addOneOffLockWhitelistAddress Bridge method
marcos-iov Jan 17, 2024
4ee6b00
Add tests for addUnlimitedLockWhitelistAddress Bridge method
marcos-iov Jan 17, 2024
f4797e1
Add tests for addSignature Bridge method
marcos-iov Jan 18, 2024
9b8c7b2
Add tests for commitFederation Bridge method
marcos-iov Jan 18, 2024
851ba77
Add tests for getBtcBlockchainBestChainHeight Bridge method
marcos-iov Jan 18, 2024
3e52710
Add tests fro various gettter Bridge methods
marcos-iov Jan 23, 2024
da60d81
Finish creating unit tests for bridge methods. Move all those tests t…
julia-zack Jan 24, 2024
eb7cea1
Remove unnecessary comments
julia-zack Jan 24, 2024
d3f6c70
Adjust Bridge tests
marcos-iov Jan 25, 2024
52fdbec
Move tests to BridgeTests class
marcos-iov Jan 25, 2024
5fc0099
Refactor Bridge execute method to have the current behaviour as the d…
marcos-iov Jan 26, 2024
46c4e4d
Return empty array for void Bridge methods executions
marcos-iov Jan 26, 2024
bfb0e31
Rename RSKIP_ARROWHEAD to RSKIP414
marcos-iov Jan 26, 2024
a685721
Assert correct response value of void Bridge methods
marcos-iov Jan 26, 2024
5ac7267
Add new log messages to BridgeSupport::releaseBtc
marcos-iov Jan 29, 2024
c5b7946
Update comment in BridgeUtils::isContractTx
marcos-iov Jan 31, 2024
6b364a7
Replace RSKIP414 for RSKIP417
marcos-iov Feb 8, 2024
fc602f5
Return Optional.empty instead of null in executeBridgeMethod before R…
julia-zack Feb 9, 2024
4be3f98
Fix execution handling for bridge void methods
julia-zack Feb 9, 2024
58e0bd1
Refactor thrown bridge exception in execute bridge method
julia-zack Feb 9, 2024
723aa4a
Rethrow the same exception
marcos-iov Feb 9, 2024
30fba23
Add RSKIP417 to ActivationConfigTest
marcos-iov Feb 9, 2024
fc4e43a
Add rskip417 to expected.conf file
julia-zack Feb 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 128 additions & 45 deletions rskj-core/src/main/java/co/rsk/peg/Bridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
*/
package co.rsk.peg;

import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIP417;

import co.rsk.bitcoinj.core.*;
import co.rsk.bitcoinj.script.Script;
import co.rsk.bitcoinj.store.BlockStoreException;
import co.rsk.config.BridgeConstants;
import co.rsk.core.RskAddress;
import co.rsk.crypto.Keccak256;
import co.rsk.panic.PanicProcessor;
import co.rsk.peg.BridgeMethods.BridgeMethodExecutor;
import co.rsk.peg.bitcoin.MerkleBranch;
import co.rsk.peg.flyover.FlyoverTxResponseCodes;
import co.rsk.peg.utils.BtcTransactionFormatUtils;
Expand All @@ -37,6 +40,7 @@
import org.ethereum.core.*;
import org.ethereum.util.ByteUtil;
import org.ethereum.vm.DataWord;
import org.ethereum.vm.MessageCall.MsgType;
import org.ethereum.vm.PrecompiledContractArgs;
import org.ethereum.vm.PrecompiledContracts;
import org.ethereum.vm.exception.VMException;
Expand Down Expand Up @@ -223,15 +227,34 @@ public class Bridge extends PrecompiledContracts.PrecompiledContract {
private final BiFunction<List<Sha256Hash>, Integer, MerkleBranch> merkleBranchFactory;

private final SignatureCache signatureCache;

public Bridge(RskAddress contractAddress, Constants constants, ActivationConfig activationConfig,
BridgeSupportFactory bridgeSupportFactory, SignatureCache signatureCache) {
this(contractAddress, constants, activationConfig, bridgeSupportFactory, MerkleBranch::new, signatureCache);
private MsgType msgType;

public Bridge(
RskAddress contractAddress,
Constants constants,
ActivationConfig activationConfig,
BridgeSupportFactory bridgeSupportFactory,
SignatureCache signatureCache) {

this(
contractAddress,
constants,
activationConfig,
bridgeSupportFactory,
MerkleBranch::new,
signatureCache
);
}

@VisibleForTesting
Bridge(RskAddress contractAddress, Constants constants, ActivationConfig activationConfig,
BridgeSupportFactory bridgeSupportFactory, BiFunction<List<Sha256Hash>, Integer, MerkleBranch> merkleBranchFactory, SignatureCache signatureCache) {
Bridge(
RskAddress contractAddress,
Constants constants,
ActivationConfig activationConfig,
BridgeSupportFactory bridgeSupportFactory,
BiFunction<List<Sha256Hash>, Integer, MerkleBranch> merkleBranchFactory,
SignatureCache signatureCache) {

this.bridgeSupportFactory = bridgeSupportFactory;
this.contractAddress = contractAddress;
this.constants = constants;
Expand Down Expand Up @@ -316,12 +339,14 @@ public void init(PrecompiledContractArgs args) {
Block rskExecutionBlock = args.getExecutionBlock();
this.activations = activationConfig.forBlock(rskExecutionBlock.getNumber());
this.rskTx = args.getTransaction();
this.msgType = args.getMsgType();

this.bridgeSupport = bridgeSupportFactory.newInstance(
args.getRepository(),
rskExecutionBlock,
contractAddress,
args.getLogs());
args.getRepository(),
rskExecutionBlock,
contractAddress,
args.getLogs()
);
}

@Override
Expand All @@ -342,50 +367,98 @@ public byte[] execute(byte[] data) throws VMException {
// Function parsing from data returned null => invalid function selected, halt!
if (bridgeParsedData == null) {
String errorMessage = String.format("Invalid data given: %s.", ByteUtil.toHexString(data));
logger.info(errorMessage);
if (activations.isActive(ConsensusRule.RSKIP88)) {
throw new BridgeIllegalArgumentException(errorMessage);
logger.info("[execute] {}", errorMessage);
if (!activations.isActive(ConsensusRule.RSKIP88)) {
return null;
}

return null;
}

// If this is not a local call, then first check whether the function
// allows for non-local calls
if (activations.isActive(ConsensusRule.RSKIP88) &&
!isLocalCall() &&
bridgeParsedData.bridgeMethod.onlyAllowsLocalCalls(this, bridgeParsedData.args)) {

String errorMessage = String.format("Non-local-call to %s. Returning without execution.", bridgeParsedData.bridgeMethod.getFunction().name);
logger.info(errorMessage);
throw new BridgeIllegalArgumentException(errorMessage);
}

validateCall(bridgeParsedData);
Optional<?> result;
try {
// bridgeParsedData.function should be one of the CallTransaction.Function declared above.
// If the user tries to call an non-existent function, parseData() will return null.
result = bridgeParsedData.bridgeMethod.getExecutor().execute(this, bridgeParsedData.args);
result = executeBridgeMethod(bridgeParsedData);
} catch (BridgeIllegalArgumentException ex) {
String errorMessage = String.format("Error executing: %s", bridgeParsedData.bridgeMethod);
logger.warn(errorMessage, ex);
if (activations.isActive(ConsensusRule.RSKIP88)) {
throw new BridgeIllegalArgumentException(errorMessage);
if (shouldReturnNullInsteadOfException()) {
return null;
}

return null;
throw ex;
}

teardown();

return result.map(bridgeParsedData.bridgeMethod.getFunction()::encodeOutputs).orElse(null);
byte[] voidReturnValue = calculateVoidReturnValue();
return result.map(bridgeParsedData.bridgeMethod.getFunction()::encodeOutputs).orElse(voidReturnValue);
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
panicProcessor.panic("bridgeexecute", ex.getMessage());
throw new VMException(String.format("Exception executing bridge: %s", ex.getMessage()), ex);
}
}

private void validateCall(BridgeParsedData bridgeParsedData) throws BridgeIllegalArgumentException {
validateLocalCall(bridgeParsedData);
validateCallMessageType(bridgeParsedData);
}

private void validateLocalCall(BridgeParsedData bridgeParsedData) throws BridgeIllegalArgumentException {
// If this is not a local call, then check whether the function allows for non-local calls
if (activations.isActive(ConsensusRule.RSKIP88) &&
!isLocalCall() &&
bridgeParsedData.bridgeMethod.onlyAllowsLocalCalls(this, bridgeParsedData.args)) {

String errorMessage = String.format(
"Non-local-call to %s. Returning without execution.",
bridgeParsedData.bridgeMethod.getFunction().name
);
logger.info("[validateLocalCall] {}", errorMessage);
throw new BridgeIllegalArgumentException(errorMessage);
}
}

private void validateCallMessageType(BridgeParsedData bridgeParsedData) throws BridgeIllegalArgumentException {
if (activations.isActive(RSKIP417) &&
!bridgeParsedData.bridgeMethod.acceptsThisTypeOfCall(this.msgType)) {
String errorMessage = String.format(
"Call type (%s) not accepted by %s. Returning without execution.",
this.msgType.name(),
bridgeParsedData.bridgeMethod.getFunction().name
);
logger.info("[validateCallMessageType] {}", errorMessage);

throw new BridgeIllegalArgumentException(errorMessage);
}
}

private Optional<?> executeBridgeMethod(BridgeParsedData bridgeParsedData) throws Exception {
try {
// bridgeParsedData.function should be one of the CallTransaction.Function declared above.
// If the user tries to call an non-existent function, parseData() will return null.
BridgeMethodExecutor executor = bridgeParsedData.bridgeMethod.getExecutor();
return executor.execute(this, bridgeParsedData.args);
} catch (BridgeIllegalArgumentException ex) {
String errorMessage = String.format("Error executing: %s", bridgeParsedData.bridgeMethod);
logger.warn(errorMessage, ex);

throw new BridgeIllegalArgumentException(errorMessage);
}
}

private boolean shouldReturnNullInsteadOfException() {
return !activations.isActive(ConsensusRule.RSKIP88);
}

private byte[] calculateVoidReturnValue() {
if (shouldReturnNullOnVoidMethods()) {
return null;
}
return new byte[]{};
}

private boolean shouldReturnNullOnVoidMethods() {
return !activations.isActive(RSKIP417);
}

private void teardown() throws IOException {
bridgeSupport.save();
}
Expand Down Expand Up @@ -628,7 +701,7 @@ public byte[] getBtcBlockchainBlockHashAtDepth(Object[] args) throws VMException
logger.trace("getBtcBlockchainBlockHashAtDepth");

int depth = ((BigInteger) args[0]).intValue();
Sha256Hash blockHash = null;
Sha256Hash blockHash;
try {
blockHash = bridgeSupport.getBtcBlockchainBlockHashAtDepth(depth);
} catch (Exception e) {
Expand Down Expand Up @@ -831,8 +904,8 @@ public Integer createFederation(Object[] args) throws BridgeIllegalArgumentExcep
logger.trace("createFederation");

return bridgeSupport.voteFederationChange(
rskTx,
new ABICallSpec("create", new byte[][]{})
rskTx,
new ABICallSpec("create", new byte[][]{})
);
}

Expand All @@ -848,8 +921,8 @@ public Integer addFederatorPublicKey(Object[] args) throws BridgeIllegalArgument
}

return bridgeSupport.voteFederationChange(
rskTx,
new ABICallSpec("add", new byte[][]{ publicKeyBytes })
rskTx,
new ABICallSpec("add", new byte[][]{ publicKeyBytes })
);
}

Expand All @@ -861,8 +934,11 @@ public Integer addFederatorPublicKeyMultikey(Object[] args) throws BridgeIllegal
byte[] mstPublicKeyBytes = (byte[]) args[2];

return bridgeSupport.voteFederationChange(
rskTx,
new ABICallSpec("add-multi", new byte[][]{ btcPublicKeyBytes, rskPublicKeyBytes, mstPublicKeyBytes })
rskTx,
new ABICallSpec(
"add-multi",
new byte[][]{ btcPublicKeyBytes, rskPublicKeyBytes, mstPublicKeyBytes }
)
);
}

Expand All @@ -878,8 +954,8 @@ public Integer commitFederation(Object[] args) throws BridgeIllegalArgumentExcep
}

return bridgeSupport.voteFederationChange(
rskTx,
new ABICallSpec("commit", new byte[][]{ hash })
rskTx,
new ABICallSpec("commit", new byte[][]{ hash })
);
}

Expand Down Expand Up @@ -1100,7 +1176,14 @@ public void registerBtcCoinbaseTransaction(Object[] args) throws VMException {
byte[] pmtSerialized = (byte[]) args[2];
Sha256Hash witnessMerkleRoot = Sha256Hash.wrap((byte[]) args[3]);
byte[] witnessReservedValue = (byte[]) args[4];
bridgeSupport.registerBtcCoinbaseTransaction(btcTxSerialized, blockHash, pmtSerialized, witnessMerkleRoot, witnessReservedValue);

bridgeSupport.registerBtcCoinbaseTransaction(
btcTxSerialized,
blockHash,
pmtSerialized,
witnessMerkleRoot,
witnessReservedValue
);
}

public boolean hasBtcBlockCoinbaseTransactionInformation(Object[] args) {
Expand Down
Loading
Loading