iotaledger / one-click-tangle

"One Click Tangle" intends to make lives easier to IOTA adopters by providing pre-configured scripts and recipes that allow to deploy IOTA Networks and Nodes "in one click".
MIT License
55 stars 35 forks source link

Address generation should use a mnemonic #49

Open Thoralf-M opened 3 years ago

Thoralf-M commented 3 years ago

Description

When a snapshot is generated the address for it should be derived from a mnemonic.

Motivation

Will be much easier to use because people can use Firefly, wallet.rs or any client library to send the funds, that's not possible with the private key alone.

Requirements

  1. Generate address using a mnemonic
  2. Store the mnemonic so the user can access it

Open questions (optional)

Instead of generating a random mnemonic, maybe the default should be the same? If the one from here would be used one doesn't need to generate anything and if it's by default the same one can just send this one if someone has issues finding it.

Are you planning to do it yourself in a pull request?

No.

Thoralf-M commented 2 years ago

In the meantime this script can be used with iota.js

// tsc .\index.ts --lib "es2018, dom"; node index.js
import {
    Bech32Helper,
    Ed25519Address,
    ED25519_ADDRESS_TYPE,
    IKeyPair,
    ISigLockedSingleOutput,
    IUTXOInput,
    sendAdvanced,
    SIG_LOCKED_SINGLE_OUTPUT_TYPE,
    SingleNodeClient,
    UTXO_INPUT_TYPE
} from "@iota/iota.js";
import { Converter } from "@iota/util.js";

async function run() {
    const client = new SingleNodeClient("https://api.thin-hornet-0.h.chrysalis-devnet.iota.cafe/");
    const amountToSend = 1000000;
    const newAddress = "atoi1qzt0nhsf38nh6rs4p6zs5knqp6psgha9wsv74uajqgjmwc75ugupx3y7x0r";
    const privateKey = "7955f50024c663167e6f741a88a04c16cffd6b2cdef26635e851330fdeb041912842e5cc739fc27f363ff77cccc4226d0e793ada4dc364c9988fa616a32733d6";
    const publicKey = "2842e5cc739fc27f363ff77cccc4226d0e793ada4dc364c9988fa616a32733d6";

    let bech32Address = Bech32Helper.fromBech32(newAddress, "atoi");
    let newAddressHex = Converter.bytesToHex(bech32Address.addressBytes);

    const nodeInfo = await client.info();

    let walletKeyPair = {publicKey:new Uint8Array(publicKey.match(/.{1,2}/g).map(byte => parseInt(byte, 16))), privateKey: new Uint8Array(privateKey.match(/.{1,2}/g).map(byte => parseInt(byte, 16)))};
    const ed25519Address = new Ed25519Address(walletKeyPair.publicKey);
    const walletAddress = ed25519Address.toAddress();
    const walletAddressHex = Converter.bytesToHex(walletAddress);
    console.log("\tAddress Ed25519", walletAddressHex);
    console.log(
        "\tAddress Bech32",
        Bech32Helper.toBech32(ED25519_ADDRESS_TYPE, walletAddress, nodeInfo.bech32HRP)
    );
    const addressOutputs = await client.addressEd25519Outputs(walletAddressHex);

    const inputsWithKeyPairs: {
        input: IUTXOInput;
        addressKeyPair: IKeyPair;
    }[] = [];

    let availableBalance = 0;

    for (let i = 0; i < addressOutputs.outputIds.length; i++) {
        const output = await client.output(addressOutputs.outputIds[i]);
        if (!output.isSpent) {
            inputsWithKeyPairs.push({
                input: {
                    type: UTXO_INPUT_TYPE,
                    transactionId: output.transactionId,
                    transactionOutputIndex: output.outputIndex
                },
                addressKeyPair: walletKeyPair
            });
            if (output.output.type === SIG_LOCKED_SINGLE_OUTPUT_TYPE) {
                availableBalance += (output.output as ISigLockedSingleOutput).amount;
            }
        }
    }

    const outputs: {
        address: string;
        addressType: number;
        amount: number;
    }[] = [
        // This is the transfer to the new address
        {
            address: newAddressHex,
            addressType: ED25519_ADDRESS_TYPE,
            amount: amountToSend
        },
        // Sending remainder back to own address
        {
            address: walletAddressHex,
            addressType: ED25519_ADDRESS_TYPE,
            amount: availableBalance - amountToSend
        }
    ];

    const { messageId } = await sendAdvanced(client, inputsWithKeyPairs, outputs, {
        key: Converter.utf8ToBytes("WALLET"),
        data: Converter.utf8ToBytes("Not trinity")
    });

    console.log("Created Message Id", messageId);
}

run()
    .then(() => console.log("Done"))
    .catch((err) => console.error(err));
sikhness commented 2 years ago

@Thoralf-M could you describe what that script does and how to use it?

Thoralf-M commented 2 years ago

@sikhness you have to install iota.js with npm install @iota/iota.js, then set these values with your node and the keys you got from the private tangle setup

    const client = new SingleNodeClient("https://api.thin-hornet-0.h.chrysalis-devnet.iota.cafe/");
    const amountToSend = 1000000;
    const newAddress = "atoi1qzt0nhsf38nh6rs4p6zs5knqp6psgha9wsv74uajqgjmwc75ugupx3y7x0r";
    const privateKey = "7955f50024c663167e6f741a88a04c16cffd6b2cdef26635e851330fdeb041912842e5cc739fc27f363ff77cccc4226d0e793ada4dc364c9988fa616a32733d6";
    const publicKey = "2842e5cc739fc27f363ff77cccc4226d0e793ada4dc364c9988fa616a32733d6";

and run it, then it will transfer the amountToSend to the newAddress you could generate with Firefly or a wallet/client library

sikhness commented 2 years ago

Hi @Thoralf-M, do you have that same script but created using the Java API? I've been trying to move funds using the Java API and using the Public Key that the one-click-tangle generates and it keeps saying I have a 0 balance for that seed.

Thoralf-M commented 2 years ago

No, doesn't work with the Java library

sikhness commented 2 years ago

@Thoralf-M is there no way to transfer value from the Genesis account via Java (aka the IOTA rust API) at all? Is this a limitation?

Thoralf-M commented 2 years ago

It depends on how the genesis is created, if the private key would be derived from a seed then it would work, but if it's not like now, then you need a library that supports signing with a private key directly

sikhness commented 2 years ago

@Thoralf-M, In this case I'm using the one-click-tangle directly, and just using the key-pair that it generates in the key-pair.txt file. Does that key generate from the seed itself?

Thoralf-M commented 2 years ago

No, there is no seed, that's why I opened this issue

peterokwara commented 2 years ago

With hornet v1.20 the mnemonic seed is displayed in the keypar.txt

image

One can use the script here https://github.com/iotaledger/iota.js/blob/dev/packages/iota/examples/transaction/src/index.ts adding the mnemonic and do a transaction with it