Solana Web3.jsで「connect ECONNREFUSED ::1:8899」エラー

いままで動いていたのに突然エラーになった現象。

現象

ターミナル:

% ts-node create_mint_and_transfer_tokens.ts
/Users/user/Documents/Programming/Blockchain/solana-anchor-react-minimal-example/scripts/solana/spl-token-v0.2.0/node_modules/node-fetch/lib/index.js:1491
            reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err));
          ^
FetchError: request to http://localhost:8899/ failed, reason: connect ECONNREFUSED ::1:8899
    at ClientRequest.<anonymous> (/Users/user/Documents/Programming/Blockchain/solana-anchor-react-minimal-example/scripts/solana/spl-token-v0.2.0/node_modules/node-fetch/lib/index.js:1491:11)
    at ClientRequest.emit (node:events:513:28)
    at ClientRequest.emit (node:domain:489:12)
    at Socket.socketErrorListener (node:_http_client:494:9)
    at Socket.emit (node:events:513:28)
    at Socket.emit (node:domain:489:12)
    at emitErrorNT (node:internal/streams/destroy:151:8)
    at emitErrorCloseNT (node:internal/streams/destroy:116:3)
    at processTicksAndRejections (node:internal/process/task_queues:82:21) {
  type: 'system',
  errno: 'ECONNREFUSED',
  code: 'ECONNREFUSED'
}

ソース(create_mint_and_transfer_tokens.ts):

// Source: https://github.com/solana-labs/solana-program-library/blob/895747f29fd38fc61961ffc1bc5c73dab57bba1a/token/js/examples/create_mint_and_transfer_tokens.ts
import { clusterApiUrl, Connection, Keypair, LAMPORTS_PER_SOL } from '@solana/web3.js';
import { createMint, getOrCreateAssociatedTokenAccount, mintTo, transfer, setAuthority, TOKEN_PROGRAM_ID } from '@solana/spl-token';

export const main = async() => {
    // Connect to cluster
    let connection = new Connection('http://localhost:8899', 'confirmed');
    // const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');

    // Generate a new wallet keypair and airdrop SOL
    const fromWallet = Keypair.generate();
    const fromAirdropSignature = await connection.requestAirdrop(fromWallet.publicKey, LAMPORTS_PER_SOL);

    let latestBlockHash = await connection.getLatestBlockhash();

    // Wait for airdrop confirmation
    await connection.confirmTransaction({
      blockhash: latestBlockHash.blockhash,
      lastValidBlockHeight: latestBlockHash.lastValidBlockHeight,
      signature: fromAirdropSignature,
    });

    // Generate a new wallet to receive newly minted token
    const toWallet = Keypair.generate();

    // Create new token mint
    const mint = await createMint(
        connection, // connection
        fromWallet, // payer
        fromWallet.publicKey, // mintAuthority
        null, // freezeAuthority
        9 // decimals
    );

    // Get the token account of the fromWallet address, and if it does not exist, create it
    const fromTokenAccount = await getOrCreateAssociatedTokenAccount(
        connection, // connection
        fromWallet, // payer
        mint, // mint
        fromWallet.publicKey // owner
    );

    // Get the token account of the toWallet address, and if it does not exist, create it
    const toTokenAccount = await getOrCreateAssociatedTokenAccount(
        connection,
        fromWallet,
        mint,
        toWallet.publicKey
    );

    // Mint 1 new token to the "fromTokenAccount" account we just created
    const signature_mint = await mintTo(
        connection, // connection
        fromWallet, // payer
        mint, // mint
        fromTokenAccount.address, // destination
        fromWallet.publicKey, // authority
        1000000000, // amount
        [] // signer(s)
    );

    // Transfer the new token to the "toTokenAccount" we just created
    const signature_tx = await transfer(
        connection, // connection
        fromWallet, // payer
        fromTokenAccount.address, // source
        toTokenAccount.address, // destination
        fromWallet.publicKey, // owner
        1000000000, // amount
        [] // signer
    );

    // Set new authority for Mint Account
    // https://solana-labs.github.io/solana-program-library/token/js/modules.html#setAuthority
    const randomWallet = Keypair.generate();

    const mint_rand_auth_tx = await setAuthority(
        connection, // connection
        fromWallet, // payer
        mint, // account
        fromWallet.publicKey, // currentAuthority
        0, // authorityType. MintTokens = 0, FreezeAccount = 1, AccountOwner = 2, CloseAccount = 3
        randomWallet.publicKey, // newAuthority
        [fromWallet], // multiSigners
        {}, // (Optional) confirmOptions
        TOKEN_PROGRAM_ID, // ProgramId
    );

    // Set new authority for Token Account
    // https://solana-labs.github.io/solana-program-library/token/js/modules.html#setAuthority
    const token_acc_rand_auth_tx = await setAuthority(
        connection, // connection
        fromWallet, // payer
        fromTokenAccount.address, // account
        fromWallet.publicKey, // currentAuthority
        2, // authorityType. MintTokens = 0, FreezeAccount = 1, AccountOwner = 2, CloseAccount = 3
        randomWallet.publicKey, // newAuthority
        [fromWallet], // multiSigners
        {}, // (Optional) confirmOptions
        TOKEN_PROGRAM_ID, // ProgramId
    );


    console.log('fromWallet.publicKey       =>', fromWallet.publicKey.toString());
    console.log('toWallet.publicKey         =>', toWallet.publicKey.toString());
    console.log('fromTokenAccount.address   =>', fromTokenAccount.address.toString());
    console.log('toTokenAccount.address     =>', toTokenAccount.address.toString());
    console.log('mint address               =>', mint.toString());

    console.log('mint tx                    =>', signature_mint);
    console.log('transfer tx                =>', signature_tx);
    console.log('randomWallet.publicKey     =>', randomWallet.publicKey.toString());
    console.log('mint_rand_auth_tx          =>', mint_rand_auth_tx);
    console.log('token_acc_rand_auth_tx     =>', token_acc_rand_auth_tx);
};

main();

/*
% ts-node <THIS FILE>
fromWallet.publicKey       => CSyFrHrGa1XRxphmXE9EtJzUgGBpm1bX166PGSFKwnM4
toWallet.publicKey         => 5e4LFx4o5nJA9gaweE1iKEC1aL5ELMY7oAsbNZKuvadG
fromTokenAccount.address   => AEQmpEmT9MmMTDAfx7jv76Vqv8d3ZaRn6rzpQcRZNJ5K
toTokenAccount.address     => 7txFE1FvcrfNzoMcjZeA5uxFc95iwQ7nvBNZSNqeo3Zk
mint address               => 5482WN9J8je5qvDg8SRkeST8uAAuR4qXPC76HkqQTkvd
mint tx                    => 3twy8ntB5fPH6Tqw8NiXRJDybSKx28ZcV2zoGhN6QEjcu88fnLbSJ4vgYZUvrLmzcBdBnsw8HoJHQrLCkBMzjj7f
transfer tx                => 3CQ1mvJDgNWAYefcqJfmm2Gj1CRow27qKDDzidTcGe3WhWNSBWv5FAcjz6G5SpSvYwSeSxVZf2XKNAUY2Ygo7jMd
randomWallet.publicKey     => 2QB6vsBPtPBw4ukeHgjBhBZbRRr97sosaronGUb2uJ6Y
mint_rand_auth_tx          => 44UgTv2AiPVBkong3tmLnJhY8JE9BoD2bVwpAzqHpWD6WbU2aREYkcNjEVAo5zzKS8NoNGRt6cUUZbhcR28k26Sj
token_acc_rand_auth_tx     => 3MZ2h9vceZ4V1qe1RA812uKNRfQQaBxrHa2HyJG3RE6HvN1phmp8pJ15Nh9GdCkteg9naT4i9bFTcgocw1sNAuWj
*/

ターミナル(solana-test-validator):

% solana-test-validator
Ledger location: test-ledger
Log: test-ledger/validator.log
⠦ Initializing...
Identity: AWd5D3Jazj4UbhKsyS4JuiczZXr3iNwgufjWvh3MXP7G
Genesis Hash: GQTugGRMD9G3LHj3PG3VvtUViyqRxJpkTq8UH2p1W6JC
Version: 1.10.8
Shred Version: 44120
Gossip Address: 127.0.0.1:1024
TPU Address: 127.0.0.1:1027
JSON RPC URL: http://127.0.0.1:8899
⠠ 00:24:08 | Processed Slot: 3079 | Confirmed Slot: 3079 | Finalized Slot: 3047 | Full Snapshot Slot: 3000 | Incremental Snapshot Slot: - | Transactions: 3090 | ◎499.984642500

原因

どうやら、Connectionするときにホスト名が厳密化されたようで、エイリアスが使えない(?)っぽい。localhost = 127.0.0.1 として今まで動いていたが、これがダメだった。

該当部分:

let connection = new Connection('http://localhost:8899', 'confirmed');

対応

solana-test-validatorを127.0.0.1:8899で起動しているため、そのホスト名にあわせて修正する。

let connection = new Connection('http://127.0.0.1:8899', 'confirmed');

補足

nodeのバージョンを下げると直るという情報があり、v18からv16に下げることで、たしかにそれでも動作できた。