0

I'm attempting to ling 3 validator nodes - with the objective of becoming a Full Validator.

I'm performing this operation based on the Link Validator requirements as described on https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0020.md. This firstly requires creating an account. I'm doing this based on the description on https://github.com/stellar/new-docs/blob/master/content/docs/tutorials/create-account.mdx.

I am able to link 1 node, but always the other two fail with a tx_bad_seq error. I'm unfamiliar with this error. I wondering if I need to close the transaction, or maybe the network is too busy.

Js

import get_new_keys  from './create_keys.js';
import get_account  from './account.js';
import create_linked_node  from './link_node.js';
import StellarSdk from 'stellar-sdk';

const doTrx = true;

// the
const homeDomain = 'my.stellar.home.domain.com';
const my_secret = 'REDACTED';

// const servers = ['SERVER_1','SERVER_2','SERVER_3']
const servers = ['SERVER_1','SERVER_2']

var keyed_servers = [];

var keyed = servers.forEach( function(server){
    var keyed_server = {
        "server": server,
        "keys": get_new_keys()
    }
    keyed_servers.push(keyed_server);
});

console.log(keyed_servers);

console.log('ACCOUNT');
const myKey = StellarSdk.Keypair.fromSecret(my_secret);  
const publicKey = myKey.publicKey()

const account = await get_account(true, publicKey);

console.log("Balances for account: " + publicKey);
account.balances.forEach(function (balance) {
    console.log("Type:", balance.asset_type, ", Balance:", balance.balance);
});

var linked = keyed_servers.forEach(async function(server){
    let nodeKeys = StellarSdk.Keypair.fromSecret(server.keys.secret);
    console.log(`Server (with keys): ${server.server}`)
    // console.log(`Server key: ${Object.values(nodeKeys)}`)

    const tx_payload = {
        "nodeKeys": nodeKeys,
        "node_server": server,
        "authorizing_act": myKey,
        "home_domain": homeDomain
    }

    if (doTrx) {
         
        const tx_result = await create_linked_node(tx_payload);
        if (tx_result) {
            console.log(`${server.server} linked! TX result ${Object.values(tx_result)}`);
        }else{
            console.log(`TX FAILED for ${server.server}`);
        }
        
    } else {
        console.log('Transaction capability disabled');        
    }
    
});

linking function

export default async function create_linked_node(tx_payload) {
    
    const userPair = tx_payload.authorizing_act;
    const node_server = tx_payload.node_server;
    const homeDomain = tx_payload.home_domain;
    const nodeKeys = tx_payload.nodeKeys;

    const server = new StellarSdk.Server("https://horizon.stellar.org");
    const account = await server.loadAccount(userPair.publicKey());
    const nodeId = nodeKeys.publicKey();
    try {
        const tx = new StellarSdk.TransactionBuilder(account,
            {
                fee: StellarSdk.BASE_FEE,
                networkPassphrase: StellarSdk.Networks.PUBLIC,
            })
            .addOperation(StellarSdk.Operation.createAccount({
                destination: nodeId,
                startingBalance: '1'
            }))
            .addOperation(StellarSdk.Operation.setOptions({
                source: nodeId,
                homeDomain: homeDomain
            }))
            .setTimeout(1000)
            .build();

        tx.sign(userPair);
        tx.sign(nodeKeys);
        console.log(`Submitting transaction for ${node_server.server}`);
        const result = await server.submitTransaction(tx);

        // console.log(result);
        return result;

    } catch (e) {
        console.error(`ERROR linking ${node_server.server}`);
        // console.error(e);
        if (e.response) {
            // console.error(e.response);
            if (e.response.data) {
                // console.error(e.response.data);
                if (e.response.data.extras) {
                    console.log('RESULTS CODES');
                    console.error(e.response.data.extras.result_codes);
                    console.error(e.response.data.extras.result_codes.operations);
                }
            }
        }

    }

}
2

"tx_bad_seq" occurs when the transactions sequence number isn't exactly tx source accounts current sequence number + 1. When a transaction gets submitted successfully the sequence number is consumed and you'll need to calculate (or fetch) the next one.

The SDKs normally do that for you with server.loadAccount().

So why does it fail in your case then? It fails because you are trying to submit multiple transactions in parallel. server.loadAccount will fetch the same sequence number for userPair.publicKey() multiple times and then the sequence number of your account will change, after the first transaction got submitted.

You could swap your keyed_servers.forEach for a normal for loop and await the results within it. That would make your transactions run sequential and therefore not fail.

You could also run most of your operations in one transaction. A transaction in stellar can contains up to 100 operations, so if you filter your data and than build one "giant" transaction out of it, you could probably fit in most if not all of your operations in one transaction.

1
  • Wonderful! That makes a lot of sense! Thank you for that very clear explanation.
    – Magick
    Jul 14 at 9:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.