blockfrost / blockfrost-js

Node.js SDK for the Blockfrost.io API.
https://blockfrost.io
Apache License 2.0
104 stars 24 forks source link

Unable to create a staking transaction #192

Closed akshitv1 closed 2 years ago

akshitv1 commented 2 years ago

Hi guys, i am trying to create a staking transaction using cardano serialisation library and then use blockfrost/infinito to send the transaction. I am using @emurgo/cardano-serialization-lib-nodejs: ^9.1.4,

Here's my code :

createStakeTx(inputs, ttl, poolID, isFirstStake) {
        try {
            // let rootKey = Cardano.Bip32PrivateKey.from_bytes(Buffer.from(this.masterKey, 'hex'));
            console.log(
                'inside create stake tx ::::::::::::::'
            )
            console.log('inputs :', inputs); 
            console.log('ttl :', ttl);
            console.log('poolId :', poolID);
            console.log('isFirstStake :', isFirstStake);

            // this.masterKey = Cardano.Bip32PrivateKey.from_bip39_entropy(Buffer.from(entropy, 'hex'), Buffer.from(''))
            let rootKey = this.masterKey 
            let spend = rootKey
                .derive(harden(1852))
                .derive(harden(1815))
                .derive(harden(0))
                .derive(this.accountKey) // 1
                .derive(this.addressKey); // 34
            let spendPubKey = spend.to_public();

            let stake = rootKey
                .derive(harden(1852))
                .derive(harden(1815))
                .derive(harden(0))
                .derive(2)
                .derive(0);
            let stakePubkey = stake.to_public();
            let ed = stake.to_public().to_raw_key().hash();
            // console.log('ed :', ed);
            let spendCred = Cardano.StakeCredential.from_keyhash(stake.to_public().to_raw_key().hash());
            let stakeCred = Cardano.StakeCredential.from_keyhash(spend.to_public().to_raw_key().hash());

            // testnet: 0, mainnet: 1
            let address = Cardano.BaseAddress.new(1, spendCred, stakeCred).to_address().to_bech32();

            let linearFee = Cardano.LinearFee.new(
                Cardano.BigNum.from_str('44'),
                Cardano.BigNum.from_str('155381')
            );
            let txBuilder = Cardano.TransactionBuilder.new(
                linearFee,
                Cardano.BigNum.from_str('1000000'),
                Cardano.BigNum.from_str('500000000'),
                Cardano.BigNum.from_str('2000000'),
                16384,
                5000,
            );

            let sumInputs = bigNumber(0);
            inputs.forEach(element => {
                txBuilder.add_key_input(
                    spendPubKey.to_raw_key().hash(),
                    Cardano.TransactionInput.new(
                        Cardano.TransactionHash.from_bytes(Buffer.from(element.tx_id, 'hex')),
                        element.vout
                    ),
                    Cardano.Value.new(Cardano.BigNum.from_str(element.amount))
                );
                sumInputs = sumInputs.plus(element.amount);
            });

            txBuilder.set_ttl(ttl + 2000);

            let poolKeyHash = Cardano.Ed25519KeyHash.from_bytes(Buffer.from(poolID, 'hex'));
            let certs = Cardano.Certificates.new();

            if (isFirstStake == true) {
                certs.add(
                    Cardano.Certificate.new_stake_registration(
                        Cardano.StakeRegistration.new(
                            stakeCred
                        )
                    )
                );                
            }

            certs.add(
                Cardano.Certificate.new_stake_delegation(
                    Cardano.StakeDelegation.new(
                        stakeCred,
                        poolKeyHash
                    )
                )
            );

            txBuilder.set_certs(certs);

            let estimate = this.estimateStakeTxFee(inputs, poolID, isFirstStake);
            txBuilder.set_fee(Cardano.BigNum.from_str(bigNumber(estimate.fee).toFixed()));

            let changeAmount = bigNumber(0);
            console.log('sumInputs :', sumInputs)
            if (isFirstStake == true) {
                changeAmount = sumInputs.minus(KEY_DEPOSIT).minus(estimate.fee);
            } else {
                changeAmount = sumInputs.minus(estimate.fee);
            }

            console.log('changeAmount :', changeAmount)
            txBuilder.add_output(
                Cardano.TransactionOutput.new(
                    Cardano.Address.from_bech32(address),
                    Cardano.Value.new(Cardano.BigNum.from_str(changeAmount.toFixed()))
                )
            );

            let txBody = txBuilder.build();

            const txHash = Cardano.hash_transaction(txBody);

            let witnesses = Cardano.TransactionWitnessSet.new();
            let vkeyWitnesses = Cardano.Vkeywitnesses.new();
            vkeyWitnesses.add(Cardano.make_vkey_witness(
                txHash,
                spend.to_raw_key(),
            ));

            vkeyWitnesses.add(Cardano.make_vkey_witness(
                 txHash,
                 stake.to_raw_key(),
             ));

            witnesses.set_vkeys(vkeyWitnesses);

            let tx = Cardano.Transaction.new(
                txBody,
                witnesses,
            );
            console.log('rawtx ::', Buffer.from(tx.to_bytes()).toString('hex'))
            const res = await client.txSubmit(Buffer.from(tx.to_bytes()).toString('hex'));
            return res;
        } catch (err) {
            console.log('err: ', err);
            return null;
        }
    }

// Error when i use blockfrost :

transaction submit error ShelleyTxValidationError ShelleyBasedEraAlonzo (ApplyTxError [UtxowFailure (WrappedShelleyEraFailure (MissingVKeyWitnessesUTXOW (WitHashes (fromList [KeyHash \"some hash\"])))),DelegsFailure (DelplFailure (DelegFailure (StakeDelegationImpossibleDELEG (KeyHashObj (KeyHash \"somehash\")))))])

//Error when i use infinito : {"type":"jsonwsp/fault","version":"1.0","servicename":"ogmios","fault":{"code":"client","string":"Invalid request: DeserialiseFailure 539 \"Size mismatch when decoding \nRecord RecD.\nExpected 4, but found 3.\"."},"reflection":{"requestId":"XEpib"}} is an unknown result

Maybe i am just creating the transaction in a wrong way or my keys might be messed up.

Open to any help and suggestions

THnaks

slowbackspace commented 2 years ago

I didn't go through all of the code, but derivation of spend key looks weird

let spend = rootKey
                .derive(harden(1852))
                .derive(harden(1815))
                .derive(harden(0)) // THIS IS ACCOUNT INDEX
                .derive(this.accountKey) // 1 THIS SHOULD BE INDEED SET TO 1 FOR EXTERNAL ADDRESS, ARE YOU SURE IT IS SINCE YOU ARE PASSING accountKey
                .derive(this.addressKey); // 34 // THIS IS ADDRESS INDEX

are you sure this is a correct key here?

You are probably better to ask on cardano stack exchange as there are way more people that are working with CSL and could probably spot the error right away

akshitv1 commented 2 years ago

Hi @slowbackspace i used this same key to sign transaction here (https://github.com/blockfrost/blockfrost-js/blob/fb9a55fee07927f8d7c8b0a5aa9efd2d3daf6c77/examples/simple-transaction/src/helpers/signTransaction.ts#L10) and it worked.

I am pretty sure that this is correct derivation path.

Anyways, thanks for replying ill add this on cardano stack exchange.

slowbackspace commented 2 years ago

@akshitv1

I just noticed another thing, stakeCred is using spend key, instead of stake key. Are you sure that's what you want?

let spendCred = Cardano.StakeCredential.from_keyhash(stake.to_public().to_raw_key().hash());
let stakeCred = Cardano.StakeCredential.from_keyhash(spend.to_public().to_raw_key().hash());

If that doesn't help then your best bet is cardano stack exchange as this is not really related to Blockfrost. I'll close this issue, if you have any other problems with Blockfrost sdk itself don't hesitate to open an issue!