Skip to content

Commit b231346

Browse files
committed
support for transaction
1 parent b027316 commit b231346

36 files changed

+2874
-113
lines changed

android/app/src/androidTest/java/com/bc/libwally/ScriptTest.kt

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
44
import com.bc.libwally.address.Address
55
import com.bc.libwally.address.PubKey
66
import com.bc.libwally.bip32.Network
7+
import com.bc.libwally.core.Core
8+
import com.bc.libwally.core.Core.bytes2Hex
79
import com.bc.libwally.core.Core.hex2Bytes
810
import com.bc.libwally.crypto.CryptoConstants.EC_SIGNATURE_DER_MAX_LOW_R_LEN
9-
import com.bc.libwally.script.ScriptPubKey
11+
import com.bc.libwally.script.*
1012
import com.bc.libwally.script.ScriptPubKey.ScriptType
11-
import com.bc.libwally.script.ScriptSig
12-
import com.bc.libwally.script.ScriptSigType
1313
import org.junit.Assert.*
1414
import org.junit.Test
1515
import org.junit.runner.RunWith
@@ -69,7 +69,26 @@ class ScriptTest {
6969

7070
@Test
7171
fun testWitnessP2WPKH() {
72-
// TODO later after add Witness.createWallyStack()
72+
val pubKey = PubKey(
73+
"03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c",
74+
Network.MAINNET
75+
)
76+
val witness = Witness(WitnessType.payToWitnessPubKeyHash(pubKey))
77+
assertTrue(witness.isDummy)
78+
79+
val stack = witness.createWallyTxWitnessStack()
80+
assertEquals(2, stack.items.size.toLong())
81+
82+
assertEquals(
83+
"76a914bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe88ac",
84+
bytes2Hex(witness.scriptCode)
85+
)
86+
val signedWitness = Witness(
87+
WitnessType.payToWitnessPubKeyHash(pubKey),
88+
hex2Bytes("01")
89+
)
90+
val signedStack = signedWitness.createWallyTxWitnessStack()
91+
assertEquals(2, signedStack.items.size.toLong())
7392
}
7493

7594
@Test
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package com.bc.libwally
2+
3+
import androidx.test.ext.junit.runners.AndroidJUnit4
4+
import com.bc.libwally.address.PubKey
5+
import com.bc.libwally.bip32.HDKey
6+
import com.bc.libwally.bip32.Network
7+
import com.bc.libwally.script.*
8+
import com.bc.libwally.tx.Transaction
9+
import com.bc.libwally.tx.TxInput
10+
import com.bc.libwally.tx.TxOutput
11+
import org.junit.Assert.*
12+
import org.junit.Test
13+
import org.junit.runner.RunWith
14+
15+
@RunWith(AndroidJUnit4::class)
16+
class TransactionInstanceTest {
17+
18+
companion object {
19+
private const val LEGACY_INPUT_BYTES = 192
20+
private const val NATIVE_SEGWIT_INPUT_BYTES = 113
21+
private const val WRAPPED_SEGWIT_INPUT_BYTES = 136
22+
23+
private val SCRIPT_PUB_KEY1 =
24+
ScriptPubKey("76a914bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe88ac")
25+
private val SCRIPT_PUB_KEY2 = ScriptPubKey("0014bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe")
26+
private val SCRIPT_PUB_KEY3 = ScriptPubKey("a91486cc442a97817c245ce90ed0d31d6dbcde3841f987")
27+
28+
private val PUB_KEY = PubKey(
29+
"03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c",
30+
Network.MAINNET
31+
)
32+
33+
private val PREV_TX =
34+
Transaction("0000000000000000000000000000000000000000000000000000000000000000")
35+
private val TX_INPUT1 = TxInput(
36+
PREV_TX.hash,
37+
0,
38+
1000L + LEGACY_INPUT_BYTES,
39+
ScriptSig(ScriptSigType.payToPubKeyHash(PUB_KEY)),
40+
SCRIPT_PUB_KEY1
41+
)
42+
private val TX_INPUT2 = TxInput(
43+
PREV_TX.hash,
44+
0,
45+
1000L + NATIVE_SEGWIT_INPUT_BYTES,
46+
Witness(WitnessType.payToWitnessPubKeyHash(PUB_KEY)),
47+
SCRIPT_PUB_KEY2
48+
)
49+
private val TX_INPUT3 = TxInput(
50+
PREV_TX.hash,
51+
0,
52+
1000L + WRAPPED_SEGWIT_INPUT_BYTES,
53+
Witness(WitnessType.payToScriptHashPayToWitnessPubKeyHash(PUB_KEY)),
54+
SCRIPT_PUB_KEY3
55+
)
56+
57+
private val TX_OUTPUT = TxOutput(SCRIPT_PUB_KEY1, 1000L, Network.MAINNET)
58+
59+
private val TX1 = Transaction(arrayOf(TX_INPUT1), arrayOf(TX_OUTPUT))
60+
private val TX2 = Transaction(arrayOf(TX_INPUT2), arrayOf(TX_OUTPUT))
61+
private val TX3 = Transaction(arrayOf(TX_INPUT3), arrayOf(TX_OUTPUT))
62+
private val HD_KEY =
63+
HDKey("xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs")
64+
}
65+
66+
67+
@Test
68+
fun testTotalIn() {
69+
assertEquals(1000L + LEGACY_INPUT_BYTES, TX1.totalIn.toLong())
70+
assertEquals(1000L + NATIVE_SEGWIT_INPUT_BYTES, TX2.totalIn.toLong())
71+
assertEquals(1000L + WRAPPED_SEGWIT_INPUT_BYTES, TX3.totalIn.toLong())
72+
val tx = Transaction("0000000000000000000000000000000000000000000000000000000000000000")
73+
assertNull(tx.totalIn)
74+
}
75+
76+
@Test
77+
fun testTotalOut() {
78+
assertEquals(1000L, TX1.totalOut.toLong())
79+
val tx = Transaction("0000000000000000000000000000000000000000000000000000000000000000")
80+
assertNull(tx.totalOut)
81+
}
82+
83+
@Test
84+
fun testFunded() {
85+
assertTrue(TX1.isFunded)
86+
}
87+
88+
@Test
89+
fun testSize() {
90+
assertEquals(LEGACY_INPUT_BYTES, TX1.vBytes.toInt())
91+
assertEquals(NATIVE_SEGWIT_INPUT_BYTES, TX2.vBytes.toInt())
92+
assertEquals(WRAPPED_SEGWIT_INPUT_BYTES, TX3.vBytes.toInt())
93+
val tx = Transaction("0000000000000000000000000000000000000000000000000000000000000000")
94+
assertNull(tx.vBytes)
95+
}
96+
97+
@Test
98+
fun testFee() {
99+
assertEquals(LEGACY_INPUT_BYTES, TX1.fee.toInt())
100+
assertEquals(NATIVE_SEGWIT_INPUT_BYTES, TX2.fee.toInt())
101+
assertEquals(WRAPPED_SEGWIT_INPUT_BYTES, TX3.fee.toInt())
102+
}
103+
104+
@Test
105+
fun testFeeRate() {
106+
assertEquals(1.0f, TX1.feeRate, 0.0f)
107+
assertEquals(1.0f, TX2.feeRate, 0.0f)
108+
assertEquals(1.0f, TX3.feeRate, 0.0f)
109+
}
110+
111+
112+
@Test
113+
fun testSign() {
114+
val signedTx = TX1.signed(arrayOf(HD_KEY))
115+
assertTrue(signedTx.inputs[0].isSigned)
116+
assertEquals(
117+
"01000000010000000000000000000000000000000000000000000000000000000000000000000000006a47304402203d274300310c06582d0186fc197106120c4838fa5d686fe3aa0478033c35b97802205379758b11b869ede2f5ab13a738493a93571268d66b2a875ae148625bd20578012103501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711cffffffff01e8030000000000001976a914bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe88ac00000000",
118+
signedTx.description
119+
)
120+
121+
assertEquals(
122+
LEGACY_INPUT_BYTES - 1.toLong(),
123+
signedTx.vBytes.toInt().toLong()
124+
)
125+
}
126+
127+
@Test
128+
fun testSignNativeSegWit() {
129+
val signedTx = TX2.signed(arrayOf(HD_KEY))
130+
assertTrue(signedTx.inputs[0].isSigned)
131+
assertEquals(
132+
"0100000000010100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a914bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe88ac0247304402204094361e267c39fb942b3d30c6efb96de32ea0f81e87fc36c53e00de2c24555c022069f368ac9cacea21be7b5e7a7c1dad01aa244e437161d000408343a4d6f5da0e012103501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c00000000",
133+
signedTx.description
134+
)
135+
assertEquals(NATIVE_SEGWIT_INPUT_BYTES, signedTx.vBytes)
136+
}
137+
138+
@Test
139+
fun testSignWrappedSegWit() {
140+
val signedTx = TX3.signed(arrayOf(HD_KEY))
141+
assertTrue(signedTx.inputs[0].isSigned)
142+
assertEquals(
143+
"0100000000010100000000000000000000000000000000000000000000000000000000000000000000000017160014bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbeffffffff01e8030000000000001976a914bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe88ac024730440220514e02e6d4aff5e1bfcf72a98eab3a415176c757e2bf6feb7ccb893f8ffcf09b022048fe33e6a1dc80585f30aac20f58442d711739ac07d192a3a7867a1dbef6b38d012103501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c00000000",
144+
signedTx.description
145+
)
146+
assertEquals(WRAPPED_SEGWIT_INPUT_BYTES, signedTx.vBytes)
147+
}
148+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.bc.libwally
2+
3+
import androidx.test.ext.junit.runners.AndroidJUnit4
4+
import com.bc.libwally.address.PubKey
5+
import com.bc.libwally.bip32.Network
6+
import com.bc.libwally.core.Core
7+
import com.bc.libwally.script.ScriptPubKey
8+
import com.bc.libwally.script.ScriptSig
9+
import com.bc.libwally.script.ScriptSigType
10+
import com.bc.libwally.tx.Transaction
11+
import com.bc.libwally.tx.TxException
12+
import com.bc.libwally.tx.TxInput
13+
import com.bc.libwally.tx.TxOutput
14+
import com.bc.libwally.util.assertThrows
15+
import org.junit.Assert.*
16+
import org.junit.Test
17+
import org.junit.runner.RunWith
18+
19+
@RunWith(AndroidJUnit4::class)
20+
class TransactionTest {
21+
22+
private val scriptPubKey = ScriptPubKey("76a914bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe88ac")
23+
private val pubKey = PubKey(
24+
"03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c",
25+
Network.MAINNET
26+
)
27+
28+
@Test
29+
fun testFromHash() {
30+
val hash = "0000000000000000000000000000000000000000000000000000000000000000"
31+
val tx = Transaction(hash)
32+
assertEquals(hash, Core.bytes2Hex(tx.hash))
33+
assertThrows<TxException>(
34+
"Test invalid hex length failed"
35+
) { Transaction("00") }
36+
}
37+
38+
@Test
39+
fun testOutput() {
40+
val output = TxOutput(scriptPubKey, 1000, Network.MAINNET)
41+
assertEquals(1000, output.amount)
42+
assertArrayEquals(scriptPubKey.data, output.scriptPubKey.data)
43+
}
44+
45+
@Test
46+
fun testInput() {
47+
val prevTx = Transaction(
48+
"0000000000000000000000000000000000000000000000000000000000000000"
49+
)
50+
val vout = 0L
51+
val amount = 1000L
52+
val scriptSig = ScriptSig(ScriptSigType.payToPubKeyHash(pubKey))
53+
val input = TxInput(
54+
prevTx.hash,
55+
vout,
56+
0xffffffff,
57+
amount,
58+
scriptSig,
59+
scriptPubKey
60+
)
61+
assertArrayEquals(prevTx.hash, input.txHash)
62+
assertEquals(0, input.vout)
63+
assertEquals(0xffffffff, input.sequence)
64+
assertEquals(scriptSig.type, input.scriptSig.type)
65+
assertFalse(input.isSigned)
66+
}
67+
68+
@Test
69+
fun testComposeTransaction() {
70+
val prevTx = Transaction(
71+
"0000000000000000000000000000000000000000000000000000000000000000"
72+
)
73+
val vout = 0L
74+
val amount = 1000L
75+
val scriptSig = ScriptSig(ScriptSigType.payToPubKeyHash(pubKey))
76+
val input = TxInput(
77+
prevTx.hash,
78+
vout,
79+
0xffffffff,
80+
amount,
81+
scriptSig,
82+
scriptPubKey
83+
)
84+
val output = TxOutput(scriptPubKey, 1000L, Network.MAINNET)
85+
val tx = Transaction(arrayOf(input), arrayOf(output))
86+
assertNull(tx.hash)
87+
assertEquals(1, tx.tx.version)
88+
assertEquals(1, tx.tx.inputs.size.toLong())
89+
assertEquals(1, tx.tx.outputs.size.toLong())
90+
}
91+
92+
@Test
93+
fun testDeserialize() {
94+
val hex =
95+
"01000000010000000000000000000000000000000000000000000000000000000000000000000000006a47304402203d274300310c06582d0186fc197106120c4838fa5d686fe3aa0478033c35b97802205379758b11b869ede2f5ab13a738493a93571268d66b2a875ae148625bd20578012103501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711cffffffff01e8030000000000001976a914bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe88ac00000000"
96+
val tx = Transaction(hex)
97+
assertEquals(hex, tx.description)
98+
}
99+
}

android/scripts/build-host.sh

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,7 @@ for ARCH in $ARCH_LIST; do
4040
echo "Copying libwally-core binary file..."
4141
OUT_DIR=../../android/app/src/main/jniLibs/$ARCH
4242
mkdir -p "$OUT_DIR"
43-
if is_osx; then
44-
cp "$LIBWALLY_CORE_DIR/$LIBWALLY_CORE_FILE" "$OUT_DIR/$LIBWALLY_CORE_FILE"
45-
else
46-
find "$LIBWALLY_CORE_DIR" -name "$LIBWALLY_CORE_FILE*" -exec cp '{}' "$OUT_DIR" ';'
47-
fi
43+
find "$LIBWALLY_CORE_DIR" -name "$LIBWALLY_CORE_FILE*" -exec cp '{}' "$OUT_DIR" ';'
4844

4945
echo "Done! $OUT_DIR/$LIBWALLY_CORE_FILE"
5046

android/scripts/build-jni.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ source ../deps/libwally-core/tools/android_helpers.sh
1111
ARCH_LIST=$(android_get_arch_list)
1212
TOOLCHAIN_DIR=$(android_get_build_tools_dir)
1313
JNI_MD_DIR="darwin"
14-
JNI_LIBS=(bc-libwally-address-jni bc-libwally-bip32-jni bc-libwally-bip39-jni bc-libwally-crypto-jni bc-libwally-core-jni bc-libwally-script-jni)
14+
JNI_LIBS=(bc-libwally-address-jni bc-libwally-bip32-jni bc-libwally-bip39-jni bc-libwally-crypto-jni bc-libwally-core-jni bc-libwally-script-jni bc-libwally-tx-jni)
1515
LIBWALY_CORE_FILE=libwallycore.so
1616

1717
if ! is_osx; then

java/scripts/build-host.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ source scripts/helper.sh
1111
pushd ../deps/libwally-core
1212
./tools/cleanup.sh
1313
./tools/autogen.sh
14-
./configure --disable-swig-jav --enable-debug
15-
make
14+
./configure --disable-swig-java --enable-debug
15+
make install
1616
popd
1717

1818
# Copy binary file

java/scripts/build-jni.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ source scripts/helper.sh
1010
OUT_DIR=src/main/libs
1111
LIBWALLY_CORE_FILE=libwallycore.so
1212
JNI_MD_DIR="linux"
13-
JNI_LIBS=(bc-libwally-address-jni bc-libwally-bip32-jni bc-libwally-bip39-jni bc-libwally-crypto-jni bc-libwally-core-jni bc-libwally-script-jni)
13+
JNI_LIBS=(bc-libwally-address-jni bc-libwally-bip32-jni bc-libwally-bip39-jni bc-libwally-crypto-jni bc-libwally-core-jni bc-libwally-script-jni bc-libwally-tx-jni)
1414

1515
if is_osx; then
1616
JNI_MD_DIR="darwin"

java/src/main/java/com/bc/libwally/ArrayUtils.java

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.bc.libwally;
22

3+
import java.nio.ByteBuffer;
4+
import java.util.Arrays;
5+
36
public class ArrayUtils {
47

58
public static String joinToString(String[] array, String delimiter) {
@@ -21,14 +24,44 @@ public static byte[] append(byte[] a, byte[] b) {
2124
}
2225

2326
public static byte[] append(byte[] a, byte[] b, byte[] c) {
24-
byte[] ab = append(a, b);
25-
return append(ab, c);
27+
return append(append(a, b), c);
2628
}
2729

2830
public static byte[] append(byte[] a, byte[] b, byte[] c, byte[] d, byte[] e) {
29-
byte[] abc = append(a, b, c);
30-
byte[] de = append(d, e);
31-
return append(abc, de);
31+
return append(append(a, b, c), append(d, e));
32+
}
33+
34+
public static byte[] slice(byte[] bytes, int count) {
35+
return slice(bytes, 0, count);
36+
}
37+
38+
public static byte[] slice(byte[] bytes, int start, int end) {
39+
return Arrays.copyOfRange(bytes, start, end);
40+
}
41+
42+
public static long bytes2Long(byte[] bytes) {
43+
return ByteBuffer.wrap(bytes).getLong();
44+
}
45+
46+
public static byte[] reversed(byte[] array) {
47+
if (array == null) {
48+
return null;
49+
}
50+
51+
byte[] clone = new byte[array.length];
52+
System.arraycopy(array, 0, clone, 0, array.length);
53+
54+
int i = 0;
55+
int j = clone.length - 1;
56+
byte tmp;
57+
while (j > i) {
58+
tmp = clone[j];
59+
clone[j] = clone[i];
60+
clone[i] = tmp;
61+
j--;
62+
i++;
63+
}
64+
return clone;
3265
}
3366

3467
}

0 commit comments

Comments
 (0)