Skip to content

Commit

Permalink
feat(btc): Add Android, iOS tests
Browse files Browse the repository at this point in the history
  • Loading branch information
satoshiotomakan committed Sep 23, 2024
1 parent 0af0634 commit 6361de6
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.trustwallet.core.app.blockchains.bitcoin

import com.google.protobuf.ByteString
import com.trustwallet.core.app.utils.Numeric
import com.trustwallet.core.app.utils.toHex
import com.trustwallet.core.app.utils.toHexBytes
import com.trustwallet.core.app.utils.toHexBytesInByteString
import org.junit.Assert.assertEquals
import org.junit.Test
import wallet.core.jni.BitcoinPsbt
import wallet.core.jni.BitcoinScript
import wallet.core.jni.BitcoinSigHashType
import wallet.core.jni.CoinType
import wallet.core.jni.CoinType.BITCOIN
import wallet.core.jni.Hash
import wallet.core.jni.PrivateKey
import wallet.core.jni.PublicKey
import wallet.core.jni.PublicKeyType
import wallet.core.jni.proto.Bitcoin
import wallet.core.jni.proto.Bitcoin.SigningOutput
import wallet.core.jni.proto.BitcoinV2
import wallet.core.jni.proto.Common.SigningError

class TestBitcoinPsbt {

init {
System.loadLibrary("TrustWalletCore")
}

@Test
fun testSignThorSwap() {
// Successfully broadcasted tx: https://mempool.space/tx/634a416e82ac710166725f6a4090ac7b5db69687e86b2d2e38dcb3d91c956c32

val privateKey = "f00ffbe44c5c2838c13d2778854ac66b75e04eb6054f0241989e223223ad5e55".toHexBytesInByteString()
val psbt = "70736274ff0100bc0200000001147010db5fbcf619067c1090fec65c131443fbc80fb4aaeebe940e44206098c60000000000ffffffff0360ea000000000000160014f22a703617035ef7f490743d50f26ae08c30d0a70000000000000000426a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a35303e12000000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d000000000001011f6603010000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d00000000".toHexBytesInByteString()

val input = BitcoinV2.PsbtSigningInput.newBuilder()
.setPsbt(psbt)
.addPrivateKeys(privateKey)
.build()

val outputData = BitcoinPsbt.sign(input.toByteArray(), BITCOIN)
val output = BitcoinV2.PsbtSigningOutput.parseFrom(outputData)

assertEquals(output.error, SigningError.OK)
assertEquals(
output.psbt.toByteArray().toHex(),
"0x70736274ff0100bc0200000001147010db5fbcf619067c1090fec65c131443fbc80fb4aaeebe940e44206098c60000000000ffffffff0360ea000000000000160014f22a703617035ef7f490743d50f26ae08c30d0a70000000000000000426a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a35303e12000000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d000000000001011f6603010000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d01086c02483045022100b1229a008f20691639767bf925d6b8956ea957ccc633ad6b5de3618733a55e6b02205774d3320489b8a57a6f8de07f561de3e660ff8e587f6ac5422c49020cd4dc9101210306d8c664ea8fd2683eebea1d3114d90e0a5429e5783ba49b80ddabce04ff28f300000000"
)
assertEquals(
output.encoded.toByteArray().toHex(),
"0x02000000000101147010db5fbcf619067c1090fec65c131443fbc80fb4aaeebe940e44206098c60000000000ffffffff0360ea000000000000160014f22a703617035ef7f490743d50f26ae08c30d0a70000000000000000426a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a35303e12000000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d02483045022100b1229a008f20691639767bf925d6b8956ea957ccc633ad6b5de3618733a55e6b02205774d3320489b8a57a6f8de07f561de3e660ff8e587f6ac5422c49020cd4dc9101210306d8c664ea8fd2683eebea1d3114d90e0a5429e5783ba49b80ddabce04ff28f300000000"
)
assertEquals(
output.txid.toByteArray().toHex(),
"0x634a416e82ac710166725f6a4090ac7b5db69687e86b2d2e38dcb3d91c956c32"
)
}

@Test
fun testPlanThorSwap() {
// Successfully broadcasted tx: https://mempool.space/tx/634a416e82ac710166725f6a4090ac7b5db69687e86b2d2e38dcb3d91c956c32

val privateKey = PrivateKey("f00ffbe44c5c2838c13d2778854ac66b75e04eb6054f0241989e223223ad5e55".toHexBytes())
val publicKey = privateKey.getPublicKeySecp256k1(true)
val psbt = "70736274ff0100bc0200000001147010db5fbcf619067c1090fec65c131443fbc80fb4aaeebe940e44206098c60000000000ffffffff0360ea000000000000160014f22a703617035ef7f490743d50f26ae08c30d0a70000000000000000426a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a35303e12000000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d000000000001011f6603010000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d00000000".toHexBytesInByteString()

val input = BitcoinV2.PsbtSigningInput.newBuilder()
.setPsbt(psbt)
.addPublicKeys(ByteString.copyFrom(publicKey.data()))
.build()

val outputData = BitcoinPsbt.plan(input.toByteArray(), BITCOIN)
val output = BitcoinV2.TransactionPlan.parseFrom(outputData)

assertEquals(output.error, SigningError.OK)

assertEquals(output.getInputs(0).receiverAddress, "bc1qkyu3n8k8jmekl3pwvdl59k5w8enjp25akz2r3z")
assertEquals(output.getInputs(0).value, 66_406)

// Vault transfer
assertEquals(output.getOutputs(0).toAddress, "bc1q7g48qdshqd000aysws74pun2uzxrp598gcfum0")
assertEquals(output.getOutputs(0).value, 60_000)

// OP_RETURN
assertEquals(
output.getOutputs(1).customScriptPubkey.toByteArray().toHex(),
"0x6a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a3530"
)
assertEquals(output.getOutputs(1).value, 0)

// Change output
assertEquals(output.getOutputs(2).toAddress, "bc1qkyu3n8k8jmekl3pwvdl59k5w8enjp25akz2r3z")
assertEquals(output.getOutputs(2).value, 4_670)

assertEquals(output.feeEstimate, 1736)
// Please note that `change` in PSBT planning is always 0.
// That's because we aren't able to determine which output is an actual change from PSBT.
assertEquals(output.change, 0)
}
}
2 changes: 1 addition & 1 deletion docs/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ This list is generated from [./registry.json](../registry.json)
| 10004689 | IoTeX EVM | IOTX | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/iotexevm/info/logo.png" width="32" /> | <https://iotex.io/> |
| 10007000 | NativeZetaChain | ZETA | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/zetachain/info/logo.png" width="32" /> | <https://www.zetachain.com/> |
| 10007700 | NativeCanto | CANTO | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/nativecanto/info/logo.png" width="32" /> | <https://canto.io/> |
| 10008217 | Kaia | KLAY | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/kaia/info/logo.png" width="32" /> | <https://kaia.io> |
| 10008217 | Kaia | KLAY | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/kaia/info/logo.png" width="32" /> | <https://kaia.io> |
| 10009000 | Avalanche C-Chain | AVAX | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/avalanchec/info/logo.png" width="32" /> | <https://www.avalabs.org/> |
| 10009001 | Evmos | EVMOS | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/evmos/info/logo.png" width="32" /> | <https://evmos.org/> |
| 10042170 | Arbitrum Nova | ETH | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/arbitrumnova/info/logo.png" width="32" /> | <https://nova.arbitrum.io> |
Expand Down
6 changes: 3 additions & 3 deletions rust/chains/tw_bitcoin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ bitcoin = { version = "0.30.0", features = ["rand-std"] }
secp256k1 = { version = "0.27.0", features = ["global-context", "rand-std"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tw_bech32_address = { path = "../../tw_bech32_address" }
tw_base58_address = { path = "../../tw_base58_address" }
tw_coin_entry = { path = "../../tw_coin_entry", features = ["test-utils"] }
tw_encoding = { path = "../../tw_encoding" }
tw_hash = { path = "../../tw_hash" }
tw_keypair = { path = "../../tw_keypair" }
tw_memory = { path = "../../tw_memory" }
tw_hash = { path = "../../tw_hash" }
tw_misc = { path = "../../tw_misc" }
tw_proto = { path = "../../tw_proto" }
tw_utxo = { path = "../../frameworks/tw_utxo" }
tw_bech32_address = { path = "../../tw_bech32_address" }
tw_base58_address = { path = "../../tw_base58_address" }
59 changes: 59 additions & 0 deletions swift/Tests/Blockchains/BitcoinTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,65 @@ class BitcoinTransactionSignerTests: XCTestCase {
XCTAssertEqual(output.error, .ok)
XCTAssertEqual(output.encoded.hexString, "01000000026c90312e53a3411347a197bfd637c2583d617dd2317262a70e1b5245d2f1e36a000000008a47304402201a631068ea5ddea19467ef7c932a0f3b04f366ca2beaf70e18958e47456124980220614816c449e39cf6acc6625e1cf3100db1db7c0b755bdbb6804d4fa3c4b735d10141041b3937fac1f14074447cde9d3a324ed292d2865ed0d7a7da26cb43558ce4db4ef33c47e820e53031ae16bb0c39205def059a5ca8e1d617650eabc72c5206a81dffffffff13bf27945c669cf3c1d70cf3048f4ab14f1ab6acf06d10d425e8288217a81efd000000008a473044022051d381d8f48a9a4866ca4109f12647922514604a4733e8da8aac046e19275f700220797c3ebf20df7d2a9fed283f9d0ad14cbd656cafb5ec70a2b1c85646ea7485190141041b3937fac1f14074447cde9d3a324ed292d2865ed0d7a7da26cb43558ce4db4ef33c47e820e53031ae16bb0c39205def059a5ca8e1d617650eabc72c5206a81dffffffff0194590000000000001976a914a0c0a50f986924e65ae9bd18eafae448f83117ed88ac00000000")
}

func testSignPsbtThorSwap() throws {
let privateKey = Data(hexString: "f00ffbe44c5c2838c13d2778854ac66b75e04eb6054f0241989e223223ad5e55")!
let psbt = Data(hexString: "70736274ff0100bc0200000001147010db5fbcf619067c1090fec65c131443fbc80fb4aaeebe940e44206098c60000000000ffffffff0360ea000000000000160014f22a703617035ef7f490743d50f26ae08c30d0a70000000000000000426a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a35303e12000000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d000000000001011f6603010000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d00000000")!

let input = BitcoinV2PsbtSigningInput.with {
$0.psbt = psbt
$0.privateKeys = [privateKey]
}

let outputData = try BitcoinPsbt.sign(input: input.serializedData(), coin: .bitcoin)
let output = try BitcoinV2PsbtSigningOutput(serializedData: outputData)

XCTAssertEqual(output.error, .ok)
XCTAssertEqual(output.psbt.hexString, "70736274ff0100bc0200000001147010db5fbcf619067c1090fec65c131443fbc80fb4aaeebe940e44206098c60000000000ffffffff0360ea000000000000160014f22a703617035ef7f490743d50f26ae08c30d0a70000000000000000426a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a35303e12000000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d000000000001011f6603010000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d01086c02483045022100b1229a008f20691639767bf925d6b8956ea957ccc633ad6b5de3618733a55e6b02205774d3320489b8a57a6f8de07f561de3e660ff8e587f6ac5422c49020cd4dc9101210306d8c664ea8fd2683eebea1d3114d90e0a5429e5783ba49b80ddabce04ff28f300000000")
XCTAssertEqual(output.encoded.hexString, "02000000000101147010db5fbcf619067c1090fec65c131443fbc80fb4aaeebe940e44206098c60000000000ffffffff0360ea000000000000160014f22a703617035ef7f490743d50f26ae08c30d0a70000000000000000426a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a35303e12000000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d02483045022100b1229a008f20691639767bf925d6b8956ea957ccc633ad6b5de3618733a55e6b02205774d3320489b8a57a6f8de07f561de3e660ff8e587f6ac5422c49020cd4dc9101210306d8c664ea8fd2683eebea1d3114d90e0a5429e5783ba49b80ddabce04ff28f300000000")
XCTAssertEqual(output.txid.hexString, "634a416e82ac710166725f6a4090ac7b5db69687e86b2d2e38dcb3d91c956c32")
}

func testPlanPsbtThorSwap() throws {
let privateKeyBytes = Data(hexString: "f00ffbe44c5c2838c13d2778854ac66b75e04eb6054f0241989e223223ad5e55")!
let privateKey = PrivateKey(data: privateKeyBytes)!
let publicKey = privateKey.getPublicKeySecp256k1(compressed: true)

let psbt = Data(hexString: "70736274ff0100bc0200000001147010db5fbcf619067c1090fec65c131443fbc80fb4aaeebe940e44206098c60000000000ffffffff0360ea000000000000160014f22a703617035ef7f490743d50f26ae08c30d0a70000000000000000426a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a35303e12000000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d000000000001011f6603010000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d00000000")!

let input = BitcoinV2PsbtSigningInput.with {
$0.psbt = psbt
$0.publicKeys = [publicKey.data]
}

let outputData = try BitcoinPsbt.plan(input: input.serializedData(), coin: .bitcoin)
let output = try BitcoinV2TransactionPlan(serializedData: outputData)

XCTAssertEqual(output.error, .ok)

XCTAssertEqual(output.inputs[0].receiverAddress, "bc1qkyu3n8k8jmekl3pwvdl59k5w8enjp25akz2r3z")
XCTAssertEqual(output.inputs[0].value, 66_406)

// Vault transfer
XCTAssertEqual(output.outputs[0].toAddress, "bc1q7g48qdshqd000aysws74pun2uzxrp598gcfum0")
XCTAssertEqual(output.outputs[0].value, 60_000)

// OP_RETURN
XCTAssertEqual(
output.outputs[1].customScriptPubkey.hexString,
"6a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a3530"
)
XCTAssertEqual(output.outputs[1].value, 0)

// Change output
XCTAssertEqual(output.outputs[2].toAddress, "bc1qkyu3n8k8jmekl3pwvdl59k5w8enjp25akz2r3z")
XCTAssertEqual(output.outputs[2].value, 4_670)

XCTAssertEqual(output.feeEstimate, 1736)
// Please note that `change` in PSBT planning is always 0.
// That's because we aren't able to determine which output is an actual change from PSBT.
XCTAssertEqual(output.change, 0)
}

func testBitcoinMessageSigner() {
let verifyResult = BitcoinMessageSigner.verifyMessage(
Expand Down

0 comments on commit 6361de6

Please sign in to comment.