Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix incorrect Tx hash for some mainnet transaction #30

Merged
merged 4 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.bloxbean.cardano.yaci.core.config;

/**
* YaciConfig is a singleton class that holds the configuration for Yaci.
*/
public enum YaciConfig {
INSTANCE;

private boolean returnBlockCbor;
private boolean returnTxBodyCbor;

YaciConfig() {
returnBlockCbor = false;
returnTxBodyCbor = false;
}

/**
* Returns true if the block cbor is returned
* @return
*/
public boolean isReturnBlockCbor() {
return returnBlockCbor;
}

/**
* Set to true to return block cbor
* @param returnBlockCbor
*/
public void setReturnBlockCbor(boolean returnBlockCbor) {
this.returnBlockCbor = returnBlockCbor;
}

/**
* Returns true if the transaction body cbor is returned
* @return
*/
public boolean isReturnTxBodyCbor() {
return returnTxBodyCbor;
}

/**
*
* @param returnTxBodyCbor
*/
public void setReturnTxBodyCbor(boolean returnTxBodyCbor) {
this.returnTxBodyCbor = returnTxBodyCbor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ public class Block {
private List<Witnesses> transactionWitness = new ArrayList<>();
private Map<Integer, AuxData> auxiliaryDataMap = new LinkedHashMap();
private List<Integer> invalidTransactions = new ArrayList<>();

private String cbor;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
public class TransactionBody {
//Derived
private String txHash;
private String cbor; //tx body cbor

private Set<TransactionInput> inputs;
private List<TransactionOutput> outputs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
public class ByronEbBlock {
private ByronEbHead header;
private ByronEbBody body;
private String cbor;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
public class ByronMainBlock {
private ByronBlockHead header;
private ByronBlockBody body;
private String cbor;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import co.nstant.in.cbor.model.*;
import com.bloxbean.cardano.yaci.core.common.EraUtil;
import com.bloxbean.cardano.yaci.core.config.YaciConfig;
import com.bloxbean.cardano.yaci.core.model.*;
import com.bloxbean.cardano.yaci.core.protocol.Serializer;
import com.bloxbean.cardano.yaci.core.util.CborSerializationUtil;
import com.bloxbean.cardano.yaci.core.util.HexUtil;
import com.bloxbean.cardano.yaci.core.util.Tuple;

import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -19,11 +22,10 @@ public enum BlockSerializer implements Serializer<Block> {
@Override
public Block deserialize(byte[] bytes) {
DataItem dataItem = CborSerializationUtil.deserializeOne(bytes);
return deserializeDI(dataItem);
return deserializeBlock(dataItem, bytes);
}

@Override
public Block deserializeDI(DataItem di) {
private Block deserializeBlock(DataItem di, byte[] blockBody) {
Array array = (Array) di;
int eraValue = ((UnsignedInteger)array.getDataItems().get(0)).getValue().intValue();
Era era = EraUtil.getEra(eraValue);
Expand All @@ -38,6 +40,7 @@ public Block deserializeDI(DataItem di) {
blockBuilder.header(blockHeader);

//transaction bodies 1
/**
Array txnBodiesArr = (Array) blockArray.getDataItems().get(1);

List<TransactionBody> txnBodies = new ArrayList<>();
Expand All @@ -47,6 +50,15 @@ public Block deserializeDI(DataItem di) {
TransactionBody txBody = TransactionBodySerializer.INSTANCE.deserializeDI(txnBodyDI);
txnBodies.add(txBody);
}
**/

//Extract transaction bodies from block bytes directly to keep the tx hash same
List<Tuple<DataItem, byte[]>> txBodyTuples = TransactionBodyExtractor.getTxBodiesFromBlock(blockBody);
List<TransactionBody> txnBodies = new ArrayList<>();
for (var tuple: txBodyTuples) {
TransactionBody txBody = TransactionBodySerializer.INSTANCE.deserializeDI(tuple._1, tuple._2);
txnBodies.add(txBody);
}
blockBuilder.transactionBodies(txnBodies);

//witnesses
Expand Down Expand Up @@ -88,6 +100,10 @@ public Block deserializeDI(DataItem di) {
blockBuilder.invalidTransactions(invalidTransactions);
}

if (YaciConfig.INSTANCE.isReturnBlockCbor()) {
blockBuilder.cbor(HexUtil.encodeHexString(blockBody));
}

return blockBuilder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.bloxbean.cardano.client.crypto.Blake2bUtil;
import com.bloxbean.cardano.client.util.JsonUtil;
import com.bloxbean.cardano.yaci.core.common.EraUtil;
import com.bloxbean.cardano.yaci.core.config.YaciConfig;
import com.bloxbean.cardano.yaci.core.model.Epoch;
import com.bloxbean.cardano.yaci.core.model.Era;
import com.bloxbean.cardano.yaci.core.model.byron.*;
Expand All @@ -27,7 +28,13 @@
public enum ByronBlockSerializer implements Serializer<ByronMainBlock> {
INSTANCE;

public ByronMainBlock deserializeDI(DataItem di) {
@Override
public ByronMainBlock deserialize(byte[] bytes) {
DataItem dataItem = CborSerializationUtil.deserializeOne(bytes);
return deserializeByronBlock(dataItem, bytes);
}

private ByronMainBlock deserializeByronBlock(DataItem di, byte[] blockBytes) {
Array array = (Array) di;
int eraValue = ((UnsignedInteger) array.getDataItems().get(0)).getValue().intValue();
Era era = EraUtil.getEra(eraValue);
Expand All @@ -47,9 +54,12 @@ public ByronMainBlock deserializeDI(DataItem di) {
ByronBlockHead header = deserializeHeader(headerArr);
ByronBlockBody body = deserializeBlockBody(bodyArr);

String cbor = YaciConfig.INSTANCE.isReturnBlockCbor()? HexUtil.encodeHexString(blockBytes) : null;

return ByronMainBlock.builder()
.header(header)
.body(body)
.cbor(cbor)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.bloxbean.cardano.client.crypto.Blake2bUtil;
import com.bloxbean.cardano.client.util.JsonUtil;
import com.bloxbean.cardano.yaci.core.common.EraUtil;
import com.bloxbean.cardano.yaci.core.config.YaciConfig;
import com.bloxbean.cardano.yaci.core.model.Era;
import com.bloxbean.cardano.yaci.core.model.byron.ByronEbBlock;
import com.bloxbean.cardano.yaci.core.model.byron.ByronEbBlockCons;
Expand All @@ -25,7 +26,12 @@ public enum ByronEbBlockSerializer implements Serializer<ByronEbBlock> {
INSTANCE;

@Override
public ByronEbBlock deserializeDI(DataItem di) {
public ByronEbBlock deserialize(byte[] bytes) {
DataItem dataItem = CborSerializationUtil.deserializeOne(bytes);
return deserializeByronEbBlock(dataItem, bytes);
}

private ByronEbBlock deserializeByronEbBlock(DataItem di, byte[] blockBody) {
Array array = (Array) di;
int eraValue = ((UnsignedInteger) array.getDataItems().get(0)).getValue().intValue();
Era era = EraUtil.getEra(eraValue);
Expand All @@ -42,9 +48,12 @@ public ByronEbBlock deserializeDI(DataItem di) {
ByronEbBody body = deserializeBody(bodyArr);
//TODO -- Other fields

String cbor = YaciConfig.INSTANCE.isReturnBlockCbor()? HexUtil.encodeHexString(blockBody) : null;

return ByronEbBlock.builder()
.header(header)
.body(body)
.cbor(cbor)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package com.bloxbean.cardano.yaci.core.model.serializers;

import co.nstant.in.cbor.CborDecoder;
import co.nstant.in.cbor.CborException;
import co.nstant.in.cbor.model.AdditionalInformation;
import co.nstant.in.cbor.model.DataItem;
import co.nstant.in.cbor.model.Special;
import com.bloxbean.cardano.yaci.core.util.Tuple;
import lombok.SneakyThrows;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;

//This is only for Shelley and post Shelley era blocks
public class TransactionBodyExtractor {

private static final int INFINITY = -1;

@SneakyThrows
public static List<Tuple<DataItem, byte[]>> getTxBodiesFromBlock(byte[] blockBody) {
List<Tuple<DataItem, byte[]>> txBodyTuples = new ArrayList<>();
ByteArrayInputStream bais = new ByteArrayInputStream(blockBody);
CborDecoder decoder = new CborDecoder(bais);

//era and body
bais.read();
decoder.decodeNext();
//body
bais.read();
decoder.decodeNext(); // skip header

//tx bodies
int arrTxBodySize = bais.read();

long length = getLength(arrTxBodySize,getSymbolBytes(blockBody.length - bais.available(),blockBody));
int start = blockBody.length - bais.available();
for(int i = 0 ; i < length; i++){
int previous = bais.available();
DataItem dataItem = decoder.decodeNext();
byte[] txBodyRaw = new byte[previous - bais.available()];
System.arraycopy(blockBody,start,txBodyRaw,0,txBodyRaw.length);
txBodyTuples.add(new Tuple<>(dataItem, txBodyRaw));
start = blockBody.length - bais.available();
}
if(AdditionalInformation.INDEFINITE.equals(AdditionalInformation.ofByte(arrTxBodySize))) {
for (;;) {
int previous = bais.available();
var dataItem = decoder.decodeNext();
if (dataItem == null) {
throw new CborException("Unexpected end of stream");
}
if (Special.BREAK.equals(dataItem)) {
break;
}
byte[] txBodyRaw = new byte[previous - bais.available()];
System.arraycopy(blockBody, start, txBodyRaw, 0, txBodyRaw.length);
txBodyTuples.add(new Tuple<>(dataItem, txBodyRaw));
start = blockBody.length - bais.available();
}
}
return txBodyTuples;
}

private static byte[] getSymbolBytes(int start, byte[] src){
if(start >= src.length){
return new byte[]{};
}
byte[] symbol = new byte[src.length - start];
System.arraycopy(src,start,symbol,0,src.length - start);
return symbol;
}

private static long getLength(int initialByte, byte[] symbols) throws CborException {
switch (AdditionalInformation.ofByte(initialByte)) {
case DIRECT:
return initialByte & 31;
case ONE_BYTE:
return symbols[0];
case TWO_BYTES:
long twoByteValue = 0;
twoByteValue |= (symbols[0] & 0xFF) << 8;
twoByteValue |= (symbols[1] & 0xFF) << 0;
return twoByteValue;
case FOUR_BYTES:
long fourByteValue = 0L;
fourByteValue |= (long) (symbols[0] & 0xFF) << 24;
fourByteValue |= (long) (symbols[1] & 0xFF) << 16;
fourByteValue |= (long) (symbols[2] & 0xFF) << 8;
fourByteValue |= (long) (symbols[3] & 0xFF) << 0;
return fourByteValue;
case EIGHT_BYTES:
long eightByteValue = 0;
eightByteValue |= (long) (symbols[0] & 0xFF) << 56;
eightByteValue |= (long) (symbols[1] & 0xFF) << 48;
eightByteValue |= (long) (symbols[2] & 0xFF) << 40;
eightByteValue |= (long) (symbols[3] & 0xFF) << 32;
eightByteValue |= (long) (symbols[4] & 0xFF) << 24;
eightByteValue |= (long) (symbols[5] & 0xFF) << 16;
eightByteValue |= (long) (symbols[6] & 0xFF) << 8;
eightByteValue |= (long) (symbols[7] & 0xFF) << 0;
return eightByteValue;
case INDEFINITE:
return INFINITY;
case RESERVED:
default:
throw new CborException("Reserved additional information");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import co.nstant.in.cbor.model.*;
import co.nstant.in.cbor.model.Map;
import com.bloxbean.cardano.client.api.util.AssetUtil;
import com.bloxbean.cardano.yaci.core.config.YaciConfig;
import com.bloxbean.cardano.yaci.core.model.*;
import com.bloxbean.cardano.yaci.core.model.certs.Certificate;
import com.bloxbean.cardano.yaci.core.protocol.Serializer;
Expand All @@ -20,16 +21,19 @@
public enum TransactionBodySerializer implements Serializer<TransactionBody> {
INSTANCE;

@Override
public TransactionBody deserializeDI(DataItem di) {
public TransactionBody deserializeDI(DataItem di, byte[] txBytes) {
Map bodyMap = (Map) di;

TransactionBody.TransactionBodyBuilder transactionBodyBuilder = TransactionBody.builder();

//derive
String txHash = TxUtil.calculateTxHash(CborSerializationUtil.serialize(di, false)); //disable canonical ordering
String txHash = TxUtil.calculateTxHash(txBytes);
transactionBodyBuilder.txHash(txHash);

if (YaciConfig.INSTANCE.isReturnTxBodyCbor()) {
transactionBodyBuilder.cbor(HexUtil.encodeHexString(txBytes));
}

Array inputArray = (Array)bodyMap.get(new UnsignedInteger(0));
Set<TransactionInput> inputs = new LinkedHashSet<>();
for(DataItem inputItem: inputArray.getDataItems()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ private void onReceiveBlocks(MsgBlock message) {

if (era == Era.Byron) {
if (eraValue == 0) { //Epoch boundry block
ByronEbBlock block = ByronEbBlockSerializer.INSTANCE.deserializeDI(array);
ByronEbBlock block = ByronEbBlockSerializer.INSTANCE.deserialize(body);

//move from cursor
counter++;
Expand All @@ -113,7 +113,7 @@ private void onReceiveBlocks(MsgBlock message) {
block.getHeader().getConsensusData().getEpoch(), 0);
this.from = new Point(absoluteSlot, block.getHeader().getBlockHash());
} else if (eraValue == 1) {
ByronMainBlock block = ByronBlockSerializer.INSTANCE.deserializeDI(array);
ByronMainBlock block = ByronBlockSerializer.INSTANCE.deserialize(body);

//move from cursor
counter++;
Expand All @@ -125,7 +125,7 @@ private void onReceiveBlocks(MsgBlock message) {
this.from = new Point(absoluteSlot, block.getHeader().getBlockHash());
}
} else {
Block block = BlockSerializer.INSTANCE.deserializeDI(array);
Block block = BlockSerializer.INSTANCE.deserialize(body);
if (log.isDebugEnabled())
log.info("Block >> {}, {}, {}", eraValue, block.getHeader().getHeaderBody().getBlockNumber(), block.getHeader().getHeaderBody().getSlot());

Expand Down
Loading
Loading