Commit d4f474db authored by Ivan Jose Lozano Fernandez's avatar Ivan Jose Lozano Fernandez

Merge branches 'cash-0.14' and 'jsonscripttests' of...

Merge branches 'cash-0.14' and 'jsonscripttests' of https://github.com/nchain-research/bitcoinj-cash into jsonscripttests
parents b2cfdbab 3403a6c1
......@@ -160,6 +160,24 @@ public class Transaction extends ChildMessage {
// can properly keep track of optimal encoded size
private int optimalEncodingMessageSize;
public boolean isOpReturn() {
if (getOpReturnData() != null) {
return true;
}
return false;
}
public byte[] getOpReturnData() {
// Only one OP_RETURN output per transaction is allowed as "standard" transaction
// So just return the first OP_RETURN data found
for (TransactionOutput output : outputs) {
if (output.isOpReturn()) {
return output.getOpReturnData();
}
}
return null;
}
/**
* This enum describes the underlying reason the transaction was created. It's useful for rendering wallet GUIs
* more appropriately.
......@@ -949,6 +967,12 @@ public class Transaction extends ChildMessage {
return addOutput(new TransactionOutput(params, this, value, address));
}
public TransactionOutput addData(byte[] data) {
Script script = ScriptBuilder.createOpReturnScript(data);
return addOutput(new TransactionOutput(params, this, Coin.ZERO, script.getProgram()));
}
/**
* Creates an output that pays to the given pubkey directly (no address) with the given value, adds it to this
* transaction, and returns the new output.
......
......@@ -427,4 +427,16 @@ public class TransactionOutput extends ChildMessage {
public int hashCode() {
return Objects.hashCode(value, parent, Arrays.hashCode(scriptBytes));
}
public boolean isOpReturn() {
return getScriptPubKey() != null && getScriptPubKey().isOpReturn();
}
public byte[] getOpReturnData() {
if (isOpReturn()) {
return getScriptPubKey().getChunks().get(1).data;
}
return null;
}
}
......@@ -3804,6 +3804,16 @@ public class Wallet extends BaseTaggableObject
}
public SendResult sendData(TransactionBroadcaster broadcaster, byte[] data, boolean useforkId) throws InsufficientMoneyException {
Transaction tx = new Transaction(params);
tx.addData(data);
SendRequest request = SendRequest.forTx(tx);
request.setUseForkId(useforkId);
return sendCoins(broadcaster, request);
}
public SendResult sendCoins(TransactionBroadcaster broadcaster, Address to, Coin value, boolean useforkId) throws InsufficientMoneyException {
SendRequest request = SendRequest.to(to, value);
request.setUseForkId(useforkId);
......
......@@ -419,4 +419,30 @@ public class TransactionTest {
EnumSet<Script.VerifyFlag> flags = EnumSet.of(Script.VerifyFlag.STRICTENC, Script.VerifyFlag.SIGHASH_FORKID);
sig.correctlySpends(tx, 0, txConnected.getOutput(1).getScriptPubKey(), txConnected.getOutput(1).getValue(), flags);
}
@Test
public void testRawParseAndExport() {
NetworkParameters params = MainNetParams.get();
// https://blockchain.info/tx/ed27cf72886af7c830faeff136b3859185310334330a4856f60c768ab46b9c1c
String rawTx1 = "010000000193e3073ecc1d27f17e3d287ccefdfdba5f7d8c160242dbcd547b18baef12f9b31a0000006b483045022100af501dc9ef2907247d28a5169b8362ca494e1993f833928b77264e604329eec40220313594f38f97c255bcea6d5a4a68e920508ef93fd788bcf5b0ad2fa5d34940180121034bb555cc39ba30561793cf39a35c403fe8cf4a89403b02b51e058960520bd1e3ffffffff02b3bb0200000000001976a914f7d52018971f4ab9b56f0036958f84ae0325ccdc88ac98100700000000001976a914f230f0a16a98433eca0fa70487b85fb83f7b61cd88ac00000000";
Transaction tx1 = new Transaction(params, HEX.decode(rawTx1));
assertEquals(rawTx1, HEX.encode(tx1.bitcoinSerialize()));
// https://blockchain.info/tx/0024db8e11da76b2344e0722bf9488ba2aed611913f9803a62ac3b41f5603946
String rawTx2 = "01000000011c9c6bb48a760cf656480a33340331859185b336f1effa30c8f76a8872cf27ed000000006a47304402201c999cf44dc6576783c0f55b8ff836a1e22db87ed67dc3c39515a6676cfb58e902200b4a925f9c8d6895beed841db135051f8664ab349f2e3ea9f8523a6f47f93883012102e58d7b931b5d43780fda0abc50cfd568fcc26fb7da6a71591a43ac8e0738b9a4ffffffff029b010100000000001976a9140f0fcdf818c0c88df6860c85c9cc248b9f37eaff88ac95300100000000001976a9140663d2403f560f8d053a25fbea618eb47071617688ac00000000";
Transaction tx2 = new Transaction(params, HEX.decode(rawTx2));
assertEquals(rawTx2, HEX.encode(tx2.bitcoinSerialize()));
// https://blockchair.com/bitcoin-cash/transaction/0eab89a271380b09987bcee5258fca91f28df4dadcedf892658b9bc261050d96
String rawTx3 = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2c03ccec051f4d696e656420627920416e74506f6f6c20626a3515d2158520566e53850b00110000008c7a0900ffffffff01e170f895000000001976a9149524440a5b54cca9c46ef277c34739e9b521856d88ac00000000";
Transaction tx3 = new Transaction(params, HEX.decode(rawTx3));
assertEquals(rawTx3, HEX.encode(tx3.bitcoinSerialize()));
// https://blockchair.com/bitcoin-cash/transaction/1e24eaaa72b6c10a4d57084ab3acb612bd123bbf64c2a5746b6221b02202090e
String rawTx4 = "0200000001a73374e059d610c0f8ee6fcbc1f89b54ebf7b109426b38d8e3e744e698abf8a5010000006a47304402200dfc3bacafb825c0c457ff3756e9c243965be45d5d490e70c5dfb2f6060445870220431e3d9f852d4b5803ab0d189d8931dc6c35f3724d6be3e8928043b7c789f66a4121022e46d40245e27e8ef260f8d724838c850a5447b81ae9f77d2d5e28fd2640a36a0000000001d4092800000000001976a9147775f3423eb410a4184d9d3ef93f7ed4d1c1d4e988ac00000000";
Transaction tx4 = new Transaction(params, HEX.decode(rawTx4));
assertEquals(rawTx4, HEX.encode(tx4.bitcoinSerialize()));
}
}
......@@ -82,6 +82,30 @@ public class FakeTxBuilder {
return roundTripTransaction(params, t);
}
/**
* Create a fake TX of sufficient realism to exercise the unit tests. Two outputs, one to nobody (return data, 0 coins) and other
* to us (the change) with all the value expended from the UTXO
*/
public static Transaction createFakeTxToMeWithReturnData(NetworkParameters params, Coin value, Address to, byte[] data) {
Transaction t = new Transaction(params);
t.addData(data);
TransactionOutput change = new TransactionOutput(params, t, value, to);
t.addOutput(change);
// Make a previous tx simply to send us sufficient coins. This prev tx is not really valid but it doesn't
// matter for our purposes.
Transaction prevTx = new Transaction(params);
TransactionOutput prevOut = new TransactionOutput(params, prevTx, value, to);
prevTx.addOutput(prevOut);
// Connect it.
t.addInput(prevOut).setScriptSig(ScriptBuilder.createInputScript(TransactionSignature.dummy()));
// Fake signature.
// Serialize/deserialize to ensure internal state is stripped, as if it had been read from the wire.
return roundTripTransaction(params, t);
}
/**
* Create a fake TX for unit tests, for use with unit tests that need greater control. One outputs, 2 random inputs,
* split randomly to create randomness.
......
......@@ -3498,4 +3498,37 @@ public class WalletTest extends TestWithWallet {
// TODO: test shared wallet calculation here
}
@Test
public void testReceiveWithReturnData() throws Exception {
final String dataSent = "hello world";
final StringBuffer dataReceived = new StringBuffer();
// Configure listener
wallet.addCoinsReceivedEventListener(new WalletCoinsReceivedEventListener() {
@Override
public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
if (tx.isOpReturn()) {
dataReceived.append(new String(tx.getOpReturnData()));
}
}
});
// Receive 2 BTC in 2 separate transactions
Transaction toMe1 = createFakeTxToMeWithReturnData(PARAMS, COIN.multiply(2), myAddress, "hello world".getBytes());
sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, toMe1);
// Check we calculate the total received correctly
assertEquals(COIN.multiply(2), wallet.getTotalReceived());
sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, COIN);
log.info("Wait for user thread");
Threading.waitForUserCode();
assertEquals(dataSent, dataReceived.toString());
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment