Coverage Summary for Class: BridgeSupport (co.rsk.peg)

Class Method, % Line, %
BridgeSupport 0% (0/140) 0% (0/1220)
BridgeSupport$1 0% (0/1) 0% (0/1)
BridgeSupport$TxType 0% (0/1) 0% (0/5)
Total 0% (0/142) 0% (0/1226)


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.Address; 22 import co.rsk.bitcoinj.core.AddressFormatException; 23 import co.rsk.bitcoinj.core.BtcBlock; 24 import co.rsk.bitcoinj.core.BtcBlockChain; 25 import co.rsk.bitcoinj.core.BtcECKey; 26 import co.rsk.bitcoinj.core.BtcTransaction; 27 import co.rsk.bitcoinj.core.CheckpointManager; 28 import co.rsk.bitcoinj.core.Coin; 29 import co.rsk.bitcoinj.core.Context; 30 import co.rsk.bitcoinj.core.InsufficientMoneyException; 31 import co.rsk.bitcoinj.core.NetworkParameters; 32 import co.rsk.bitcoinj.core.PartialMerkleTree; 33 import co.rsk.bitcoinj.core.Sha256Hash; 34 import co.rsk.bitcoinj.core.StoredBlock; 35 import co.rsk.bitcoinj.core.TransactionInput; 36 import co.rsk.bitcoinj.core.TransactionOutput; 37 import co.rsk.bitcoinj.core.UTXO; 38 import co.rsk.bitcoinj.core.UTXOProviderException; 39 import co.rsk.bitcoinj.core.VerificationException; 40 import co.rsk.bitcoinj.crypto.TransactionSignature; 41 import co.rsk.bitcoinj.script.FastBridgeRedeemScriptParser; 42 import co.rsk.bitcoinj.script.Script; 43 import co.rsk.bitcoinj.script.ScriptBuilder; 44 import co.rsk.bitcoinj.script.ScriptChunk; 45 import co.rsk.bitcoinj.store.BlockStoreException; 46 import co.rsk.bitcoinj.wallet.SendRequest; 47 import co.rsk.bitcoinj.wallet.Wallet; 48 import co.rsk.config.BridgeConstants; 49 import co.rsk.core.RskAddress; 50 import co.rsk.crypto.Keccak256; 51 import co.rsk.panic.PanicProcessor; 52 import co.rsk.peg.bitcoin.CoinbaseInformation; 53 import co.rsk.peg.bitcoin.MerkleBranch; 54 import co.rsk.peg.bitcoin.RskAllowUnconfirmedCoinSelector; 55 import co.rsk.peg.btcLockSender.BtcLockSender.TxSenderAddressType; 56 import co.rsk.peg.btcLockSender.BtcLockSenderProvider; 57 import co.rsk.peg.fastbridge.FastBridgeFederationInformation; 58 import co.rsk.peg.pegininstructions.PeginInstructionsException; 59 import co.rsk.peg.pegininstructions.PeginInstructionsProvider; 60 import co.rsk.peg.utils.*; 61 import co.rsk.peg.whitelist.LockWhitelist; 62 import co.rsk.peg.whitelist.LockWhitelistEntry; 63 import co.rsk.peg.whitelist.OneOffWhiteListEntry; 64 import co.rsk.peg.whitelist.UnlimitedWhiteListEntry; 65 import co.rsk.rpc.modules.trace.CallType; 66 import co.rsk.rpc.modules.trace.ProgramSubtrace; 67 import com.google.common.annotations.VisibleForTesting; 68 import java.io.IOException; 69 import java.io.InputStream; 70 import java.math.BigInteger; 71 import java.time.Instant; 72 import java.util.ArrayList; 73 import java.util.Arrays; 74 import java.util.Collections; 75 import java.util.List; 76 import java.util.Map; 77 import java.util.Objects; 78 import java.util.Optional; 79 import java.util.Set; 80 import java.util.stream.Collectors; 81 import javax.annotation.Nullable; 82 import org.apache.commons.lang3.tuple.Pair; 83 import org.bouncycastle.util.encoders.Hex; 84 import org.ethereum.config.blockchain.upgrades.ActivationConfig; 85 import org.ethereum.config.blockchain.upgrades.ConsensusRule; 86 import org.ethereum.core.Block; 87 import org.ethereum.core.Repository; 88 import org.ethereum.core.Transaction; 89 import org.ethereum.crypto.ECKey; 90 import org.ethereum.crypto.HashUtil; 91 import org.ethereum.util.ByteUtil; 92 import org.ethereum.vm.DataWord; 93 import org.ethereum.vm.PrecompiledContracts; 94 import org.ethereum.vm.exception.VMException; 95 import org.ethereum.vm.program.InternalTransaction; 96 import org.ethereum.vm.program.Program; 97 import org.ethereum.vm.program.ProgramResult; 98 import org.ethereum.vm.program.invoke.TransferInvoke; 99 import org.slf4j.Logger; 100 import org.slf4j.LoggerFactory; 101  102 import static co.rsk.peg.BridgeUtils.getRegularPegoutTxSize; 103 import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIP186; 104 import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIP219; 105  106 /** 107  * Helper class to move funds from btc to rsk and rsk to btc 108  * @author Oscar Guindzberg 109  */ 110 public class BridgeSupport { 111  public static final RskAddress BURN_ADDRESS = new RskAddress("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); 112  113  public static final int MAX_RELEASE_ITERATIONS = 30; 114  115  public static final Integer FEDERATION_CHANGE_GENERIC_ERROR_CODE = -10; 116  public static final Integer LOCK_WHITELIST_GENERIC_ERROR_CODE = -10; 117  public static final Integer LOCK_WHITELIST_INVALID_ADDRESS_FORMAT_ERROR_CODE = -2; 118  public static final Integer LOCK_WHITELIST_ALREADY_EXISTS_ERROR_CODE = -1; 119  public static final Integer LOCK_WHITELIST_UNKNOWN_ERROR_CODE = 0; 120  public static final Integer LOCK_WHITELIST_SUCCESS_CODE = 1; 121  public static final Integer FEE_PER_KB_GENERIC_ERROR_CODE = -10; 122  public static final Integer NEGATIVE_FEE_PER_KB_ERROR_CODE = -1; 123  public static final Integer EXCESSIVE_FEE_PER_KB_ERROR_CODE = -2; 124  125  public static final Integer BTC_TRANSACTION_CONFIRMATION_INEXISTENT_BLOCK_HASH_ERROR_CODE = -1; 126  public static final Integer BTC_TRANSACTION_CONFIRMATION_BLOCK_NOT_IN_BEST_CHAIN_ERROR_CODE = -2; 127  public static final Integer BTC_TRANSACTION_CONFIRMATION_INCONSISTENT_BLOCK_ERROR_CODE = -3; 128  public static final Integer BTC_TRANSACTION_CONFIRMATION_BLOCK_TOO_OLD_ERROR_CODE = -4; 129  public static final Integer BTC_TRANSACTION_CONFIRMATION_INVALID_MERKLE_BRANCH_ERROR_CODE = -5; 130  131  public static final long FAST_BRIDGE_REFUNDED_USER_ERROR_CODE = -100; 132  public static final long FAST_BRIDGE_REFUNDED_LP_ERROR_CODE = -200; 133  public static final long FAST_BRIDGE_UNPROCESSABLE_TX_NOT_CONTRACT_ERROR_CODE = -300; 134  public static final long FAST_BRIDGE_UNPROCESSABLE_TX_INVALID_SENDER_ERROR_CODE = -301; 135  public static final long FAST_BRIDGE_UNPROCESSABLE_TX_ALREADY_PROCESSED_ERROR_CODE = -302; 136  public static final long FAST_BRIDGE_UNPROCESSABLE_TX_VALIDATIONS_ERROR = -303; 137  public static final long FAST_BRIDGE_UNPROCESSABLE_TX_VALUE_ZERO_ERROR = -304; 138  public static final long FAST_BRIDGE_GENERIC_ERROR = -900; 139  140  public static final Integer RECEIVE_HEADER_CALLED_TOO_SOON = -1; 141  public static final Integer RECEIVE_HEADER_BLOCK_TOO_OLD = -2; 142  public static final Integer RECEIVE_HEADER_CANT_FOUND_PREVIOUS_BLOCK = -3; 143  public static final Integer RECEIVE_HEADER_BLOCK_PREVIOUSLY_SAVED = -4; 144  public static final Integer RECEIVE_HEADER_UNEXPECTED_EXCEPTION = -99; 145  146  // Enough depth to be able to search backwards one month worth of blocks 147  // (6 blocks/hour, 24 hours/day, 30 days/month) 148  public static final Integer BTC_TRANSACTION_CONFIRMATION_MAX_DEPTH = 4320; 149  150  private static final Logger logger = LoggerFactory.getLogger("BridgeSupport"); 151  private static final PanicProcessor panicProcessor = new PanicProcessor(); 152  153  private static final String INVALID_ADDRESS_FORMAT_MESSAGE = "invalid address format"; 154  155  private final List<String> FEDERATION_CHANGE_FUNCTIONS = Collections.unmodifiableList(Arrays.asList( 156  "create", 157  "add", 158  "add-multi", 159  "commit", 160  "rollback")); 161  162  private final BridgeConstants bridgeConstants; 163  private final BridgeStorageProvider provider; 164  private final Repository rskRepository; 165  private final BridgeEventLogger eventLogger; 166  private final List<ProgramSubtrace> subtraces = new ArrayList<>(); 167  private final BtcLockSenderProvider btcLockSenderProvider; 168  private final PeginInstructionsProvider peginInstructionsProvider; 169  170  private final FederationSupport federationSupport; 171  172  private final Context btcContext; 173  private final BtcBlockStoreWithCache.Factory btcBlockStoreFactory; 174  private BtcBlockStoreWithCache btcBlockStore; 175  private BtcBlockChain btcBlockChain; 176  private final org.ethereum.core.Block rskExecutionBlock; 177  private final ActivationConfig.ForBlock activations; 178  179  protected enum TxType { 180  PEGIN, 181  PEGOUT, 182  MIGRATION, 183  UNKNOWN 184  } 185  186  public BridgeSupport( 187  BridgeConstants bridgeConstants, 188  BridgeStorageProvider provider, 189  BridgeEventLogger eventLogger, 190  BtcLockSenderProvider btcLockSenderProvider, 191  PeginInstructionsProvider peginInstructionsProvider, 192  Repository repository, 193  Block executionBlock, 194  Context btcContext, 195  FederationSupport federationSupport, 196  BtcBlockStoreWithCache.Factory btcBlockStoreFactory, 197  ActivationConfig.ForBlock activations) { 198  this.rskRepository = repository; 199  this.provider = provider; 200  this.rskExecutionBlock = executionBlock; 201  this.bridgeConstants = bridgeConstants; 202  this.eventLogger = eventLogger; 203  this.btcLockSenderProvider = btcLockSenderProvider; 204  this.peginInstructionsProvider = peginInstructionsProvider; 205  this.btcContext = btcContext; 206  this.federationSupport = federationSupport; 207  this.btcBlockStoreFactory = btcBlockStoreFactory; 208  this.activations = activations; 209  } 210  211  public List<ProgramSubtrace> getSubtraces() { 212  return Collections.unmodifiableList(this.subtraces); 213  } 214  215  @VisibleForTesting 216  InputStream getCheckPoints() { 217  InputStream checkpoints = BridgeSupport.class.getResourceAsStream("/rskbitcoincheckpoints/" + bridgeConstants.getBtcParams().getId() + ".checkpoints"); 218  if (checkpoints == null) { 219  // If we don't have a custom checkpoints file, try to use bitcoinj's default checkpoints for that network 220  checkpoints = BridgeSupport.class.getResourceAsStream("/" + bridgeConstants.getBtcParams().getId() + ".checkpoints"); 221  } 222  return checkpoints; 223  } 224  225  @VisibleForTesting 226  ActivationConfig.ForBlock getActivations() { 227  return this.activations; 228  } 229  230  public void save() throws IOException { 231  provider.save(); 232  } 233  234  /** 235  * Receives an array of serialized Bitcoin block headers and adds them to the internal BlockChain structure. 236  * @param headers The bitcoin headers 237  */ 238  public void receiveHeaders(BtcBlock[] headers) throws IOException, BlockStoreException { 239  if (headers.length > 0) { 240  logger.debug("Received {} headers. First {}, last {}.", headers.length, headers[0].getHash(), headers[headers.length - 1].getHash()); 241  } else { 242  logger.warn("Received 0 headers"); 243  } 244  245  Context.propagate(btcContext); 246  this.ensureBtcBlockChain(); 247  for (int i = 0; i < headers.length; i++) { 248  try { 249  btcBlockChain.add(headers[i]); 250  } catch (Exception e) { 251  // If we tray to add an orphan header bitcoinj throws an exception 252  // This catches that case and any other exception that may be thrown 253  logger.warn("Exception adding btc header {}", headers[i].getHash(), e); 254  } 255  } 256  } 257  258  /** 259  * Receives only one header of serialized Bitcoin block headers and adds them to the internal BlockChain structure. 260  * @param header The bitcoin headers 261  */ 262  public Integer receiveHeader(BtcBlock header) throws IOException, BlockStoreException { 263  Context.propagate(btcContext); 264  this.ensureBtcBlockChain(); 265  266  if (btcBlockStore.get(header.getHash()) != null) { 267  return RECEIVE_HEADER_BLOCK_PREVIOUSLY_SAVED; 268  } 269  270  long diffTimeStamp = bridgeConstants.getMinSecondsBetweenCallsToReceiveHeader(); 271  272  long currentTimeStamp = rskExecutionBlock.getTimestamp(); //in seconds 273  Optional<Long> optionalLastTimeStamp = provider.getReceiveHeadersLastTimestamp(); 274  if (optionalLastTimeStamp.isPresent() && (currentTimeStamp - optionalLastTimeStamp.get() < diffTimeStamp)) { 275  logger.warn("Receive header last TimeStamp less than {} milliseconds", diffTimeStamp); 276  return RECEIVE_HEADER_CALLED_TOO_SOON; 277  } 278  279  //Depth 280  StoredBlock previousBlock = btcBlockStore.get(header.getPrevBlockHash()); 281  if (previousBlock == null) { 282  return RECEIVE_HEADER_CANT_FOUND_PREVIOUS_BLOCK; 283  } 284  285  // height of best chain - height of current header block greater than maximum depth accepted 286  if ((getBtcBlockchainBestChainHeight() - (previousBlock.getHeight() + 1)) > bridgeConstants.getMaxDepthBlockchainAccepted()) { 287  return RECEIVE_HEADER_BLOCK_TOO_OLD; 288  } 289  290  try { 291  btcBlockChain.add(header); 292  } catch (Exception e) { 293  // If we tray to add an orphan header bitcoinj throws an exception 294  // This catches that case and any other exception that may be thrown 295  logger.warn("Exception adding btc header {}", header.getHash(), e); 296  return RECEIVE_HEADER_UNEXPECTED_EXCEPTION; 297  } 298  provider.setReceiveHeadersLastTimestamp(currentTimeStamp); 299  return 0; 300  } 301  302  /** 303  * Get the wallet for the currently active federation 304  * @return A BTC wallet for the currently active federation 305  * 306  * @throws IOException 307  * @param shouldConsiderFastBridgeUTXOs 308  */ 309  public Wallet getActiveFederationWallet(boolean shouldConsiderFastBridgeUTXOs) throws IOException { 310  Federation federation = getActiveFederation(); 311  List<UTXO> utxos = getActiveFederationBtcUTXOs(); 312  313  return BridgeUtils.getFederationSpendWallet( 314  btcContext, 315  federation, 316  utxos, 317  shouldConsiderFastBridgeUTXOs, 318  provider 319  ); 320  } 321  322  /** 323  * Get the wallet for the currently retiring federation 324  * or null if there's currently no retiring federation 325  * @return A BTC wallet for the currently active federation 326  * 327  * @throws IOException 328  * @param shouldConsiderFastBridgeUTXOs 329  */ 330  public Wallet getRetiringFederationWallet(boolean shouldConsiderFastBridgeUTXOs) throws IOException { 331  Federation federation = getRetiringFederation(); 332  if (federation == null) { 333  return null; 334  } 335  336  List<UTXO> utxos = getRetiringFederationBtcUTXOs(); 337  338  return BridgeUtils.getFederationSpendWallet( 339  btcContext, 340  federation, 341  utxos, 342  shouldConsiderFastBridgeUTXOs, 343  provider 344  ); 345  } 346  347  /** 348  * Get the wallet for the currently live federations 349  * but limited to a specific list of UTXOs 350  * @return A BTC wallet for the currently live federation(s) 351  * limited to the given list of UTXOs 352  * 353  */ 354  public Wallet getUTXOBasedWalletForLiveFederations(List<UTXO> utxos, boolean isFastBridgeCompatible) { 355  return BridgeUtils.getFederationsSpendWallet(btcContext, getLiveFederations(), utxos, isFastBridgeCompatible, provider); 356  } 357  358  /** 359  * Get a no spend wallet for the currently live federations 360  * @return A no spend BTC wallet for the currently live federation(s) 361  * 362  */ 363  public Wallet getNoSpendWalletForLiveFederations(boolean isFastBridgeCompatible) { 364  return BridgeUtils.getFederationsNoSpendWallet(btcContext, getLiveFederations(), isFastBridgeCompatible, provider); 365  } 366  367  /** 368  * In case of a lock tx: Transfers some SBTCs to the sender of the btc tx and keeps track of the new UTXOs available for spending. 369  * In case of a release tx: Keeps track of the change UTXOs, now available for spending. 370  * @param rskTx The RSK transaction 371  * @param btcTxSerialized The raw BTC tx 372  * @param height The height of the BTC block that contains the tx 373  * @param pmtSerialized The raw partial Merkle tree 374  * @throws BlockStoreException 375  * @throws IOException 376  */ 377  public void registerBtcTransaction(Transaction rskTx, byte[] btcTxSerialized, int height, byte[] pmtSerialized) 378  throws IOException, BlockStoreException, BridgeIllegalArgumentException { 379  Context.propagate(btcContext); 380  Sha256Hash btcTxHash = BtcTransactionFormatUtils.calculateBtcTxHash(btcTxSerialized); 381  382  try { 383  // Check the tx was not already processed 384  if (isAlreadyBtcTxHashProcessed(btcTxHash)) { 385  throw new RegisterBtcTransactionException("Transaction already processed"); 386  } 387  388  // Validations for register 389  if (!validationsForRegisterBtcTransaction(btcTxHash, height, pmtSerialized, btcTxSerialized)) { 390  throw new RegisterBtcTransactionException("Could not validate transaction"); 391  } 392  393  BtcTransaction btcTx = new BtcTransaction(bridgeConstants.getBtcParams(), btcTxSerialized); 394  btcTx.verify(); 395  396  // Check again that the tx was not already processed but making sure to use the txid (no witness) 397  if (isAlreadyBtcTxHashProcessed(btcTx.getHash(false))) { 398  throw new RegisterBtcTransactionException("Transaction already processed"); 399  } 400  401  // Specific code for pegin/pegout/migration/none txs 402  switch (getTransactionType(btcTx)) { 403  case PEGIN: 404  processPegIn(btcTx, rskTx, height, btcTxHash); 405  break; 406  case PEGOUT: 407  processRelease(btcTx, btcTxHash); 408  break; 409  case MIGRATION: 410  processMigration(btcTx, btcTxHash); 411  break; 412  default: 413  logger.warn("[registerBtcTransaction] This is not a lock, a release nor a migration tx {}", btcTx); 414  panicProcessor.panic("btclock", "This is not a lock, a release nor a migration tx " + btcTx); 415  } 416  } catch (RegisterBtcTransactionException e) { 417  logger.warn("[registerBtcTransaction] Could not register transaction {}. Message: {}", btcTxHash, 418  e.getMessage()); 419  } 420  } 421  422  protected TxType getTransactionType(BtcTransaction btcTx) { 423  Script retiredFederationP2SHScript = provider.getLastRetiredFederationP2SHScript().orElse(null); 424  425  /************************************************************************/ 426  /** Special case to migrate funds from an old federation **/ 427  /************************************************************************/ 428  if (activations.isActive(ConsensusRule.RSKIP199) && txIsFromOldFederation(btcTx)) { 429  return TxType.MIGRATION; 430  } 431  432  if (BridgeUtils.isValidPegInTx( 433  btcTx, 434  getLiveFederations(), 435  retiredFederationP2SHScript, 436  btcContext, 437  bridgeConstants, 438  activations 439  )) { 440  return TxType.PEGIN; 441  } 442  443  if (BridgeUtils.isMigrationTx( 444  btcTx, 445  getActiveFederation(), 446  getRetiringFederation(), 447  retiredFederationP2SHScript, 448  btcContext, 449  bridgeConstants, 450  activations 451  )) { 452  return TxType.MIGRATION; 453  } 454  455  if (BridgeUtils.isPegOutTx(btcTx, getLiveFederations(), activations)) { 456  return TxType.PEGOUT; 457  } 458  459  return TxType.UNKNOWN; 460  } 461  462  private boolean txIsFromOldFederation(BtcTransaction btcTx) { 463  Address oldFederationAddress = Address.fromBase58(bridgeConstants.getBtcParams(), bridgeConstants.getOldFederationAddress()); 464  Script p2shScript = ScriptBuilder.createP2SHOutputScript(oldFederationAddress.getHash160()); 465  466  for (int i = 0; i < btcTx.getInputs().size(); i++) { 467  if (BridgeUtils.scriptCorrectlySpendsTx(btcTx, i, p2shScript)) { 468  return true; 469  } 470  } 471  472  return false; 473  } 474  475  476  protected void processPegIn( 477  BtcTransaction btcTx, 478  Transaction rskTx, 479  int height, 480  Sha256Hash btcTxHash) throws IOException, RegisterBtcTransactionException { 481  482  logger.debug("[processPegIn] This is a lock tx {}", btcTx); 483  484  Coin totalAmount = computeTotalAmountSent(btcTx); 485  486  PeginInformation peginInformation = new PeginInformation( 487  btcLockSenderProvider, 488  peginInstructionsProvider, 489  activations 490  ); 491  try { 492  peginInformation.parse(btcTx); 493  } catch (PeginInstructionsException e) { 494  if (activations.isActive(ConsensusRule.RSKIP170)) { 495  if (activations.isActive(ConsensusRule.RSKIP181)) { 496  eventLogger.logRejectedPegin(btcTx, RejectedPeginReason.PEGIN_V1_INVALID_PAYLOAD); 497  } 498  499  // If possible to get the sender address, refund 500  refundTxSender(btcTx, rskTx, peginInformation, totalAmount); 501  markTxAsProcessed(btcTx); 502  } 503  504  String message = String.format( 505  "Error while trying to parse peg-in information for tx %s. %s", 506  btcTx.getHash(), 507  e.getMessage() 508  ); 509  logger.warn("[processPegIn] {}", message); 510  throw new RegisterBtcTransactionException(message); 511  } 512  513  int protocolVersion = peginInformation.getProtocolVersion(); 514  logger.debug("[processPegIn] Protocol version: {}", protocolVersion); 515  switch (protocolVersion) { 516  case 0: 517  processPegInVersionLegacy(btcTx, rskTx, height, peginInformation, totalAmount); 518  break; 519  case 1: 520  processPegInVersion1(btcTx, rskTx, peginInformation, totalAmount); 521  break; 522  default: 523  markTxAsProcessed(btcTx); 524  525  String message = String.format("Invalid peg-in protocol version: %d", protocolVersion); 526  logger.warn("[processPegIn] {}", message); 527  throw new RegisterBtcTransactionException(message); 528  } 529  530  markTxAsProcessed(btcTx); 531  logger.info("[processPegIn] BTC Tx {} processed in RSK", btcTxHash); 532  } 533  534  private void processPegInVersionLegacy( 535  BtcTransaction btcTx, 536  Transaction rskTx, 537  int height, 538  PeginInformation peginInformation, 539  Coin totalAmount) throws IOException, RegisterBtcTransactionException { 540  541  Address senderBtcAddress = peginInformation.getSenderBtcAddress(); 542  TxSenderAddressType senderBtcAddressType = peginInformation.getSenderBtcAddressType(); 543  544  if (!BridgeUtils.txIsProcessableInLegacyVersion(senderBtcAddressType, activations)) { 545  logger.warn("[processPeginVersionLegacy] [btcTx:{}] Could not get BtcLockSender from Btc tx", btcTx.getHash()); 546  547  if (activations.isActive(ConsensusRule.RSKIP181)) { 548  eventLogger.logRejectedPegin(btcTx, RejectedPeginReason.LEGACY_PEGIN_UNDETERMINED_SENDER); 549  } 550  551  throw new RegisterBtcTransactionException("Could not get BtcLockSender from Btc tx"); 552  } 553  554  // Confirm we should process this lock 555  if (shouldProcessPegInVersionLegacy(senderBtcAddressType, btcTx, senderBtcAddress, totalAmount, height)) { 556  executePegIn(btcTx, peginInformation, totalAmount); 557  } else { 558  if (activations.isActive(ConsensusRule.RSKIP181)) { 559  if (!isTxLockableForLegacyVersion(senderBtcAddressType, btcTx, senderBtcAddress)) { 560  eventLogger.logRejectedPegin(btcTx, RejectedPeginReason.LEGACY_PEGIN_MULTISIG_SENDER); 561  } else if (!verifyLockDoesNotSurpassLockingCap(btcTx, totalAmount)) { 562  eventLogger.logRejectedPegin(btcTx, RejectedPeginReason.PEGIN_CAP_SURPASSED); 563  } 564  } 565  566  generateRejectionRelease(btcTx, senderBtcAddress, rskTx, totalAmount); 567  } 568  } 569  570  private void processPegInVersion1( 571  BtcTransaction btcTx, 572  Transaction rskTx, 573  PeginInformation peginInformation, 574  Coin totalAmount) throws RegisterBtcTransactionException, IOException { 575  576  if (!activations.isActive(ConsensusRule.RSKIP170)) { 577  throw new RegisterBtcTransactionException("Can't process version 1 peg-ins before RSKIP 170 activation"); 578  } 579  580  // Confirm we should process this lock 581  if (verifyLockDoesNotSurpassLockingCap(btcTx, totalAmount)) { 582  executePegIn(btcTx, peginInformation, totalAmount); 583  } else { 584  logger.debug("[processPegInVersion1] Peg-in attempt surpasses locking cap. Amount attempted to lock: {}", totalAmount); 585  586  if (activations.isActive(ConsensusRule.RSKIP181)) { 587  eventLogger.logRejectedPegin(btcTx, RejectedPeginReason.PEGIN_CAP_SURPASSED); 588  } 589  590  refundTxSender(btcTx, rskTx, peginInformation, totalAmount); 591  } 592  } 593  594  private void executePegIn(BtcTransaction btcTx, PeginInformation peginInformation, Coin amount) throws IOException { 595  RskAddress rskDestinationAddress = peginInformation.getRskDestinationAddress(); 596  Address senderBtcAddress = peginInformation.getSenderBtcAddress(); 597  TxSenderAddressType senderBtcAddressType = peginInformation.getSenderBtcAddressType(); 598  int protocolVersion = peginInformation.getProtocolVersion(); 599  co.rsk.core.Coin amountInWeis = co.rsk.core.Coin.fromBitcoin(amount); 600  601  logger.debug("[executePegIn] [btcTx:{}] Is a lock from a {} sender", btcTx.getHash(), senderBtcAddressType); 602  this.transferTo(peginInformation.getRskDestinationAddress(), amountInWeis); 603  logger.info( 604  "[executePegIn] Transferring from BTC Address {}. RSK Address: {}. Amount: {}", 605  senderBtcAddress, 606  rskDestinationAddress, 607  amountInWeis 608  ); 609  610  if (activations.isActive(ConsensusRule.RSKIP146)) { 611  if (activations.isActive(ConsensusRule.RSKIP170)) { 612  eventLogger.logPeginBtc(rskDestinationAddress, btcTx, amount, protocolVersion); 613  } else { 614  eventLogger.logLockBtc(rskDestinationAddress, btcTx, senderBtcAddress, amount); 615  } 616  } 617  618  // Save UTXOs from the federation(s) only if we actually locked the funds 619  saveNewUTXOs(btcTx); 620  } 621  622  private void refundTxSender( 623  BtcTransaction btcTx, 624  Transaction rskTx, 625  PeginInformation peginInformation, 626  Coin amount) throws IOException { 627  628  Address btcRefundAddress = peginInformation.getBtcRefundAddress(); 629  if (btcRefundAddress != null) { 630  generateRejectionRelease(btcTx, btcRefundAddress, rskTx, amount); 631  } else { 632  logger.debug("[refundTxSender] No btc refund address provided, couldn't get sender address either. Can't refund"); 633  634  if (activations.isActive(ConsensusRule.RSKIP181)) { 635  if (peginInformation.getProtocolVersion() == 1) { 636  eventLogger.logUnrefundablePegin(btcTx, UnrefundablePeginReason.PEGIN_V1_REFUND_ADDRESS_NOT_SET); 637  } else { 638  eventLogger.logUnrefundablePegin(btcTx, UnrefundablePeginReason.LEGACY_PEGIN_UNDETERMINED_SENDER); 639  } 640  } 641  } 642  } 643  644  private void markTxAsProcessed(BtcTransaction btcTx) throws IOException { 645  // Mark tx as processed on this block (and use the txid without the witness) 646  provider.setHeightBtcTxhashAlreadyProcessed(btcTx.getHash(false), rskExecutionBlock.getNumber()); 647  } 648  649  protected void processRelease(BtcTransaction btcTx, Sha256Hash btcTxHash) throws IOException { 650  logger.debug("[processRelease] This is a release tx {}", btcTx); 651  // do-nothing 652  // We could call removeUsedUTXOs(btcTx) here, but we decided to not do that. 653  // Used utxos should had been removed when we created the release tx. 654  // Invoking removeUsedUTXOs() here would make "some" sense in theses scenarios: 655  // a) In testnet, devnet or local: we restart the RSK blockchain without changing the federation address. We don't want to have utxos that were already spent. 656  // Open problem: TxA spends TxB. registerBtcTransaction() for TxB is called, it spends a utxo the bridge is not yet aware of, 657  // so nothing is removed. Then registerBtcTransaction() for TxA and the "already spent" utxo is added as it was not spent. 658  // When is not guaranteed to be called in the chronological order, so a Federator can inform 659  // b) In prod: Federator created a tx manually or the federation was compromised and some utxos were spent. Better not try to spend them. 660  // Open problem: For performance removeUsedUTXOs() just removes 1 utxo 661  662  markTxAsProcessed(btcTx); 663  664  // Generate new change UTXO 665  saveNewUTXOs(btcTx); 666  logger.info("[processRelease] BTC Tx {} processed in RSK", btcTxHash); 667  } 668  669  protected void processMigration(BtcTransaction btcTx, Sha256Hash btcTxHash) throws IOException { 670  logger.debug("[processMigration] This is a migration tx {}", btcTx); 671  672  markTxAsProcessed(btcTx); 673  674  // Input spent on retiring federation and a new UTXO that is created on active federation. 675  // It is probably merging multiple UTXOs from the retiring federation 676  saveNewUTXOs(btcTx); 677  logger.info("[processMigration] BTC Tx {} processed in RSK", btcTxHash); 678  } 679  680  private boolean shouldProcessPegInVersionLegacy(TxSenderAddressType txSenderAddressType, BtcTransaction btcTx, 681  Address senderBtcAddress, Coin totalAmount, int height) { 682  return isTxLockableForLegacyVersion(txSenderAddressType, btcTx, senderBtcAddress) && 683  verifyLockSenderIsWhitelisted(senderBtcAddress, totalAmount, height) && 684  verifyLockDoesNotSurpassLockingCap(btcTx, totalAmount); 685  } 686  687  protected boolean isTxLockableForLegacyVersion(TxSenderAddressType txSenderAddressType, BtcTransaction btcTx, Address senderBtcAddress) { 688  689  if (txSenderAddressType == TxSenderAddressType.P2PKH || 690  (txSenderAddressType == TxSenderAddressType.P2SHP2WPKH && activations.isActive(ConsensusRule.RSKIP143))) { 691  return true; 692  } else { 693  logger.warn( 694  "[isTxLockableForLegacyVersion]: [btcTx:{}] Btc tx type not supported: {}, returning funds to sender: {}", 695  btcTx.getHash(), 696  txSenderAddressType, 697  senderBtcAddress 698  ); 699  return false; 700  } 701  } 702  703  /** 704  * Internal method to transfer RSK to an RSK account 705  * It also produce the appropiate internal transaction subtrace if needed 706  * 707  * @param receiver address that receives the amount 708  * @param amount amount to transfer 709  */ 710  private void transferTo(RskAddress receiver, co.rsk.core.Coin amount) { 711  rskRepository.transfer( 712  PrecompiledContracts.BRIDGE_ADDR, 713  receiver, 714  amount 715  ); 716  717  DataWord from = DataWord.valueOf(PrecompiledContracts.BRIDGE_ADDR.getBytes()); 718  DataWord to = DataWord.valueOf(receiver.getBytes()); 719  long gas = 0L; 720  DataWord value = DataWord.valueOf(amount.getBytes()); 721  722  TransferInvoke invoke = new TransferInvoke(from, to, gas, value); 723  ProgramResult result = ProgramResult.empty(); 724  ProgramSubtrace subtrace = ProgramSubtrace.newCallSubtrace(CallType.CALL, invoke, result, null, Collections.emptyList()); 725  726  logger.info("Transferred {} weis to {}", amount, receiver); 727  728  this.subtraces.add(subtrace); 729  } 730  731  /* 732  Add the btcTx outputs that send btc to the federation(s) to the UTXO list 733  */ 734  private void saveNewUTXOs(BtcTransaction btcTx) throws IOException { 735  // Outputs to the active federation 736  List<TransactionOutput> outputsToTheActiveFederation = btcTx.getWalletOutputs(getActiveFederationWallet( 737  false)); 738  for (TransactionOutput output : outputsToTheActiveFederation) { 739  UTXO utxo = new UTXO(btcTx.getHash(), output.getIndex(), output.getValue(), 0, btcTx.isCoinBase(), output.getScriptPubKey()); 740  getActiveFederationBtcUTXOs().add(utxo); 741  } 742  743  // Outputs to the retiring federation (if any) 744  Wallet retiringFederationWallet = getRetiringFederationWallet(false); 745  if (retiringFederationWallet != null) { 746  List<TransactionOutput> outputsToTheRetiringFederation = btcTx.getWalletOutputs(retiringFederationWallet); 747  for (TransactionOutput output : outputsToTheRetiringFederation) { 748  UTXO utxo = new UTXO(btcTx.getHash(), output.getIndex(), output.getValue(), 0, btcTx.isCoinBase(), output.getScriptPubKey()); 749  getRetiringFederationBtcUTXOs().add(utxo); 750  } 751  } 752  } 753  754  /** 755  * Initiates the process of sending coins back to BTC. 756  * This is the default contract method. 757  * The funds will be sent to the bitcoin address controlled by the private key that signed the rsk tx. 758  * The amount sent to the bridge in this tx will be the amount sent in the btc network minus fees. 759  * @param rskTx The rsk tx being executed. 760  * @throws IOException 761  */ 762  public void releaseBtc(Transaction rskTx) throws IOException { 763  Coin value = rskTx.getValue().toBitcoin(); 764  final RskAddress senderAddress = rskTx.getSender(); 765  //as we can't send btc from contracts we want to send them back to the senderAddressStr 766  if (BridgeUtils.isContractTx(rskTx)) { 767  logger.trace("Contract {} tried to release funds. Release is just allowed from standard accounts.", rskTx); 768  if (activations.isActive(ConsensusRule.RSKIP185)) { 769  emitRejectEvent(value, senderAddress.toHexString(), RejectedPegoutReason.CALLER_CONTRACT); 770  return; 771  } else { 772  throw new Program.OutOfGasException("Contract calling releaseBTC"); 773  } 774  } 775  776  Context.propagate(btcContext); 777  NetworkParameters btcParams = bridgeConstants.getBtcParams(); 778  Address btcDestinationAddress = BridgeUtils.recoverBtcAddressFromEthTransaction(rskTx, btcParams); 779  780  requestRelease(btcDestinationAddress, value, rskTx); 781  } 782  783  private void refundAndEmitRejectEvent(Coin value, RskAddress senderAddress, RejectedPegoutReason reason) { 784  String senderAddressStr = senderAddress.toHexString(); 785  logger.trace("Executing a refund of {} to {}. Reason: {}", value, senderAddressStr, reason); 786  rskRepository.transfer( 787  PrecompiledContracts.BRIDGE_ADDR, 788  senderAddress, 789  co.rsk.core.Coin.fromBitcoin(value) 790  ); 791  emitRejectEvent(value, senderAddressStr, reason); 792  } 793  794  private void emitRejectEvent(Coin value, String senderAddressStr, RejectedPegoutReason reason) { 795  eventLogger.logReleaseBtcRequestRejected(senderAddressStr, value, reason); 796  } 797  798  /** 799  * Creates a request for BTC release and 800  * adds it to the request queue for it 801  * to be processed later. 802  * 803  * @param destinationAddress the destination BTC address. 804  * @param value the amount of BTC to release. 805  * @throws IOException 806  */ 807  private void requestRelease(Address destinationAddress, Coin value, Transaction rskTx) throws IOException { 808  Optional<RejectedPegoutReason> optionalRejectedPegoutReason = Optional.empty(); 809  if (activations.isActive(RSKIP219)) { 810  int pegoutSize = getRegularPegoutTxSize(getActiveFederation()); 811  Coin feePerKB = getFeePerKb(); 812  // The pegout transaction has a cost related to its size and the current feePerKB 813  // The actual cost cannot be asserted exactly so the calculation is approximated 814  // On top of this, the remainder after the fee should be enough for the user to be able to operate 815  // For this, the calculation includes an additional percentage to assert for this 816  Coin requireFundsForFee = feePerKB 817  .multiply(pegoutSize) // times the size in bytes 818  .divide(1000); // Get the s/b 819  requireFundsForFee = requireFundsForFee 820  .add(requireFundsForFee 821  .times(bridgeConstants.getMinimumPegoutValuePercentageToReceiveAfterFee()) 822  .divide(100) 823  ); // add the gap 824  825  // The pegout value should be greater or equals than the max of these two values 826  Coin minValue = Coin.valueOf(Math.max(bridgeConstants.getMinimumPegoutTxValueInSatoshis().value, requireFundsForFee.value)); 827  828  // Since Iris the peg-out the rule is that the minimum is inclusive 829  if (value.isLessThan(minValue)) { 830  optionalRejectedPegoutReason = Optional.of( 831  Objects.equals(minValue, requireFundsForFee) ? 832  RejectedPegoutReason.FEE_ABOVE_VALUE: 833  RejectedPegoutReason.LOW_AMOUNT 834  ); 835  } 836  } else { 837  // For legacy peg-outs the rule stated that the minimum was exclusive 838  if (!value.isGreaterThan(bridgeConstants.getLegacyMinimumPegoutTxValueInSatoshis())) { 839  optionalRejectedPegoutReason = Optional.of(RejectedPegoutReason.LOW_AMOUNT); 840  } 841  } 842  843  if (optionalRejectedPegoutReason.isPresent()) { 844  logger.warn( 845  "releaseBtc ignored. To {}. Tx {}. Value {}. Reason: {}", 846  destinationAddress, 847  rskTx, 848  value, 849  optionalRejectedPegoutReason.get() 850  ); 851  if (activations.isActive(ConsensusRule.RSKIP185)) { 852  refundAndEmitRejectEvent( 853  value, 854  rskTx.getSender(), 855  optionalRejectedPegoutReason.get() 856  ); 857  } 858  } else { 859  if (activations.isActive(ConsensusRule.RSKIP146)) { 860  provider.getReleaseRequestQueue().add(destinationAddress, value, rskTx.getHash()); 861  } else { 862  provider.getReleaseRequestQueue().add(destinationAddress, value); 863  } 864  865  if (activations.isActive(ConsensusRule.RSKIP185)) { 866  eventLogger.logReleaseBtcRequestReceived(rskTx.getSender().toHexString(), destinationAddress.getHash160(), value); 867  } 868  logger.info("releaseBtc succesful to {}. Tx {}. Value {}.", destinationAddress, rskTx, value); 869  } 870  } 871  872  /** 873  * @return Current fee per kb in BTC. 874  */ 875  public Coin getFeePerKb() { 876  Coin currentFeePerKb = provider.getFeePerKb(); 877  878  if (currentFeePerKb == null) { 879  currentFeePerKb = bridgeConstants.getGenesisFeePerKb(); 880  } 881  882  return currentFeePerKb; 883  } 884  885  /** 886  * Executed every now and then. 887  * Performs a few tasks: processing of any pending btc funds 888  * migrations from retiring federations; 889  * processing of any outstanding btc release requests; and 890  * processing of any outstanding release btc transactions. 891  * @throws IOException 892  * @param rskTx current RSK transaction 893  */ 894  public void updateCollections(Transaction rskTx) throws IOException { 895  Context.propagate(btcContext); 896  897  eventLogger.logUpdateCollections(rskTx); 898  899  processFundsMigration(rskTx); 900  901  processReleaseRequests(); 902  903  processReleaseTransactions(rskTx); 904  905  updateFederationCreationBlockHeights(); 906  } 907  908  private boolean federationIsInMigrationAge(Federation federation) { 909  long federationAge = rskExecutionBlock.getNumber() - federation.getCreationBlockNumber(); 910  long ageBegin = bridgeConstants.getFederationActivationAge() + bridgeConstants.getFundsMigrationAgeSinceActivationBegin(); 911  long ageEnd = bridgeConstants.getFederationActivationAge() + bridgeConstants.getFundsMigrationAgeSinceActivationEnd(); 912  913  return federationAge > ageBegin && federationAge < ageEnd; 914  } 915  916  private boolean federationIsPastMigrationAge(Federation federation) { 917  long federationAge = rskExecutionBlock.getNumber() - federation.getCreationBlockNumber(); 918  long ageEnd = bridgeConstants.getFederationActivationAge() + bridgeConstants.getFundsMigrationAgeSinceActivationEnd(); 919  920  return federationAge >= ageEnd; 921  } 922  923  private boolean hasMinimumFundsToMigrate(@Nullable Wallet retiringFederationWallet) { 924  // This value is set according to the average 500 bytes transaction size 925  Coin minimumFundsToMigrate = getFeePerKb().divide(2); 926  return retiringFederationWallet != null 927  && retiringFederationWallet.getBalance().isGreaterThan(minimumFundsToMigrate); 928  } 929  930  private void processFundsMigration(Transaction rskTx) throws IOException { 931  Wallet retiringFederationWallet = getRetiringFederationWallet(true); 932  List<UTXO> availableUTXOs = getRetiringFederationBtcUTXOs(); 933  ReleaseTransactionSet releaseTransactionSet = provider.getReleaseTransactionSet(); 934  Federation activeFederation = getActiveFederation(); 935  936  if (federationIsInMigrationAge(activeFederation) 937  && hasMinimumFundsToMigrate(retiringFederationWallet)) { 938  logger.info("Active federation (age={}) is in migration age and retiring federation has funds to migrate: {}.", 939  rskExecutionBlock.getNumber() - activeFederation.getCreationBlockNumber(), 940  retiringFederationWallet.getBalance().toFriendlyString()); 941  942  Pair<BtcTransaction, List<UTXO>> createResult = createMigrationTransaction(retiringFederationWallet, activeFederation.getAddress()); 943  BtcTransaction btcTx = createResult.getLeft(); 944  List<UTXO> selectedUTXOs = createResult.getRight(); 945  946  // Add the TX to the release set 947  if (activations.isActive(ConsensusRule.RSKIP146)) { 948  Coin amountMigrated = selectedUTXOs.stream().map(UTXO::getValue) 949  .reduce(Coin.ZERO, Coin::add); 950  releaseTransactionSet.add(btcTx, rskExecutionBlock.getNumber(), rskTx.getHash()); 951  // Log the Release request 952  eventLogger.logReleaseBtcRequested(rskTx.getHash().getBytes(), btcTx, amountMigrated); 953  } else { 954  releaseTransactionSet.add(btcTx, rskExecutionBlock.getNumber()); 955  } 956  957  // Mark UTXOs as spent 958  availableUTXOs.removeIf(utxo -> selectedUTXOs.stream().anyMatch(selectedUtxo -> 959  utxo.getHash().equals(selectedUtxo.getHash()) && 960  utxo.getIndex() == selectedUtxo.getIndex() 961  )); 962  } 963  964  if (retiringFederationWallet != null && federationIsPastMigrationAge(activeFederation)) { 965  if (retiringFederationWallet.getBalance().isGreaterThan(Coin.ZERO)) { 966  logger.info("Federation is past migration age and will try to migrate remaining balance: {}.", 967  retiringFederationWallet.getBalance().toFriendlyString()); 968  969  try { 970  Pair<BtcTransaction, List<UTXO>> createResult = createMigrationTransaction(retiringFederationWallet, activeFederation.getAddress()); 971  BtcTransaction btcTx = createResult.getLeft(); 972  List<UTXO> selectedUTXOs = createResult.getRight(); 973  974  // Add the TX to the release set 975  if (activations.isActive(ConsensusRule.RSKIP146)) { 976  Coin amountMigrated = selectedUTXOs.stream().map(UTXO::getValue) 977  .reduce(Coin.ZERO, Coin::add); 978  releaseTransactionSet.add(btcTx, rskExecutionBlock.getNumber(), rskTx.getHash()); 979  // Log the Release request 980  eventLogger.logReleaseBtcRequested(rskTx.getHash().getBytes(), btcTx, amountMigrated); 981  } else { 982  releaseTransactionSet.add(btcTx, rskExecutionBlock.getNumber()); 983  } 984  985  // Mark UTXOs as spent 986  availableUTXOs.removeIf(utxo -> selectedUTXOs.stream().anyMatch(selectedUtxo -> 987  utxo.getHash().equals(selectedUtxo.getHash()) && 988  utxo.getIndex() == selectedUtxo.getIndex() 989  )); 990  } catch (Exception e) { 991  logger.error("Unable to complete retiring federation migration. Balance left: {} in {}", 992  retiringFederationWallet.getBalance().toFriendlyString(), 993  getRetiringFederationAddress()); 994  panicProcessor.panic("updateCollection", "Unable to complete retiring federation migration."); 995  } 996  } 997  998  logger.info("Retiring federation migration finished. Available UTXOs left: {}.", availableUTXOs.size()); 999  provider.setOldFederation(null); 1000  } 1001  } 1002  1003  /** 1004  * Processes the current btc release request queue 1005  * and tries to build btc transactions using (and marking as spent) 1006  * the current active federation's utxos. 1007  * Newly created btc transactions are added to the btc release tx set, 1008  * and failed attempts are kept in the release queue for future 1009  * processing. 1010  * 1011  */ 1012  private void processReleaseRequests() { 1013  final Wallet activeFederationWallet; 1014  final ReleaseRequestQueue releaseRequestQueue; 1015  1016  try { 1017  activeFederationWallet = getActiveFederationWallet(true); 1018  releaseRequestQueue = provider.getReleaseRequestQueue(); 1019  } catch (IOException e) { 1020  logger.error("Unexpected error accessing storage while attempting to process release requests", e); 1021  return; 1022  } 1023  1024  // Releases are attempted using the currently active federation 1025  // wallet. 1026  final ReleaseTransactionBuilder txBuilder = new ReleaseTransactionBuilder( 1027  btcContext.getParams(), 1028  activeFederationWallet, 1029  getFederationAddress(), 1030  getFeePerKb(), 1031  activations 1032  ); 1033  1034  releaseRequestQueue.process(MAX_RELEASE_ITERATIONS, (ReleaseRequestQueue.Entry releaseRequest) -> { 1035  Optional<ReleaseTransactionBuilder.BuildResult> result = txBuilder.buildAmountTo( 1036  releaseRequest.getDestination(), 1037  releaseRequest.getAmount() 1038  ); 1039  1040  // Couldn't build a transaction to release these funds 1041  // Log the event and return false so that the request remains in the 1042  // queue for future processing. 1043  // Further logging is done at the tx builder level. 1044  if (!result.isPresent()) { 1045  logger.warn( 1046  "Couldn't build a release BTC tx for <{}, {}>", 1047  releaseRequest.getDestination().toBase58(), 1048  releaseRequest.getAmount()); 1049  return false; 1050  } 1051  1052  // We have a BTC transaction, mark the UTXOs as spent and add the tx 1053  // to the release set. 1054  1055  List<UTXO> selectedUTXOs = result.get().getSelectedUTXOs(); 1056  BtcTransaction generatedTransaction = result.get().getBtcTx(); 1057  List<UTXO> availableUTXOs; 1058  ReleaseTransactionSet releaseTransactionSet; 1059  1060  // Attempt access to storage first 1061  // (any of these could fail and would invalidate both 1062  // the tx build and utxo selection, so treat as atomic) 1063  try { 1064  availableUTXOs = getActiveFederationBtcUTXOs(); 1065  releaseTransactionSet = provider.getReleaseTransactionSet(); 1066  } catch (IOException exception) { 1067  // Unexpected error accessing storage, log and fail 1068  logger.error( 1069  String.format( 1070  "Unexpected error accessing storage while attempting to add a release BTC tx for <%s, %s>", 1071  releaseRequest.getDestination().toString(), 1072  releaseRequest.getAmount().toString() 1073  ), 1074  exception 1075  ); 1076  return false; 1077  } 1078  1079  if (activations.isActive(ConsensusRule.RSKIP146)) { 1080  Keccak256 rskTxHash = releaseRequest.getRskTxHash(); 1081  // Add the TX 1082  releaseTransactionSet.add(generatedTransaction, rskExecutionBlock.getNumber(), rskTxHash); 1083  // For a short time period, there could be items in the release request queue that don't have the rskTxHash 1084  // (these are releases created right before the consensus rule activation, that weren't processed before its activation) 1085  // We shouldn't generate the event for those releases 1086  if (rskTxHash != null) { 1087  // Log the Release request 1088  eventLogger.logReleaseBtcRequested(rskTxHash.getBytes(), generatedTransaction, releaseRequest.getAmount()); 1089  } 1090  } else { 1091  releaseTransactionSet.add(generatedTransaction, rskExecutionBlock.getNumber()); 1092  } 1093  1094  // Mark UTXOs as spent 1095  availableUTXOs.removeAll(selectedUTXOs); 1096  1097  // TODO: (Ariel Mendelzon, 07/12/2017) 1098  // TODO: Balance adjustment assumes that change output is output with index 1. 1099  // TODO: This will change if we implement multiple releases per BTC tx, so 1100  // TODO: it would eventually need to be fixed. 1101  // Adjust balances in edge cases 1102  adjustBalancesIfChangeOutputWasDust(generatedTransaction, releaseRequest.getAmount()); 1103  1104  return true; 1105  }); 1106  } 1107  1108  /** 1109  * Processes the current btc release transaction set. 1110  * It basically looks for transactions with enough confirmations 1111  * and marks them as ready for signing as well as removes them 1112  * from the set. 1113  * @param rskTx the RSK transaction that is causing this processing. 1114  */ 1115  private void processReleaseTransactions(Transaction rskTx) { 1116  final Map<Keccak256, BtcTransaction> txsWaitingForSignatures; 1117  final ReleaseTransactionSet releaseTransactionSet; 1118  1119  try { 1120  txsWaitingForSignatures = provider.getRskTxsWaitingForSignatures(); 1121  releaseTransactionSet = provider.getReleaseTransactionSet(); 1122  } catch (IOException e) { 1123  logger.error("Unexpected error accessing storage while attempting to process release btc transactions", e); 1124  return; 1125  } 1126  1127  // TODO: (Ariel Mendelzon - 07/12/2017) 1128  // TODO: at the moment, there can only be one btc transaction 1129  // TODO: per rsk transaction in the txsWaitingForSignatures 1130  // TODO: map, and the rest of the processing logic is 1131  // TODO: dependant upon this. That is the reason we 1132  // TODO: add only one btc transaction at a time 1133  // TODO: (at least at this stage). 1134  1135  // IMPORTANT: sliceWithEnoughConfirmations also modifies the transaction set in place 1136  Set<ReleaseTransactionSet.Entry> txsWithEnoughConfirmations = releaseTransactionSet.sliceWithConfirmations( 1137  rskExecutionBlock.getNumber(), 1138  bridgeConstants.getRsk2BtcMinimumAcceptableConfirmations(), 1139  Optional.of(1) 1140  ); 1141  if (txsWithEnoughConfirmations.size() > 0) { 1142  ReleaseTransactionSet.Entry entry = txsWithEnoughConfirmations.iterator().next(); 1143  // Since RSKIP176 we are moving back to using the updateCollections related txHash as the set key 1144  if (activations.isActive(ConsensusRule.RSKIP146) && !activations.isActive(ConsensusRule.RSKIP176)) { 1145  // The release transaction may have been created prior to the Consensus Rule activation 1146  // therefore it won't have a rskTxHash value, fallback to this transaction's hash 1147  txsWaitingForSignatures.put(entry.getRskTxHash() == null ? rskTx.getHash() : entry.getRskTxHash(), entry.getTransaction()); 1148  } 1149  else { 1150  txsWaitingForSignatures.put(rskTx.getHash(), entry.getTransaction()); 1151  } 1152  } 1153  } 1154  1155  private void updateFederationCreationBlockHeights() { 1156  if (!activations.isActive(RSKIP186)) { 1157  return; 1158  } 1159  1160  Optional<Long> nextFederationCreationBlockHeightOpt = provider.getNextFederationCreationBlockHeight(); 1161  if (nextFederationCreationBlockHeightOpt.isPresent()) { 1162  long nextFederationCreationBlockHeight = nextFederationCreationBlockHeightOpt.get(); 1163  long curBlockHeight = rskExecutionBlock.getNumber(); 1164  1165  if (curBlockHeight >= nextFederationCreationBlockHeight + bridgeConstants.getFederationActivationAge()) { 1166  provider.setActiveFederationCreationBlockHeight(nextFederationCreationBlockHeight); 1167  provider.clearNextFederationCreationBlockHeight(); 1168  } 1169  } 1170  } 1171  1172  /** 1173  * If federation change output value had to be increased to be non-dust, the federation now has 1174  * more BTC than it should. So, we burn some sBTC to make balances match. 1175  * 1176  * @param btcTx The btc tx that was just completed 1177  * @param sentByUser The number of sBTC originaly sent by the user 1178  */ 1179  private void adjustBalancesIfChangeOutputWasDust(BtcTransaction btcTx, Coin sentByUser) { 1180  if (btcTx.getOutputs().size() <= 1) { 1181  // If there is no change, do-nothing 1182  return; 1183  } 1184  Coin sumInputs = Coin.ZERO; 1185  for (TransactionInput transactionInput : btcTx.getInputs()) { 1186  sumInputs = sumInputs.add(transactionInput.getValue()); 1187  } 1188  Coin change = btcTx.getOutput(1).getValue(); 1189  Coin spentByFederation = sumInputs.subtract(change); 1190  if (spentByFederation.isLessThan(sentByUser)) { 1191  Coin coinsToBurn = sentByUser.subtract(spentByFederation); 1192  this.transferTo(BURN_ADDRESS, co.rsk.core.Coin.fromBitcoin(coinsToBurn)); 1193  } 1194  } 1195  1196  /** 1197  * Adds a federator signature to a btc release tx. 1198  * The hash for the signature must be calculated with Transaction.SigHash.ALL and anyoneCanPay=false. The signature must be canonical. 1199  * If enough signatures were added, ask federators to broadcast the btc release tx. 1200  * 1201  * @param federatorPublicKey Federator who is signing 1202  * @param signatures 1 signature per btc tx input 1203  * @param rskTxHash The id of the rsk tx 1204  */ 1205  public void addSignature(BtcECKey federatorPublicKey, List<byte[]> signatures, byte[] rskTxHash) throws Exception { 1206  Context.propagate(btcContext); 1207  1208  Federation retiringFederation = getRetiringFederation(); 1209  Federation activeFederation = getActiveFederation(); 1210  Federation federation = 1211  activeFederation.hasBtcPublicKey(federatorPublicKey) ? 1212  activeFederation : 1213  (retiringFederation != null && retiringFederation.hasBtcPublicKey(federatorPublicKey) ? 1214  retiringFederation: 1215  null); 1216  1217  if (federation == null) { 1218  logger.warn("Supplied federator public key {} does not belong to any of the federators.", federatorPublicKey); 1219  return; 1220  } 1221  1222  BtcTransaction btcTx = provider.getRskTxsWaitingForSignatures().get(new Keccak256(rskTxHash)); 1223  if (btcTx == null) { 1224  logger.warn("No tx waiting for signature for hash {}. Probably fully signed already.", new Keccak256(rskTxHash)); 1225  return; 1226  } 1227  if (btcTx.getInputs().size() != signatures.size()) { 1228  logger.warn("Expected {} signatures but received {}.", btcTx.getInputs().size(), signatures.size()); 1229  return; 1230  } 1231  eventLogger.logAddSignature(federatorPublicKey, btcTx, rskTxHash); 1232  processSigning(federatorPublicKey, signatures, rskTxHash, btcTx, federation); 1233  } 1234  1235  private void processSigning(BtcECKey federatorPublicKey, List<byte[]> signatures, byte[] rskTxHash, BtcTransaction btcTx, Federation federation) throws IOException { 1236  // Build input hashes for signatures 1237  int numInputs = btcTx.getInputs().size(); 1238  1239  List<Sha256Hash> sighashes = new ArrayList<>(); 1240  List<TransactionSignature> txSigs = new ArrayList<>(); 1241  for (int i = 0; i < numInputs; i++) { 1242  TransactionInput txIn = btcTx.getInput(i); 1243  Script inputScript = txIn.getScriptSig(); 1244  List<ScriptChunk> chunks = inputScript.getChunks(); 1245  byte[] program = chunks.get(chunks.size() - 1).data; 1246  Script redeemScript = new Script(program); 1247  sighashes.add(btcTx.hashForSignature(i, redeemScript, BtcTransaction.SigHash.ALL, false)); 1248  } 1249  1250  // Verify given signatures are correct before proceeding 1251  for (int i = 0; i < numInputs; i++) { 1252  BtcECKey.ECDSASignature sig; 1253  try { 1254  sig = BtcECKey.ECDSASignature.decodeFromDER(signatures.get(i)); 1255  } catch (RuntimeException e) { 1256  logger.warn("Malformed signature for input {} of tx {}: {}", i, new Keccak256(rskTxHash), ByteUtil.toHexString(signatures.get(i))); 1257  return; 1258  } 1259  1260  Sha256Hash sighash = sighashes.get(i); 1261  1262  if (!federatorPublicKey.verify(sighash, sig)) { 1263  logger.warn( 1264  "Signature {} {} is not valid for hash {} and public key {}", 1265  i, 1266  ByteUtil.toHexString(sig.encodeToDER()), 1267  sighash, 1268  federatorPublicKey 1269  ); 1270  return; 1271  } 1272  1273  TransactionSignature txSig = new TransactionSignature(sig, BtcTransaction.SigHash.ALL, false); 1274  txSigs.add(txSig); 1275  if (!txSig.isCanonical()) { 1276  logger.warn("Signature {} {} is not canonical.", i, ByteUtil.toHexString(signatures.get(i))); 1277  return; 1278  } 1279  } 1280  1281  // All signatures are correct. Proceed to signing 1282  for (int i = 0; i < numInputs; i++) { 1283  Sha256Hash sighash = sighashes.get(i); 1284  TransactionInput input = btcTx.getInput(i); 1285  Script inputScript = input.getScriptSig(); 1286  1287  boolean alreadySignedByThisFederator = BridgeUtils.isInputSignedByThisFederator( 1288  federatorPublicKey, 1289  sighash, 1290  input); 1291  1292  // Sign the input if it wasn't already 1293  if (!alreadySignedByThisFederator) { 1294  try { 1295  int sigIndex = inputScript.getSigInsertionIndex(sighash, federatorPublicKey); 1296  inputScript = ScriptBuilder.updateScriptWithSignature(inputScript, txSigs.get(i).encodeToBitcoin(), sigIndex, 1, 1); 1297  input.setScriptSig(inputScript); 1298  logger.debug("Tx input {} for tx {} signed.", i, new Keccak256(rskTxHash)); 1299  } catch (IllegalStateException e) { 1300  Federation retiringFederation = getRetiringFederation(); 1301  if (getActiveFederation().hasBtcPublicKey(federatorPublicKey)) { 1302  logger.debug("A member of the active federation is trying to sign a tx of the retiring one"); 1303  return; 1304  } else if (retiringFederation != null && retiringFederation.hasBtcPublicKey(federatorPublicKey)) { 1305  logger.debug("A member of the retiring federation is trying to sign a tx of the active one"); 1306  return; 1307  } 1308  throw e; 1309  } 1310  } else { 1311  logger.warn("Input {} of tx {} already signed by this federator.", i, new Keccak256(rskTxHash)); 1312  break; 1313  } 1314  } 1315  1316  if (BridgeUtils.hasEnoughSignatures(btcContext, btcTx)) { 1317  logger.info("Tx fully signed {}. Hex: {}", btcTx, Hex.toHexString(btcTx.bitcoinSerialize())); 1318  provider.getRskTxsWaitingForSignatures().remove(new Keccak256(rskTxHash)); 1319  1320  eventLogger.logReleaseBtc(btcTx, rskTxHash); 1321  } else if (logger.isDebugEnabled()) { 1322  int missingSignatures = BridgeUtils.countMissingSignatures(btcContext, btcTx); 1323  int neededSignatures = federation.getNumberOfSignaturesRequired(); 1324  int signaturesCount = neededSignatures - missingSignatures; 1325  1326  logger.debug("Tx {} not yet fully signed. Requires {}/{} signatures but has {}", 1327  new Keccak256(rskTxHash), neededSignatures, getFederationSize(), signaturesCount); 1328  } 1329  } 1330  1331  /** 1332  * Returns the btc tx that federators need to sign or broadcast 1333  * @return a StateForFederator serialized in RLP 1334  */ 1335  public byte[] getStateForBtcReleaseClient() throws IOException { 1336  StateForFederator stateForFederator = new StateForFederator(provider.getRskTxsWaitingForSignatures()); 1337  return stateForFederator.getEncoded(); 1338  } 1339  1340  /** 1341  * Returns the insternal state of the bridge 1342  * @return a BridgeState serialized in RLP 1343  */ 1344  public byte[] getStateForDebugging() throws IOException, BlockStoreException { 1345  BridgeState stateForDebugging = new BridgeState(getBtcBlockchainBestChainHeight(), provider, activations); 1346  1347  return stateForDebugging.getEncoded(); 1348  } 1349  1350  /** 1351  * Returns the bitcoin blockchain best chain height know by the bridge contract 1352  */ 1353  public int getBtcBlockchainBestChainHeight() throws IOException, BlockStoreException { 1354  return getBtcBlockchainChainHead().getHeight(); 1355  } 1356  1357  /** 1358  * Returns the bitcoin blockchain initial stored block height 1359  */ 1360  public int getBtcBlockchainInitialBlockHeight() throws IOException { 1361  return getLowestBlock().getHeight(); 1362  } 1363  1364  /** 1365  * @deprecated 1366  * Returns an array of block hashes known by the bridge contract. 1367  * Federators can use this to find what is the latest block in the mainchain the bridge has. 1368  * @return a List of bitcoin block hashes 1369  */ 1370  @Deprecated 1371  public List<Sha256Hash> getBtcBlockchainBlockLocator() throws IOException, BlockStoreException { 1372  StoredBlock initialBtcStoredBlock = this.getLowestBlock(); 1373  final int maxHashesToInform = 100; 1374  List<Sha256Hash> blockLocator = new ArrayList<>(); 1375  StoredBlock cursor = getBtcBlockchainChainHead(); 1376  int bestBlockHeight = cursor.getHeight(); 1377  blockLocator.add(cursor.getHeader().getHash()); 1378  if (bestBlockHeight > initialBtcStoredBlock.getHeight()) { 1379  boolean stop = false; 1380  int i = 0; 1381  try { 1382  while (blockLocator.size() <= maxHashesToInform && !stop) { 1383  int blockHeight = (int) (bestBlockHeight - Math.pow(2, i)); 1384  if (blockHeight <= initialBtcStoredBlock.getHeight()) { 1385  blockLocator.add(initialBtcStoredBlock.getHeader().getHash()); 1386  stop = true; 1387  } else { 1388  cursor = this.getPrevBlockAtHeight(cursor, blockHeight); 1389  blockLocator.add(cursor.getHeader().getHash()); 1390  } 1391  i++; 1392  } 1393  } catch (Exception e) { 1394  logger.error("Failed to walk the block chain whilst constructing a locator"); 1395  panicProcessor.panic("btcblockchain", "Failed to walk the block chain whilst constructing a locator"); 1396  throw new RuntimeException(e); 1397  } 1398  if (!stop) { 1399  blockLocator.add(initialBtcStoredBlock.getHeader().getHash()); 1400  } 1401  } 1402  return blockLocator; 1403  } 1404  1405  public byte[] getBtcBlockchainBestBlockHeader() throws BlockStoreException, IOException { 1406  return serializeBlockHeader(getBtcBlockchainChainHead()); 1407  } 1408  1409  public byte[] getBtcBlockchainBlockHeaderByHash(Sha256Hash hash) throws IOException, BlockStoreException { 1410  this.ensureBtcBlockStore(); 1411  1412  return serializeBlockHeader(btcBlockStore.get(hash)); 1413  } 1414  1415  public byte[] getBtcBlockchainBlockHeaderByHeight(int height) throws BlockStoreException, IOException { 1416  Context.propagate(btcContext); 1417  this.ensureBtcBlockStore(); 1418  1419  StoredBlock block = btcBlockStore.getStoredBlockAtMainChainHeight(height); 1420  1421  return serializeBlockHeader(block); 1422  } 1423  1424  public byte[] getBtcBlockchainParentBlockHeaderByHash(Sha256Hash hash) throws IOException, BlockStoreException { 1425  this.ensureBtcBlockStore(); 1426  1427  StoredBlock block = btcBlockStore.get(hash); 1428  1429  if (block == null) { 1430  return ByteUtil.EMPTY_BYTE_ARRAY; 1431  } 1432  1433  return serializeBlockHeader(btcBlockStore.get(block.getHeader().getPrevBlockHash())); 1434  } 1435  1436  public Sha256Hash getBtcBlockchainBlockHashAtDepth(int depth) throws BlockStoreException, IOException { 1437  Context.propagate(btcContext); 1438  this.ensureBtcBlockStore(); 1439  1440  StoredBlock head = btcBlockStore.getChainHead(); 1441  int maxDepth = head.getHeight() - getLowestBlock().getHeight(); 1442  1443  if (depth < 0 || depth > maxDepth) { 1444  throw new IndexOutOfBoundsException(String.format("Depth must be between 0 and %d", maxDepth)); 1445  } 1446  1447  StoredBlock blockAtDepth = btcBlockStore.getStoredBlockAtMainChainDepth(depth); 1448  return blockAtDepth.getHeader().getHash(); 1449  } 1450  1451  public Long getBtcTransactionConfirmationsGetCost(Object[] args) { 1452  final long BASIC_COST = 27_000; 1453  final long STEP_COST = 315; 1454  final long DOUBLE_HASH_COST = 144; // 72 * 2. 72 is the cost of the hash operation 1455  1456  Sha256Hash btcBlockHash; 1457  int branchHashesSize; 1458  try { 1459  btcBlockHash = Sha256Hash.wrap((byte[]) args[1]); 1460  Object[] merkleBranchHashesArray = (Object[]) args[3]; 1461  branchHashesSize = merkleBranchHashesArray.length; 1462  } catch (NullPointerException | IllegalArgumentException e) { 1463  return BASIC_COST; 1464  } 1465  1466  // Dynamic cost based on the depth of the block that contains 1467  // the transaction. Find such depth first, then calculate 1468  // the cost. 1469  Context.propagate(btcContext); 1470  try { 1471  this.ensureBtcBlockStore(); 1472  final StoredBlock block = btcBlockStore.getFromCache(btcBlockHash); 1473  1474  // Block not found, default to basic cost 1475  if (block == null) { 1476  return BASIC_COST; 1477  } 1478  1479  final int bestChainHeight = getBtcBlockchainBestChainHeight(); 1480  1481  // Make sure calculated depth is >= 0 1482  final int blockDepth = Math.max(0, bestChainHeight - block.getHeight()); 1483  1484  // Block too deep, default to basic cost 1485  if (blockDepth > BTC_TRANSACTION_CONFIRMATION_MAX_DEPTH) { 1486  return BASIC_COST; 1487  } 1488  1489  return BASIC_COST + blockDepth*STEP_COST + branchHashesSize*DOUBLE_HASH_COST; 1490  } catch (IOException | BlockStoreException e) { 1491  logger.warn("getBtcTransactionConfirmationsGetCost btcBlockHash:{} there was a problem " + 1492  "gathering the block depth while calculating the gas cost. " + 1493  "Defaulting to basic cost.", btcBlockHash, e); 1494  return BASIC_COST; 1495  } 1496  } 1497  1498  /** 1499  * @param btcTxHash The BTC transaction Hash 1500  * @param btcBlockHash The BTC block hash 1501  * @param merkleBranch The merkle branch 1502  * @throws BlockStoreException 1503  * @throws IOException 1504  */ 1505  public Integer getBtcTransactionConfirmations(Sha256Hash btcTxHash, Sha256Hash btcBlockHash, MerkleBranch merkleBranch) throws BlockStoreException, IOException { 1506  Context.propagate(btcContext); 1507  this.ensureBtcBlockChain(); 1508  1509  // Get the block using the given block hash 1510  StoredBlock block = btcBlockStore.getFromCache(btcBlockHash); 1511  if (block == null) { 1512  return BTC_TRANSACTION_CONFIRMATION_INEXISTENT_BLOCK_HASH_ERROR_CODE; 1513  } 1514  1515  final int bestChainHeight = getBtcBlockchainBestChainHeight(); 1516  1517  // Prevent diving too deep in the blockchain to avoid high processing costs 1518  final int blockDepth = Math.max(0, bestChainHeight - block.getHeight()); 1519  if (blockDepth > BTC_TRANSACTION_CONFIRMATION_MAX_DEPTH) { 1520  return BTC_TRANSACTION_CONFIRMATION_BLOCK_TOO_OLD_ERROR_CODE; 1521  } 1522  1523  try { 1524  StoredBlock storedBlock = btcBlockStore.getStoredBlockAtMainChainHeight(block.getHeight()); 1525  // Make sure it belongs to the best chain 1526  if (storedBlock == null || !storedBlock.equals(block)){ 1527  return BTC_TRANSACTION_CONFIRMATION_BLOCK_NOT_IN_BEST_CHAIN_ERROR_CODE; 1528  } 1529  } catch (BlockStoreException e) { 1530  logger.warn(String.format( 1531  "Illegal state trying to get block with hash %s", 1532  btcBlockHash 1533  ), e); 1534  return BTC_TRANSACTION_CONFIRMATION_INCONSISTENT_BLOCK_ERROR_CODE; 1535  } 1536  1537  Sha256Hash merkleRoot = merkleBranch.reduceFrom(btcTxHash); 1538  1539  if (!isBlockMerkleRootValid(merkleRoot, block.getHeader())) { 1540  return BTC_TRANSACTION_CONFIRMATION_INVALID_MERKLE_BRANCH_ERROR_CODE; 1541  } 1542  1543  return bestChainHeight - block.getHeight() + 1; 1544  } 1545  1546  private StoredBlock getPrevBlockAtHeight(StoredBlock cursor, int height) throws BlockStoreException { 1547  if (cursor.getHeight() == height) { 1548  return cursor; 1549  } 1550  1551  boolean stop = false; 1552  StoredBlock current = cursor; 1553  while (!stop) { 1554  current = current.getPrev(this.btcBlockStore); 1555  stop = current.getHeight() == height; 1556  } 1557  return current; 1558  } 1559  1560  /** 1561  * Returns whether a given btc transaction hash has already 1562  * been processed by the bridge. 1563  * @param btcTxHash the btc tx hash to check. 1564  * @return a Boolean indicating whether the given btc tx hash was 1565  * already processed by the bridge. 1566  * @throws IOException 1567  */ 1568  public Boolean isBtcTxHashAlreadyProcessed(Sha256Hash btcTxHash) throws IOException { 1569  return provider.getHeightIfBtcTxhashIsAlreadyProcessed(btcTxHash).isPresent(); 1570  } 1571  1572  /** 1573  * Returns the RSK blockchain height a given btc transaction hash 1574  * was processed at by the bridge. 1575  * @param btcTxHash the btc tx hash for which to retrieve the height. 1576  * @return a Long with the processed height. If the hash was not processed 1577  * -1 is returned. 1578  * @throws IOException 1579  */ 1580  public Long getBtcTxHashProcessedHeight(Sha256Hash btcTxHash) throws IOException { 1581  // Return -1 if the transaction hasn't been processed 1582  return provider.getHeightIfBtcTxhashIsAlreadyProcessed(btcTxHash).orElse(-1L); 1583  } 1584  1585  /** 1586  * Returns if tx was already processed by the bridge 1587  * @param btcTxHash the btc tx hash for which to retrieve the height. 1588  * @return true or false according 1589  * @throws IOException 1590  * */ 1591  public boolean isAlreadyBtcTxHashProcessed(Sha256Hash btcTxHash) throws IOException { 1592  if (getBtcTxHashProcessedHeight(btcTxHash) > -1L) { 1593  logger.warn("Supplied Btc Tx {} was already processed", btcTxHash); 1594  return true; 1595  } 1596  1597  return false; 1598  } 1599  1600  /** 1601  * Returns the currently active federation. 1602  * See getActiveFederationReference() for details. 1603  * @return the currently active federation. 1604  */ 1605  public Federation getActiveFederation() { 1606  return federationSupport.getActiveFederation(); 1607  } 1608  1609  /** 1610  * Returns the currently retiring federation. 1611  * See getRetiringFederationReference() for details. 1612  * @return the retiring federation. 1613  */ 1614  @Nullable 1615  public Federation getRetiringFederation() { 1616  return federationSupport.getRetiringFederation(); 1617  } 1618  1619  private List<UTXO> getActiveFederationBtcUTXOs() throws IOException { 1620  return federationSupport.getActiveFederationBtcUTXOs(); 1621  } 1622  1623  private List<UTXO> getRetiringFederationBtcUTXOs() throws IOException { 1624  return federationSupport.getRetiringFederationBtcUTXOs(); 1625  } 1626  1627  /** 1628  * Returns the federation bitcoin address. 1629  * @return the federation bitcoin address. 1630  */ 1631  public Address getFederationAddress() { 1632  return getActiveFederation().getAddress(); 1633  } 1634  1635  /** 1636  * Returns the federation's size 1637  * @return the federation size 1638  */ 1639  public Integer getFederationSize() { 1640  return getActiveFederation().getBtcPublicKeys().size(); 1641  } 1642  1643  /** 1644  * Returns the federation's minimum required signatures 1645  * @return the federation minimum required signatures 1646  */ 1647  public Integer getFederationThreshold() { 1648  return getActiveFederation().getNumberOfSignaturesRequired(); 1649  } 1650  1651  /** 1652  * Returns the public key of the federation's federator at the given index 1653  * @param index the federator's index (zero-based) 1654  * @return the federator's public key 1655  */ 1656  public byte[] getFederatorPublicKey(int index) { 1657  return federationSupport.getFederatorBtcPublicKey(index); 1658  } 1659  1660  /** 1661  * Returns the public key of given type of the federation's federator at the given index 1662  * @param index the federator's index (zero-based) 1663  * @param keyType the key type 1664  * @return the federator's public key 1665  */ 1666  public byte[] getFederatorPublicKeyOfType(int index, FederationMember.KeyType keyType) { 1667  return federationSupport.getFederatorPublicKeyOfType(index, keyType); 1668  } 1669  1670  /** 1671  * Returns the federation's creation time 1672  * @return the federation creation time 1673  */ 1674  public Instant getFederationCreationTime() { 1675  return getActiveFederation().getCreationTime(); 1676  } 1677  1678  /** 1679  * Returns the federation's creation block number 1680  * @return the federation creation block number 1681  */ 1682  public long getFederationCreationBlockNumber() { 1683  return getActiveFederation().getCreationBlockNumber(); 1684  } 1685  1686  /** 1687  * Returns the retiring federation bitcoin address. 1688  * @return the retiring federation bitcoin address, null if no retiring federation exists 1689  */ 1690  public Address getRetiringFederationAddress() { 1691  Federation retiringFederation = getRetiringFederation(); 1692  if (retiringFederation == null) { 1693  return null; 1694  } 1695  1696  return retiringFederation.getAddress(); 1697  } 1698  1699  /** 1700  * Returns the retiring federation's size 1701  * @return the retiring federation size, -1 if no retiring federation exists 1702  */ 1703  public Integer getRetiringFederationSize() { 1704  Federation retiringFederation = getRetiringFederation(); 1705  if (retiringFederation == null) { 1706  return -1; 1707  } 1708  1709  return retiringFederation.getBtcPublicKeys().size(); 1710  } 1711  1712  /** 1713  * Returns the retiring federation's minimum required signatures 1714  * @return the retiring federation minimum required signatures, -1 if no retiring federation exists 1715  */ 1716  public Integer getRetiringFederationThreshold() { 1717  Federation retiringFederation = getRetiringFederation(); 1718  if (retiringFederation == null) { 1719  return -1; 1720  } 1721  1722  return retiringFederation.getNumberOfSignaturesRequired(); 1723  } 1724  1725  /** 1726  * Returns the public key of the retiring federation's federator at the given index 1727  * @param index the retiring federator's index (zero-based) 1728  * @return the retiring federator's public key, null if no retiring federation exists 1729  */ 1730  public byte[] getRetiringFederatorPublicKey(int index) { 1731  Federation retiringFederation = getRetiringFederation(); 1732  if (retiringFederation == null) { 1733  return null; 1734  } 1735  1736  List<BtcECKey> publicKeys = retiringFederation.getBtcPublicKeys(); 1737  1738  if (index < 0 || index >= publicKeys.size()) { 1739  throw new IndexOutOfBoundsException(String.format("Retiring federator index must be between 0 and %d", publicKeys.size() - 1)); 1740  } 1741  1742  return publicKeys.get(index).getPubKey(); 1743  } 1744  1745  /** 1746  * Returns the public key of the given type of the retiring federation's federator at the given index 1747  * @param index the retiring federator's index (zero-based) 1748  * @param keyType the key type 1749  * @return the retiring federator's public key of the given type, null if no retiring federation exists 1750  */ 1751  public byte[] getRetiringFederatorPublicKeyOfType(int index, FederationMember.KeyType keyType) { 1752  Federation retiringFederation = getRetiringFederation(); 1753  if (retiringFederation == null) { 1754  return null; 1755  } 1756  1757  return federationSupport.getMemberPublicKeyOfType(retiringFederation.getMembers(), index, keyType, "Retiring federator"); 1758  } 1759  1760  /** 1761  * Returns the retiring federation's creation time 1762  * @return the retiring federation creation time, null if no retiring federation exists 1763  */ 1764  public Instant getRetiringFederationCreationTime() { 1765  Federation retiringFederation = getRetiringFederation(); 1766  if (retiringFederation == null) { 1767  return null; 1768  } 1769  1770  return retiringFederation.getCreationTime(); 1771  } 1772  1773  /** 1774  * Returns the retiring federation's creation block number 1775  * @return the retiring federation creation block number, 1776  * -1 if no retiring federation exists 1777  */ 1778  public long getRetiringFederationCreationBlockNumber() { 1779  Federation retiringFederation = getRetiringFederation(); 1780  if (retiringFederation == null) { 1781  return -1L; 1782  } 1783  return retiringFederation.getCreationBlockNumber(); 1784  } 1785  1786  /** 1787  * Returns the currently live federations 1788  * This would be the active federation plus 1789  * potentially the retiring federation 1790  * @return a list of live federations 1791  */ 1792  private List<Federation> getLiveFederations() { 1793  List<Federation> liveFederations = new ArrayList<>(); 1794  liveFederations.add(getActiveFederation()); 1795  Federation retiringFederation = getRetiringFederation(); 1796  if (retiringFederation != null) { 1797  liveFederations.add(retiringFederation); 1798  } 1799  return liveFederations; 1800  } 1801  1802  /** 1803  * Creates a new pending federation 1804  * If there's currently no pending federation and no funds remain 1805  * to be moved from a previous federation, a new one is created. 1806  * Otherwise, -1 is returned if there's already a pending federation, 1807  * -2 is returned if there is a federation awaiting to be active, 1808  * or -3 if funds are left from a previous one. 1809  * @param dryRun whether to just do a dry run 1810  * @return 1 upon success, -1 when a pending federation is present, 1811  * -2 when a federation is to be activated, 1812  * and if -3 funds are still to be moved between federations. 1813  */ 1814  private Integer createFederation(boolean dryRun) { 1815  PendingFederation currentPendingFederation = provider.getPendingFederation(); 1816  1817  if (currentPendingFederation != null) { 1818  return -1; 1819  } 1820  1821  if (federationSupport.amAwaitingFederationActivation()) { 1822  return -2; 1823  } 1824  1825  if (getRetiringFederation() != null) { 1826  return -3; 1827  } 1828  1829  if (dryRun) { 1830  return 1; 1831  } 1832  1833  currentPendingFederation = new PendingFederation(Collections.emptyList()); 1834  1835  provider.setPendingFederation(currentPendingFederation); 1836  1837  // Clear votes on election 1838  provider.getFederationElection(bridgeConstants.getFederationChangeAuthorizer()).clear(); 1839  1840  return 1; 1841  } 1842  1843  /** 1844  * Adds the given keys to the current pending federation. 1845  * 1846  * @param dryRun whether to just do a dry run 1847  * @param btcKey the BTC public key to add 1848  * @param rskKey the RSK public key to add 1849  * @param mstKey the MST public key to add 1850  * @return 1 upon success, -1 if there was no pending federation, -2 if the key was already in the pending federation 1851  */ 1852  private Integer addFederatorPublicKeyMultikey(boolean dryRun, BtcECKey btcKey, ECKey rskKey, ECKey mstKey) { 1853  PendingFederation currentPendingFederation = provider.getPendingFederation(); 1854  1855  if (currentPendingFederation == null) { 1856  return -1; 1857  } 1858  1859  if (currentPendingFederation.getBtcPublicKeys().contains(btcKey) || 1860  currentPendingFederation.getMembers().stream().map(FederationMember::getRskPublicKey).anyMatch(k -> k.equals(rskKey)) || 1861  currentPendingFederation.getMembers().stream().map(FederationMember::getMstPublicKey).anyMatch(k -> k.equals(mstKey))) { 1862  return -2; 1863  } 1864  1865  if (dryRun) { 1866  return 1; 1867  } 1868  1869  FederationMember member = new FederationMember(btcKey, rskKey, mstKey); 1870  1871  currentPendingFederation = currentPendingFederation.addMember(member); 1872  1873  provider.setPendingFederation(currentPendingFederation); 1874  1875  return 1; 1876  } 1877  1878  /** 1879  * Commits the currently pending federation. 1880  * That is, the retiring federation is set to be the currently active federation, 1881  * the active federation is replaced with a new federation generated from the pending federation, 1882  * and the pending federation is wiped out. 1883  * Also, UTXOs are moved from active to retiring so that the transfer of funds can 1884  * begin. 1885  * @param dryRun whether to just do a dry run 1886  * @param hash the pending federation's hash. This is checked the execution block's pending federation hash for equality. 1887  * @return 1 upon success, -1 if there was no pending federation, -2 if the pending federation was incomplete, 1888  * -3 if the given hash doesn't match the current pending federation's hash. 1889  */ 1890  protected Integer commitFederation(boolean dryRun, Keccak256 hash) throws IOException { 1891  PendingFederation currentPendingFederation = provider.getPendingFederation(); 1892  1893  if (currentPendingFederation == null) { 1894  return -1; 1895  } 1896  1897  if (!currentPendingFederation.isComplete()) { 1898  return -2; 1899  } 1900  1901  if (!hash.equals(currentPendingFederation.getHash())) { 1902  return -3; 1903  } 1904  1905  if (dryRun) { 1906  return 1; 1907  } 1908  1909  // Move UTXOs from the new federation into the old federation 1910  // and clear the new federation's UTXOs 1911  List<UTXO> utxosToMove = new ArrayList<>(provider.getNewFederationBtcUTXOs()); 1912  provider.getNewFederationBtcUTXOs().clear(); 1913  List<UTXO> oldFederationUTXOs = provider.getOldFederationBtcUTXOs(); 1914  oldFederationUTXOs.clear(); 1915  oldFederationUTXOs.addAll(utxosToMove); 1916  1917  // Network parameters for the new federation are taken from the bridge constants. 1918  // Creation time is the block's timestamp. 1919  Instant creationTime = Instant.ofEpochMilli(rskExecutionBlock.getTimestamp()); 1920  Federation oldFederation = getActiveFederation(); 1921  provider.setOldFederation(oldFederation); 1922  provider.setNewFederation( 1923  currentPendingFederation.buildFederation( 1924  creationTime, 1925  rskExecutionBlock.getNumber(), 1926  bridgeConstants, 1927  activations 1928  ) 1929  ); 1930  provider.setPendingFederation(null); 1931  1932  // Clear votes on election 1933  provider.getFederationElection(bridgeConstants.getFederationChangeAuthorizer()).clear(); 1934  1935  if (activations.isActive(RSKIP186)) { 1936  // Preserve federation change info 1937  long nextFederationCreationBlockHeight = rskExecutionBlock.getNumber(); 1938  provider.setNextFederationCreationBlockHeight(nextFederationCreationBlockHeight); 1939  Script oldFederationP2SHScript = oldFederation.getP2SHScript(); 1940  provider.setLastRetiredFederationP2SHScript(oldFederationP2SHScript); 1941  } 1942  1943  logger.debug("[commitFederation] New Federation committed: {}", provider.getNewFederation().getAddress()); 1944  eventLogger.logCommitFederation(rskExecutionBlock, provider.getOldFederation(), provider.getNewFederation()); 1945  1946  return 1; 1947  } 1948  1949  /** 1950  * Rolls back the currently pending federation 1951  * That is, the pending federation is wiped out. 1952  * @param dryRun whether to just do a dry run 1953  * @return 1 upon success, 1 if there was no pending federation 1954  */ 1955  private Integer rollbackFederation(boolean dryRun) { 1956  PendingFederation currentPendingFederation = provider.getPendingFederation(); 1957  1958  if (currentPendingFederation == null) { 1959  return -1; 1960  } 1961  1962  if (dryRun) { 1963  return 1; 1964  } 1965  1966  provider.setPendingFederation(null); 1967  1968  // Clear votes on election 1969  provider.getFederationElection(bridgeConstants.getFederationChangeAuthorizer()).clear(); 1970  1971  return 1; 1972  } 1973  1974  public Integer voteFederationChange(Transaction tx, ABICallSpec callSpec) throws BridgeIllegalArgumentException { 1975  // Must be on one of the allowed functions 1976  if (!FEDERATION_CHANGE_FUNCTIONS.contains(callSpec.getFunction())) { 1977  return FEDERATION_CHANGE_GENERIC_ERROR_CODE; 1978  } 1979  1980  AddressBasedAuthorizer authorizer = bridgeConstants.getFederationChangeAuthorizer(); 1981  1982  // Must be authorized to vote (checking for signature) 1983  if (!authorizer.isAuthorized(tx)) { 1984  return FEDERATION_CHANGE_GENERIC_ERROR_CODE; 1985  } 1986  1987  // Try to do a dry-run and only register the vote if the 1988  // call would be successful 1989  ABICallVoteResult result; 1990  try { 1991  result = executeVoteFederationChangeFunction(true, callSpec); 1992  } catch (IOException | BridgeIllegalArgumentException e) { 1993  result = new ABICallVoteResult(false, FEDERATION_CHANGE_GENERIC_ERROR_CODE); 1994  } 1995  1996  // Return if the dry run failed or we are on a reversible execution 1997  if (!result.wasSuccessful()) { 1998  return (Integer) result.getResult(); 1999  } 2000  2001  ABICallElection election = provider.getFederationElection(authorizer); 2002  // Register the vote. It is expected to succeed, since all previous checks succeeded 2003  if (!election.vote(callSpec, tx.getSender())) { 2004  logger.warn("Unexpected federation change vote failure"); 2005  return FEDERATION_CHANGE_GENERIC_ERROR_CODE; 2006  } 2007  2008  // If enough votes have been reached, then actually execute the function 2009  ABICallSpec winnerSpec = election.getWinner(); 2010  if (winnerSpec != null) { 2011  try { 2012  result = executeVoteFederationChangeFunction(false, winnerSpec); 2013  } catch (IOException e) { 2014  logger.warn("Unexpected federation change vote exception: {}", e.getMessage()); 2015  return FEDERATION_CHANGE_GENERIC_ERROR_CODE; 2016  } finally { 2017  // Clear the winner so that we don't repeat ourselves 2018  election.clearWinners(); 2019  } 2020  } 2021  2022  return (Integer) result.getResult(); 2023  } 2024  2025  private ABICallVoteResult executeVoteFederationChangeFunction(boolean dryRun, ABICallSpec callSpec) throws IOException, BridgeIllegalArgumentException { 2026  // Try to do a dry-run and only register the vote if the 2027  // call would be successful 2028  ABICallVoteResult result; 2029  Integer executionResult; 2030  switch (callSpec.getFunction()) { 2031  case "create": 2032  executionResult = createFederation(dryRun); 2033  result = new ABICallVoteResult(executionResult == 1, executionResult); 2034  break; 2035  case "add": 2036  byte[] publicKeyBytes = callSpec.getArguments()[0]; 2037  BtcECKey publicKey; 2038  ECKey publicKeyEc; 2039  try { 2040  publicKey = BtcECKey.fromPublicOnly(publicKeyBytes); 2041  publicKeyEc = ECKey.fromPublicOnly(publicKeyBytes); 2042  } catch (Exception e) { 2043  throw new BridgeIllegalArgumentException("Public key could not be parsed " + ByteUtil.toHexString(publicKeyBytes), e); 2044  } 2045  executionResult = addFederatorPublicKeyMultikey(dryRun, publicKey, publicKeyEc, publicKeyEc); 2046  result = new ABICallVoteResult(executionResult == 1, executionResult); 2047  break; 2048  case "add-multi": 2049  BtcECKey btcPublicKey; 2050  ECKey rskPublicKey, mstPublicKey; 2051  try { 2052  btcPublicKey = BtcECKey.fromPublicOnly(callSpec.getArguments()[0]); 2053  } catch (Exception e) { 2054  throw new BridgeIllegalArgumentException("BTC public key could not be parsed " + ByteUtil.toHexString(callSpec.getArguments()[0]), e); 2055  } 2056  2057  try { 2058  rskPublicKey = ECKey.fromPublicOnly(callSpec.getArguments()[1]); 2059  } catch (Exception e) { 2060  throw new BridgeIllegalArgumentException("RSK public key could not be parsed " + ByteUtil.toHexString(callSpec.getArguments()[1]), e); 2061  } 2062  2063  try { 2064  mstPublicKey = ECKey.fromPublicOnly(callSpec.getArguments()[2]); 2065  } catch (Exception e) { 2066  throw new BridgeIllegalArgumentException("MST public key could not be parsed " + ByteUtil.toHexString(callSpec.getArguments()[2]), e); 2067  } 2068  executionResult = addFederatorPublicKeyMultikey(dryRun, btcPublicKey, rskPublicKey, mstPublicKey); 2069  result = new ABICallVoteResult(executionResult == 1, executionResult); 2070  break; 2071  case "commit": 2072  Keccak256 hash = new Keccak256((byte[]) callSpec.getArguments()[0]); 2073  executionResult = commitFederation(dryRun, hash); 2074  result = new ABICallVoteResult(executionResult == 1, executionResult); 2075  break; 2076  case "rollback": 2077  executionResult = rollbackFederation(dryRun); 2078  result = new ABICallVoteResult(executionResult == 1, executionResult); 2079  break; 2080  default: 2081  // Fail by default 2082  result = new ABICallVoteResult(false, FEDERATION_CHANGE_GENERIC_ERROR_CODE); 2083  } 2084  2085  return result; 2086  } 2087  2088  /** 2089  * Returns the currently pending federation hash, or null if none exists 2090  * @return the currently pending federation hash, or null if none exists 2091  */ 2092  public byte[] getPendingFederationHash() { 2093  PendingFederation currentPendingFederation = provider.getPendingFederation(); 2094  2095  if (currentPendingFederation == null) { 2096  return null; 2097  } 2098  2099  return currentPendingFederation.getHash().getBytes(); 2100  } 2101  2102  /** 2103  * Returns the currently pending federation size, or -1 if none exists 2104  * @return the currently pending federation size, or -1 if none exists 2105  */ 2106  public Integer getPendingFederationSize() { 2107  PendingFederation currentPendingFederation = provider.getPendingFederation(); 2108  2109  if (currentPendingFederation == null) { 2110  return -1; 2111  } 2112  2113  return currentPendingFederation.getBtcPublicKeys().size(); 2114  } 2115  2116  /** 2117  * Returns the currently pending federation federator's public key at the given index, or null if none exists 2118  * @param index the federator's index (zero-based) 2119  * @return the pending federation's federator public key 2120  */ 2121  public byte[] getPendingFederatorPublicKey(int index) { 2122  PendingFederation currentPendingFederation = provider.getPendingFederation(); 2123  2124  if (currentPendingFederation == null) { 2125  return null; 2126  } 2127  2128  List<BtcECKey> publicKeys = currentPendingFederation.getBtcPublicKeys(); 2129  2130  if (index < 0 || index >= publicKeys.size()) { 2131  throw new IndexOutOfBoundsException(String.format("Federator index must be between 0 and %d", publicKeys.size() - 1)); 2132  } 2133  2134  return publicKeys.get(index).getPubKey(); 2135  } 2136  2137  /** 2138  * Returns the public key of the given type of the pending federation's federator at the given index 2139  * @param index the federator's index (zero-based) 2140  * @param keyType the key type 2141  * @return the pending federation's federator public key of given type 2142  */ 2143  public byte[] getPendingFederatorPublicKeyOfType(int index, FederationMember.KeyType keyType) { 2144  PendingFederation currentPendingFederation = provider.getPendingFederation(); 2145  2146  if (currentPendingFederation == null) { 2147  return null; 2148  } 2149  2150  return federationSupport.getMemberPublicKeyOfType(currentPendingFederation.getMembers(), index, keyType, "Federator"); 2151  } 2152  2153  /** 2154  * Returns the lock whitelist size, that is, 2155  * the number of whitelisted addresses 2156  * @return the lock whitelist size 2157  */ 2158  public Integer getLockWhitelistSize() { 2159  return provider.getLockWhitelist().getSize(); 2160  } 2161  2162  /** 2163  * Returns the lock whitelist address stored 2164  * at the given index, or null if the 2165  * index is out of bounds 2166  * @param index the index at which to get the address 2167  * @return the base58-encoded address stored at the given index, or null if index is out of bounds 2168  */ 2169  public LockWhitelistEntry getLockWhitelistEntryByIndex(int index) { 2170  List<LockWhitelistEntry> entries = provider.getLockWhitelist().getAll(); 2171  2172  if (index < 0 || index >= entries.size()) { 2173  return null; 2174  } 2175  2176  return entries.get(index); 2177  } 2178  2179  /** 2180  * 2181  * @param addressBase58 2182  * @return 2183  */ 2184  public LockWhitelistEntry getLockWhitelistEntryByAddress(String addressBase58) { 2185  try { 2186  Address address = getParsedAddress(addressBase58); 2187  return provider.getLockWhitelist().get(address); 2188  } catch (AddressFormatException e) { 2189  logger.warn(INVALID_ADDRESS_FORMAT_MESSAGE, e); 2190  return null; 2191  } 2192  } 2193  2194  /** 2195  * Adds the given address to the lock whitelist. 2196  * Returns 1 upon success, or -1 if the address was 2197  * already in the whitelist. 2198  * @param addressBase58 the base58-encoded address to add to the whitelist 2199  * @param maxTransferValue the max amount of satoshis enabled to transfer for this address 2200  * @return 1 upon success, -1 if the address was already 2201  * in the whitelist, -2 if address is invalid 2202  * LOCK_WHITELIST_GENERIC_ERROR_CODE otherwise. 2203  */ 2204  public Integer addOneOffLockWhitelistAddress(Transaction tx, String addressBase58, BigInteger maxTransferValue) { 2205  try { 2206  Address address = getParsedAddress(addressBase58); 2207  Coin maxTransferValueCoin = Coin.valueOf(maxTransferValue.longValueExact()); 2208  return this.addLockWhitelistAddress(tx, new OneOffWhiteListEntry(address, maxTransferValueCoin)); 2209  } catch (AddressFormatException e) { 2210  logger.warn(INVALID_ADDRESS_FORMAT_MESSAGE, e); 2211  return LOCK_WHITELIST_INVALID_ADDRESS_FORMAT_ERROR_CODE; 2212  } 2213  } 2214  2215  public Integer addUnlimitedLockWhitelistAddress(Transaction tx, String addressBase58) { 2216  try { 2217  Address address = getParsedAddress(addressBase58); 2218  return this.addLockWhitelistAddress(tx, new UnlimitedWhiteListEntry(address)); 2219  } catch (AddressFormatException e) { 2220  logger.warn(INVALID_ADDRESS_FORMAT_MESSAGE, e); 2221  return LOCK_WHITELIST_INVALID_ADDRESS_FORMAT_ERROR_CODE; 2222  } 2223  } 2224  2225  private Integer addLockWhitelistAddress(Transaction tx, LockWhitelistEntry entry) { 2226  if (!isLockWhitelistChangeAuthorized(tx)) { 2227  return LOCK_WHITELIST_GENERIC_ERROR_CODE; 2228  } 2229  2230  LockWhitelist whitelist = provider.getLockWhitelist(); 2231  2232  try { 2233  if (whitelist.isWhitelisted(entry.address())) { 2234  return LOCK_WHITELIST_ALREADY_EXISTS_ERROR_CODE; 2235  } 2236  whitelist.put(entry.address(), entry); 2237  return LOCK_WHITELIST_SUCCESS_CODE; 2238  } catch (Exception e) { 2239  logger.error("Unexpected error in addLockWhitelistAddress: {}", e.getMessage()); 2240  panicProcessor.panic("lock-whitelist", e.getMessage()); 2241  return LOCK_WHITELIST_UNKNOWN_ERROR_CODE; 2242  } 2243  } 2244  2245  private boolean isLockWhitelistChangeAuthorized(Transaction tx) { 2246  AddressBasedAuthorizer authorizer = bridgeConstants.getLockWhitelistChangeAuthorizer(); 2247  2248  return authorizer.isAuthorized(tx); 2249  } 2250  2251  /** 2252  * Removes the given address from the lock whitelist. 2253  * Returns 1 upon success, or -1 if the address was 2254  * not in the whitelist. 2255  * @param addressBase58 the base58-encoded address to remove from the whitelist 2256  * @return 1 upon success, -1 if the address was not 2257  * in the whitelist, -2 if the address is invalid, 2258  * LOCK_WHITELIST_GENERIC_ERROR_CODE otherwise. 2259  */ 2260  public Integer removeLockWhitelistAddress(Transaction tx, String addressBase58) { 2261  if (!isLockWhitelistChangeAuthorized(tx)) { 2262  return LOCK_WHITELIST_GENERIC_ERROR_CODE; 2263  } 2264  2265  LockWhitelist whitelist = provider.getLockWhitelist(); 2266  2267  try { 2268  Address address = getParsedAddress(addressBase58); 2269  2270  if (!whitelist.remove(address)) { 2271  return -1; 2272  } 2273  2274  return 1; 2275  } catch (AddressFormatException e) { 2276  return -2; 2277  } catch (Exception e) { 2278  logger.error("Unexpected error in removeLockWhitelistAddress: {}", e.getMessage()); 2279  panicProcessor.panic("lock-whitelist", e.getMessage()); 2280  return 0; 2281  } 2282  } 2283  2284  /** 2285  * Returns the minimum amount of satoshis a user should send to the federation. 2286  * @return the minimum amount of satoshis a user should send to the federation. 2287  */ 2288  public Coin getMinimumPeginTxValue() { 2289  return activations.isActive(RSKIP219) ? bridgeConstants.getMinimumPeginTxValueInSatoshis() : bridgeConstants.getLegacyMinimumPeginTxValueInSatoshis(); 2290  } 2291  2292  /** 2293  * Votes for a fee per kb value. 2294  * 2295  * @return 1 upon successful vote, -1 when the vote was unsuccessful, 2296  * FEE_PER_KB_GENERIC_ERROR_CODE when there was an un expected error. 2297  */ 2298  public Integer voteFeePerKbChange(Transaction tx, Coin feePerKb) { 2299  AddressBasedAuthorizer authorizer = bridgeConstants.getFeePerKbChangeAuthorizer(); 2300  if (!authorizer.isAuthorized(tx)) { 2301  return FEE_PER_KB_GENERIC_ERROR_CODE; 2302  } 2303  2304  if(!feePerKb.isPositive()){ 2305  return NEGATIVE_FEE_PER_KB_ERROR_CODE; 2306  } 2307  2308  if(feePerKb.isGreaterThan(bridgeConstants.getMaxFeePerKb())) { 2309  return EXCESSIVE_FEE_PER_KB_ERROR_CODE; 2310  } 2311  2312  ABICallElection feePerKbElection = provider.getFeePerKbElection(authorizer); 2313  ABICallSpec feeVote = new ABICallSpec("setFeePerKb", new byte[][]{BridgeSerializationUtils.serializeCoin(feePerKb)}); 2314  boolean successfulVote = feePerKbElection.vote(feeVote, tx.getSender()); 2315  if (!successfulVote) { 2316  return -1; 2317  } 2318  2319  ABICallSpec winner = feePerKbElection.getWinner(); 2320  if (winner == null) { 2321  logger.info("Successful fee per kb vote for {}", feePerKb); 2322  return 1; 2323  } 2324  2325  Coin winnerFee; 2326  try { 2327  winnerFee = BridgeSerializationUtils.deserializeCoin(winner.getArguments()[0]); 2328  } catch (Exception e) { 2329  logger.warn("Exception deserializing winner feePerKb", e); 2330  return FEE_PER_KB_GENERIC_ERROR_CODE; 2331  } 2332  2333  if (winnerFee == null) { 2334  logger.warn("Invalid winner feePerKb: feePerKb can't be null"); 2335  return FEE_PER_KB_GENERIC_ERROR_CODE; 2336  } 2337  2338  if (!winnerFee.equals(feePerKb)) { 2339  logger.debug("Winner fee is different than the last vote: maybe you forgot to clear winners"); 2340  } 2341  2342  logger.info("Fee per kb changed to {}", winnerFee); 2343  provider.setFeePerKb(winnerFee); 2344  feePerKbElection.clear(); 2345  return 1; 2346  } 2347  2348  /** 2349  * Sets a delay in the BTC best chain to disable lock whitelist 2350  * @param tx current RSK transaction 2351  * @param disableBlockDelayBI block since current BTC best chain height to disable lock whitelist 2352  * @return 1 if it was successful, -1 if a delay was already set, -2 if disableBlockDelay contains an invalid value 2353  */ 2354  public Integer setLockWhitelistDisableBlockDelay(Transaction tx, BigInteger disableBlockDelayBI) throws IOException, BlockStoreException { 2355  if (!isLockWhitelistChangeAuthorized(tx)) { 2356  return LOCK_WHITELIST_GENERIC_ERROR_CODE; 2357  } 2358  LockWhitelist lockWhitelist = provider.getLockWhitelist(); 2359  if (lockWhitelist.isDisableBlockSet()) { 2360  return -1; 2361  } 2362  int disableBlockDelay = disableBlockDelayBI.intValueExact(); 2363  int bestChainHeight = getBtcBlockchainBestChainHeight(); 2364  if (disableBlockDelay + bestChainHeight <= bestChainHeight) { 2365  return -2; 2366  } 2367  lockWhitelist.setDisableBlockHeight(bestChainHeight + disableBlockDelay); 2368  return 1; 2369  } 2370  2371  public Coin getLockingCap() { 2372  // Before returning the locking cap, check if it was already set 2373  if (activations.isActive(ConsensusRule.RSKIP134) && this.provider.getLockingCap() == null) { 2374  // Set the initial locking cap value 2375  logger.debug("Setting initial locking cap value"); 2376  this.provider.setLockingCap(bridgeConstants.getInitialLockingCap()); 2377  } 2378  2379  return this.provider.getLockingCap(); 2380  } 2381  2382  public boolean increaseLockingCap(Transaction tx, Coin newCap) { 2383  // Only pre configured addresses can modify locking cap 2384  AddressBasedAuthorizer authorizer = bridgeConstants.getIncreaseLockingCapAuthorizer(); 2385  if (!authorizer.isAuthorized(tx)) { 2386  logger.warn("not authorized address tried to increase locking cap. Address: {}", tx.getSender()); 2387  return false; 2388  } 2389  // new locking cap must be bigger than current locking cap 2390  Coin currentLockingCap = this.getLockingCap(); 2391  if (newCap.compareTo(currentLockingCap) < 0) { 2392  logger.warn("attempted value doesn't increase locking cap. Attempted: {}", newCap.value); 2393  return false; 2394  } 2395  Coin maxLockingCap = currentLockingCap.multiply(bridgeConstants.getLockingCapIncrementsMultiplier()); 2396  if (newCap.compareTo(maxLockingCap) > 0) { 2397  logger.warn("attempted value increases locking cap above its limit. Attempted: {}", newCap.value); 2398  return false; 2399  } 2400  2401  logger.info("increased locking cap: {}", newCap.value); 2402  this.provider.setLockingCap(newCap); 2403  2404  return true; 2405  } 2406  2407  public void registerBtcCoinbaseTransaction(byte[] btcTxSerialized, Sha256Hash blockHash, byte[] pmtSerialized, Sha256Hash witnessMerkleRoot, byte[] witnessReservedValue) throws VMException { 2408  Context.propagate(btcContext); 2409  try{ 2410  this.ensureBtcBlockStore(); 2411  }catch (BlockStoreException | IOException e) { 2412  logger.warn("Exception in registerBtcCoinbaseTransaction", e); 2413  throw new VMException("Exception in registerBtcCoinbaseTransaction", e); 2414  } 2415  2416  Sha256Hash btcTxHash = BtcTransactionFormatUtils.calculateBtcTxHash(btcTxSerialized); 2417  2418  if (witnessReservedValue.length != 32) { 2419  logger.warn("[btcTx:{}] WitnessResevedValue length can't be different than 32 bytes", btcTxHash); 2420  throw new BridgeIllegalArgumentException("WitnessResevedValue length can't be different than 32 bytes"); 2421  } 2422  2423  if (!PartialMerkleTreeFormatUtils.hasExpectedSize(pmtSerialized)) { 2424  logger.warn("[btcTx:{}] PartialMerkleTree doesn't have expected size", btcTxHash); 2425  throw new BridgeIllegalArgumentException("PartialMerkleTree doesn't have expected size"); 2426  } 2427  2428  Sha256Hash merkleRoot; 2429  2430  try { 2431  PartialMerkleTree pmt = new PartialMerkleTree(bridgeConstants.getBtcParams(), pmtSerialized, 0); 2432  List<Sha256Hash> hashesInPmt = new ArrayList<>(); 2433  merkleRoot = pmt.getTxnHashAndMerkleRoot(hashesInPmt); 2434  if (!hashesInPmt.contains(btcTxHash)) { 2435  logger.warn("Supplied Btc Tx {} is not in the supplied partial merkle tree", btcTxHash); 2436  return; 2437  } 2438  } catch (VerificationException e) { 2439  logger.warn("[btcTx:{}] PartialMerkleTree could not be parsed", btcTxHash); 2440  throw new BridgeIllegalArgumentException(String.format("PartialMerkleTree could not be parsed %s", ByteUtil.toHexString(pmtSerialized)), e); 2441  } 2442  2443  // Check merkle root equals btc block merkle root at the specified height in the btc best chain 2444  // Btc blockstore is available since we've already queried the best chain height 2445  StoredBlock storedBlock = btcBlockStore.getFromCache(blockHash); 2446  if (storedBlock == null) { 2447  logger.warn("[btcTx:{}] Block not registered", btcTxHash); 2448  throw new BridgeIllegalArgumentException(String.format("Block not registered %s", blockHash.toString())); 2449  } 2450  BtcBlock blockHeader = storedBlock.getHeader(); 2451  if (!blockHeader.getMerkleRoot().equals(merkleRoot)) { 2452  String panicMessage = String.format( 2453  "Btc Tx %s Supplied merkle root %s does not match block's merkle root %s", 2454  btcTxHash.toString(), 2455  merkleRoot, 2456  blockHeader.getMerkleRoot() 2457  ); 2458  logger.warn(panicMessage); 2459  panicProcessor.panic("btclock", panicMessage); 2460  return; 2461  } 2462  2463  BtcTransaction btcTx = new BtcTransaction(bridgeConstants.getBtcParams(), btcTxSerialized); 2464  btcTx.verify(); 2465  2466  Sha256Hash witnessCommitment = Sha256Hash.twiceOf(witnessMerkleRoot.getReversedBytes(), witnessReservedValue); 2467  2468  if(!witnessCommitment.equals(btcTx.findWitnessCommitment())){ 2469  logger.warn("[btcTx:{}] WitnessCommitment does not match", btcTxHash); 2470  throw new BridgeIllegalArgumentException("WitnessCommitment does not match"); 2471  } 2472  2473  CoinbaseInformation coinbaseInformation = new CoinbaseInformation(witnessMerkleRoot); 2474  provider.setCoinbaseInformation(blockHeader.getHash(), coinbaseInformation); 2475  2476  logger.warn("[btcTx:{}] Registered coinbase information", btcTxHash); 2477  } 2478  2479  public boolean hasBtcBlockCoinbaseTransactionInformation(Sha256Hash blockHash) { 2480  CoinbaseInformation coinbaseInformation = provider.getCoinbaseInformation(blockHash); 2481  return coinbaseInformation != null; 2482  } 2483  2484  public long getActiveFederationCreationBlockHeight() { 2485  if (!activations.isActive(RSKIP186)) { 2486  return 0L; 2487  } 2488  2489  Optional<Long> nextFederationCreationBlockHeightOpt = provider.getNextFederationCreationBlockHeight(); 2490  if (nextFederationCreationBlockHeightOpt.isPresent()) { 2491  long nextFederationCreationBlockHeight = nextFederationCreationBlockHeightOpt.get(); 2492  long curBlockHeight = rskExecutionBlock.getNumber(); 2493  if (curBlockHeight >= nextFederationCreationBlockHeight + bridgeConstants.getFederationActivationAge()) { 2494  return nextFederationCreationBlockHeight; 2495  } 2496  } 2497  2498  Optional<Long> activeFederationCreationBlockHeightOpt = provider.getActiveFederationCreationBlockHeight(); 2499  return activeFederationCreationBlockHeightOpt.orElse(0L); 2500  } 2501  2502  public long registerFastBridgeBtcTransaction( 2503  Transaction rskTx, 2504  byte[] btcTxSerialized, 2505  int height, 2506  byte[] pmtSerialized, 2507  Keccak256 derivationArgumentsHash, 2508  Address userRefundAddress, 2509  RskAddress lbcAddress, 2510  Address lpBtcAddress, 2511  boolean shouldTransferToContract 2512  ) 2513  throws BlockStoreException, IOException, BridgeIllegalArgumentException { 2514  if (!BridgeUtils.isContractTx(rskTx)) { 2515  logger.debug("[registerFastBridgeBtcTransaction] (rskTx:{}) Sender not a contract", rskTx.getHash()); 2516  return FAST_BRIDGE_UNPROCESSABLE_TX_NOT_CONTRACT_ERROR_CODE; 2517  } 2518  2519  if (!rskTx.getSender().equals(lbcAddress)) { 2520  logger.debug( 2521  "[registerFastBridgeBtcTransaction] Expected sender to be the same as lbcAddress. (sender: {}) (lbcAddress:{})", 2522  rskTx.getSender(), 2523  lbcAddress 2524  ); 2525  return FAST_BRIDGE_UNPROCESSABLE_TX_INVALID_SENDER_ERROR_CODE; 2526  } 2527  2528  Context.propagate(btcContext); 2529  Sha256Hash btcTxHash = BtcTransactionFormatUtils.calculateBtcTxHash(btcTxSerialized); 2530  2531  Keccak256 fastBridgeDerivationHash = getFastBridgeDerivationHash( 2532  derivationArgumentsHash, 2533  userRefundAddress, 2534  lpBtcAddress, 2535  lbcAddress 2536  ); 2537  2538  if (provider.isFastBridgeFederationDerivationHashUsed(btcTxHash, fastBridgeDerivationHash)) { 2539  logger.debug("[registerFastBridgeBtcTransaction] Transaction and derivation hash already used"); 2540  return FAST_BRIDGE_UNPROCESSABLE_TX_ALREADY_PROCESSED_ERROR_CODE; 2541  } 2542  2543  if (!validationsForRegisterBtcTransaction(btcTxHash, height, pmtSerialized, btcTxSerialized)) { 2544  logger.debug( 2545  "[registerFastBridgeBtcTransaction] (btcTx:{}) error during validationsForRegisterBtcTransaction", 2546  btcTxHash 2547  ); 2548  return FAST_BRIDGE_UNPROCESSABLE_TX_VALIDATIONS_ERROR; 2549  } 2550  2551  BtcTransaction btcTx = new BtcTransaction(bridgeConstants.getBtcParams(), btcTxSerialized); 2552  btcTx.verify(); 2553  2554  Sha256Hash btcTxHashWithoutWitness = btcTx.getHash(false); 2555  if (!btcTxHashWithoutWitness.equals(btcTxHash) && 2556  (provider.isFastBridgeFederationDerivationHashUsed(btcTxHashWithoutWitness, derivationArgumentsHash))) { 2557  logger.debug("[registerFastBridgeBtcTransaction] Transaction and derivation hash already used"); 2558  return FAST_BRIDGE_UNPROCESSABLE_TX_ALREADY_PROCESSED_ERROR_CODE; 2559  } 2560  2561  FastBridgeFederationInformation fastBridgeFederationInformation = 2562  createFastBridgeFederationInformation(fastBridgeDerivationHash); 2563  2564  Address fastBridgeFedAddress = 2565  fastBridgeFederationInformation.getFastBridgeFederationAddress(bridgeConstants.getBtcParams()); 2566  2567  Coin totalAmount = getAmountSentToAddress(btcTx, fastBridgeFedAddress); 2568  2569  if (totalAmount == Coin.ZERO) { 2570  logger.debug("[registerFastBridgeBtcTransaction] Amount sent can't be 0"); 2571  return FAST_BRIDGE_UNPROCESSABLE_TX_VALUE_ZERO_ERROR; 2572  } 2573  2574  if (!verifyLockDoesNotSurpassLockingCap(btcTx, totalAmount)) { 2575  InternalTransaction internalTx = (InternalTransaction)rskTx; 2576  logger.info("[registerFastBridgeBtcTransaction] Locking cap surpassed, going to return funds!"); 2577  WalletProvider walletProvider = createFastBridgeWalletProvider(fastBridgeFederationInformation); 2578  2579  provider.markFastBridgeFederationDerivationHashAsUsed(btcTxHash, fastBridgeDerivationHash); 2580  2581  if (shouldTransferToContract) { 2582  logger.debug("[registerFastBridgeBtcTransaction] Returning to liquidity provider"); 2583  generateRejectionRelease(btcTx, lpBtcAddress, fastBridgeFedAddress, new Keccak256(internalTx.getOriginHash()), totalAmount, walletProvider); 2584  return FAST_BRIDGE_REFUNDED_LP_ERROR_CODE; 2585  } else { 2586  logger.debug("[registerFastBridgeBtcTransaction] Returning to user"); 2587  generateRejectionRelease(btcTx, userRefundAddress, fastBridgeFedAddress, new Keccak256(internalTx.getOriginHash()), totalAmount, walletProvider); 2588  return FAST_BRIDGE_REFUNDED_USER_ERROR_CODE; 2589  } 2590  } 2591  2592  transferTo(lbcAddress, co.rsk.core.Coin.fromBitcoin(totalAmount)); 2593  2594  saveFastBridgeDataInStorage( 2595  btcTxHashWithoutWitness, 2596  fastBridgeDerivationHash, 2597  fastBridgeFederationInformation, 2598  getUTXOsForAddress(btcTx, fastBridgeFedAddress) 2599  ); 2600  2601  logger.info("[registerFastBridgeBtcTransaction] (btcTx:{}) transaction registered successfully", btcTxHashWithoutWitness); 2602  2603  return totalAmount.getValue(); 2604  } 2605  2606  protected FastBridgeFederationInformation createFastBridgeFederationInformation(Keccak256 fastBridgeDerivationHash) { 2607  Script fastBridgeScript = FastBridgeRedeemScriptParser.createMultiSigFastBridgeRedeemScript( 2608  getActiveFederation().getRedeemScript(), 2609  Sha256Hash.wrap(fastBridgeDerivationHash.getBytes()) 2610  ); 2611  2612  Script fastBridgeScriptHash = ScriptBuilder.createP2SHOutputScript(fastBridgeScript); 2613  2614  return new FastBridgeFederationInformation( 2615  fastBridgeDerivationHash, 2616  getActiveFederation().getP2SHScript().getPubKeyHash(), 2617  fastBridgeScriptHash.getPubKeyHash() 2618  ); 2619  } 2620  2621  private WalletProvider createFastBridgeWalletProvider( 2622  FastBridgeFederationInformation fastBridgeFederationInformation) { 2623  return (BtcTransaction a, Address b) -> { 2624  List<UTXO> utxosList = getUTXOsForAddress(a, b); 2625  return getFastBridgeWallet(btcContext, utxosList, fastBridgeFederationInformation); 2626  }; 2627  } 2628  2629  protected List<UTXO> getUTXOsForAddress(BtcTransaction btcTx, Address btcAddress) { 2630  List<UTXO> utxosList = new ArrayList<>(); 2631  for (TransactionOutput o : btcTx.getOutputs()) { 2632  if (o.getScriptPubKey().getToAddress(bridgeConstants.getBtcParams()).equals(btcAddress)) { 2633  utxosList.add( 2634  new UTXO( 2635  btcTx.getHash(), 2636  o.getIndex(), 2637  o.getValue(), 2638  0, 2639  btcTx.isCoinBase(), 2640  o.getScriptPubKey() 2641  ) 2642  ); 2643  } 2644  } 2645  2646  return utxosList; 2647  } 2648  2649  protected Wallet getFastBridgeWallet(Context btcContext, List<UTXO> utxos, FastBridgeFederationInformation fb) { 2650  Wallet wallet = new FastBridgeCompatibleBtcWalletWithSingleScript(btcContext, getLiveFederations(), fb); 2651  RskUTXOProvider utxoProvider = new RskUTXOProvider(btcContext.getParams(), utxos); 2652  wallet.setUTXOProvider(utxoProvider); 2653  wallet.setCoinSelector(new RskAllowUnconfirmedCoinSelector()); 2654  return wallet; 2655  } 2656  2657  protected Keccak256 getFastBridgeDerivationHash( 2658  Keccak256 derivationArgumentsHash, 2659  Address userRefundAddress, 2660  Address lpBtcAddress, 2661  RskAddress lbcAddress 2662  ) { 2663  byte[] fastBridgeDerivationHashData = derivationArgumentsHash.getBytes(); 2664  byte[] userRefundAddressBytes = getBytesFromBtcAddress(userRefundAddress); 2665  byte[] lpBtcAddressBytes = getBytesFromBtcAddress(lpBtcAddress); 2666  byte[] lbcAddressBytes = lbcAddress.getBytes(); 2667  byte[] result = new byte[fastBridgeDerivationHashData.length + 2668  userRefundAddressBytes.length + lpBtcAddressBytes.length + lbcAddressBytes.length]; 2669  2670  int dstPosition = 0; 2671  2672  System.arraycopy( 2673  fastBridgeDerivationHashData, 2674  0, 2675  result, 2676  dstPosition, 2677  fastBridgeDerivationHashData.length 2678  ); 2679  2680  dstPosition += fastBridgeDerivationHashData.length; 2681  2682  System.arraycopy( 2683  userRefundAddressBytes, 2684  0, 2685  result, 2686  dstPosition, 2687  userRefundAddressBytes.length 2688  ); 2689  2690  dstPosition += userRefundAddressBytes.length; 2691  2692  System.arraycopy( 2693  lbcAddressBytes, 2694  0, 2695  result, 2696  dstPosition, 2697  lbcAddressBytes.length 2698  ); 2699  2700  dstPosition += lbcAddressBytes.length; 2701  2702  System.arraycopy( 2703  lpBtcAddressBytes, 2704  0, 2705  result, 2706  dstPosition, 2707  lpBtcAddressBytes.length 2708  ); 2709  return new Keccak256(HashUtil.keccak256(result)); 2710  } 2711  2712  protected byte[] getBytesFromBtcAddress(Address btcAddress) { 2713  byte[] hash160 = btcAddress.getHash160(); 2714  byte[] version = BigInteger.valueOf(btcAddress.getVersion()).toByteArray(); 2715  byte[] btcAddressBytes = new byte[hash160.length + version.length]; 2716  System.arraycopy(version, 0, btcAddressBytes, 0, version.length); 2717  System.arraycopy(hash160, 0, btcAddressBytes, version.length, hash160.length); 2718  2719  return btcAddressBytes; 2720  } 2721  2722  protected Coin getAmountSentToAddress(BtcTransaction btcTx, Address btcAddress) { 2723  Coin v = Coin.ZERO; 2724  for (TransactionOutput o : btcTx.getOutputs()) { 2725  if (o.getScriptPubKey().getToAddress(bridgeConstants.getBtcParams()).equals(btcAddress)) { 2726  v = v.add(o.getValue()); 2727  } 2728  } 2729  return v; 2730  } 2731  2732  // This method will be used by registerBtcTransfer to save all the data required on storage (utxos, btcTxHash-derivationHash), 2733  // and will look like. 2734  protected void saveFastBridgeDataInStorage( 2735  Sha256Hash btcTxHash, 2736  Keccak256 derivationHash, 2737  FastBridgeFederationInformation fastBridgeFederationInformation, 2738  List<UTXO> utxosList) throws IOException { 2739  provider.markFastBridgeFederationDerivationHashAsUsed(btcTxHash, derivationHash); 2740  provider.setFastBridgeFederationInformation(fastBridgeFederationInformation); 2741  for (UTXO utxo : utxosList) { 2742  getActiveFederationBtcUTXOs().add(utxo); 2743  } 2744  } 2745  2746  private StoredBlock getBtcBlockchainChainHead() throws IOException, BlockStoreException { 2747  // Gather the current btc chain's head 2748  // IMPORTANT: we assume that getting the chain head from the btc blockstore 2749  // is enough since we're not manipulating the blockchain here, just querying it. 2750  this.ensureBtcBlockStore(); 2751  return btcBlockStore.getChainHead(); 2752  } 2753  2754  /** 2755  * Returns the first bitcoin block we have. It is either a checkpoint or the genesis 2756  */ 2757  private StoredBlock getLowestBlock() throws IOException { 2758  InputStream checkpoints = this.getCheckPoints(); 2759  if (checkpoints == null) { 2760  BtcBlock genesis = bridgeConstants.getBtcParams().getGenesisBlock(); 2761  return new StoredBlock(genesis, genesis.getWork(), 0); 2762  } 2763  CheckpointManager manager = new CheckpointManager(bridgeConstants.getBtcParams(), checkpoints); 2764  long time = getActiveFederation().getCreationTime().toEpochMilli(); 2765  // Go back 1 week to match CheckpointManager.checkpoint() behaviour 2766  time -= 86400 * 7; 2767  return manager.getCheckpointBefore(time); 2768  } 2769  2770  private Pair<BtcTransaction, List<UTXO>> createMigrationTransaction(Wallet originWallet, Address destinationAddress) { 2771  Coin expectedMigrationValue = originWallet.getBalance(); 2772  logger.debug("[createMigrationTransaction] Balance to migrate: {}", expectedMigrationValue); 2773  for(;;) { 2774  BtcTransaction migrationBtcTx = new BtcTransaction(originWallet.getParams()); 2775  migrationBtcTx.addOutput(expectedMigrationValue, destinationAddress); 2776  2777  SendRequest sr = SendRequest.forTx(migrationBtcTx); 2778  sr.changeAddress = destinationAddress; 2779  sr.feePerKb = getFeePerKb(); 2780  sr.missingSigsMode = Wallet.MissingSigsMode.USE_OP_ZERO; 2781  sr.recipientsPayFees = true; 2782  try { 2783  originWallet.completeTx(sr); 2784  for (TransactionInput transactionInput : migrationBtcTx.getInputs()) { 2785  transactionInput.disconnect(); 2786  } 2787  2788  List<UTXO> selectedUTXOs = originWallet 2789  .getUTXOProvider().getOpenTransactionOutputs(originWallet.getWatchedAddresses()).stream() 2790  .filter(utxo -> 2791  migrationBtcTx.getInputs().stream().anyMatch(input -> 2792  input.getOutpoint().getHash().equals(utxo.getHash()) && 2793  input.getOutpoint().getIndex() == utxo.getIndex() 2794  ) 2795  ) 2796  .collect(Collectors.toList()); 2797  2798  return Pair.of(migrationBtcTx, selectedUTXOs); 2799  } catch (InsufficientMoneyException | Wallet.ExceededMaxTransactionSize | Wallet.CouldNotAdjustDownwards e) { 2800  expectedMigrationValue = expectedMigrationValue.divide(2); 2801  } catch(Wallet.DustySendRequested e) { 2802  throw new IllegalStateException("Retiring federation wallet cannot be emptied", e); 2803  } catch (UTXOProviderException e) { 2804  throw new RuntimeException("Unexpected UTXO provider error", e); 2805  } 2806  } 2807  } 2808  2809  // Make sure the local bitcoin blockchain is instantiated 2810  private void ensureBtcBlockChain() throws IOException, BlockStoreException { 2811  this.ensureBtcBlockStore(); 2812  2813  if (this.btcBlockChain == null) { 2814  this.btcBlockChain = new BtcBlockChain(btcContext, btcBlockStore); 2815  } 2816  } 2817  2818  // Make sure the local bitcoin blockstore is instantiated 2819  private void ensureBtcBlockStore() throws IOException, BlockStoreException { 2820  if(btcBlockStore == null) { 2821  btcBlockStore = btcBlockStoreFactory.newInstance( 2822  rskRepository, 2823  bridgeConstants, 2824  provider, 2825  activations 2826  ); 2827  NetworkParameters btcParams = this.bridgeConstants.getBtcParams(); 2828  2829  if (this.btcBlockStore.getChainHead().getHeader().getHash().equals(btcParams.getGenesisBlock().getHash())) { 2830  // We are building the blockstore for the first time, so we have not set the checkpoints yet. 2831  long time = federationSupport.getActiveFederation().getCreationTime().toEpochMilli(); 2832  InputStream checkpoints = this.getCheckPoints(); 2833  if (time > 0 && checkpoints != null) { 2834  CheckpointManager.checkpoint(btcParams, checkpoints, this.btcBlockStore, time); 2835  } 2836  } 2837  } 2838  } 2839  2840  private Address getParsedAddress(String base58Address) throws AddressFormatException { 2841  return Address.fromBase58(btcContext.getParams(), base58Address); 2842  } 2843  2844  private void generateRejectionRelease( 2845  BtcTransaction btcTx, 2846  Address btcRefundAddress, 2847  Address spendingAddress, 2848  Keccak256 rskTxHash, 2849  Coin totalAmount, 2850  WalletProvider walletProvider) throws IOException { 2851  2852  ReleaseTransactionBuilder txBuilder = new ReleaseTransactionBuilder( 2853  btcContext.getParams(), 2854  walletProvider.provide(btcTx, spendingAddress), 2855  btcRefundAddress, 2856  getFeePerKb(), 2857  activations 2858  ); 2859  2860  Optional<ReleaseTransactionBuilder.BuildResult> buildReturnResult = txBuilder.buildEmptyWalletTo(btcRefundAddress); 2861  if (buildReturnResult.isPresent()) { 2862  if (activations.isActive(ConsensusRule.RSKIP146)) { 2863  provider.getReleaseTransactionSet().add(buildReturnResult.get().getBtcTx(), rskExecutionBlock.getNumber(), rskTxHash); 2864  eventLogger.logReleaseBtcRequested(rskTxHash.getBytes(), buildReturnResult.get().getBtcTx(), totalAmount); 2865  } else { 2866  provider.getReleaseTransactionSet().add(buildReturnResult.get().getBtcTx(), rskExecutionBlock.getNumber()); 2867  } 2868  logger.info("Rejecting peg-in: return tx build successful to {}. Tx {}. Value {}.", btcRefundAddress, rskTxHash, totalAmount); 2869  } else { 2870  logger.warn("Rejecting peg-in: return tx build for btc tx {} error. Return was to {}. Tx {}. Value {}", btcTx.getHash(), btcRefundAddress, rskTxHash, totalAmount); 2871  panicProcessor.panic("peg-in-refund", String.format("peg-in money return tx build for btc tx %s error. Return was to %s. Tx %s. Value %s", btcTx.getHash(), btcRefundAddress, rskTxHash, totalAmount)); 2872  } 2873  } 2874  2875  private void generateRejectionRelease( 2876  BtcTransaction btcTx, 2877  Address senderBtcAddress, 2878  Transaction rskTx, 2879  Coin totalAmount 2880  ) throws IOException { 2881  WalletProvider createWallet = (BtcTransaction a, Address b) -> { 2882  // Build the list of UTXOs in the BTC transaction sent to either the active 2883  // or retiring federation 2884  List<UTXO> utxosToUs = btcTx.getWalletOutputs( 2885  getNoSpendWalletForLiveFederations(false) 2886  ) 2887  .stream() 2888  .map(output -> 2889  new UTXO( 2890  btcTx.getHash(), 2891  output.getIndex(), 2892  output.getValue(), 2893  0, 2894  btcTx.isCoinBase(), 2895  output.getScriptPubKey() 2896  ) 2897  ).collect(Collectors.toList()); 2898  // Use the list of UTXOs to build a transaction builder 2899  // for the return btc transaction generation 2900  return getUTXOBasedWalletForLiveFederations(utxosToUs, false); 2901  }; 2902  2903  generateRejectionRelease(btcTx, senderBtcAddress, null, rskTx.getHash(), totalAmount, createWallet); 2904  } 2905  2906  private boolean verifyLockSenderIsWhitelisted(Address senderBtcAddress, Coin totalAmount, int height) { 2907  // If the address is not whitelisted, then return the funds 2908  // using the exact same utxos sent to us. 2909  // That is, build a release transaction and get it in the release transaction set. 2910  // Otherwise, transfer SBTC to the sender of the BTC 2911  // The RSK account to update is the one that matches the pubkey "spent" on the first bitcoin tx input 2912  LockWhitelist lockWhitelist = provider.getLockWhitelist(); 2913  if (!lockWhitelist.isWhitelistedFor(senderBtcAddress, totalAmount, height)) { 2914  logger.info("Rejecting lock. Address {} is not whitelisted.", senderBtcAddress); 2915  return false; 2916  } 2917  2918  // Consume this whitelisted address 2919  lockWhitelist.consume(senderBtcAddress); 2920  2921  return true; 2922  } 2923  2924  private boolean verifyLockDoesNotSurpassLockingCap(BtcTransaction btcTx, Coin totalAmount) { 2925  if (!activations.isActive(ConsensusRule.RSKIP134)) { 2926  return true; 2927  } 2928  2929  Coin fedCurrentFunds = getBtcLockedInFederation(); 2930  Coin lockingCap = this.getLockingCap(); 2931  logger.trace("Evaluating locking cap for: TxId {}. Value to lock {}. Current funds {}. Current locking cap {}", btcTx.getHash(true), totalAmount, fedCurrentFunds, lockingCap); 2932  Coin fedUTXOsAfterThisLock = fedCurrentFunds.add(totalAmount); 2933  // If the federation funds (including this new UTXO) are smaller than or equals to the current locking cap, we are fine. 2934  if (fedUTXOsAfterThisLock.compareTo(lockingCap) <= 0) { 2935  return true; 2936  } 2937  2938  logger.info("locking cap exceeded! btc Tx {}", btcTx); 2939  return false; 2940  } 2941  2942  private Coin getBtcLockedInFederation() { 2943  Coin maxRbtc = this.bridgeConstants.getMaxRbtc(); 2944  Coin currentBridgeBalance = rskRepository.getBalance(PrecompiledContracts.BRIDGE_ADDR).toBitcoin(); 2945  2946  return maxRbtc.subtract(currentBridgeBalance); 2947  } 2948  2949  @VisibleForTesting 2950  protected boolean isBlockMerkleRootValid(Sha256Hash merkleRoot, BtcBlock blockHeader) { 2951  boolean isValid = false; 2952  2953  if (blockHeader.getMerkleRoot().equals(merkleRoot)) { 2954  logger.trace("block merkle root is valid"); 2955  isValid = true; 2956  } 2957  else { 2958  if (activations.isActive(ConsensusRule.RSKIP143)) { 2959  CoinbaseInformation coinbaseInformation = provider.getCoinbaseInformation(blockHeader.getHash()); 2960  if (coinbaseInformation == null) { 2961  logger.trace("coinbase information for block {} is not yet registered", blockHeader.getHash()); 2962  } 2963  isValid = coinbaseInformation != null && coinbaseInformation.getWitnessMerkleRoot().equals(merkleRoot); 2964  logger.trace("witness merkle root is {} valid", (isValid ? "":"NOT")); 2965  } else { 2966  logger.trace("RSKIP143 is not active, avoid checking witness merkle root"); 2967  } 2968  } 2969  return isValid; 2970  } 2971  2972  @VisibleForTesting 2973  protected boolean validationsForRegisterBtcTransaction(Sha256Hash btcTxHash, int height, byte[] pmtSerialized, byte[] btcTxSerialized) 2974  throws BlockStoreException, VerificationException.EmptyInputsOrOutputs, BridgeIllegalArgumentException { 2975  2976  // Validates height and confirmations for tx 2977  try { 2978  int acceptableConfirmationsAmount = bridgeConstants.getBtc2RskMinimumAcceptableConfirmations(); 2979  if (!BridgeUtils.validateHeightAndConfirmations(height, getBtcBlockchainBestChainHeight(), acceptableConfirmationsAmount, btcTxHash)) { 2980  return false; 2981  } 2982  } catch (Exception e) { 2983  String panicMessage = String.format("Btc Tx %s Supplied Height is %d but should be greater than 0", btcTxHash, height); 2984  logger.warn(panicMessage); 2985  panicProcessor.panic("btclock", panicMessage); 2986  return false; 2987  } 2988  2989  // Validates pmt size 2990  if (!PartialMerkleTreeFormatUtils.hasExpectedSize(pmtSerialized)) { 2991  throw new BridgeIllegalArgumentException("PartialMerkleTree doesn't have expected size"); 2992  } 2993  2994  // Calculates merkleRoot 2995  Sha256Hash merkleRoot; 2996  try { 2997  NetworkParameters networkParameters = bridgeConstants.getBtcParams(); 2998  merkleRoot = BridgeUtils.calculateMerkleRoot(networkParameters, pmtSerialized, btcTxHash); 2999  if (merkleRoot == null) { 3000  return false; 3001  } 3002  } catch (VerificationException e) { 3003  throw new BridgeIllegalArgumentException(e.getMessage(), e); 3004  } 3005  3006  // Validates inputs count 3007  logger.info("Going to validate inputs for btc tx {}", btcTxHash); 3008  BridgeUtils.validateInputsCount(btcTxSerialized, activations.isActive(ConsensusRule.RSKIP143)); 3009  3010  // Check the the merkle root equals merkle root of btc block at specified height in the btc best chain 3011  // BTC blockstore is available since we've already queried the best chain height 3012  logger.trace("Getting btc block at height: {}", height); 3013  BtcBlock blockHeader = btcBlockStore.getStoredBlockAtMainChainHeight(height).getHeader(); 3014  logger.trace("Validating block merkle root at height: {}", height); 3015  if (!isBlockMerkleRootValid(merkleRoot, blockHeader)){ 3016  String panicMessage = String.format( 3017  "Btc Tx %s Supplied merkle root %s does not match block's merkle root %s", 3018  btcTxHash.toString(), 3019  merkleRoot, 3020  blockHeader.getMerkleRoot() 3021  ); 3022  logger.warn(panicMessage); 3023  panicProcessor.panic("btclock", panicMessage); 3024  return false; 3025  } 3026  3027  return true; 3028  } 3029  3030  private Coin computeTotalAmountSent(BtcTransaction btcTx) throws IOException { 3031  // Compute the total amount sent. Value could have been sent both to the 3032  // currently active federation as well as to the currently retiring federation. 3033  // Add both amounts up in that case. 3034  Coin amountToActive = btcTx.getValueSentToMe(getActiveFederationWallet(false)); 3035  Coin amountToRetiring = Coin.ZERO; 3036  Wallet retiringFederationWallet = getRetiringFederationWallet(false); 3037  if (retiringFederationWallet != null) { 3038  amountToRetiring = btcTx.getValueSentToMe(retiringFederationWallet); 3039  } 3040  return amountToActive.add(amountToRetiring); 3041  } 3042  3043  private static byte[] serializeBlockHeader(StoredBlock block) { 3044  if (block == null) { 3045  return ByteUtil.EMPTY_BYTE_ARRAY; 3046  } 3047  3048  byte[] bytes = block.getHeader().unsafeBitcoinSerialize(); 3049  3050  byte[] header = new byte[80]; 3051  3052  System.arraycopy(bytes, 0, header, 0, 80); 3053  3054  return header; 3055  } 3056 }