Coverage Summary for Class: Bridge (co.rsk.peg)
Class |
Method, %
|
Line, %
|
Bridge |
0%
(0/76)
|
0%
(0/540)
|
Bridge$BridgeParsedData |
0%
(0/1)
|
0%
(0/1)
|
Total |
0%
(0/77)
|
0%
(0/541)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2017 RSK Labs Ltd.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package co.rsk.peg;
20
21 import co.rsk.bitcoinj.core.*;
22 import co.rsk.bitcoinj.store.BlockStoreException;
23 import co.rsk.config.BridgeConstants;
24 import co.rsk.core.RskAddress;
25 import co.rsk.crypto.Keccak256;
26 import co.rsk.panic.PanicProcessor;
27 import co.rsk.peg.bitcoin.MerkleBranch;
28 import co.rsk.peg.utils.BtcTransactionFormatUtils;
29 import co.rsk.peg.whitelist.LockWhitelistEntry;
30 import co.rsk.peg.whitelist.OneOffWhiteListEntry;
31 import co.rsk.rpc.modules.trace.ProgramSubtrace;
32 import com.google.common.annotations.VisibleForTesting;
33 import org.ethereum.config.Constants;
34 import org.ethereum.config.blockchain.upgrades.ActivationConfig;
35 import org.ethereum.config.blockchain.upgrades.ConsensusRule;
36 import org.ethereum.core.Block;
37 import org.ethereum.core.CallTransaction;
38 import org.ethereum.core.Repository;
39 import org.ethereum.core.Transaction;
40 import org.ethereum.db.BlockStore;
41 import org.ethereum.db.ReceiptStore;
42 import org.ethereum.util.ByteUtil;
43 import org.ethereum.vm.DataWord;
44 import org.ethereum.vm.LogInfo;
45 import org.ethereum.vm.PrecompiledContracts;
46 import org.ethereum.vm.exception.VMException;
47 import org.ethereum.vm.program.Program;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 import java.io.IOException;
52 import java.math.BigInteger;
53 import java.time.Instant;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.List;
57 import java.util.Optional;
58 import java.util.stream.Collectors;
59
60 /**
61 * Precompiled contract that manages the 2 way peg between bitcoin and RSK.
62 * This class is just a wrapper, actual functionality is found in BridgeSupport.
63 * @author Oscar Guindzberg
64 */
65 public class Bridge extends PrecompiledContracts.PrecompiledContract {
66
67 private static final Logger logger = LoggerFactory.getLogger("bridge");
68 private static final PanicProcessor panicProcessor = new PanicProcessor();
69
70 // No parameters
71 public static final CallTransaction.Function UPDATE_COLLECTIONS = BridgeMethods.UPDATE_COLLECTIONS.getFunction();
72 // Parameters: an array of bitcoin blocks serialized with the bitcoin wire protocol format
73 public static final CallTransaction.Function RECEIVE_HEADERS = BridgeMethods.RECEIVE_HEADERS.getFunction();
74 // Parameters: a header of bitcoin blocks serialized with the bitcoin wire protocol format
75 public static final CallTransaction.Function RECEIVE_HEADER = BridgeMethods.RECEIVE_HEADER.getFunction();
76 // Parameters:
77 // - A bitcoin tx, serialized with the bitcoin wire protocol format
78 // - The bitcoin block height that contains the tx
79 // - A merkle tree that shows the tx is included in that block, serialized with the bitcoin wire protocol format.
80 public static final CallTransaction.Function REGISTER_BTC_TRANSACTION = BridgeMethods.REGISTER_BTC_TRANSACTION.getFunction();
81 // No parameters, the current rsk tx is used as input.
82 public static final CallTransaction.Function RELEASE_BTC = BridgeMethods.RELEASE_BTC.getFunction();
83 // Parameters:
84 // Federator public key.
85 // Transaction signature array, one for each btc tx input.
86 // Rsk tx hash of the tx that required the release of funds.
87 public static final CallTransaction.Function ADD_SIGNATURE = BridgeMethods.ADD_SIGNATURE.getFunction();
88 // Returns a StateForFederator encoded in RLP
89 public static final CallTransaction.Function GET_STATE_FOR_BTC_RELEASE_CLIENT = BridgeMethods.GET_STATE_FOR_BTC_RELEASE_CLIENT.getFunction();
90 // Returns a BridgeState encoded in RLP
91 public static final CallTransaction.Function GET_STATE_FOR_DEBUGGING = BridgeMethods.GET_STATE_FOR_DEBUGGING.getFunction();
92 // Return the bitcoin blockchain best chain height know by the bridge contract
93 public static final CallTransaction.Function GET_BTC_BLOCKCHAIN_BEST_CHAIN_HEIGHT = BridgeMethods.GET_BTC_BLOCKCHAIN_BEST_CHAIN_HEIGHT.getFunction();
94 // Returns an array of block hashes known by the bridge contract. Federators can use this to find what is the latest block in the mainchain the bridge has.
95 // The goal of this function is to help synchronize bridge and federators blockchains.
96 // Protocol inspired by bitcoin sync protocol, see block locator in https://en.bitcoin.it/wiki/Protocol_documentation#getheaders
97 public static final CallTransaction.Function GET_BTC_BLOCKCHAIN_BLOCK_LOCATOR = BridgeMethods.GET_BTC_BLOCKCHAIN_BLOCK_LOCATOR.getFunction();
98 // Return the height of the initial block stored in the bridge's bitcoin blockchain
99 public static final CallTransaction.Function GET_BTC_BLOCKCHAIN_INITIAL_BLOCK_HEIGHT = BridgeMethods.GET_BTC_BLOCKCHAIN_INITIAL_BLOCK_HEIGHT.getFunction();
100 // Returns the block hash of the bridge contract's best chain at the given depth, meaning depth zero will
101 // yield the best chain head hash and depth one will yield its parent hash, and so on and so forth.
102 // Federators use this to find what is the latest block in the mainchain the bridge has
103 // (replacing the need for getBtcBlockchainBlockLocator).
104 // The goal of this function is to help synchronize bridge and federators blockchains.
105 public static final CallTransaction.Function GET_BTC_BLOCKCHAIN_BLOCK_HASH_AT_DEPTH = BridgeMethods.GET_BTC_BLOCKCHAIN_BLOCK_HASH_AT_DEPTH.getFunction();
106 // Returns the confirmations number of the block for the given transaction. If its not valid or its not part of the main chain it returns a negative number
107 // The goal of this function is to help contracts can use this to validate BTC transactions
108 public static final CallTransaction.Function GET_BTC_TRANSACTION_CONFIRMATIONS = BridgeMethods.GET_BTC_TRANSACTION_CONFIRMATIONS.getFunction();
109 // Returns the minimum amount of satoshis a user should send to the federation.
110 public static final CallTransaction.Function GET_MINIMUM_LOCK_TX_VALUE = BridgeMethods.GET_MINIMUM_LOCK_TX_VALUE.getFunction();
111
112 // Returns whether a given btc tx hash was already processed by the bridge
113 public static final CallTransaction.Function IS_BTC_TX_HASH_ALREADY_PROCESSED = BridgeMethods.IS_BTC_TX_HASH_ALREADY_PROCESSED.getFunction();
114 // Returns whether a given btc tx hash was already processed by the bridge
115 public static final CallTransaction.Function GET_BTC_TX_HASH_PROCESSED_HEIGHT = BridgeMethods.GET_BTC_TX_HASH_PROCESSED_HEIGHT.getFunction();
116
117 // Returns the federation bitcoin address
118 public static final CallTransaction.Function GET_FEDERATION_ADDRESS = BridgeMethods.GET_FEDERATION_ADDRESS.getFunction();
119 // Returns the number of federates in the currently active federation
120 public static final CallTransaction.Function GET_FEDERATION_SIZE = BridgeMethods.GET_FEDERATION_SIZE.getFunction();
121 // Returns the number of minimum required signatures from the currently active federation
122 public static final CallTransaction.Function GET_FEDERATION_THRESHOLD = BridgeMethods.GET_FEDERATION_THRESHOLD.getFunction();
123 // Returns the public key of the federator at the specified index
124 public static final CallTransaction.Function GET_FEDERATOR_PUBLIC_KEY = BridgeMethods.GET_FEDERATOR_PUBLIC_KEY.getFunction();
125 // Returns the public key of given type of the federator at the specified index
126 public static final CallTransaction.Function GET_FEDERATOR_PUBLIC_KEY_OF_TYPE = BridgeMethods.GET_FEDERATOR_PUBLIC_KEY_OF_TYPE.getFunction();
127 // Returns the creation time of the federation
128 public static final CallTransaction.Function GET_FEDERATION_CREATION_TIME = BridgeMethods.GET_FEDERATION_CREATION_TIME.getFunction();
129 // Returns the block number of the creation of the federation
130 public static final CallTransaction.Function GET_FEDERATION_CREATION_BLOCK_NUMBER = BridgeMethods.GET_FEDERATION_CREATION_BLOCK_NUMBER.getFunction();
131
132 // Returns the retiring federation bitcoin address
133 public static final CallTransaction.Function GET_RETIRING_FEDERATION_ADDRESS = BridgeMethods.GET_RETIRING_FEDERATION_ADDRESS.getFunction();
134 // Returns the number of federates in the retiring federation
135 public static final CallTransaction.Function GET_RETIRING_FEDERATION_SIZE = BridgeMethods.GET_RETIRING_FEDERATION_SIZE.getFunction();
136 // Returns the number of minimum required signatures from the retiring federation
137 public static final CallTransaction.Function GET_RETIRING_FEDERATION_THRESHOLD = BridgeMethods.GET_RETIRING_FEDERATION_THRESHOLD.getFunction();
138 // Returns the public key of the retiring federation's federator at the specified index
139 public static final CallTransaction.Function GET_RETIRING_FEDERATOR_PUBLIC_KEY = BridgeMethods.GET_RETIRING_FEDERATOR_PUBLIC_KEY.getFunction();
140 // Returns the public key of given type of the retiring federation's federator at the specified index
141 public static final CallTransaction.Function GET_RETIRING_FEDERATOR_PUBLIC_KEY_OF_TYPE = BridgeMethods.GET_RETIRING_FEDERATOR_PUBLIC_KEY_OF_TYPE.getFunction();
142 // Returns the creation time of the retiring federation
143 public static final CallTransaction.Function GET_RETIRING_FEDERATION_CREATION_TIME = BridgeMethods.GET_RETIRING_FEDERATION_CREATION_TIME.getFunction();
144 // Returns the block number of the creation of the retiring federation
145 public static final CallTransaction.Function GET_RETIRING_FEDERATION_CREATION_BLOCK_NUMBER = BridgeMethods.GET_RETIRING_FEDERATION_CREATION_BLOCK_NUMBER.getFunction();
146
147 // Creates a new pending federation and returns its id
148 public static final CallTransaction.Function CREATE_FEDERATION = BridgeMethods.CREATE_FEDERATION.getFunction();
149 // Adds the given key to the current pending federation
150 public static final CallTransaction.Function ADD_FEDERATOR_PUBLIC_KEY = BridgeMethods.ADD_FEDERATOR_PUBLIC_KEY.getFunction();
151 // Adds the given key to the current pending federation (multiple-key version)
152 public static final CallTransaction.Function ADD_FEDERATOR_PUBLIC_KEY_MULTIKEY = BridgeMethods.ADD_FEDERATOR_PUBLIC_KEY_MULTIKEY.getFunction();
153 // Commits the currently pending federation
154 public static final CallTransaction.Function COMMIT_FEDERATION = BridgeMethods.COMMIT_FEDERATION.getFunction();
155 // Rolls back the currently pending federation
156 public static final CallTransaction.Function ROLLBACK_FEDERATION = BridgeMethods.ROLLBACK_FEDERATION.getFunction();
157
158 // Returns the current pending federation's hash
159 public static final CallTransaction.Function GET_PENDING_FEDERATION_HASH = BridgeMethods.GET_PENDING_FEDERATION_HASH.getFunction();
160 // Returns the number of federates in the current pending federation
161 public static final CallTransaction.Function GET_PENDING_FEDERATION_SIZE = BridgeMethods.GET_PENDING_FEDERATION_SIZE.getFunction();
162 // Returns the public key of the federator at the specified index for the current pending federation
163 public static final CallTransaction.Function GET_PENDING_FEDERATOR_PUBLIC_KEY = BridgeMethods.GET_PENDING_FEDERATOR_PUBLIC_KEY.getFunction();
164 // Returns the public key of given type the federator at the specified index for the current pending federation
165 public static final CallTransaction.Function GET_PENDING_FEDERATOR_PUBLIC_KEY_OF_TYPE = BridgeMethods.GET_PENDING_FEDERATOR_PUBLIC_KEY_OF_TYPE.getFunction();
166
167 // Returns the lock whitelist size
168 public static final CallTransaction.Function GET_LOCK_WHITELIST_SIZE = BridgeMethods.GET_LOCK_WHITELIST_SIZE.getFunction();
169 // Returns the lock whitelist address stored at the specified index
170 public static final CallTransaction.Function GET_LOCK_WHITELIST_ADDRESS = BridgeMethods.GET_LOCK_WHITELIST_ADDRESS.getFunction();
171 // Returns the lock whitelist entry stored at the specified address
172 public static final CallTransaction.Function GET_LOCK_WHITELIST_ENTRY_BY_ADDRESS = BridgeMethods.GET_LOCK_WHITELIST_ENTRY_BY_ADDRESS.getFunction();
173 // Adds the given address to the lock whitelist
174 public static final CallTransaction.Function ADD_LOCK_WHITELIST_ADDRESS = BridgeMethods.ADD_LOCK_WHITELIST_ADDRESS.getFunction();
175 // Adds the given address to the lock whitelist in "one-off" mode
176 public static final CallTransaction.Function ADD_ONE_OFF_LOCK_WHITELIST_ADDRESS = BridgeMethods.ADD_ONE_OFF_LOCK_WHITELIST_ADDRESS.getFunction();
177 // Adds the given address to the lock whitelist in "unlimited" mode
178 public static final CallTransaction.Function ADD_UNLIMITED_LOCK_WHITELIST_ADDRESS = BridgeMethods.ADD_UNLIMITED_LOCK_WHITELIST_ADDRESS.getFunction();
179 // Adds the given address to the lock whitelist
180 public static final CallTransaction.Function REMOVE_LOCK_WHITELIST_ADDRESS = BridgeMethods.REMOVE_LOCK_WHITELIST_ADDRESS.getFunction();
181
182 public static final CallTransaction.Function SET_LOCK_WHITELIST_DISABLE_BLOCK_DELAY = BridgeMethods.SET_LOCK_WHITELIST_DISABLE_BLOCK_DELAY.getFunction();
183
184 // Returns the current fee per kb
185 public static final CallTransaction.Function GET_FEE_PER_KB = BridgeMethods.GET_FEE_PER_KB.getFunction();
186 // Adds the given key to the current pending federation
187 public static final CallTransaction.Function VOTE_FEE_PER_KB = BridgeMethods.VOTE_FEE_PER_KB.getFunction();
188
189 // Increases the peg locking cap
190 public static final CallTransaction.Function INCREASE_LOCKING_CAP = BridgeMethods.INCREASE_LOCKING_CAP.getFunction();
191 // Gets the peg locking cap
192 public static final CallTransaction.Function GET_LOCKING_CAP = BridgeMethods.GET_LOCKING_CAP.getFunction();
193 public static final CallTransaction.Function REGISTER_BTC_COINBASE_TRANSACTION = BridgeMethods.REGISTER_BTC_COINBASE_TRANSACTION.getFunction();
194 public static final CallTransaction.Function HAS_BTC_BLOCK_COINBASE_TRANSACTION_INFORMATION = BridgeMethods.HAS_BTC_BLOCK_COINBASE_TRANSACTION_INFORMATION.getFunction();
195 public static final CallTransaction.Function REGISTER_FAST_BRIDGE_BTC_TRANSACTION = BridgeMethods.REGISTER_FAST_BRIDGE_BTC_TRANSACTION.getFunction();
196
197 public static final int LOCK_WHITELIST_UNLIMITED_MODE_CODE = 0;
198 public static final int LOCK_WHITELIST_ENTRY_NOT_FOUND_CODE = -1;
199 public static final int LOCK_WHITELIST_INVALID_ADDRESS_FORMAT_ERROR_CODE = -2;
200
201 // Log topics used by Bridge Contract pre RSKIP146
202 public static final DataWord RELEASE_BTC_TOPIC = DataWord.fromString("release_btc_topic");
203 public static final DataWord UPDATE_COLLECTIONS_TOPIC = DataWord.fromString("update_collections_topic");
204 public static final DataWord ADD_SIGNATURE_TOPIC = DataWord.fromString("add_signature_topic");
205 public static final DataWord COMMIT_FEDERATION_TOPIC = DataWord.fromString("commit_federation_topic");
206
207 private static final Integer RECEIVE_HEADER_ERROR_SIZE_MISTMATCH = -20;
208
209 private final Constants constants;
210 private final BridgeConstants bridgeConstants;
211 private final ActivationConfig activationConfig;
212
213 private ActivationConfig.ForBlock activations;
214 private org.ethereum.core.Transaction rskTx;
215
216 private BridgeSupport bridgeSupport;
217 private BridgeSupportFactory bridgeSupportFactory;
218
219 public Bridge(RskAddress contractAddress, Constants constants, ActivationConfig activationConfig,
220 BridgeSupportFactory bridgeSupportFactory) {
221 this.bridgeSupportFactory = bridgeSupportFactory;
222 this.contractAddress = contractAddress;
223 this.constants = constants;
224 this.bridgeConstants = constants.getBridgeConstants();
225 this.activationConfig = activationConfig;
226 }
227
228 @Override
229 public long getGasForData(byte[] data) {
230 if (!activations.isActive(ConsensusRule.RSKIP88) && BridgeUtils.isContractTx(rskTx)) {
231 logger.warn("Call from contract before Orchid");
232 throw new NullPointerException();
233 }
234
235 if (BridgeUtils.isFreeBridgeTx(rskTx, constants, activations)) {
236 return 0;
237 }
238
239 BridgeParsedData bridgeParsedData = parseData(data);
240
241 Long functionCost;
242 Long totalCost;
243 if (bridgeParsedData == null) {
244 functionCost = BridgeMethods.RELEASE_BTC.getCost(this, activations, new Object[0]);
245 totalCost = functionCost;
246 } else {
247 functionCost = bridgeParsedData.bridgeMethod.getCost(this, activations, bridgeParsedData.args);
248 int dataCost = data == null ? 0 : data.length * 2;
249
250 totalCost = functionCost + dataCost;
251 }
252
253 return totalCost;
254 }
255
256 @VisibleForTesting
257 BridgeParsedData parseData(byte[] data) {
258 BridgeParsedData bridgeParsedData = new BridgeParsedData();
259
260 if (data != null && (data.length >= 1 && data.length <= 3)) {
261 logger.warn("Invalid function signature {}.", ByteUtil.toHexString(data));
262 return null;
263 }
264
265 if (data == null || data.length == 0) {
266 bridgeParsedData.bridgeMethod = BridgeMethods.RELEASE_BTC;
267 bridgeParsedData.args = new Object[]{};
268 } else {
269 byte[] functionSignature = Arrays.copyOfRange(data, 0, 4);
270 Optional<BridgeMethods> invokedMethod = BridgeMethods.findBySignature(functionSignature);
271 if (!invokedMethod.isPresent()) {
272 logger.warn("Invalid function signature {}.", ByteUtil.toHexString(functionSignature));
273 return null;
274 }
275 bridgeParsedData.bridgeMethod = invokedMethod.get();
276 try {
277 bridgeParsedData.args = bridgeParsedData.bridgeMethod.getFunction().decode(data);
278 } catch (Exception e) {
279 logger.warn("Invalid function arguments {} for function {}.", ByteUtil.toHexString(data), ByteUtil.toHexString(functionSignature));
280 return null;
281 }
282 }
283
284 if (!bridgeParsedData.bridgeMethod.isEnabled(activations)) {
285 logger.warn("'{}' is not enabled to run", bridgeParsedData.bridgeMethod.name());
286 return null;
287 }
288
289 return bridgeParsedData;
290 }
291
292 // Parsed rsk transaction data field
293 private static class BridgeParsedData {
294 public BridgeMethods bridgeMethod;
295 public Object[] args;
296 }
297
298 @Override
299 public void init(Transaction rskTx, Block rskExecutionBlock, Repository repository, BlockStore rskBlockStore, ReceiptStore rskReceiptStore, List<LogInfo> logs) {
300 this.activations = activationConfig.forBlock(rskExecutionBlock.getNumber());
301 this.rskTx = rskTx;
302
303 this.bridgeSupport = bridgeSupportFactory.newInstance(
304 repository,
305 rskExecutionBlock,
306 contractAddress,
307 logs);
308 }
309
310 @Override
311 public List<ProgramSubtrace> getSubtraces() {
312 return this.bridgeSupport.getSubtraces();
313 }
314
315 @Override
316 public byte[] execute(byte[] data) throws VMException {
317 try {
318 // Preliminary validation: the transaction on which we execute cannot be null
319 if (rskTx == null) {
320 throw new VMException("Rsk Transaction is null");
321 }
322
323 BridgeParsedData bridgeParsedData = parseData(data);
324
325 // Function parsing from data returned null => invalid function selected, halt!
326 if (bridgeParsedData == null) {
327 String errorMessage = String.format("Invalid data given: %s.", ByteUtil.toHexString(data));
328 logger.info(errorMessage);
329 if (activations.isActive(ConsensusRule.RSKIP88)) {
330 throw new BridgeIllegalArgumentException(errorMessage);
331 }
332
333 return null;
334 }
335
336 // If this is not a local call, then first check whether the function
337 // allows for non-local calls
338 if (activations.isActive(ConsensusRule.RSKIP88) &&
339 !isLocalCall() &&
340 bridgeParsedData.bridgeMethod.onlyAllowsLocalCalls(this, bridgeParsedData.args)) {
341
342 String errorMessage = String.format("Non-local-call to %s. Returning without execution.", bridgeParsedData.bridgeMethod.getFunction().name);
343 logger.info(errorMessage);
344 throw new BridgeIllegalArgumentException(errorMessage);
345 }
346
347 Optional<?> result;
348 try {
349 // bridgeParsedData.function should be one of the CallTransaction.Function declared above.
350 // If the user tries to call an non-existent function, parseData() will return null.
351 result = bridgeParsedData.bridgeMethod.getExecutor().execute(this, bridgeParsedData.args);
352 } catch (BridgeIllegalArgumentException ex) {
353 String errorMessage = String.format("Error executing: %s", bridgeParsedData.bridgeMethod);
354 logger.warn(errorMessage, ex);
355 if (activations.isActive(ConsensusRule.RSKIP88)) {
356 throw new BridgeIllegalArgumentException(errorMessage);
357 }
358
359 return null;
360 }
361
362 teardown();
363
364 return result.map(bridgeParsedData.bridgeMethod.getFunction()::encodeOutputs).orElse(null);
365 } catch (Exception ex) {
366 logger.error(ex.getMessage(), ex);
367 panicProcessor.panic("bridgeexecute", ex.getMessage());
368 throw new VMException(String.format("Exception executing bridge: %s", ex.getMessage()), ex);
369 }
370 }
371
372 private void teardown() throws IOException {
373 bridgeSupport.save();
374 }
375
376 public void updateCollections(Object[] args) throws VMException {
377 logger.trace("updateCollections");
378
379 try {
380 bridgeSupport.updateCollections(rskTx);
381 } catch (Exception e) {
382 logger.warn("Exception onBlock", e);
383 throw new VMException("Exception onBlock", e);
384 }
385 }
386
387 public boolean receiveHeadersIsPublic() {
388 return (activations.isActive(ConsensusRule.RSKIP124)
389 && (!activations.isActive(ConsensusRule.RSKIP200)));
390 }
391
392 public long receiveHeadersGetCost(Object[] args) {
393 // Old, private method fixed cost. Only applies before the corresponding RSKIP
394 if (!activations.isActive(ConsensusRule.RSKIP124)) {
395 return 22_000L;
396 }
397
398 final long BASE_COST = activations.isActive(ConsensusRule.RSKIP132) ? 25_000L : 66_000L;
399 if (args == null) {
400 return BASE_COST;
401 }
402
403 final int numberOfHeaders = ((Object[]) args[0]).length;
404
405 if (numberOfHeaders == 0) {
406 return BASE_COST;
407 }
408 // Dynamic cost based on the number of headers
409 // We add each additional header times 1650 to the base cost
410 final long COST_PER_ADDITIONAL_HEADER = activations.isActive(ConsensusRule.RSKIP132) ? 3_500 : 1_650;
411 return BASE_COST + (numberOfHeaders - 1) * COST_PER_ADDITIONAL_HEADER;
412 }
413
414 public void receiveHeaders(Object[] args) throws VMException {
415 logger.trace("receiveHeaders");
416
417 Object[] btcBlockSerializedArray = (Object[]) args[0];
418
419 // Before going and actually deserializing and calling the underlying function,
420 // check that all block headers passed in are actually block headers doing
421 // a simple size check. If this check fails, just fail.
422 if (Arrays.stream(btcBlockSerializedArray).anyMatch(bytes -> !BtcTransactionFormatUtils.isBlockHeaderSize(((byte[]) bytes).length, activations))) {
423 // This exception type bypasses bridge teardown, signalling no work done
424 // and preventing the overhead of saving bridge storage
425 logger.warn("Unexpected BTC header(s) received (size mismatch). Aborting processing.");
426 throw new BridgeIllegalArgumentException("Unexpected BTC header(s) received (size mismatch). Aborting processing.");
427 }
428
429 BtcBlock[] btcBlockArray = new BtcBlock[btcBlockSerializedArray.length];
430 for (int i = 0; i < btcBlockSerializedArray.length; i++) {
431 byte[] btcBlockSerialized = (byte[]) btcBlockSerializedArray[i];
432 try {
433 BtcBlock header = bridgeConstants.getBtcParams().getDefaultSerializer().makeBlock(btcBlockSerialized);
434 btcBlockArray[i] = header;
435 } catch (ProtocolException e) {
436 throw new BridgeIllegalArgumentException("Block " + i + " could not be parsed " + ByteUtil.toHexString(btcBlockSerialized), e);
437 }
438 }
439 try {
440 bridgeSupport.receiveHeaders(btcBlockArray);
441 } catch (Exception e) {
442 logger.warn("Exception adding header", e);
443 throw new VMException("Exception adding header", e);
444 }
445 }
446
447 public boolean registerBtcTransactionIsPublic() {
448 return activations.isActive(ConsensusRule.RSKIP199);
449 }
450
451 public int receiveHeader(Object[] args) throws VMException {
452 logger.trace("receiveHeader");
453
454 byte[] headerArg = (byte[]) args[0];
455
456 if (!BtcTransactionFormatUtils.isBlockHeaderSize(headerArg.length, activations)) {
457 logger.warn("Unexpected BTC header received (size mismatch). Aborting processing.");
458 return RECEIVE_HEADER_ERROR_SIZE_MISTMATCH;
459 }
460
461 BtcBlock header = bridgeConstants.getBtcParams().getDefaultSerializer().makeBlock(headerArg);
462
463 try {
464 return bridgeSupport.receiveHeader(header);
465 } catch (Exception e) {
466 String errorMessage = "Exception adding header in receiveHeader";
467 logger.warn(errorMessage, e);
468 throw new VMException(errorMessage, e);
469 }
470 }
471
472 public void registerBtcTransaction(Object[] args) throws VMException {
473 logger.trace("registerBtcTransaction");
474
475 byte[] btcTxSerialized = (byte[]) args[0];
476 int height = ((BigInteger)args[1]).intValue();
477
478 byte[] pmtSerialized = (byte[]) args[2];
479 try {
480 bridgeSupport.registerBtcTransaction(rskTx, btcTxSerialized, height, pmtSerialized);
481 } catch (IOException | BlockStoreException e) {
482 logger.warn("Exception in registerBtcTransaction", e);
483 throw new VMException("Exception in registerBtcTransaction", e);
484 }
485 }
486
487 public void releaseBtc(Object[] args) throws VMException {
488 logger.trace("releaseBtc");
489
490 try {
491 bridgeSupport.releaseBtc(rskTx);
492 } catch (Program.OutOfGasException e) {
493 throw e;
494 } catch (Exception e) {
495 logger.warn("Exception in releaseBtc", e);
496 throw new VMException("Exception in releaseBtc", e);
497 }
498 }
499
500 public void addSignature(Object[] args) throws VMException {
501 logger.trace("addSignature");
502
503 byte[] federatorPublicKeySerialized = (byte[]) args[0];
504 BtcECKey federatorPublicKey;
505 try {
506 federatorPublicKey = BtcECKey.fromPublicOnly(federatorPublicKeySerialized);
507 } catch (Exception e) {
508 throw new BridgeIllegalArgumentException("Public key could not be parsed " + ByteUtil.toHexString(federatorPublicKeySerialized), e);
509 }
510 Object[] signaturesObjectArray = (Object[]) args[1];
511 if (signaturesObjectArray.length == 0) {
512 throw new BridgeIllegalArgumentException("Signatures array is empty");
513 }
514 List<byte[]> signatures = new ArrayList<>();
515 for (Object signatureObject : signaturesObjectArray) {
516 byte[] signatureByteArray = (byte[])signatureObject;
517 try {
518 BtcECKey.ECDSASignature.decodeFromDER((byte[])signatureObject);
519 } catch (Exception e) {
520 throw new BridgeIllegalArgumentException("Signature could not be parsed " + ByteUtil.toHexString(signatureByteArray), e);
521 }
522 signatures.add(signatureByteArray);
523 }
524 byte[] rskTxHash = (byte[]) args[2];
525 if (rskTxHash.length!=32) {
526 throw new BridgeIllegalArgumentException("Invalid rsk tx hash " + ByteUtil.toHexString(rskTxHash));
527 }
528 try {
529 bridgeSupport.addSignature(federatorPublicKey, signatures, rskTxHash);
530 } catch (BridgeIllegalArgumentException e) {
531 throw e;
532 } catch (Exception e) {
533 logger.warn("Exception in addSignature", e);
534 throw new VMException("Exception in addSignature", e);
535 }
536 }
537
538 public byte[] getStateForBtcReleaseClient(Object[] args) throws VMException {
539 logger.trace("getStateForBtcReleaseClient");
540
541 try {
542 return bridgeSupport.getStateForBtcReleaseClient();
543 } catch (Exception e) {
544 logger.warn("Exception in getStateForBtcReleaseClient", e);
545 throw new VMException("Exception in getStateForBtcReleaseClient", e);
546 }
547 }
548
549 public byte[] getStateForDebugging(Object[] args) throws VMException {
550 logger.trace("getStateForDebugging");
551
552 try {
553 return bridgeSupport.getStateForDebugging();
554 } catch (Exception e) {
555 logger.warn("Exception in getStateForDebugging", e);
556 throw new VMException("Exception in getStateForDebugging", e);
557 }
558 }
559
560 public Integer getBtcBlockchainBestChainHeight(Object[] args) throws VMException {
561 logger.trace("getBtcBlockchainBestChainHeight");
562
563 try {
564 return bridgeSupport.getBtcBlockchainBestChainHeight();
565 } catch (Exception e) {
566 logger.warn("Exception in getBtcBlockchainBestChainHeight", e);
567 throw new VMException("Exception in getBtcBlockchainBestChainHeight", e);
568 }
569 }
570
571 public boolean getBtcBlockchainBestChainHeightOnlyAllowsLocalCalls(Object[] args) {
572 return !activations.isActive(ConsensusRule.RSKIP220);
573 }
574
575 public Integer getBtcBlockchainInitialBlockHeight(Object[] args) throws VMException {
576 logger.trace("getBtcBlockchainInitialBlockHeight");
577
578 try {
579 return bridgeSupport.getBtcBlockchainInitialBlockHeight();
580 } catch (Exception e) {
581 logger.warn("Exception in getBtcBlockchainInitialBlockHeight", e);
582 throw new VMException("Exception in getBtcBlockchainInitialBlockHeight", e);
583 }
584 }
585
586 /**
587 * @deprecated
588 * @param args
589 * @return
590 */
591 @Deprecated
592 public Object[] getBtcBlockchainBlockLocator(Object[] args) throws VMException {
593 logger.trace("getBtcBlockchainBlockLocator");
594
595 try {
596 List<Sha256Hash> blockLocatorList = bridgeSupport.getBtcBlockchainBlockLocator();
597 Object[] blockLocatorArray = new Object[blockLocatorList.size()];
598 int i = 0;
599 for (Sha256Hash blockHash: blockLocatorList) {
600 blockLocatorArray[i] = blockHash.toString();
601 i++;
602 }
603 return blockLocatorArray;
604 } catch (Exception e) {
605 logger.warn("Exception in getBtcBlockchainBlockLocator", e);
606 throw new VMException("Exception in getBtcBlockchainBlockLocator", e);
607 }
608 }
609
610 public byte[] getBtcBlockchainBlockHashAtDepth(Object[] args) throws VMException {
611 logger.trace("getBtcBlockchainBlockHashAtDepth");
612
613 int depth = ((BigInteger) args[0]).intValue();
614 Sha256Hash blockHash = null;
615 try {
616 blockHash = bridgeSupport.getBtcBlockchainBlockHashAtDepth(depth);
617 } catch (Exception e) {
618 logger.warn("Exception in getBtcBlockchainBlockHashAtDepth", e);
619 throw new VMException("Exception in getBtcBlockchainBlockHashAtDepth", e);
620 }
621
622 return blockHash.getBytes();
623 }
624
625 public long getBtcTransactionConfirmationsGetCost(Object[] args) {
626 return bridgeSupport.getBtcTransactionConfirmationsGetCost(args);
627 }
628
629 public int getBtcTransactionConfirmations(Object[] args) throws VMException {
630 logger.trace("getBtcTransactionConfirmations");
631 try {
632 Sha256Hash btcTxHash = Sha256Hash.wrap((byte[]) args[0]);
633 Sha256Hash btcBlockHash = Sha256Hash.wrap((byte[]) args[1]);
634
635 int merkleBranchPath = ((BigInteger) args[2]).intValue();
636
637 Object[] merkleBranchHashesArray = (Object[]) args[3];
638 List<Sha256Hash> merkleBranchHashes = Arrays.stream(merkleBranchHashesArray)
639 .map(hash -> Sha256Hash.wrap((byte[]) hash)).collect(Collectors.toList());
640
641 MerkleBranch merkleBranch = new MerkleBranch(merkleBranchHashes, merkleBranchPath);
642
643 return bridgeSupport.getBtcTransactionConfirmations(btcTxHash, btcBlockHash, merkleBranch);
644 } catch (Exception e) {
645 logger.warn("Exception in getBtcTransactionConfirmations", e);
646 throw new VMException("Exception in getBtcTransactionConfirmations", e);
647 }
648 }
649
650 public Long getMinimumLockTxValue(Object[] args) {
651 logger.trace("getMinimumLockTxValue");
652 return bridgeSupport.getMinimumPeginTxValue().getValue();
653 }
654
655 public Boolean isBtcTxHashAlreadyProcessed(Object[] args) throws VMException {
656 logger.trace("isBtcTxHashAlreadyProcessed");
657
658 try {
659 Sha256Hash btcTxHash = Sha256Hash.wrap((String) args[0]);
660 return bridgeSupport.isBtcTxHashAlreadyProcessed(btcTxHash);
661 } catch (Exception e) {
662 logger.warn("Exception in isBtcTxHashAlreadyProcessed", e);
663 throw new VMException("Exception in isBtcTxHashAlreadyProcessed", e);
664 }
665 }
666
667 public Long getBtcTxHashProcessedHeight(Object[] args) throws VMException {
668 logger.trace("getBtcTxHashProcessedHeight");
669
670 try {
671 Sha256Hash btcTxHash = Sha256Hash.wrap((String) args[0]);
672 return bridgeSupport.getBtcTxHashProcessedHeight(btcTxHash);
673 } catch (Exception e) {
674 logger.warn("Exception in getBtcTxHashProcessedHeight", e);
675 throw new VMException("Exception in getBtcTxHashProcessedHeight", e);
676 }
677 }
678
679 public String getFederationAddress(Object[] args) {
680 logger.trace("getFederationAddress");
681
682 return bridgeSupport.getFederationAddress().toBase58();
683 }
684
685 public Integer getFederationSize(Object[] args) {
686 logger.trace("getFederationSize");
687
688 return bridgeSupport.getFederationSize();
689 }
690
691 public Integer getFederationThreshold(Object[] args) {
692 logger.trace("getFederationThreshold");
693
694 return bridgeSupport.getFederationThreshold();
695 }
696
697 public byte[] getFederatorPublicKey(Object[] args) {
698 logger.trace("getFederatorPublicKey");
699
700 int index = ((BigInteger) args[0]).intValue();
701 return bridgeSupport.getFederatorPublicKey(index);
702 }
703
704 public byte[] getFederatorPublicKeyOfType(Object[] args) throws VMException {
705 logger.trace("getFederatorPublicKeyOfType");
706
707 int index = ((BigInteger) args[0]).intValue();
708
709 FederationMember.KeyType keyType;
710 try {
711 keyType = FederationMember.KeyType.byValue((String) args[1]);
712 } catch (Exception e) {
713 logger.warn("Exception in getFederatorPublicKeyOfType", e);
714 throw new VMException("Exception in getFederatorPublicKeyOfType", e);
715 }
716
717 return bridgeSupport.getFederatorPublicKeyOfType(index, keyType);
718 }
719
720 public Long getFederationCreationTime(Object[] args) {
721 logger.trace("getFederationCreationTime");
722
723 // Return the creation time in milliseconds from the epoch
724 return bridgeSupport.getFederationCreationTime().toEpochMilli();
725 }
726
727 public long getFederationCreationBlockNumber(Object[] args) {
728 logger.trace("getFederationCreationBlockNumber");
729 return bridgeSupport.getFederationCreationBlockNumber();
730 }
731
732 public String getRetiringFederationAddress(Object[] args) {
733 logger.trace("getRetiringFederationAddress");
734
735 Address address = bridgeSupport.getRetiringFederationAddress();
736
737 if (address == null) {
738 // When there's no address, empty string is returned
739 return "";
740 }
741
742 return address.toBase58();
743 }
744
745 public Integer getRetiringFederationSize(Object[] args) {
746 logger.trace("getRetiringFederationSize");
747
748 return bridgeSupport.getRetiringFederationSize();
749 }
750
751 public Integer getRetiringFederationThreshold(Object[] args) {
752 logger.trace("getRetiringFederationThreshold");
753
754 return bridgeSupport.getRetiringFederationThreshold();
755 }
756
757 public byte[] getRetiringFederatorPublicKey(Object[] args) {
758 logger.trace("getRetiringFederatorPublicKey");
759
760 int index = ((BigInteger) args[0]).intValue();
761 byte[] publicKey = bridgeSupport.getRetiringFederatorPublicKey(index);
762
763 if (publicKey == null) {
764 // Empty array is returned when public key is not found or there's no retiring federation
765 return new byte[]{};
766 }
767
768 return publicKey;
769 }
770
771 public byte[] getRetiringFederatorPublicKeyOfType(Object[] args) throws VMException {
772 logger.trace("getRetiringFederatorPublicKeyOfType");
773
774 int index = ((BigInteger) args[0]).intValue();
775
776 FederationMember.KeyType keyType;
777 try {
778 keyType = FederationMember.KeyType.byValue((String) args[1]);
779 } catch (Exception e) {
780 logger.warn("Exception in getRetiringFederatorPublicKeyOfType", e);
781 throw new VMException("Exception in getRetiringFederatorPublicKeyOfType", e);
782 }
783
784 byte[] publicKey = bridgeSupport.getRetiringFederatorPublicKeyOfType(index, keyType);
785
786 if (publicKey == null) {
787 // Empty array is returned when public key is not found or there's no retiring federation
788 return new byte[]{};
789 }
790
791 return publicKey;
792 }
793
794 public Long getRetiringFederationCreationTime(Object[] args) {
795 logger.trace("getRetiringFederationCreationTime");
796
797 Instant creationTime = bridgeSupport.getRetiringFederationCreationTime();
798
799 if (creationTime == null) {
800 // -1 is returned when no retiring federation
801 return -1L;
802 }
803
804 // Return the creation time in milliseconds from the epoch
805 return creationTime.toEpochMilli();
806 }
807
808 public long getRetiringFederationCreationBlockNumber(Object[] args) {
809 logger.trace("getRetiringFederationCreationBlockNumber");
810 return bridgeSupport.getRetiringFederationCreationBlockNumber();
811 }
812
813 public Integer createFederation(Object[] args) throws BridgeIllegalArgumentException {
814 logger.trace("createFederation");
815
816 return bridgeSupport.voteFederationChange(
817 rskTx,
818 new ABICallSpec("create", new byte[][]{})
819 );
820 }
821
822 public Integer addFederatorPublicKey(Object[] args) throws BridgeIllegalArgumentException {
823 logger.trace("addFederatorPublicKey");
824
825 byte[] publicKeyBytes;
826 try {
827 publicKeyBytes = (byte[]) args[0];
828 } catch (Exception e) {
829 logger.warn("Exception in addFederatorPublicKey", e);
830 return -10;
831 }
832
833 return bridgeSupport.voteFederationChange(
834 rskTx,
835 new ABICallSpec("add", new byte[][]{ publicKeyBytes })
836 );
837 }
838
839 public Integer addFederatorPublicKeyMultikey(Object[] args) throws BridgeIllegalArgumentException {
840 logger.trace("addFederatorPublicKeyMultikey");
841
842 byte[] btcPublicKeyBytes = (byte[]) args[0];
843 byte[] rskPublicKeyBytes = (byte[]) args[1];
844 byte[] mstPublicKeyBytes = (byte[]) args[2];
845
846 return bridgeSupport.voteFederationChange(
847 rskTx,
848 new ABICallSpec("add-multi", new byte[][]{ btcPublicKeyBytes, rskPublicKeyBytes, mstPublicKeyBytes })
849 );
850 }
851
852 public Integer commitFederation(Object[] args) throws BridgeIllegalArgumentException {
853 logger.trace("commitFederation");
854
855 byte[] hash;
856 try {
857 hash = (byte[]) args[0];
858 } catch (Exception e) {
859 logger.warn("Exception in commitFederation", e);
860 return -10;
861 }
862
863 return bridgeSupport.voteFederationChange(
864 rskTx,
865 new ABICallSpec("commit", new byte[][]{ hash })
866 );
867 }
868
869 public Integer rollbackFederation(Object[] args) throws BridgeIllegalArgumentException {
870 logger.trace("rollbackFederation");
871
872 return bridgeSupport.voteFederationChange(
873 rskTx,
874 new ABICallSpec("rollback", new byte[][]{})
875 );
876 }
877
878 public byte[] getPendingFederationHash(Object[] args) {
879 logger.trace("getPendingFederationHash");
880
881 byte[] hash = bridgeSupport.getPendingFederationHash();
882
883 if (hash == null) {
884 // Empty array is returned when pending federation is not present
885 return new byte[]{};
886 }
887
888 return hash;
889 }
890
891 public Integer getPendingFederationSize(Object[] args) {
892 logger.trace("getPendingFederationSize");
893
894 return bridgeSupport.getPendingFederationSize();
895 }
896
897 public byte[] getPendingFederatorPublicKey(Object[] args) {
898 logger.trace("getPendingFederatorPublicKey");
899
900 int index = ((BigInteger) args[0]).intValue();
901 byte[] publicKey = bridgeSupport.getPendingFederatorPublicKey(index);
902
903 if (publicKey == null) {
904 // Empty array is returned when public key is not found
905 return new byte[]{};
906 }
907
908 return publicKey;
909 }
910
911 public byte[] getPendingFederatorPublicKeyOfType(Object[] args) throws VMException {
912 logger.trace("getPendingFederatorPublicKeyOfType");
913
914 int index = ((BigInteger) args[0]).intValue();
915
916 FederationMember.KeyType keyType;
917 try {
918 keyType = FederationMember.KeyType.byValue((String) args[1]);
919 } catch (Exception e) {
920 logger.warn("Exception in getPendingFederatorPublicKeyOfType", e);
921 throw new VMException("Exception in getPendingFederatorPublicKeyOfType", e);
922 }
923
924 byte[] publicKey = bridgeSupport.getPendingFederatorPublicKeyOfType(index, keyType);
925
926 if (publicKey == null) {
927 // Empty array is returned when public key is not found
928 return new byte[]{};
929 }
930
931 return publicKey;
932 }
933
934 public Integer getLockWhitelistSize(Object[] args) {
935 logger.trace("getLockWhitelistSize");
936
937 return bridgeSupport.getLockWhitelistSize();
938 }
939
940 public String getLockWhitelistAddress(Object[] args) {
941 logger.trace("getLockWhitelistAddress");
942
943 int index = ((BigInteger) args[0]).intValue();
944 LockWhitelistEntry entry = bridgeSupport.getLockWhitelistEntryByIndex(index);
945
946 if (entry == null) {
947 // Empty string is returned when address is not found
948 return "";
949 }
950
951 return entry.address().toBase58();
952 }
953
954 public long getLockWhitelistEntryByAddress(Object[] args) {
955 logger.trace("getLockWhitelistEntryByAddress");
956
957 String addressBase58;
958 try {
959 addressBase58 = (String) args[0];
960 } catch (Exception e) {
961 logger.warn("Exception in getLockWhitelistEntryByAddress", e);
962 return LOCK_WHITELIST_INVALID_ADDRESS_FORMAT_ERROR_CODE;
963 }
964
965 LockWhitelistEntry entry = bridgeSupport.getLockWhitelistEntryByAddress(addressBase58);
966
967 if (entry == null) {
968 // Empty string is returned when address is not found
969 return LOCK_WHITELIST_ENTRY_NOT_FOUND_CODE;
970 }
971
972 return entry.getClass() == OneOffWhiteListEntry.class ?
973 ((OneOffWhiteListEntry)entry).maxTransferValue().getValue() :
974 LOCK_WHITELIST_UNLIMITED_MODE_CODE;
975 }
976
977 public Integer addOneOffLockWhitelistAddress(Object[] args) {
978 logger.trace("addOneOffLockWhitelistAddress");
979
980 String addressBase58;
981 BigInteger maxTransferValue;
982 try {
983 addressBase58 = (String) args[0];
984 maxTransferValue = (BigInteger) args[1];
985 } catch (Exception e) {
986 logger.warn("Exception in addOneOffLockWhitelistAddress", e);
987 return 0;
988 }
989
990 return bridgeSupport.addOneOffLockWhitelistAddress(rskTx, addressBase58, maxTransferValue);
991 }
992
993 public Integer addUnlimitedLockWhitelistAddress(Object[] args) {
994 logger.trace("addUnlimitedLockWhitelistAddress");
995
996 String addressBase58;
997 try {
998 addressBase58 = (String) args[0];
999 } catch (Exception e) {
1000 logger.warn("Exception in addUnlimitedLockWhitelistAddress", e);
1001 return 0;
1002 }
1003
1004 return bridgeSupport.addUnlimitedLockWhitelistAddress(rskTx, addressBase58);
1005 }
1006
1007 public Integer removeLockWhitelistAddress(Object[] args) {
1008 logger.trace("removeLockWhitelistAddress");
1009
1010 String addressBase58;
1011 try {
1012 addressBase58 = (String) args[0];
1013 } catch (Exception e) {
1014 logger.warn("Exception in removeLockWhitelistAddress", e);
1015 return 0;
1016 }
1017
1018 return bridgeSupport.removeLockWhitelistAddress(rskTx, addressBase58);
1019 }
1020
1021 public Integer setLockWhitelistDisableBlockDelay(Object[] args) throws IOException, BlockStoreException {
1022 logger.trace("setLockWhitelistDisableBlockDelay");
1023 BigInteger lockWhitelistDisableBlockDelay = (BigInteger) args[0];
1024 return bridgeSupport.setLockWhitelistDisableBlockDelay(rskTx, lockWhitelistDisableBlockDelay);
1025 }
1026
1027 public Integer voteFeePerKbChange(Object[] args) {
1028 logger.trace("voteFeePerKbChange");
1029
1030 Coin feePerKb;
1031 try {
1032 feePerKb = Coin.valueOf(((BigInteger) args[0]).longValueExact());
1033 } catch (Exception e) {
1034 logger.warn("Exception in voteFeePerKbChange", e);
1035 return -10;
1036 }
1037
1038 return bridgeSupport.voteFeePerKbChange(rskTx, feePerKb);
1039 }
1040
1041 public long getFeePerKb(Object[] args) {
1042 logger.trace("getFeePerKb");
1043
1044 return bridgeSupport.getFeePerKb().getValue();
1045 }
1046
1047 public long getLockingCap(Object[] args) {
1048 logger.trace("getLockingCap");
1049
1050 Coin lockingCap = bridgeSupport.getLockingCap();
1051
1052 return lockingCap.getValue();
1053 }
1054
1055 public boolean increaseLockingCap(Object[] args) throws BridgeIllegalArgumentException {
1056 logger.trace("increaseLockingCap");
1057
1058 Coin newLockingCap = BridgeUtils.getCoinFromBigInteger((BigInteger) args[0]);
1059 if (newLockingCap.getValue() <= 0) {
1060 throw new BridgeIllegalArgumentException("Locking cap must be bigger than zero");
1061 }
1062
1063 return bridgeSupport.increaseLockingCap(rskTx, newLockingCap);
1064 }
1065
1066 public void registerBtcCoinbaseTransaction(Object[] args) throws VMException {
1067 logger.trace("registerBtcCoinbaseTransaction");
1068
1069 byte[] btcTxSerialized = (byte[]) args[0];
1070 Sha256Hash blockHash = Sha256Hash.wrap((byte[]) args[1]);
1071 byte[] pmtSerialized = (byte[]) args[2];
1072 Sha256Hash witnessMerkleRoot = Sha256Hash.wrap((byte[]) args[3]);
1073 byte[] witnessReservedValue = (byte[]) args[4];
1074 bridgeSupport.registerBtcCoinbaseTransaction(btcTxSerialized, blockHash, pmtSerialized, witnessMerkleRoot, witnessReservedValue);
1075 }
1076
1077 public boolean hasBtcBlockCoinbaseTransactionInformation(Object[] args) {
1078 logger.trace("hasBtcBlockCoinbaseTransactionInformation");
1079 Sha256Hash blockHash = Sha256Hash.wrap((byte[]) args[0]);
1080
1081 return bridgeSupport.hasBtcBlockCoinbaseTransactionInformation(blockHash);
1082 }
1083
1084 public long getActiveFederationCreationBlockHeight(Object[] args) {
1085 logger.trace("getActiveFederationCreationBlockHeight");
1086
1087 return bridgeSupport.getActiveFederationCreationBlockHeight();
1088 }
1089
1090 public long registerFastBridgeBtcTransaction(Object[] args) {
1091 logger.trace("registerFastBridgeBtcTransaction");
1092
1093 try {
1094 byte[] btcTxSerialized = (byte[]) args[0];
1095 int height = ((BigInteger) args[1]).intValue();
1096 byte[] pmtSerialized = (byte[]) args[2];
1097 Keccak256 derivationArgumentsHash = new Keccak256((byte[]) args[3]);
1098 // Parse data to create BTC user refund address with version and hash
1099 byte[] refundAddressInfo = (byte[]) args[4];
1100 Address userRefundAddress = new Address(
1101 bridgeConstants.getBtcParams(),
1102 BridgeUtils.extractAddressVersionFromBytes(refundAddressInfo),
1103 BridgeUtils.extractHash160FromBytes(refundAddressInfo)
1104 );
1105 // A DataWord cast is used because a SolidityType "address" is decoded using this specific type.
1106 RskAddress lbcAddress = new RskAddress((DataWord) args[5]);
1107 // Parse data to create BTC liquidity provider address with version and hash
1108 byte[] lpAddressInfo = (byte[]) args[6];
1109 Address lpBtcAddress = new Address(
1110 bridgeConstants.getBtcParams(),
1111 BridgeUtils.extractAddressVersionFromBytes(lpAddressInfo),
1112 BridgeUtils.extractHash160FromBytes(lpAddressInfo)
1113 );
1114 boolean shouldTransferToContract = ((boolean) args[7]);
1115
1116 return bridgeSupport.registerFastBridgeBtcTransaction(
1117 rskTx,
1118 btcTxSerialized,
1119 height,
1120 pmtSerialized,
1121 derivationArgumentsHash,
1122 userRefundAddress,
1123 lbcAddress,
1124 lpBtcAddress,
1125 shouldTransferToContract
1126 );
1127 } catch (Exception e) {
1128 logger.warn("Exception in registerFastBridgeBtcTransaction", e);
1129 return BridgeSupport.FAST_BRIDGE_GENERIC_ERROR;
1130 }
1131 }
1132
1133 public byte[] getBtcBlockchainBestBlockHeader(Object[] args) {
1134 logger.trace("getBtcBlockchainBestBlockHeader");
1135
1136 try {
1137 return this.bridgeSupport.getBtcBlockchainBestBlockHeader();
1138 } catch (Exception e) {
1139 logger.warn("Exception in getBtcBlockchainBestBlockHeader", e);
1140 return ByteUtil.EMPTY_BYTE_ARRAY;
1141 }
1142 }
1143
1144 public byte[] getBtcBlockchainBlockHeaderByHash(Object[] args) {
1145 logger.trace("getBtcBlockchainBlockHeaderByHash");
1146
1147 try {
1148 byte[] hashBytes = (byte[])args[0];
1149 Sha256Hash hash = Sha256Hash.wrap(hashBytes);
1150
1151 return this.bridgeSupport.getBtcBlockchainBlockHeaderByHash(hash);
1152 } catch (Exception e) {
1153 logger.warn("Exception in getBtcBlockchainBlockHeaderByHash", e);
1154 return ByteUtil.EMPTY_BYTE_ARRAY;
1155 }
1156 }
1157
1158 public byte[] getBtcBlockchainBlockHeaderByHeight(Object[] args) {
1159 logger.trace("getBtcBlockchainBlockHeaderByHeight");
1160
1161 try {
1162 int height = ((BigInteger) args[0]).intValue();
1163
1164 return this.bridgeSupport.getBtcBlockchainBlockHeaderByHeight(height);
1165 } catch (Exception e) {
1166 logger.warn("Exception in getBtcBlockchainBlockHeaderByHeight", e);
1167 return ByteUtil.EMPTY_BYTE_ARRAY;
1168 }
1169 }
1170
1171 public byte[] getBtcBlockchainParentBlockHeaderByHash(Object[] args) {
1172 logger.trace("getBtcBlockchainParentBlockHeaderByHash");
1173
1174 try {
1175 byte[] hashBytes = (byte[])args[0];
1176 Sha256Hash hash = Sha256Hash.wrap(hashBytes);
1177
1178 return this.bridgeSupport.getBtcBlockchainParentBlockHeaderByHash(hash);
1179 } catch (Exception e) {
1180 logger.warn("Exception in getBtcBlockchainParentBlockHeaderByHash", e);
1181 return ByteUtil.EMPTY_BYTE_ARRAY;
1182 }
1183 }
1184
1185 public static BridgeMethods.BridgeMethodExecutor activeAndRetiringFederationOnly(BridgeMethods.BridgeMethodExecutor decoratee, String funcName) {
1186 return (self, args) -> {
1187 Federation retiringFederation = self.bridgeSupport.getRetiringFederation();
1188
1189 if (!BridgeUtils.isFromFederateMember(self.rskTx, self.bridgeSupport.getActiveFederation())
1190 && ( retiringFederation == null || (retiringFederation != null && !BridgeUtils.isFromFederateMember(self.rskTx, retiringFederation)))) {
1191 String errorMessage = String.format("Sender is not part of the active or retiring federations, so he is not enabled to call the function '%s'",funcName);
1192 logger.warn(errorMessage);
1193 throw new VMException(errorMessage);
1194 }
1195 return decoratee.execute(self, args);
1196 };
1197 }
1198
1199 public static BridgeMethods.BridgeMethodExecutor executeIfElse(
1200 BridgeMethods.BridgeCondition condition,
1201 BridgeMethods.BridgeMethodExecutor ifTrue,
1202 BridgeMethods.BridgeMethodExecutor ifFalse) {
1203
1204 return (self, args) -> {
1205 if (condition.isTrue(self)) {
1206 return ifTrue.execute(self, args);
1207 } else {
1208 return ifFalse.execute(self, args);
1209 }
1210 };
1211 }
1212
1213 private boolean isLocalCall() {
1214 return rskTx.isLocalCallTransaction();
1215 }
1216 }