kinecosystem / kin-node

DEPRECATED! Please use Kinetic: https://developer.kin.org/docs/kinetic
https://developer.kin.org/docs/kinetic
MIT License
16 stars 14 forks source link

Kin 3 signing does not work #10

Closed doc-l closed 3 years ago

doc-l commented 3 years ago

For kin 3 it's not enough to simply return a 200 when signing a webhook. I use serverless, so express is not possible and as the webhooks assume serverless, I cannot really use the webhook code. I am trying to build it myself using the transaction signer as example and have this now. I just want to get a transaction working, so I removed all the request stuff to get a minimum working example.

import {
    Environment,
    PrivateKey,
    NetworkPasshrase,
} from '@kinecosystem/kin-sdk-v2';
import {
    SignTransactionResponse,
} from '@kinecosystem/kin-sdk-v2/dist/webhook';
import { xdr } from 'stellar-base';

export default async (event) => {
    console.log(event);
    const env = process.env.KIN_ENV === 'prod' ? Environment.Prod : Environment.Test;
    let networkPassphrase;
    switch (env) {
    case Environment.Test:
        networkPassphrase = NetworkPasshrase.Test;
        break;
    case Environment.Prod:
        networkPassphrase = NetworkPasshrase.Prod;
        break;
    default:
        throw (Error('No env'));
    }
    const reqBody = JSON.parse(event.body);

    let signResponse;

    const kinVersion = (reqBody.kin_version ? reqBody.kin_version : 3);
    if (kinVersion === 4) {
        if (!reqBody.solana_transaction || typeof reqBody.solana_transaction !== 'string') {
            return {
                statusCode: 400,
            };
        }

        signResponse = new SignTransactionResponse();
    } else {
        if (!reqBody.envelope_xdr || typeof reqBody.envelope_xdr !== 'string') {
            return {
                statusCode: 400,
            };
        }

        const envelope = xdr.TransactionEnvelope.fromXDR(Buffer.from(reqBody.envelope_xdr, 'base64'));
        console.log(envelope)
        signResponse = new SignTransactionResponse(envelope, networkPassphrase);
        signResponse.sign(PrivateKey.fromString(process.env.HOT_WALLET_SECRET));
        console.log(signResponse)
    }

    if (signResponse.isRejected() || (signResponse.envelope && !signResponse.signedEnvelope)) {
        return {
            statusCode: 403,
            body: JSON.stringify({
                invoice_errors: signResponse.invoiceErrors,
            }),
        };
    }
    if (signResponse.envelope) {
        return {
            statusCode: 200,
            body: JSON.stringify({
                envelope_xdr: signResponse.signedEnvelope.toXDR('base64'),
            }),
        };
    }

    return {
        statusCode: 200,
    };
};

Note that I sign the transaction signResponse.sign(PrivateKey.fromString(process.env.HOT_WALLET_SECRET));.

When I do that, I get the following error:

{
    "errorType": "Error",
    "errorMessage": "Invalid TransactionEnvelope: expected an envelopeTypeTxV0 or envelopeTypeTx but received an envelopeTypeTxV0.",
    "stack": [
        "Error: Invalid TransactionEnvelope: expected an envelopeTypeTxV0 or envelopeTypeTx but received an envelopeTypeTxV0.",
        "    at new Transaction (/var/task/node_modules/@kinecosystem/kin-sdk-v2/node_modules/stellar-base/lib/transaction.js:63:13)",
        "    at Function.fromXDR (/var/task/node_modules/@kinecosystem/kin-sdk-v2/node_modules/stellar-base/lib/transaction_builder.js:358:14)",
        "    at SignTransactionResponse.sign (/var/task/node_modules/@kinecosystem/kin-sdk-v2/dist/webhook/index.js:87:63)",
        "    at Runtime.n.default [as handler] (/var/task/signTransaction.js:1:1877)",
        "    at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"
    ]
}

This is some stellar internal stuff which I have no idea what to do with.

How can I sign a transaction without using express?

showered commented 3 years ago

Just a thought - Have you looked in to using the Go SDK instead?

doc-l commented 3 years ago

Thanks, no I have never used go before but have many years of JS/TS experience. I'd like to keep it in javascript or typescript

doc-l commented 3 years ago

I got it working, I saw in the stellar source code (https://stellar.github.io/js-stellar-base/transaction.js.html) that the envelope can also be sent as string and will be converted by the stellar sdk. So I modified

const envelope = xdr.TransactionEnvelope.fromXDR(Buffer.from(reqBody.envelope_xdr, 'base64'));

to

const envelope = reqBody.envelope_xdr;

And reran and the transaction was successful.

For reference, the full code for non-express is as follows. It doesn't check the request, but that is easy enough to add in given the source code of the express code.

import {
    Environment,
    PrivateKey,
    NetworkPasshrase,
} from '@kinecosystem/kin-sdk-v2';
import {
    SignTransactionResponse,
} from '@kinecosystem/kin-sdk-v2/dist/webhook';

export default async (event) => {
    const env = process.env.KIN_ENV === 'prod' ? Environment.Prod : Environment.Test;
    let networkPassphrase;
    switch (env) {
    case Environment.Test:
        networkPassphrase = NetworkPasshrase.Test;
        break;
    case Environment.Prod:
        networkPassphrase = NetworkPasshrase.Prod;
        break;
    default:
        throw (Error('No env'));
    }
    const reqBody = JSON.parse(event.body);

    let signResponse;

    const kinVersion = (reqBody.kin_version ? reqBody.kin_version : 3);
    if (kinVersion === 4) {
        if (!reqBody.solana_transaction || typeof reqBody.solana_transaction !== 'string') {
            return {
                statusCode: 400,
            };
        }

        signResponse = new SignTransactionResponse();
    } else {
        if (!reqBody.envelope_xdr || typeof reqBody.envelope_xdr !== 'string') {
            return {
                statusCode: 400,
            };
        }

        const envelope = reqBody.envelope_xdr;
        signResponse = new SignTransactionResponse(envelope, networkPassphrase);
        signResponse.sign(PrivateKey.fromString(process.env.HOT_WALLET_SECRET));
    }

    if (signResponse.isRejected() || (signResponse.envelope && !signResponse.signedEnvelope)) {
        return {
            statusCode: 403,
            body: JSON.stringify({
                invoice_errors: signResponse.invoiceErrors,
            }),
        };
    }
    if (signResponse.envelope) {
        return {
            statusCode: 200,
            body: JSON.stringify({
                envelope_xdr: signResponse.signedEnvelope.toXDR('base64'),
            }),
        };
    }

    return {
        statusCode: 200,
    };
};

I'll keep the issue open so someone could do something with this, or close it will