Chapter IV wallet

Overview

The purpose is to create a wallet higher level abstract interface to the user to manage the transaction.

Our ultimate goal is to allow users to easily:

  • Create a new wallet
  • View wallet balance
  • Transactions between the purses

After the entry into force of these users do not need to know the details of inputs and outpus described in the previous section of these transactions, we will be able to manage the transaction. Like the Bitcoin network, you just need to enter the corresponding Bitcoin address Bitcoin will be able to break into someone else, while you only need to publish your own address out, others will be able to give you a bit of money.

See this section complete code here

Generate wallet

For this tutorial we will initialize and storage of wallet with the most simple way: the unencrypted private key stored in the node / wallet / private_key this file.

const privateKeyLocation = 'node/wallet/private_key';

const generatePrivatekey = (): string => {
    const keyPair = EC.genKeyPair();
    const privateKey = keyPair.getPrivate();
    return privateKey.toString(16);
};

const initWallet = () => {
    //let's not override existing private keys
    if (existsSync(privateKeyLocation)) {
        return;
    }
    const newPrivateKey = generatePrivatekey();

    writeFileSync(privateKeyLocation, newPrivateKey);
    console.log('new wallet with private key created');
};

As previously said, our public key (purse address) is deduced by the private key.

const getPublicFromWallet = (): string => {
    const privateKey = getPrivateFromWallet();
    const key = EC.keyFromPrivate(privateKey, 'hex');
    return key.getPublic().encode('hex');
};

Need to mention that the private key is stored in plain text in the file is a very unsafe practice. We do like this just for the sake of simplicity of presentation only. At the same time, a purse currently only supports a private key, so if you need a new address as the public key, then, we must create a new wallet.

Wallet Balance

A statement under review mentioned in the previous section: you have in the chain block encryption currency, in fact, refers to "non-consumer transactions outputs", the recipient address is a series of outputs their public key.

This means that we want to see if the wallet balance, then things become very simple: you just need all the addresses under the "non-consumer transactions output" record number of money together would be finished.

const getBalance = (address: string, unspentTxOuts: UnspentTxOut[]): number => {
    return _(unspentTxOuts)
        .filter((uTxO: UnspentTxOut) => uTxO.address === address)
        .map((uTxO: UnspentTxOut) => uTxO.amount)
        .sum();
};

In order to make the presentation easier, we do not need to provide any private information when a query address wallet balance. In other words, anyone can view account balances of others.

Generate transaction

To encrypt a monetary transaction, you should not need to be concerned about trading in the inputs and outputs of these minutiae. However, when the account of the user A has 50 coins, if he will give the user B sends 10 coins, trading back what happened then?

In this case, the system 10 will be sent to the public key credits address B, while the remaining 40 will be returned to the user A. credits In other words, 50 coins sources of consumption must be completed, it must be split when the source is assigned to outputs currency transactions. After the transaction must be the source of 50 coins of output in deleting the words "not consumer transactions outputs", the two outputs to add newly generated. That is "not consumer transactions outputs" on the monetary aggregates will not change, but some currency is traded, address belong to different users of it.

The following illustration shows the above-mentioned transaction:

Let's look at a more sophisticated point of the scene:

  • Users are beginning to have the most C 0 coins
  • After three trading Let C were given 10, 20, a coin
  • C D want to forward the 55 coins.

In this case, all three outputs user C (the "non-consumer transactions outputs" in the address for those outputs C public key) are being lobbied to spend 55 coins to D, the remaining five coins It will be returned to C.

So how will the above description with the code logic to express it? First, we will create a corresponding inputs for the transaction. How to create it? We will traverse the entry address of the sender's public key "Transaction outputs not consumption," until you find a sufficient number of coins can be lobbied (add up the number of coins outputs greater than equal to the target number of coins) of outputs.

const findTxOutsForAmount = (amount: number, myUnspentTxOuts: UnspentTxOut[]) => {
    let currentAmount = 0;
    const includedUnspentTxOuts = [];
    for (const myUnspentTxOut of myUnspentTxOuts) {
        includedUnspentTxOuts.push(myUnspentTxOut);
        currentAmount = currentAmount + myUnspentTxOut.amount;
        if (currentAmount >= amount) {
            const leftOverAmount = currentAmount - amount;
            return {includedUnspentTxOuts, leftOverAmount}
        }
    }
    throw Error('not enough coins to send transaction');
};

As shown in the code, we find in addition to those transactions outputs to meet the conditions of non-consumption, but also record the transaction under the rest of the required number of coins leftOverAmount back to the sender.

After finding these non-consumer transactions outputs, we can think that the current transaction to create the corresponding inputs:

const toUnsignedTxIn = (unspentTxOut: UnspentTxOut) => {
    const txIn: TxIn = new TxIn();
    txIn.txOutId = unspentTxOut.txOutId;
    txIn.txOutIndex = unspentTxOut.txOutIndex;
    return txIn;
};
const {includedUnspentTxOuts, leftOverAmount} = findTxOutsForAmount(amount, myUnspentTxouts);
const unsignedTxIns: TxIn[] = includedUnspentTxOuts.map(toUnsignedTxIn);

Approach is very simple, mainly each input in txOutId point corresponding non-consumer transactions output found items above.

Followed by the need to create an example of two outputs: the output is to a recipient, the other output is the sender of their own, because the remaining number of coins needed to come back. Of course, the total number of coins if inputs are pointing exactly equal to the amount of the transaction, that leftOverAmount 0, we only need to create a send output to the target user's enough.

const createTxOuts = (receiverAddress:string, myAddress:string, amount, leftOverAmount: number) => {
    const txOut1: TxOut = new TxOut(receiverAddress, amount);
    if (leftOverAmount === 0) {
        return [txOut1]
    } else {
        const leftOverTx = new TxOut(myAddress, leftOverAmount);
        return [txOut1, leftOverTx];
    }
};

Finally, we will calculate the transaction id (for transactional content do hash), and the transaction is signed, the transaction will give the final signature to each input:

const tx: Transaction = new Transaction();
    tx.txIns = unsignedTxIns;
    tx.txOuts = createTxOuts(receiverAddress, myAddress, amount, leftOverAmount);
    tx.id = getTransactionId(tx);

    tx.txIns = tx.txIns.map((txIn: TxIn, index: number) => {
        txIn.signature = signTxIn(tx, index, privateKey, unspentTxOuts);
        return txIn;
    });

Use wallet

We will provide '/ mineTransaction' this api to allow users to easily use the wallet feature:

app.post('/mineTransaction', (req, res) => {
        const address = req.body.address;
        const amount = req.body.amount;
        const resp = generatenextBlockWithTransaction(address, amount);
        res.send(resp);
    });

As shown in the code, the user need only provide the address number of transactions and the recipient can be achieved by this transaction api. Api after the first call to conduct a mining, to obtain a sum of 50 original transaction currency, and then complete the transaction according to the specified recipient address and number of transactions, these transactions will eventually be recorded to a new increase in the block, while updating our "transaction outputs not consumption."

Testing Experience

  • start up

For testing purposes, you do the startup script for package.json some changes, so that we can quickly start two nodes for testing, without the need to enter each time you start a bunch of parameters.

npm run node1
npm run node 2

At the same time, when you start the second node, adding PEER parameters, so that the second node and Node 1 is automatically establish a P2P connection.

"scripts": {
    "prestart": "npm run compile",
    "node1": "HTTP_PORT=3001 P2P_PORT=6001 WALLET=1 npm start ",
    "node2": "HTTP_PORT=3002 P2P_PORT=6002 WALLET=2 PEER=ws://localhost:6001 npm start ",
    "start": "node src/main.js",
    "compile": "tsc"
  },

Finally, by supporting the newly added WALLET parameters, we will automatically initiate a purse for each node, we will be much easier to observe the behavior of the wallet and transactions.

  • View additional interfaces "untraded outputs consumer."

Because "consumer untraded outputs" This list is very important, when we conduct underlying transaction. Therefore, it is necessary to provide an additional interface to view data changes inside. You can send a request by GET way to " HTTP: // localhost: 3001 / unspentTransactionOutputs " interface to obtain the list.

Note that if you played two nodes, then use port 3001 and 3002 are possible. The list is distributed as a list, and our block chain, is the whole network synchronized.

Once you have these, you can easily wallet and trading functions to call the appropriate interfaces to experience through the postman.

summary

We have just implemented a non-encrypted wallet function for simple transactions. Although the trading algorithms (mineTransaction associated with the interface logic) can only have a maximum of two receiver outputs (one receiver, one is their own), but our bottom block chain interface is able to support any number of outputs. For example, you can create a inputs is 50 coins, outputs are 5, 15 and 30 currency trading, but you have to manually fill the data and call / mineRawBlock this interface to achieve.

So far, we conduct a transaction, or the need to conduct a mining, transaction information can be added to the new block inside. Our current each node will not block the transaction is not recorded in doing any exchange of information. These problems will be resolved in the next chapter.

See this section complete code here

chapter Five

This article Fenduo by the World Council of Zhuhai write, Reprinted with authorization, like a point like this, please comment Tucao, such as the project on Github give to the stars, will be greatly appreciated.

Guess you like

Origin blog.csdn.net/zhubaitian/article/details/93346504