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

[Pactus]: Support Pactus Blockchain #4057

Merged
merged 40 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
84b5df1
feat: support Pactus
b00f Aug 21, 2024
ab6cc19
fix: fix compile_impl implementation
b00f Aug 23, 2024
5387449
update Pactus address tests
b00f Aug 28, 2024
5834cdf
encoding treasury addresss
b00f Aug 28, 2024
39b009e
update tests for HD wallet
b00f Aug 28, 2024
3c35b0e
define Decodable trait and implement decode
b00f Oct 7, 2024
87add41
encode and decode for amount and address
b00f Oct 8, 2024
67ffd0e
decode transaction and calculate id
b00f Oct 8, 2024
2811e69
test(pactus): add compiler and signer tests
b00f Oct 9, 2024
cee8db8
add C++ integration tests
b00f Oct 9, 2024
4f36eea
extend central derivation and validation tests
b00f Oct 9, 2024
3b8ada5
revert changes on non-pactus files
b00f Oct 9, 2024
ec92f80
fix compile error
b00f Oct 9, 2024
66fa4b1
update tests for Kotlin and Swift
b00f Oct 11, 2024
e1b4bb5
use H160 for PubKeyHash
b00f Oct 14, 2024
c54ce7d
update VarInt test
b00f Oct 14, 2024
760d605
fix: resolve PR review comments
b00f Oct 15, 2024
3b6fe0e
fix compile error
b00f Oct 15, 2024
0fa09c6
update C++ tests
b00f Oct 15, 2024
40500be
fix formatting issue
b00f Oct 17, 2024
f645199
update Java and Swift tests
b00f Oct 17, 2024
886f1b1
update swift test
b00f Oct 17, 2024
93f6fb3
fix broken swift test
b00f Oct 22, 2024
204d030
fix: add successfully transfer broadcasted in mainnet
Ja7ad Oct 28, 2024
67deb03
add bls validator public key
Ja7ad Oct 29, 2024
c6a92ce
add bond payload encodable and decodable
Ja7ad Oct 29, 2024
345ad56
add generic for decode_fix_slice
Ja7ad Oct 29, 2024
dd09db5
add bond payload for sign transaction
Ja7ad Oct 29, 2024
9db196e
add bond payload as public use
Ja7ad Oct 29, 2024
d0bf6df
add pub use validator public key
Ja7ad Oct 29, 2024
318317a
add original test case for bond and transfer
Ja7ad Oct 29, 2024
ebd81df
refactor transfer in test compile
Ja7ad Oct 29, 2024
0062707
add bond sign test
Ja7ad Oct 29, 2024
7b12db5
refactor transfer sign payload
Ja7ad Oct 29, 2024
9f1dc75
refactor change to bond module name
Ja7ad Oct 29, 2024
e12e9fb
fix rust tests
b00f Oct 29, 2024
af12451
add transfer and bond test for swift
Ja7ad Oct 30, 2024
7e7e8bc
add test sign bond without public key
Ja7ad Oct 30, 2024
ef91a53
use broadcasted transaction data for tests
b00f Nov 1, 2024
aa869c2
fix mistake on Android test
b00f Nov 5, 2024
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
Expand Up @@ -154,5 +154,6 @@ class CoinAddressDerivationTests {
TIA -> assertEquals("celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7", address)
NATIVEZETACHAIN -> assertEquals("zeta13u6g7vqgw074mgmf2ze2cadzvkz9snlwywj304", address)
DYDX -> assertEquals("dydx142j9u5eaduzd7faumygud6ruhdwme98qeayaky", address)
PACTUS -> assertEquals("pc1r7ys2g5a4xc2qtm0t4q987m4mvs57w5g0v4pvzg", address)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

package com.trustwallet.core.app.blockchains.pactus

import com.trustwallet.core.app.utils.toHex
import com.trustwallet.core.app.utils.toHexByteArray
import org.junit.Assert.assertEquals
import org.junit.Test
import wallet.core.jni.*

class TestPactusAddress {

init {
System.loadLibrary("TrustWalletCore")
}

@Test
fun testAddress() {
val key = PrivateKey("4e51f1f3721f644ac7a193be7f5e7b8c2abaa3467871daf4eacb5d3af080e5d6".toHexByteArray())
val pubkey = key.publicKeyEd25519
val address = AnyAddress(pubkey, CoinType.PACTUS)
val expected = AnyAddress("pc1rwzvr8rstdqypr80ag3t6hqrtnss9nwymcxy3lr", CoinType.PACTUS)

assertEquals(pubkey.data().toHex(), "0x95794161374b22c696dabb98e93f6ca9300b22f3b904921fbf560bb72145f4fa")
assertEquals(address.description(), expected.description())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

package com.trustwallet.core.app.blockchains.pactus

import com.google.protobuf.ByteString
import com.trustwallet.core.app.utils.toHexByteArray
import org.junit.Assert.assertEquals
import org.junit.Test
import wallet.core.jni.PrivateKey
import wallet.core.java.AnySigner
import wallet.core.jni.CoinType
import wallet.core.jni.CoinType.PACTUS
import wallet.core.jni.proto.Pactus
import wallet.core.jni.proto.Pactus.SigningOutput
import com.trustwallet.core.app.utils.Numeric
import org.junit.Assert.assertArrayEquals

class TestPactusSigner {

init {
System.loadLibrary("TrustWalletCore")
}

@Test
fun testPactusTransferSigning() {
// Successfully broadcasted transaction:
// https://pacviewer.com/transaction/1b6b7226f7935a15f05371d1a1fefead585a89704ce464b7cc1d453d299d235f
//
val signingInput = Pactus.SigningInput.newBuilder()
signingInput.apply {
privateKey = ByteString.copyFrom(
PrivateKey("4e51f1f3721f644ac7a193be7f5e7b8c2abaa3467871daf4eacb5d3af080e5d6"
.toHexByteArray()).data()
)
transaction = Pactus.TransactionMessage.newBuilder().apply {
lockTime = 2335524
fee = 10000000
memo = "wallet-core"
transfer = Pactus.TransferPayload.newBuilder().apply {
sender = "pc1rwzvr8rstdqypr80ag3t6hqrtnss9nwymcxy3lr"
receiver = "pc1r0g22ufzn8qtw0742dmfglnw73e260hep0k3yra"
amount = 200000000
}.build()
}.build()
}

val output = AnySigner.sign(signingInput.build(), PACTUS, SigningOutput.parser())

assertEquals(
"0x1b6b7226f7935a15f05371d1a1fefead585a89704ce464b7cc1d453d299d235f",
Numeric.toHexString(output.transactionId.toByteArray())
)

assertEquals(
"0x4ed8fee3d8992e82660dd05bbe8608fc56ceabffdeeee61e3213b9b49d33a0fc8dea6d79ee7ec60f66433f189ed9b3c50b2ad6fa004e26790ee736693eda8506",
Numeric.toHexString(output.signature.toByteArray())
)

assertEquals(
"0x000124a3230080ade2040b77616c6c65742d636f726501037098338e0b6808119dfd4457ab806b9c2059b89b037a14ae24533816e7faaa6ed28fcdde8e55a7df218084af5f4ed8fee3d8992e82660dd05bbe8608fc56ceabffdeeee61e3213b9b49d33a0fc8dea6d79ee7ec60f66433f189ed9b3c50b2ad6fa004e26790ee736693eda850695794161374b22c696dabb98e93f6ca9300b22f3b904921fbf560bb72145f4fa",
Numeric.toHexString(output.signedTransactionData.toByteArray())
)
}

@Test
fun testPactusBondWithPublicKeySigning() {
// Successfully broadcasted transaction:
// https://pacviewer.com/transaction/d194b445642a04ec78ced4448696e50b733f2f0b517a23871882c0eefaf1c28f
//
val signingInput = Pactus.SigningInput.newBuilder()
signingInput.apply {
privateKey = ByteString.copyFrom(
PrivateKey("4e51f1f3721f644ac7a193be7f5e7b8c2abaa3467871daf4eacb5d3af080e5d6"
.toHexByteArray()).data()
)
transaction = Pactus.TransactionMessage.newBuilder().apply {
lockTime = 2339009
fee = 10000000
memo = "wallet-core"
bond = Pactus.BondPayload.newBuilder().apply {
sender = "pc1rwzvr8rstdqypr80ag3t6hqrtnss9nwymcxy3lr"
receiver = "pc1p9y5gmu9l002tt60wak9extgvwm69rq3a9ackrl"
stake = 1000000000
publicKey = "public1pnz75msstqdrq5eguvcwanug0zauhqjw2cc4flmez3qethnp68y64ehc4k69amapj7x4na2uda0snqz4yxujgx3jsse4f64fgy7jkh0xauvhrc5ts09vfk48g85t0js66hvajm6xruemsvlxqv3xvkyur8v9v0mtn"
}.build()
}.build()
}

val output = AnySigner.sign(signingInput.build(), PACTUS, SigningOutput.parser())

assertEquals(
"0xd194b445642a04ec78ced4448696e50b733f2f0b517a23871882c0eefaf1c28f",
Numeric.toHexString(output.transactionId.toByteArray())
)

assertEquals(
"0x0d7bc6d94927534b89e2f53bcfc9fc849e0e2982438955eda55b4338328adac79d4ee3216d143f0e1629764ab650734f8ba188e716d71f9eff65e39ce7006300",
Numeric.toHexString(output.signature.toByteArray())
)

assertEquals(
"0x0001c1b0230080ade2040b77616c6c65742d636f726502037098338e0b6808119dfd4457ab806b9c2059b89b0129288df0bf7bd4b5e9eeed8b932d0c76f451823d6098bd4dc20b03460a651c661dd9f10f17797049cac62a9fef228832bbcc3a39355cdf15b68bddf432f1ab3eab8debe1300aa43724834650866a9d552827a56bbcdde32e3c517079589b54e83d16f9435abb3b2de8c3e677067cc0644ccb13833b8094ebdc030d7bc6d94927534b89e2f53bcfc9fc849e0e2982438955eda55b4338328adac79d4ee3216d143f0e1629764ab650734f8ba188e716d71f9eff65e39ce700630095794161374b22c696dabb98e93f6ca9300b22f3b904921fbf560bb72145f4fa",
Numeric.toHexString(output.signedTransactionData.toByteArray())
)
}

@Test
fun testPactusBondWithoutPublicKeySigning() {
// Successfully broadcasted transaction:
// https://pacviewer.com/transaction/f83f583a5c40adf93a90ea536a7e4b467d30ca4f308d5da52624d80c42adec80
//
val signingInput = Pactus.SigningInput.newBuilder()
signingInput.apply {
privateKey = ByteString.copyFrom(
PrivateKey("4e51f1f3721f644ac7a193be7f5e7b8c2abaa3467871daf4eacb5d3af080e5d6"
.toHexByteArray()).data()
)
transaction = Pactus.TransactionMessage.newBuilder().apply {
lockTime = 2335580
fee = 10000000
memo = "wallet-core"
bond = Pactus.BondPayload.newBuilder().apply {
sender = "pc1rwzvr8rstdqypr80ag3t6hqrtnss9nwymcxy3lr"
receiver = "pc1p6taz5l2kq5ppnxv4agnqj48svnvsy797xpe6wd"
stake = 1000000000
}.build()
}.build()
}

val output = AnySigner.sign(signingInput.build(), PACTUS, SigningOutput.parser())

assertEquals(
"0xf83f583a5c40adf93a90ea536a7e4b467d30ca4f308d5da52624d80c42adec80",
Numeric.toHexString(output.transactionId.toByteArray())
)

assertEquals(
"0x9e6279fb64067c7d7316ac74630bbb8589df268aa4548f1c7d85c087a8748ff0715b9149afbd94c5d8ee6b37c787ec63e963cbb38be513ebc436aa58f9a8f00d",
Numeric.toHexString(output.signature.toByteArray())
)

assertEquals(
"0x00015ca3230080ade2040b77616c6c65742d636f726502037098338e0b6808119dfd4457ab806b9c2059b89b01d2fa2a7d560502199995ea260954f064d90278be008094ebdc039e6279fb64067c7d7316ac74630bbb8589df268aa4548f1c7d85c087a8748ff0715b9149afbd94c5d8ee6b37c787ec63e963cbb38be513ebc436aa58f9a8f00d95794161374b22c696dabb98e93f6ca9300b22f3b904921fbf560bb72145f4fa",
Numeric.toHexString(output.signedTransactionData.toByteArray())
)
}
}
1 change: 1 addition & 0 deletions docs/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ This list is generated from [./registry.json](../registry.json)
| 14001 | WAX | WAXP | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/wax/info/logo.png" width="32" /> | <http://wax.io> |
| 18000 | Meter | MTR | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/meter/info/logo.png" width="32" /> | <https://meter.io/> |
| 19167 | Flux | FLUX | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/zelcash/info/logo.png" width="32" /> | <https://runonflux.io> |
| 21888 | Pactus | PAC | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/pactus/info/logo.png" width="32" /> | <https://pactus.org> |
| 52752 | Celo | CELO | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/celo/info/logo.png" width="32" /> | <https://celo.org> |
| 59144 | Linea | ETH | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/linea/info/logo.png" width="32" /> | <https://linea.build> |
| 81457 | Blast | ETH | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/blast/info/logo.png" width="32" /> | <https://blast.io> |
Expand Down
1 change: 1 addition & 0 deletions include/TrustWalletCore/TWBlockchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ enum TWBlockchain {
TWBlockchainNativeEvmos = 53, // Cosmos
TWBlockchainNativeInjective = 54, // Cosmos
TWBlockchainBitcoinCash = 55,
TWBlockchainPactus = 56,
};

TW_EXTERN_C_END
1 change: 1 addition & 0 deletions include/TrustWalletCore/TWCoinType.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ enum TWCoinType {
TWCoinTypeBlast = 81457,
TWCoinTypeBounceBit = 6001,
TWCoinTypeZkLinkNova = 810180,
TWCoinTypePactus = 21888,
// end_of_tw_coin_type_marker_do_not_modify
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,5 +147,6 @@ class CoinAddressDerivationTests {
Tia -> "celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7"
NativeZetaChain -> "zeta13u6g7vqgw074mgmf2ze2cadzvkz9snlwywj304"
Dydx -> "dydx142j9u5eaduzd7faumygud6ruhdwme98qeayaky"
Pactus -> "pc1r7ys2g5a4xc2qtm0t4q987m4mvs57w5g0v4pvzg"
}
}
27 changes: 27 additions & 0 deletions registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -4787,5 +4787,32 @@
"rpc": "https://rpc.zklink.io",
"documentation": "https://docs.zklink.io"
}
},
{
"id": "pactus",
"name": "Pactus",
"coinId": 21888,
"symbol": "PAC",
"decimals": 9,
"blockchain": "Pactus",
"derivation": [
{
"path": "m/44'/21888'/3'/0'"
}
],
"curve": "ed25519",
"publicKeyType": "ed25519",
"hrp": "pc",
"explorer": {
"url": "https://pacviewer.com",
"txPath": "/transaction/",
"accountPath": "/address/"
},
"info": {
"url": "https://pactus.org",
"source": "https://github.com/pactus-project/pactus",
"rpc": "https://docs.pactus.org/api/http",
"documentation": "https://docs.pactus.org"
}
}
]
15 changes: 15 additions & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
"chains/tw_internet_computer",
"chains/tw_native_evmos",
"chains/tw_native_injective",
"chains/tw_pactus",
"chains/tw_ronin",
"chains/tw_solana",
"chains/tw_sui",
Expand Down
18 changes: 18 additions & 0 deletions rust/chains/tw_pactus/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "tw_pactus"
version = "0.1.0"
edition = "2021"

[dependencies]
bech32 = "0.9.1"
byteorder = "1.4"
tw_coin_entry = { path = "../../tw_coin_entry" }
tw_keypair = { path = "../../tw_keypair" }
tw_memory = { path = "../../tw_memory" }
tw_proto = { path = "../../tw_proto" }
tw_hash = { path = "../../tw_hash" }
tw_encoding = { path = "../../tw_encoding" }

[dev-dependencies]
tw_encoding = { path = "../../tw_encoding" }

84 changes: 84 additions & 0 deletions rust/chains/tw_pactus/src/compiler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes};
use tw_coin_entry::error::prelude::*;
use tw_coin_entry::signing_output_error;
use tw_keypair::ed25519;
use tw_proto::Pactus::Proto;
use tw_proto::TxCompiler::Proto as CompilerProto;

use crate::modules::tx_builder::TxBuilder;

pub struct PactusCompiler;

impl PactusCompiler {
#[inline]
pub fn preimage_hashes(
coin: &dyn CoinContext,
input: Proto::SigningInput<'_>,
) -> CompilerProto::PreSigningOutput<'static> {
Self::preimage_hashes_impl(coin, input)
.unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e))
}

fn preimage_hashes_impl(
_coin: &dyn CoinContext,
input: Proto::SigningInput<'_>,
) -> SigningResult<CompilerProto::PreSigningOutput<'static>> {
let trx = TxBuilder::from_proto(&input)?;
let sign_bytes = trx.sign_bytes()?;

let output = CompilerProto::PreSigningOutput {
data_hash: trx.id().into(),
data: sign_bytes.into(),
..CompilerProto::PreSigningOutput::default()
};

Ok(output)
}

#[inline]
pub fn compile(
coin: &dyn CoinContext,
input: Proto::SigningInput<'_>,
signatures: Vec<SignatureBytes>,
public_keys: Vec<PublicKeyBytes>,
) -> Proto::SigningOutput<'static> {
Self::compile_impl(coin, input, signatures, public_keys)
.unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e))
}

fn compile_impl(
_coin: &dyn CoinContext,
input: Proto::SigningInput<'_>,
signatures: Vec<SignatureBytes>,
public_keys: Vec<PublicKeyBytes>,
) -> SigningResult<Proto::SigningOutput<'static>> {
let signature_bytes = signatures
.first()
.or_tw_err(SigningErrorType::Error_signatures_count)?;
let public_key_bytes = public_keys
.first()
.or_tw_err(SigningErrorType::Error_signatures_count)?;

let public_key = ed25519::sha512::PublicKey::try_from(public_key_bytes.as_slice())?;
let signature = ed25519::Signature::try_from(signature_bytes.as_slice())?;

let mut trx = TxBuilder::from_proto(&input)?;
trx.set_signatory(public_key.to_owned(), signature.to_owned());

let data = trx.to_bytes()?;

let output = Proto::SigningOutput {
transaction_id: trx.id().into(),
signed_transaction_data: data.into(),
signature: signature.to_bytes().to_vec().into(),
..Proto::SigningOutput::default()
};

Ok(output)
}
}
Loading
Loading