Skip to content

Commit

Permalink
- #482 Generate Account/Wallet from root prv key
Browse files Browse the repository at this point in the history
- Wallet: search utxos by address vkh
  • Loading branch information
satran004 committed Jan 10, 2025
1 parent 6fb5988 commit 354331f
Show file tree
Hide file tree
Showing 10 changed files with 863 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* Address class represents Shelley address
*/
public class Address {
public static final String ADDR_VKH_PREFIX = "addr_vkh";
private String prefix;
private byte[] bytes;
private String address;
Expand Down Expand Up @@ -173,4 +174,18 @@ public boolean isStakeKeyHashInDelegationPart() {
public boolean isScriptHashInDelegationPart() {
return AddressProvider.isScriptHashInDelegationPart(this);
}

/**
* Retrieves the Bech32-encoded address verification key hash of the payment credential
* associated with the address, if available.
*
*
* @return An {@link Optional} containing the Bech32-encoded verification key hash
* if the payment credential hash is available, or an empty {@link Optional}
* if the payment credential hash is absent.
*/
public Optional<String> getBech32VerificationKeyHash() {
return getPaymentCredentialHash()
.map(paymentCred -> Bech32.encode(paymentCred, ADDR_VKH_PREFIX));
}
}
252 changes: 196 additions & 56 deletions core/src/main/java/com/bloxbean/cardano/client/account/Account.java

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public HdKeyPair getAccountKeyPairFromSecretKey(byte[] xprv, DerivationPath deri
return getKeyPairFromSecretKey(xprv, accountPath);
}

private HdKeyPair getKeyPairFromSecretKey(byte[] xprv, String path) {
public HdKeyPair getKeyPairFromSecretKey(byte[] xprv, String path) {
byte[] IL = Arrays.copyOfRange(xprv, 0, 64);
byte[] IR = Arrays.copyOfRange(xprv, 64, 96);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,25 @@ public HdKeyPair getKeyPairFromMnemonic(String mnemonicPhrase, DerivationPath de
}
}

/**
* Generates the root HdKeyPair from the given mnemonic phrase.
*
* @param mnemonicPhrase the mnemonic phrase used to generate the HdKeyPair
* @return the root HdKeyPair derived from the provided mnemonic phrase
* @throws CryptoException if the mnemonic phrase cannot be converted to entropy or if
* the key pair generation fails
*/
public HdKeyPair getRootKeyPairFromMnemonic(String mnemonicPhrase) {
var hdKeyGenerator = new HdKeyGenerator();
try {
byte[] entropy = MnemonicCode.INSTANCE.toEntropy(mnemonicPhrase);

return hdKeyGenerator.getRootKeyPairFromEntropy(entropy);
} catch (Exception ex) {
throw new CryptoException("Mnemonic to KeyPair generation failed", ex);
}
}

/**
* Get HdKeyPair from entropy
* @param entropy entropy
Expand Down Expand Up @@ -106,4 +125,35 @@ public static HdPublicKey getPublicKeyFromAccountPubKey(HdPublicKey accountHdPub
return hdKeyGenerator.getChildPublicKey(roleHdPubKey, index);
}

/**
* Generates an HdKeyPair derived from the given root key and a specified derivation path.
*
* @param rootKey the root key represented as a byte array
* @param derivationPath the hierarchical deterministic (HD) derivation path used to derive the key pair
* @return the HdKeyPair derived from the provided root key and derivation path
*/
public HdKeyPair getKeyPairFromRootKey(byte[] rootKey, DerivationPath derivationPath) {
HdKeyGenerator hdKeyGenerator = new HdKeyGenerator();
HdKeyPair rootKeyPair = hdKeyGenerator.getKeyPairFromSecretKey(rootKey, HdKeyGenerator.MASTER_PATH);

HdKeyPair purposeKey = hdKeyGenerator.getChildKeyPair(rootKeyPair, derivationPath.getPurpose().getValue(), derivationPath.getPurpose().isHarden());
HdKeyPair coinTypeKey = hdKeyGenerator.getChildKeyPair(purposeKey, derivationPath.getCoinType().getValue(), derivationPath.getCoinType().isHarden());
HdKeyPair accountKey = hdKeyGenerator.getChildKeyPair(coinTypeKey, derivationPath.getAccount().getValue(), derivationPath.getAccount().isHarden());
HdKeyPair roleKey = hdKeyGenerator.getChildKeyPair(accountKey, derivationPath.getRole().getValue(), derivationPath.getRole().isHarden());

HdKeyPair indexKey = hdKeyGenerator.getChildKeyPair(roleKey, derivationPath.getIndex().getValue(), derivationPath.getIndex().isHarden());
return indexKey;
}

/**
* Get an HdKeyPair from the given root key using the master derivation path.
*
* @param rootKey the root key represented as a byte array
* @return the HdKeyPair derived from the provided root key and the master derivation path
*/
public HdKeyPair getRootKeyPairFromRootKey(byte[] rootKey) {
HdKeyGenerator hdKeyGenerator = new HdKeyGenerator();
return hdKeyGenerator.getKeyPairFromSecretKey(rootKey, HdKeyGenerator.MASTER_PATH);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,41 @@ void getPublicKeyFromAccountPubKey_xpub_2() {

assertThat(HexUtil.encodeHexString(hdPublicKey.getBytes())).isEqualTo(expectedStakePubKey);
}

@Test
void getRootKeyPairFromMnemonic() {
String mnemonic = "top exact spice seed cloud birth orient bracket happy cat section girl such outside elder";

var rootKeyPair = new CIP1852().getRootKeyPairFromMnemonic(mnemonic);
String expectedRootKeyBech32 = "root_xsk1zza6z52v8gelnaqdhuny3ywlccud5dtm8rvvyem4utnfwzcaa9pspsmdm99qfpy2qz7sw9sts59mrkegmdqyjen5ykm4z3ccyrkn8g5mm0qw35arvwxclfh6tj3s4x7t2q85wenvppjpxckcxgnf8vd80ug0l6rw";
String expectedRootPvtKeyHex = HexUtil.encodeHexString(Bech32.decode(expectedRootKeyBech32).data);

var rootPvtKey = rootKeyPair.getPrivateKey().getBytes();

assertThat(HexUtil.encodeHexString(rootPvtKey)).isEqualTo(expectedRootPvtKeyHex);
}

@Test
void getKeyPairFromRootKeyAtDerivationPath() {
String rootKey = "root_xsk1zza6z52v8gelnaqdhuny3ywlccud5dtm8rvvyem4utnfwzcaa9pspsmdm99qfpy2qz7sw9sts59mrkegmdqyjen5ykm4z3ccyrkn8g5mm0qw35arvwxclfh6tj3s4x7t2q85wenvppjpxckcxgnf8vd80ug0l6rw";
byte[] rootKeyBytes = Bech32.decode(rootKey).data;

var addr0KeyPair = new CIP1852().getKeyPairFromRootKey(rootKeyBytes, DerivationPath.createExternalAddressDerivationPath(0));

//1852H/1815H/0H/0/0
String expectedAddr0PvtKey = "addr_xsk1artlf4j6xz246j4xqtn6d595l7sy4vk0zuvzawlg7lwvq2qaa9pkujarca9j5ju08m0dlgw2qagauw693lvmrghujzvxsdfj99pwdm7xqwpgj5asad6nl5rzact6hune2xsl5x5gv2tds75ksdptavxr6se0fk8e";
String expectedAddr0PvtKeyHex = HexUtil.encodeHexString(Bech32.decode(expectedAddr0PvtKey).data);

assertThat(HexUtil.encodeHexString(addr0KeyPair.getPrivateKey().getBytes())).isEqualTo(expectedAddr0PvtKeyHex);
}

@Test
void getRootKeyPairFromRootKey() {
String rootKeyBech32 = "root_xsk1zza6z52v8gelnaqdhuny3ywlccud5dtm8rvvyem4utnfwzcaa9pspsmdm99qfpy2qz7sw9sts59mrkegmdqyjen5ykm4z3ccyrkn8g5mm0qw35arvwxclfh6tj3s4x7t2q85wenvppjpxckcxgnf8vd80ug0l6rw";
byte[] expectedRootKeyBytes = Bech32.decode(rootKeyBech32).data;

var rootKeyPair = new CIP1852().getRootKeyPairFromRootKey(expectedRootKeyBytes);

assertThat(HexUtil.encodeHexString(rootKeyPair.getPrivateKey().getBytes())).isEqualTo(HexUtil.encodeHexString(expectedRootKeyBytes));
}
}
Loading

0 comments on commit 354331f

Please sign in to comment.