Skip to content

Commit dd4a20f

Browse files
authored
Merge pull request #360 from rsksmart/coinbase-refactor
Coinbase refactor
2 parents 6a99ec5 + ddbc553 commit dd4a20f

File tree

2 files changed

+154
-117
lines changed

2 files changed

+154
-117
lines changed

src/main/java/co/rsk/federate/BtcToRskClient.java

Lines changed: 78 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,33 @@
33
import static com.google.common.base.Preconditions.checkNotNull;
44

55
import co.rsk.bitcoinj.core.BtcTransaction;
6-
import co.rsk.federate.config.PowpegNodeSystemProperties;
7-
import co.rsk.peg.constants.BridgeConstants;
86
import co.rsk.federate.adapter.ThinConverter;
9-
import co.rsk.federate.bitcoin.BitcoinWrapper;
10-
import co.rsk.federate.bitcoin.BlockListener;
11-
import co.rsk.federate.bitcoin.TransactionListener;
12-
import co.rsk.federate.io.BtcToRskClientFileData;
13-
import co.rsk.federate.io.BtcToRskClientFileReadResult;
14-
import co.rsk.federate.io.BtcToRskClientFileStorage;
7+
import co.rsk.federate.bitcoin.*;
8+
import co.rsk.federate.config.PowpegNodeSystemProperties;
9+
import co.rsk.federate.io.*;
1510
import co.rsk.federate.timing.TurnScheduler;
1611
import co.rsk.net.NodeBlockProcessor;
1712
import co.rsk.panic.PanicProcessor;
1813
import co.rsk.peg.BridgeUtils;
1914
import co.rsk.peg.PegUtilsLegacy;
20-
import co.rsk.peg.federation.Federation;
21-
import co.rsk.peg.federation.FederationMember;
2215
import co.rsk.peg.PeginInformation;
16+
import co.rsk.peg.bitcoin.BitcoinUtils;
2317
import co.rsk.peg.btcLockSender.BtcLockSender.TxSenderAddressType;
2418
import co.rsk.peg.btcLockSender.BtcLockSenderProvider;
19+
import co.rsk.peg.constants.BridgeConstants;
20+
import co.rsk.peg.federation.Federation;
21+
import co.rsk.peg.federation.FederationMember;
2522
import co.rsk.peg.pegininstructions.PeginInstructionsException;
2623
import co.rsk.peg.pegininstructions.PeginInstructionsProvider;
2724
import com.google.common.annotations.VisibleForTesting;
2825
import com.google.common.collect.Lists;
2926
import java.io.IOException;
3027
import java.time.Clock;
31-
import java.util.ArrayList;
32-
import java.util.Collections;
33-
import java.util.LinkedList;
34-
import java.util.List;
35-
import java.util.Map;
36-
import java.util.Optional;
37-
import java.util.Set;
38-
import java.util.concurrent.Executors;
39-
import java.util.concurrent.ScheduledExecutorService;
40-
import java.util.concurrent.TimeUnit;
28+
import java.util.*;
29+
import java.util.concurrent.*;
4130
import java.util.stream.IntStream;
4231
import javax.annotation.PreDestroy;
43-
import org.bitcoinj.core.Block;
44-
import org.bitcoinj.core.NetworkParameters;
45-
import org.bitcoinj.core.PartialMerkleTree;
46-
import org.bitcoinj.core.Sha256Hash;
47-
import org.bitcoinj.core.StoredBlock;
48-
import org.bitcoinj.core.Transaction;
49-
import org.bitcoinj.core.Utils;
32+
import org.bitcoinj.core.*;
5033
import org.bitcoinj.store.BlockStoreException;
5134
import org.ethereum.config.blockchain.upgrades.ActivationConfig;
5235
import org.ethereum.config.blockchain.upgrades.ConsensusRule;
@@ -243,13 +226,12 @@ public void updateBridge() {
243226
panicProcessor.panic("btclock", e.getMessage());
244227
}
245228
}
246-
247229
}
248230

249231
@Override
250232
public void onBlock(Block block) {
251233
synchronized (this) {
252-
logger.debug("onBlock {}", block.getHash());
234+
logger.debug("[onBlock] {}", block.getHash());
253235
PartialMerkleTree tree;
254236
Transaction coinbase = null;
255237
boolean dataToWrite = false;
@@ -258,6 +240,7 @@ public void onBlock(Block block) {
258240

259241
if (tx.isCoinBase()) {
260242
// safe keep the coinbase and move on
243+
logger.debug("[onBlock] Transaction {} is the coinbase", tx.getTxId());
261244
coinbase = tx;
262245
continue;
263246
}
@@ -266,57 +249,51 @@ public void onBlock(Block block) {
266249
// this tx is not important move on
267250
continue;
268251
}
252+
logger.debug("[onBlock] Found transaction {} that needs to be registered in the Bridge", tx.getTxId());
269253

270254
List<Proof> proofs = fileData.getTransactionProofs().get(tx.getWTxId());
271255
boolean blockInProofs = proofs.stream().anyMatch(p -> p.getBlockHash().equals(block.getHash()));
272256
if (blockInProofs) {
273-
logger.info("Proof for tx {} in block {} already stored", tx, block.getHash());
257+
logger.info("[onBlock] Proof for tx {} in block {} already stored", tx.getTxId(), block.getHash());
274258
continue;
275259
}
276260

277261
// Always use the wtxid for the lock transactions
278262
tree = generatePMT(block, tx, tx.hasWitnesses());
279263
// If the transaction has a witness, then we need to store the coinbase information to inform it
280264
if (tx.hasWitnesses() && !coinbaseRegistered) {
265+
logger.debug("[onBlock] Transaction {} has witness, will need to store the coinbase information to inform it", tx.getTxId());
281266
// We don't want to generate the PMT with the wtxid for the coinbase
282267
// as it doesn't have a corresponding hash in the witness root
283268
PartialMerkleTree coinbasePmt = generatePMT(block, coinbase, false);
284269
try {
285270
Sha256Hash witnessMerkleRoot = tree.getTxnHashAndMerkleRoot(new ArrayList<>());
286271
CoinbaseInformation coinbaseInformation = new CoinbaseInformation(
287-
coinbase,
288-
witnessMerkleRoot,
289-
block.getHash(),
290-
coinbasePmt
272+
coinbase,
273+
witnessMerkleRoot,
274+
block.getHash(),
275+
coinbasePmt
291276
);
292-
// Validate information
293-
byte[] witnessReservedValue = coinbaseInformation.getCoinbaseWitnessReservedValue();
294-
if (witnessReservedValue == null) {
295-
logger.error("block {} with lock segwit tx {} has coinbase with no witness reserved value. Aborting block processing.", block.getHash(), tx.getWTxId());
296-
// Can't register this transaction, it would be rejected by the Bridge
297-
return;
298-
}
299-
Sha256Hash calculatedWitnessCommitment = Sha256Hash.twiceOf(witnessMerkleRoot.getReversedBytes(), witnessReservedValue);
300-
Sha256Hash witnessCommitment = coinbase.findWitnessCommitment();
301-
if (!witnessCommitment.equals(calculatedWitnessCommitment)) {
302-
logger.error("block {} with lock segwit tx {} generated an invalid witness merkle root", tx.getWTxId(), block.getHash());
303-
// Can't register this transaction, it would be rejected by the Bridge
304-
return;
305-
}
277+
validateCoinbaseInformation(coinbaseInformation);
278+
306279
// store the coinbase
307-
fileData.getCoinbaseInformationMap().put(coinbaseInformation.getBlockHash(), coinbaseInformation);
280+
fileData.getCoinbaseInformationMap().put(
281+
coinbaseInformation.getBlockHash(),
282+
coinbaseInformation
283+
);
284+
logger.debug("[onBlock] Coinbase information for transaction {} successully stored", tx.getTxId());
308285

309286
// Register the coinbase just once per block
310287
coinbaseRegistered = true;
311288
} catch (Exception e) {
312-
logger.error(e.getMessage(), e);
289+
logger.error("[onBlock] {}", e.getMessage());
313290
// Without a coinbase related to this transaction the Bridge would reject the transaction
314291
return;
315292
}
316293
}
317294

318295
proofs.add(new Proof(block.getHash(), tree));
319-
logger.info("New proof for tx {} in block {}", tx, block.getHash());
296+
logger.info("[onBlock] New proof for tx {} in block {}", tx.getTxId(), block.getHash());
320297
dataToWrite = true;
321298
}
322299

@@ -326,17 +303,58 @@ public void onBlock(Block block) {
326303

327304
try {
328305
this.btcToRskClientFileStorage.write(fileData);
329-
logger.info("Stored proofs for block {}", block.getHash());
306+
logger.info("[onBlock] Stored proofs for block {}", block.getHash());
330307
} catch (IOException e) {
331-
logger.error(e.getMessage(), e);
308+
logger.error("[onBlock] {}", e.getMessage());
332309
panicProcessor.panic("btclock", e.getMessage());
333310
}
334311
}
335312
}
336313

314+
private void validateCoinbaseInformation(CoinbaseInformation coinbaseInformation) {
315+
Sha256Hash witnessMerkleRoot = coinbaseInformation.getWitnessRoot();
316+
byte[] witnessReservedValue = coinbaseInformation.getCoinbaseWitnessReservedValue();
317+
BtcTransaction coinbaseTransaction = ThinConverter.toThinInstance(
318+
bridgeConstants.getBtcParams(),
319+
coinbaseInformation.getCoinbaseTransaction()
320+
);
321+
322+
if (witnessReservedValue == null) {
323+
String message = String.format(
324+
"Block %s with segwit peg-in tx %s has coinbase with no witness reserved value. Aborting block processing.",
325+
coinbaseInformation.getBlockHash(),
326+
coinbaseTransaction.getHash()
327+
);
328+
logger.error("[validateCoinbaseInformation] {}", message);
329+
throw new IllegalArgumentException(message);
330+
}
331+
332+
co.rsk.bitcoinj.core.Sha256Hash calculatedWitnessCommitment = co.rsk.bitcoinj.core.Sha256Hash.twiceOf(
333+
witnessMerkleRoot.getReversedBytes(),
334+
witnessReservedValue
335+
);
336+
337+
BitcoinUtils.findWitnessCommitment(coinbaseTransaction)
338+
.filter(commitment -> commitment.equals(calculatedWitnessCommitment))
339+
.orElseThrow(() -> {
340+
String message = String.format(
341+
"Block %s with segwit peg-in tx %s generated an invalid witness merkle root",
342+
coinbaseInformation.getBlockHash(),
343+
coinbaseTransaction.getHash()
344+
);
345+
logger.error("[validateCoinbaseInformation] {}", message);
346+
return new IllegalArgumentException(message);
347+
});
348+
logger.debug(
349+
"[validateCoinbaseInformation] Block {} with segwit peg-in tx {} has a valid witness merkle root",
350+
coinbaseInformation.getBlockHash(),
351+
coinbaseTransaction.getHash()
352+
);
353+
}
354+
337355
@Override
338356
public void onTransaction(Transaction tx) {
339-
logger.debug("onTransaction {}", tx.getWTxId());
357+
logger.debug("[onTransaction] {} (wtxid:{})", tx.getTxId(), tx.getWTxId());
340358
synchronized (this) {
341359
this.fileData.getTransactionProofs().put(tx.getWTxId(), new ArrayList<>());
342360
try {
@@ -425,12 +443,17 @@ protected void markCoinbasesAsReadyToBeInformed(List<Block> informedBlocks) thro
425443
return;
426444
}
427445
boolean modified = false;
428-
for (Block informedBlock:informedBlocks) {
446+
for (Block informedBlock : informedBlocks) {
429447
if (coinbaseInformationMap.containsKey(informedBlock.getHash())) {
430448
CoinbaseInformation coinbaseInformation = coinbaseInformationMap.get(informedBlock.getHash());
431449
coinbaseInformation.setReadyToInform(true);
432450
this.fileData.getCoinbaseInformationMap().put(informedBlock.getHash(), coinbaseInformation);
433451
modified = true;
452+
logger.debug(
453+
"[markCoinbasesAsReadyToBeInformed] Marked coinbase {} for block {} as ready to be informed",
454+
coinbaseInformation.getCoinbaseTransaction().getTxId(),
455+
informedBlock.getHash()
456+
);
434457
}
435458
}
436459
if (!modified) {

0 commit comments

Comments
 (0)