use of bisq.core.dao.state.blockchain.RawTx in project bisq-core by bisq-network.
the class RpcService method getTxFromRawTransaction.
// /////////////////////////////////////////////////////////////////////////////////////////
// Private
// /////////////////////////////////////////////////////////////////////////////////////////
private RawTx getTxFromRawTransaction(RawTransaction rawBtcTx, com.neemre.btcdcli4j.core.domain.RawBlock rawBtcBlock) {
String txId = rawBtcTx.getTxId();
// We convert block time from sec to ms
long blockTime = rawBtcBlock.getTime() * 1000;
int blockHeight = rawBtcBlock.getHeight();
String blockHash = rawBtcBlock.getHash();
final List<TxInput> txInputs = rawBtcTx.getVIn().stream().filter(rawInput -> rawInput != null && rawInput.getVOut() != null && rawInput.getTxId() != null).map(rawInput -> {
// We don't support segWit inputs yet as well as no pay to pubkey txs...
String[] split = rawInput.getScriptSig().getAsm().split("\\[ALL\\] ");
String pubKeyAsHex;
if (split.length == 2) {
pubKeyAsHex = rawInput.getScriptSig().getAsm().split("\\[ALL\\] ")[1];
} else {
// If we receive a pay to pubkey tx the pubKey is not included as
// it is in the output already.
// Bitcoin Core creates payToPubKey tx when spending mined coins (regtest)...
pubKeyAsHex = null;
log.debug("pubKeyAsHex is not set as we received a not supported sigScript " + "(segWit or payToPubKey tx). txId={}, asm={}", rawBtcTx.getTxId(), rawInput.getScriptSig().getAsm());
}
return new TxInput(rawInput.getTxId(), rawInput.getVOut(), pubKeyAsHex);
}).collect(Collectors.toList());
final List<RawTxOutput> txOutputs = rawBtcTx.getVOut().stream().filter(e -> e != null && e.getN() != null && e.getValue() != null && e.getScriptPubKey() != null).map(rawBtcTxOutput -> {
byte[] opReturnData = null;
com.neemre.btcdcli4j.core.domain.PubKeyScript scriptPubKey = rawBtcTxOutput.getScriptPubKey();
if (ScriptTypes.NULL_DATA.equals(scriptPubKey.getType()) && scriptPubKey.getAsm() != null) {
String[] chunks = scriptPubKey.getAsm().split(" ");
// We get on testnet a lot of "OP_RETURN 0" data, so we filter those away
if (chunks.length == 2 && "OP_RETURN".equals(chunks[0]) && !"0".equals(chunks[1])) {
try {
opReturnData = Utils.HEX.decode(chunks[1]);
} catch (Throwable t) {
// We get sometimes exceptions, seems BitcoinJ
// cannot handle all existing OP_RETURN data, but we ignore them
// anyway as our OP_RETURN data is valid in BitcoinJ
log.warn("Error at Utils.HEX.decode(chunks[1]): " + t.toString() + " / chunks[1]=" + chunks[1]);
}
}
}
// We don't support raw MS which are the only case where scriptPubKey.getAddresses()>1
String address = scriptPubKey.getAddresses() != null && scriptPubKey.getAddresses().size() == 1 ? scriptPubKey.getAddresses().get(0) : null;
PubKeyScript pubKeyScript = dumpBlockchainData ? new PubKeyScript(scriptPubKey) : null;
return new RawTxOutput(rawBtcTxOutput.getN(), rawBtcTxOutput.getValue().movePointRight(8).longValue(), rawBtcTx.getTxId(), pubKeyScript, address, opReturnData, blockHeight);
}).collect(Collectors.toList());
return new RawTx(txId, blockHeight, blockHash, blockTime, ImmutableList.copyOf(txInputs), ImmutableList.copyOf(txOutputs));
}
use of bisq.core.dao.state.blockchain.RawTx in project bisq-core by bisq-network.
the class TxParserTest method testGetBisqTxType.
@Test
public void testGetBisqTxType() {
// Thu Jun 20 14:04:25 CEST 2013
long time = 1371729865;
final List<TxInput> inputs = Arrays.asList(new TxInput("tx0", 0, null), new TxInput("tx1", 1, null));
RawTxOutput output = new RawTxOutput(0, 123, null, null, null, null, 100);
RawTx rawTx = new RawTx("faketx0", 100, "fakeblock0", time, ImmutableList.copyOf(inputs), ImmutableList.copyOf(Arrays.asList(output)));
TempTx tempTx = TempTx.fromRawTx(rawTx);
boolean hasOpReturnCandidate = true;
long remainingInputValue = 0;
Optional<OpReturnType> optionalOpReturnType = Optional.empty();
TxType result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
TxType want = TxType.INVALID;
Assert.assertEquals("With an OP_RETURN candidate but no optional OP_RETURN type, this tx should be invalid.", want, result);
hasOpReturnCandidate = false;
result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
want = TxType.TRANSFER_BSQ;
Assert.assertEquals("With no OP_RETURN candidate and no optional OP_RETURN type, this should be a BSQ transfer tx.", want, result);
// todo(chirhonul): this is very likely incorrect, we should see the tx as INVALID if
// !hasOpReturnCandidate but optionalOpReturnType.
hasOpReturnCandidate = false;
optionalOpReturnType = Optional.of(OpReturnType.LOCKUP);
result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
want = TxType.LOCKUP;
Assert.assertEquals("With no OP_RETURN candidate and optional OP_RETURN type of LOCKUP, this should be a LOCKUP tx.", want, result);
hasOpReturnCandidate = true;
optionalOpReturnType = Optional.of(OpReturnType.BLIND_VOTE);
result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
want = TxType.BLIND_VOTE;
Assert.assertEquals("With OP_RETURN candidate and optional OP_RETURN type of BLIND_VOTE, this should be a BLIND_VOTE tx.", want, result);
hasOpReturnCandidate = true;
optionalOpReturnType = Optional.of(OpReturnType.VOTE_REVEAL);
result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
want = TxType.VOTE_REVEAL;
Assert.assertEquals("With OP_RETURN candidate and optional OP_RETURN type of VOTE_REVEAL, this should be a VOTE_REVEAL tx.", want, result);
hasOpReturnCandidate = true;
optionalOpReturnType = Optional.of(OpReturnType.PROPOSAL);
result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
want = TxType.PROPOSAL;
Assert.assertEquals("With OP_RETURN candidate and optional OP_RETURN type of PROPOSAL, this should be a PROPOSAL tx.", want, result);
hasOpReturnCandidate = true;
optionalOpReturnType = Optional.of(OpReturnType.COMPENSATION_REQUEST);
result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
want = TxType.INVALID;
Assert.assertEquals("COMPENSATION_REQUEST has fewer than three outputs, this should be a INVALID tx.", want, result);
RawTxOutput output1 = new RawTxOutput(0, 123, null, null, null, null, 100);
RawTxOutput output2 = new RawTxOutput(0, 456, null, null, null, null, 100);
RawTxOutput output3 = new RawTxOutput(0, 678, null, null, null, null, 100);
rawTx = new RawTx("faketx1", 200, "fakeblock1", time, ImmutableList.copyOf(inputs), ImmutableList.copyOf(Arrays.asList(output1, output2, output3)));
tempTx = TempTx.fromRawTx(rawTx);
hasOpReturnCandidate = true;
optionalOpReturnType = Optional.of(OpReturnType.COMPENSATION_REQUEST);
result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
want = TxType.INVALID;
Assert.assertEquals("Output 1 at COMPENSATION_REQUEST has to be a ISSUANCE_CANDIDATE_OUTPUT, this should be a INVALID tx.", want, result);
hasOpReturnCandidate = true;
optionalOpReturnType = Optional.of(OpReturnType.COMPENSATION_REQUEST);
tempTx.getTempTxOutputs().get(1).setTxOutputType(TxOutputType.ISSUANCE_CANDIDATE_OUTPUT);
result = TxParser.getBisqTxType(tempTx, hasOpReturnCandidate, remainingInputValue, optionalOpReturnType);
want = TxType.COMPENSATION_REQUEST;
Assert.assertEquals("With OP_RETURN candidate and optional OP_RETURN type of COMPENSATION_REQUEST, this should be a COMPENSATION_REQUEST tx.", want, result);
}
use of bisq.core.dao.state.blockchain.RawTx in project bisq-core by bisq-network.
the class BlockParserTest method testIsBsqTx.
// FIXME
@Test
public void testIsBsqTx() {
// Setup a basic transaction with two inputs
int height = 200;
String hash = "abc123";
long time = new Date().getTime();
final List<TxInput> inputs = asList(new TxInput("tx1", 0, null), new TxInput("tx1", 1, null));
final List<RawTxOutput> outputs = asList(new RawTxOutput(0, 101, "tx1", null, null, null, height));
RawTx rawTx = new RawTx("vo", height, hash, time, ImmutableList.copyOf(inputs), ImmutableList.copyOf(outputs));
// Return one spendable txoutputs with value, for three test cases
// 1) - null, 0 -> not BSQ transaction
// 2) - 100, null -> BSQ transaction
// 3) - 0, 100 -> BSQ transaction
new Expectations(bsqStateService) {
{
// Expectations can be recorded on mocked instances, either with specific matching arguments or catch all
// http://jmockit.github.io/tutorial/Mocking.html#results
// Results are returned in the order they're recorded, so in this case for the first call to
// getSpendableTxOutput("tx1", 0) the return value will be Optional.empty()
// for the second call the return is Optional.of(new TxOutput(0,... and so on
bsqStateService.getUnspentTxOutput(new TxOutputKey("tx1", 0));
result = Optional.empty();
result = Optional.of(new RawTxOutput(0, 100, "txout1", null, null, null, height));
result = Optional.of(new RawTxOutput(0, 0, "txout1", null, null, null, height));
bsqStateService.getUnspentTxOutput(new TxOutputKey("tx1", 1));
result = Optional.of(new RawTxOutput(0, 0, "txout2", null, null, null, height));
result = Optional.empty();
result = Optional.of(new RawTxOutput(0, 100, "txout2", null, null, null, height));
}
};
String genesisTxId = "genesisTxId";
int blockHeight = 200;
String blockHash = "abc123";
Coin genesisTotalSupply = Coin.parseCoin("2.5");
// First time there is no BSQ value to spend so it's not a bsq transaction
assertFalse(txParser.findTx(rawTx, genesisTxId, blockHeight, genesisTotalSupply).isPresent());
// Second time there is BSQ in the first txout
assertTrue(txParser.findTx(rawTx, genesisTxId, blockHeight, genesisTotalSupply).isPresent());
// Third time there is BSQ in the second txout
assertTrue(txParser.findTx(rawTx, genesisTxId, blockHeight, genesisTotalSupply).isPresent());
}
use of bisq.core.dao.state.blockchain.RawTx in project bisq-core by bisq-network.
the class BlockParserTest method testParseBlocks.
@Test
public void testParseBlocks() throws BitcoindException, CommunicationException, BlockNotConnectingException, RpcException {
// Setup blocks to test, starting before genesis
// Only the transactions related to bsq are relevant, no checks are done on correctness of blocks or other txs
// so hashes and most other data don't matter
long time = new Date().getTime();
int genesisHeight = 200;
int startHeight = 199;
int headHeight = 201;
Coin issuance = Coin.parseCoin("2.5");
RawTransaction genTx = new RawTransaction("gen block hash", 0, 0L, 0L, genesisTxId);
// Blockhashes
String bh199 = "blockhash199";
String bh200 = "blockhash200";
String bh201 = "blockhash201";
// Block 199
String cbId199 = "cbid199";
RawTransaction tx199 = new RawTransaction(bh199, 0, 0L, 0L, cbId199);
RawTx cbTx199 = new RawTx(cbId199, 199, bh199, time, ImmutableList.copyOf(new ArrayList<TxInput>()), ImmutableList.copyOf(asList(new RawTxOutput(0, 25, cbId199, null, null, null, 199))));
RawBlock block199 = new RawBlock(bh199, 10, 10, 199, 2, "root", asList(tx199), time, Long.parseLong("1234"), "bits", BigDecimal.valueOf(1), "chainwork", "previousBlockHash", bh200);
// Genesis Block
String cbId200 = "cbid200";
RawTransaction tx200 = new RawTransaction(bh200, 0, 0L, 0L, cbId200);
RawTx cbTx200 = new RawTx(cbId200, 200, bh200, time, ImmutableList.copyOf(new ArrayList<TxInput>()), ImmutableList.copyOf(asList(new RawTxOutput(0, 25, cbId200, null, null, null, 200))));
RawTx genesisTx = new RawTx(genesisTxId, 200, bh200, time, ImmutableList.copyOf(asList(new TxInput("someoldtx", 0, null))), ImmutableList.copyOf(asList(new RawTxOutput(0, issuance.getValue(), genesisTxId, null, null, null, 200))));
RawBlock block200 = new RawBlock(bh200, 10, 10, 200, 2, "root", asList(tx200, genTx), time, Long.parseLong("1234"), "bits", BigDecimal.valueOf(1), "chainwork", bh199, bh201);
// Block 201
// Make a bsq transaction
String cbId201 = "cbid201";
String bsqTx1Id = "bsqtx1";
RawTransaction tx201 = new RawTransaction(bh201, 0, 0L, 0L, cbId201);
RawTransaction txbsqtx1 = new RawTransaction(bh201, 0, 0L, 0L, bsqTx1Id);
long bsqTx1Value1 = Coin.parseCoin("2.4").getValue();
long bsqTx1Value2 = Coin.parseCoin("0.04").getValue();
RawTx cbTx201 = new RawTx(cbId201, 201, bh201, time, ImmutableList.copyOf(new ArrayList<TxInput>()), ImmutableList.copyOf(asList(new RawTxOutput(0, 25, cbId201, null, null, null, 201))));
RawTx bsqTx1 = new RawTx(bsqTx1Id, 201, bh201, time, ImmutableList.copyOf(asList(new TxInput(genesisTxId, 0, null))), ImmutableList.copyOf(asList(new RawTxOutput(0, bsqTx1Value1, bsqTx1Id, null, null, null, 201), new RawTxOutput(1, bsqTx1Value2, bsqTx1Id, null, null, null, 201))));
RawBlock block201 = new RawBlock(bh201, 10, 10, 201, 2, "root", asList(tx201, txbsqtx1), time, Long.parseLong("1234"), "bits", BigDecimal.valueOf(1), "chainwork", bh200, "nextBlockHash");
// TODO update test with new API
/*
new Expectations(rpcService) {{
rpcService.requestBlock(199);
result = block199;
rpcService.requestBlock(200);
result = block200;
rpcService.requestBlock(201);
result = block201;
rpcService.requestTx(cbId199, 199);
result = cbTx199;
rpcService.requestTx(cbId200, genesisHeight);
result = cbTx200;
rpcService.requestTx(genesisTxId, genesisHeight);
result = genesisTx;
rpcService.requestTx(cbId201, 201);
result = cbTx201;
rpcService.requestTx(bsqTx1Id, 201);
result = bsqTx1;
}};
// Running parseBlocks to build the bsq blockchain
fullNodeParser.parseBlocks(startHeight, headHeight, block -> {
});
*/
// Verify that the genesis tx has been added to the bsq blockchain with the correct issuance amount
/* assertTrue(bsqStateService.getGenesisTx().get() == genesisTx);
assertTrue(bsqStateService.getGenesisTotalSupply().getValue() == issuance.getValue());
// And that other txs are not added
assertFalse(bsqStateService.containsTx(cbId199));
assertFalse(bsqStateService.containsTx(cbId200));
assertFalse(bsqStateService.containsTx(cbId201));
// But bsq txs are added
assertTrue(bsqStateService.containsTx(bsqTx1Id));
TxOutput bsqOut1 = bsqStateService.getUnspentAndMatureTxOutput(bsqTx1Id, 0).get();
assertTrue(bsqStateService.isUnspent(bsqOut1));
assertTrue(bsqOut1.getValue() == bsqTx1Value1);
TxOutput bsqOut2 = bsqStateService.getUnspentAndMatureTxOutput(bsqTx1Id, 1).get();
assertTrue(bsqStateService.isUnspent(bsqOut2));
assertTrue(bsqOut2.getValue() == bsqTx1Value2);
assertFalse(bsqStateService.isTxOutputSpendable(genesisTxId, 0));
assertTrue(bsqStateService.isTxOutputSpendable(bsqTx1Id, 0));
assertTrue(bsqStateService.isTxOutputSpendable(bsqTx1Id, 1));*/
}
use of bisq.core.dao.state.blockchain.RawTx in project bisq-core by bisq-network.
the class TxParserTest method testGetGenesisTx.
@Test
public void testGetGenesisTx() {
int blockHeight = 200;
String blockHash = "abc123";
Coin genesisTotalSupply = Coin.parseCoin("2.5");
// Thu Jun 20 14:04:25 CEST 2013
long time = 1371729865;
final List<TxInput> inputs = Arrays.asList(new TxInput("tx0", 0, null), new TxInput("tx1", 1, null));
RawTxOutput output = new RawTxOutput(0, genesisTotalSupply.value, null, null, null, null, blockHeight);
RawTx rawTx = new RawTx("tx2", blockHeight, blockHash, time, ImmutableList.copyOf(inputs), ImmutableList.copyOf(Arrays.asList(output)));
String genesisTxId = "genesisTxId";
int genesisBlockHeight = 150;
// With mismatch in block height and tx id, we should not get genesis tx back.
Optional<TempTx> result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx);
Optional<TempTx> want = Optional.empty();
Assert.assertEquals(want, result);
// With correct block height but mismatch in tx id, we should still not get genesis tx back.
blockHeight = 150;
rawTx = new RawTx("tx2", blockHeight, blockHash, time, ImmutableList.copyOf(inputs), ImmutableList.copyOf(Arrays.asList(output)));
result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx);
want = Optional.empty();
Assert.assertEquals(want, result);
// With correct tx id and block height, we should find our genesis tx with correct tx and output type.
rawTx = new RawTx(genesisTxId, blockHeight, blockHash, time, ImmutableList.copyOf(inputs), ImmutableList.copyOf(Arrays.asList(output)));
result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx);
TempTx tempTx = TempTx.fromRawTx(rawTx);
tempTx.setTxType(TxType.GENESIS);
for (int i = 0; i < tempTx.getTempTxOutputs().size(); ++i) {
tempTx.getTempTxOutputs().get(i).setTxOutputType(TxOutputType.GENESIS_OUTPUT);
}
want = Optional.of(tempTx);
Assert.assertEquals(want, result);
// With correct tx id and block height, but too low sum of outputs (lower than genesisTotalSupply), we
// should see an exception raised.
output = new RawTxOutput(0, genesisTotalSupply.value - 1, null, null, null, null, blockHeight);
rawTx = new RawTx(genesisTxId, blockHeight, blockHash, time, ImmutableList.copyOf(inputs), ImmutableList.copyOf(Arrays.asList(output)));
try {
result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx);
Assert.fail("Expected an InvalidGenesisTxException to be thrown when outputs are too low");
} catch (InvalidGenesisTxException igtxe) {
String wantMessage = "Genesis tx is invalid; not using all available inputs. Remaining input value is 1 sat";
Assert.assertTrue("Unexpected exception, want message starting with " + "'" + wantMessage + "', got '" + igtxe.getMessage() + "'", igtxe.getMessage().startsWith(wantMessage));
}
// With correct tx id and block height, but too high sum of outputs (higher than from genesisTotalSupply), we
// should see an exception raised.
RawTxOutput output1 = new RawTxOutput(0, genesisTotalSupply.value - 2, null, null, null, null, blockHeight);
RawTxOutput output2 = new RawTxOutput(0, 3, null, null, null, null, blockHeight);
rawTx = new RawTx(genesisTxId, blockHeight, blockHash, time, ImmutableList.copyOf(inputs), ImmutableList.copyOf(Arrays.asList(output1, output2)));
try {
result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx);
Assert.fail("Expected an InvalidGenesisTxException to be thrown when outputs are too high");
} catch (InvalidGenesisTxException igtxe) {
String wantMessage = "Genesis tx is invalid; using more than available inputs. Remaining input value is 2 sat";
Assert.assertTrue("Unexpected exception, want message starting with " + "'" + wantMessage + "', got '" + igtxe.getMessage() + "'", igtxe.getMessage().startsWith(wantMessage));
}
}
Aggregations