package bitcoinElectrum import ( "bufio" "crypto/tls" "encoding/json" "fmt" "net" "time" "github.com/btcsuite/btcd/btcutil" ) // --- Electrum JSON-RPC Structures --- type ElectrumRequest struct { ID int `json:"id"` Method string `json:"method"` Params []interface{} `json:"params"` } type ElectrumBalanceResponse struct { Result struct { Confirmed int64 `json:"confirmed"` Unconfirmed int64 `json:"unconfirmed"` } `json:"result"` Error *struct { Message string `json:"message"` } `json:"error"` } // Connects to Electrum and queries the balance for a single address. func getAddressBalance(electrumServer string, address btcutil.Address) (*ElectrumBalanceResponse, error) { scripthash, err := addressToScriptHash(address) if err != nil { return nil, fmt.Errorf("could not create scripthash: %w", err) } // Connect to the server with SSL/TLS conf := &tls.Config{InsecureSkipVerify: true} // Use InsecureSkipVerify for public servers, but be aware of MITM risks conn, err := tls.DialWithDialer(&net.Dialer{Timeout: 5 * time.Second}, "tcp", electrumServer, conf) if err != nil { return nil, fmt.Errorf("could not connect to electrum server: %w", err) } defer conn.Close() // Prepare the JSON-RPC request request := ElectrumRequest{ ID: 1, Method: "blockchain.scripthash.get_balance", Params: []interface{}{scripthash}, } requestBytes, _ := json.Marshal(request) // Send the request (with a newline delimiter) _, err = fmt.Fprintf(conn, "%s\n", requestBytes) if err != nil { return nil, fmt.Errorf("failed to send request: %w", err) } // Read the response reader := bufio.NewReader(conn) responseLine, err := reader.ReadString('\n') if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } var response ElectrumBalanceResponse if err := json.Unmarshal([]byte(responseLine), &response); err != nil { return nil, fmt.Errorf("failed to unmarshal response: %w", err) } if response.Error != nil { return nil, fmt.Errorf("electrum server error: %s", response.Error.Message) } return &response, nil }