aergoio / herajs

Javascript SDK for Aergo
https://herajs.readthedocs.io/
MIT License
8 stars 7 forks source link

Improving validation check for transaction #145

Open hayarobi opened 2 years ago

hayarobi commented 2 years ago

A digital signature is required for Argo's transactions, but when calling hashTransaction, it calculate without a error, assuming a digital signature is empty. If this transaction with wrong calculated invalid hash is sent to the server, the server returns a TX_INVALID_HASH error because the server calculates the hash value including the signature. I know tt is actually user's fault, but I think it would be better to do more detailed and kind validation at the SDK level. This validation will prevent both the client and the server from unnecessary loads.

And if the transaction without digital signature is sent, UNDEFINED_ERROR: malformed signature: too short error is returned. In this case as well, I think it would be better if the herajs check it before sending it to the server and send it.

hayarobi commented 2 years ago

Here is the sample code i wrote.

import {AergoClient, GrpcProvider} from "@herajs/client";
import {
    createIdentity,
    signTransaction,
    hashTransaction,
    verifyTxSignature,
    encodeTxHash,
    encryptPrivateKey, keystoreFromPrivateKey, identityFromKeystore, encodePrivateKey, decodePrivateKey
} from '@herajs/crypto';
import {base58, encodeBuffer, toHexString} from "@herajs/common";
import {decryptPrivateKey, identityFromPrivateKey} from "@herajs/crypto";

const testnode_url = 'localhost:7845';
// const testnode_url = 'alpha-api.aergo.io:7845';
//const testnode_url = 'testnet-api.aergo.io:7845';
const aergoClient = new AergoClient({},
    new GrpcProvider({url: testnode_url}));

//    Sign and Send Transaction
async function sendTx() {
    let identity;
    if( false ) {
        identity = createIdentity();
        console.log("address", identity.address);
        const keystore = await exportIdentity(identity.privateKey, 'pass');
    } else {
        identity = await importIdentity("488f74x19Xi86Wfe3D2yCyDhdAacpfR8qRFG1jTorJ3fc8XdvphCd7TtqwYKiq7DinxiMqbQz","pass")
    }

    const tx = {
        nonce: 2,
        from: identity.address,
        to: 'AmPJhiRahaVoMAVF9dbPi6qisGzLbs6Hh8N6g7NoLeQ3uYSrb1qL',
        amount: "1300000000000000000 aer",
        payload: "it's payload",
        chainIdHash: await aergoClient.getChainIdHash("base58")
    };
    const signedTx = await signTransaction(tx, identity.keyPair);
    // mistakenly calculated hash before set signature
    const hashBytes = await hashTransaction(tx, "bytes", true);
    const encodedTxHash = encodeBuffer(hashBytes);
    console.log("tx hash in b58",encodedTxHash);
    tx.sign = signedTx; 
    tx.hash = encodedTxHash;

    const verifiedTxSig = await verifyTxSignature(
        tx,
        identity.keyPair,
        signedTx,
    );
    // console.log("verify status",verifiedTxSig);

    const txObj = tx;
    const result = await aergoClient.sendSignedTransaction(txObj);

    console.log("result" , result);
}

async function exportIdentity(privateKey, password) {
    const keystore = await keystoreFromPrivateKey(privateKey, password);
    const wif = encodePrivateKey(await encryptPrivateKey(Uint8Array.from(privateKey), password));

    const strForm = JSON.stringify(keystore, null, 2);
    console.log("keystring",wif)
    return keystore;
}

async function importIdentity(wif, password) {
    const firstPass = decodePrivateKey(wif);
    const secondPass = await decryptPrivateKey(firstPass, password);
    const keystore = await keystoreFromPrivateKey(secondPass, password);
    const identity = await identityFromKeystore(keystore, password)
    return identity;
}

sendTx()
kroggen commented 2 years ago

I use this sequence to send a txn and it works:

  console.log("sending transaction:", tx)

  tx.sign = await crypto.signTransaction(tx, account.keyPair);
  tx.hash = await crypto.hashTransaction(tx, 'bytes');
  const txhash = await aergo.sendSignedTransaction(tx);
  const receipt = await aergo.waitForTransactionReceipt(txhash);

  console.log("transaction receipt:", receipt)
hayarobi commented 2 years ago

@kroggen Yeah, your code is right. What I want to say is it's better to show clearer error message and stack trace about this human error. One of my clients did the mistake, and was hard to find out where he made mistake since the error message was INVALID_TX_HASH while sending transaction, not while generating txHash.