Complete LanaCoin Transaction Process Documentation

This comprehensive guide details every aspect of the LanaCoin transaction process, from Electrum server connection to transaction broadcasting.

1 Electrum Server Connection

Connection Details
Server: electrum1.lanacoin.com
Port: 5097
Protocol: TCP Socket Connection
Communication: JSON-RPC over TCP
Implementation

import socket
import json

class ElectrumClient:
    def __init__(self, host='electrum1.lanacoin.com', port=5097):
        self.host = host
        self.port = port
        self.socket = None
        self.request_id = 0

    def connect(self):
        """Establish TCP connection to Electrum server"""
        try:
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.settimeout(30)  # 30-second timeout
            self.socket.connect((self.host, self.port))
            print(f"Connected to {self.host}:{self.port}")
            return True
        except Exception as e:
            print(f"Connection failed: {e}")
            return False
                        
Security Note: The connection is established over TCP without encryption. Electrum servers typically use plain TCP for public blockchain queries. Sensitive operations (private keys) are handled client-side only.

2 Electrum Server Communication Protocol

JSON-RPC Message Format
Request Format:

{
    "id": <request_id>,
    "method": "<method_name>",
    "params": [<parameters>]
}
                        
Response Format:

{
    "id": <request_id>,
    "result": <result_data>,
    "error": null
}
                        
Implementation

def send_request(self, method, params):
    """Send JSON-RPC request to Electrum server"""
    if not self.socket:
        if not self.connect():
            raise Exception("Unable to connect to Electrum server")
    
    # Increment request ID for each call
    self.request_id += 1
    
    # Prepare JSON-RPC request
    request = {
        "id": self.request_id,
        "method": method,
        "params": params
    }
    
    # Convert to JSON and add newline delimiter
    message = json.dumps(request) + '\n'
    
    try:
        # Send request
        self.socket.send(message.encode('utf-8'))
        
        # Receive response
        response_data = ""
        while True:
            chunk = self.socket.recv(4096).decode('utf-8')
            response_data += chunk
            if '\n' in response_data:
                break
        
        # Parse JSON response
        response = json.loads(response_data.strip())
        
        # Check for errors in response
        if 'error' in response and response['error']:
            raise Exception(f"Electrum error: {response['error']}")
        
        return response.get('result')
        
    except Exception as e:
        print(f"Request failed: {e}")
        self.socket = None  # Reset connection on error
        raise
                        
Key Methods Used
  • blockchain.address.get_balance - Get address balance
  • blockchain.address.listunspent - Get unspent outputs
  • blockchain.transaction.broadcast - Broadcast signed transaction
  • blockchain.headers.subscribe - Get current blockchain height

3 Retrieving Unspent Transaction Outputs (UTXOs)

UTXO Query Process
Method: blockchain.address.listunspent
Parameters: [address_string]
Purpose: Retrieve all unspent transaction outputs for a given address
Implementation

def get_unspent(self, address):
    """Get unspent transaction outputs for an address"""
    try:
        # Validate address format first
        if not self.validate_address(address):
            raise Exception("Invalid address format")
        
        # Request unspent outputs from Electrum server
        unspent_list = self.send_request('blockchain.address.listunspent', [address])
        
        # Process and validate each UTXO
        processed_utxos = []
        for utxo in unspent_list:
            processed_utxo = {
                'tx_hash': utxo['tx_hash'],      # Transaction hash (hex)
                'tx_pos': utxo['tx_pos'],        # Output position (vout)
                'value': utxo['value'],          # Value in satoshis
                'height': utxo.get('height', 0), # Block height (0 = unconfirmed)
                'confirmations': self.calculate_confirmations(utxo.get('height', 0))
            }
            processed_utxos.append(processed_utxo)
        
        return processed_utxos
        
    except Exception as e:
        print(f"Failed to get unspent outputs: {e}")
        raise

def calculate_confirmations(self, utxo_height):
    """Calculate number of confirmations for a UTXO"""
    if utxo_height == 0:
        return 0  # Unconfirmed transaction
    
    current_height = self.get_current_height()
    return max(0, current_height - utxo_height + 1)
                        
UTXO Selection Algorithm

def select_inputs(self, unspent, target_amount, send_all=False):
    """Select optimal UTXOs for transaction"""
    if send_all:
        # Use all available UTXOs
        return unspent
    
    # Sort UTXOs by value (largest first) for optimal selection
    sorted_utxos = sorted(unspent, key=lambda x: x['value'], reverse=True)
    
    selected = []
    total_selected = 0
    
    for utxo in sorted_utxos:
        selected.append(utxo)
        total_selected += utxo['value']
        
        # Check if we have enough to cover target amount + estimated fee
        estimated_fee = self.estimate_fee(len(selected), 3)  # 3 outputs typical
        if total_selected >= target_amount + estimated_fee:
            break
    
    if total_selected < target_amount:
        raise Exception("Insufficient funds")
    
    return selected
                        
UTXO Structure:
  • tx_hash: 64-character hex string of the source transaction
  • tx_pos: Integer position of output in source transaction (vout)
  • value: Integer value in satoshis (1 LANA = 100,000,000 satoshis)
  • height: Block height where transaction was confirmed (0 = unconfirmed)

4 Transaction Preparation for Signing

Transaction Structure
LanaCoin Transaction Format:
  • Version: 4 bytes (little-endian) - Always 0x00000001
  • nTime: 4 bytes (little-endian) - Current Unix timestamp
  • Input Count: Variable integer
  • Inputs: Array of transaction inputs
  • Output Count: Variable integer
  • Outputs: Array of transaction outputs
  • Lock Time: 4 bytes (little-endian) - Always 0x00000000
Input Structure Preparation

def prepare_inputs(self, selected_utxos):
    """Prepare transaction inputs from selected UTXOs"""
    inputs = []
    
    for utxo in selected_utxos:
        # Reverse byte order for tx_hash (little-endian)
        tx_hash_bytes = bytes.fromhex(utxo['tx_hash'])[::-1]
        
        # Convert output position to 4-byte little-endian
        tx_pos_bytes = utxo['tx_pos'].to_bytes(4, 'little')
        
        # Get the scriptPubKey from the original transaction
        script_pub_key = self.get_script_pub_key(utxo['tx_hash'], utxo['tx_pos'])
        
        input_data = {
            'tx_hash': utxo['tx_hash'],
            'tx_pos': utxo['tx_pos'],
            'script_pub_key': script_pub_key,
            'value': utxo['value'],
            'sequence': 0xffffffff  # Standard sequence number
        }
        inputs.append(input_data)
    
    return inputs

def get_script_pub_key(self, tx_hash, tx_pos):
    """Retrieve scriptPubKey from original transaction"""
    # Get raw transaction data
    raw_tx = self.send_request('blockchain.transaction.get', [tx_hash, True])
    
    # Extract the scriptPubKey from the specific output
    output = raw_tx['vout'][tx_pos]
    return output['scriptPubKey']['hex']
                        
Output Structure Preparation

def prepare_outputs(self, destination_address, amount_satoshis, change_address, change_amount, donation_address, donation_amount):
    """Prepare transaction outputs"""
    outputs = []
    
    # Main output to destination
    if amount_satoshis > 0:
        dest_pubkey_hash = self.get_pubkey_hash_from_address(destination_address)
        dest_script = self.create_p2pkh_script(dest_pubkey_hash)
        outputs.append({
            'value': amount_satoshis,
            'script': dest_script,
            'address': destination_address
        })
    
    # Change output (if needed)
    if change_amount > 546:  # Dust limit check
        change_pubkey_hash = self.get_pubkey_hash_from_address(change_address)
        change_script = self.create_p2pkh_script(change_pubkey_hash)
        outputs.append({
            'value': change_amount,
            'script': change_script,
            'address': change_address
        })
    
    # Donation output
    if donation_amount > 0:
        donation_pubkey_hash = self.get_pubkey_hash_from_address(donation_address)
        donation_script = self.create_p2pkh_script(donation_pubkey_hash)
        outputs.append({
            'value': donation_amount,
            'script': donation_script,
            'address': donation_address
        })
    
    return outputs

def create_p2pkh_script(self, pubkey_hash):
    """Create Pay-to-Public-Key-Hash script"""
    # Standard P2PKH script: OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
    script = bytes([0x76, 0xa9, 0x14]) + pubkey_hash + bytes([0x88, 0xac])
    return script.hex()
                        
Preimage Construction for Signing

def create_preimage_for_signing(self, inputs, outputs, input_index, sighash_type=0x01):
    """Create the preimage that will be signed"""
    preimage = bytearray()
    
    # Version (4 bytes, little-endian)
    preimage.extend((1).to_bytes(4, 'little'))
    
    # nTime (4 bytes, little-endian)
    current_time = int(time.time())
    preimage.extend(current_time.to_bytes(4, 'little'))
    
    # Input count
    preimage.extend(len(inputs).to_bytes(1, 'little'))
    
    # Process inputs
    for i, inp in enumerate(inputs):
        # Previous transaction hash (32 bytes, little-endian)
        tx_hash_bytes = bytes.fromhex(inp['tx_hash'])[::-1]
        preimage.extend(tx_hash_bytes)
        
        # Previous output index (4 bytes, little-endian)
        preimage.extend(inp['tx_pos'].to_bytes(4, 'little'))
        
        # Script length and script
        if i == input_index:
            # For the input being signed, use the scriptPubKey
            script_bytes = bytes.fromhex(inp['script_pub_key'])
        else:
            # For other inputs, use empty script
            script_bytes = b''
        
        preimage.extend(len(script_bytes).to_bytes(1, 'little'))
        preimage.extend(script_bytes)
        
        # Sequence (4 bytes, little-endian)
        preimage.extend(inp['sequence'].to_bytes(4, 'little'))
    
    # Output count
    preimage.extend(len(outputs).to_bytes(1, 'little'))
    
    # Process outputs
    for output in outputs:
        # Value (8 bytes, little-endian)
        preimage.extend(output['value'].to_bytes(8, 'little'))
        
        # Script length and script
        script_bytes = bytes.fromhex(output['script'])
        preimage.extend(len(script_bytes).to_bytes(1, 'little'))
        preimage.extend(script_bytes)
    
    # Lock time (4 bytes, little-endian)
    preimage.extend((0).to_bytes(4, 'little'))
    
    # SIGHASH type (4 bytes, little-endian)
    preimage.extend(sighash_type.to_bytes(4, 'little'))
    
    return bytes(preimage)
                        

5 Cryptographic Transaction Signing

ECDSA Signing Process
Elliptic Curve: secp256k1
Hash Function: Double SHA-256
Signature Format: DER-encoded ECDSA signature + SIGHASH flag
Critical Security: This step MUST be performed on an offline device. Private keys should never be exposed to network-connected systems.
Client-Side Signing Implementation

// JavaScript implementation for offline signing
class LanaTransactionSigner {
    constructor() {
        // Initialize elliptic curve cryptography
        this.ec = new elliptic.ec('secp256k1');
    }

    async signTransaction(privateKeyWIF, unsignedTransaction) {
        try {
            // Decode WIF private key
            const privateKey = this.decodeWIF(privateKeyWIF);
            const keyPair = this.ec.keyFromPrivate(privateKey, 'hex');
            
            // Sign each input
            const signedInputs = [];
            for (let i = 0; i < unsignedTransaction.inputs.length; i++) {
                const signature = await this.signInput(keyPair, unsignedTransaction, i);
                signedInputs.push(signature);
            }
            
            // Construct final signed transaction
            return this.buildSignedTransaction(unsignedTransaction, signedInputs);
            
        } catch (error) {
            console.error('Signing failed:', error);
            throw error;
        }
    }

    async signInput(keyPair, transaction, inputIndex) {
        // Create preimage for this input
        const preimage = this.createPreimage(transaction, inputIndex);
        
        // Double SHA-256 hash
        const hash = await this.doubleSha256(preimage);
        
        // Sign the hash
        const signature = keyPair.sign(hash, {canonical: true});
        
        // Convert signature to DER format
        let derSig = signature.toDER();
        
        // Add SIGHASH_ALL flag (0x01)
        derSig.push(0x01);
        
        // Get public key
        const publicKey = keyPair.getPublic(true, 'hex');
        
        return {
            signature: derSig,
            publicKey: publicKey
        };
    }

    decodeWIF(wif) {
        // Decode WIF (Wallet Import Format) private key
        const decoded = this.base58CheckDecode(wif);
        
        // Remove version byte and compression flag if present
        let privateKey = decoded.slice(1);
        if (privateKey.length === 33) {
            privateKey = privateKey.slice(0, 32); // Remove compression flag
        }
        
        return privateKey.toString('hex');
    }

    async doubleSha256(data) {
        // First SHA-256
        const hash1 = await crypto.subtle.digest('SHA-256', data);
        // Second SHA-256
        const hash2 = await crypto.subtle.digest('SHA-256', hash1);
        return new Uint8Array(hash2);
    }

    buildSignedTransaction(transaction, signatures) {
        let signedTx = '';
        
        // Version (4 bytes, little-endian)
        signedTx += this.toLittleEndianHex(1, 4);
        
        // nTime (4 bytes, little-endian)
        signedTx += this.toLittleEndianHex(transaction.nTime, 4);
        
        // Input count
        signedTx += this.toVarInt(transaction.inputs.length);
        
        // Inputs with signatures
        for (let i = 0; i < transaction.inputs.length; i++) {
            const input = transaction.inputs[i];
            const sig = signatures[i];
            
            // Previous transaction hash (32 bytes, little-endian)
            signedTx += this.reverseHex(input.tx_hash);
            
            // Previous output index (4 bytes, little-endian)
            signedTx += this.toLittleEndianHex(input.tx_pos, 4);
            
            // Create scriptSig (signature + public key)
            const scriptSig = this.createScriptSig(sig.signature, sig.publicKey);
            signedTx += this.toVarInt(scriptSig.length / 2);
            signedTx += scriptSig;
            
            // Sequence (4 bytes, little-endian)
            signedTx += this.toLittleEndianHex(0xffffffff, 4);
        }
        
        // Output count
        signedTx += this.toVarInt(transaction.outputs.length);
        
        // Outputs
        for (const output of transaction.outputs) {
            // Value (8 bytes, little-endian)
            signedTx += this.toLittleEndianHex(output.value, 8);
            
            // Script length and script
            const scriptBytes = output.script.length / 2;
            signedTx += this.toVarInt(scriptBytes);
            signedTx += output.script;
        }
        
        // Lock time (4 bytes, little-endian)
        signedTx += this.toLittleEndianHex(0, 4);
        
        return signedTx;
    }

    createScriptSig(signature, publicKey) {
        // Push signature onto stack
        let scriptSig = '';
        scriptSig += this.pushData(signature);
        
        // Push public key onto stack
        const pubKeyBytes = publicKey.match(/.{1,2}/g).map(byte => parseInt(byte, 16));
        scriptSig += this.pushData(pubKeyBytes);
        
        return scriptSig;
    }

    pushData(data) {
        const length = Array.isArray(data) ? data.length : data.length;
        let result = '';
        
        if (length <= 75) {
            // Direct push with length prefix
            result += length.toString(16).padStart(2, '0');
            result += Array.isArray(data) ? 
                data.map(b => b.toString(16).padStart(2, '0')).join('') :
                data.map(b => b.toString(16).padStart(2, '0')).join('');
        } else {
            throw new Error('Data too large for simple push');
        }
        
        return result;
    }
}
                        
Signature Verification

verifySignature(publicKey, signature, messageHash) {
    try {
        const key = this.ec.keyFromPublic(publicKey, 'hex');
        const sig = {
            r: signature.slice(0, 32),
            s: signature.slice(32, 64)
        };
        
        return key.verify(messageHash, sig);
    } catch (error) {
        console.error('Signature verification failed:', error);
        return false;
    }
}
                        

6 Transaction Broadcasting

Broadcasting Process
Method: blockchain.transaction.broadcast
Parameters: [signed_transaction_hex]
Response: Transaction ID (txid) if successful
Implementation

def broadcast_transaction(self, signed_tx_hex):
    """Broadcast signed transaction to the network"""
    try:
        # Validate transaction format before broadcasting
        if not self.validate_transaction_hex(signed_tx_hex):
            raise Exception("Invalid transaction format")
        
        # Broadcast to Electrum server
        result = self.send_request('blockchain.transaction.broadcast', [signed_tx_hex])
        
        # The result should be the transaction ID
        if isinstance(result, str) and len(result) == 64:
            print(f"Transaction broadcasted successfully: {result}")
            return {
                'success': True,
                'txid': result,
                'message': 'Transaction submitted to network'
            }
        else:
            raise Exception(f"Unexpected broadcast result: {result}")
            
    except Exception as e:
        error_msg = str(e)
        print(f"Broadcast failed: {error_msg}")
        
        # Parse common error messages
        if "insufficient fee" in error_msg.lower():
            return {
                'success': False,
                'error': 'Transaction fee is too low',
                'suggestion': 'Increase the transaction fee and try again'
            }
        elif "dust" in error_msg.lower():
            return {
                'success': False,
                'error': 'Transaction creates dust outputs',
                'suggestion': 'Increase output amounts above dust threshold'
            }
        elif "double spend" in error_msg.lower():
            return {
                'success': False,
                'error': 'Transaction inputs already spent',
                'suggestion': 'Refresh wallet and create new transaction'
            }
        else:
            return {
                'success': False,
                'error': error_msg,
                'suggestion': 'Check transaction format and try again'
            }

def validate_transaction_hex(self, tx_hex):
    """Validate transaction hex format"""
    try:
        # Check if hex string is valid
        if not tx_hex or len(tx_hex) % 2 != 0:
            return False
        
        # Try to decode hex
        tx_bytes = bytes.fromhex(tx_hex)
        
        # Basic structure validation
        if len(tx_bytes) < 10:  # Minimum transaction size
            return False
        
        # Check version (first 4 bytes should be 01000000 for version 1)
        version = int.from_bytes(tx_bytes[0:4], 'little')
        if version != 1:
            return False
        
        return True
        
    except Exception as e:
        print(f"Transaction validation error: {e}")
        return False
                        
Post-Broadcast Monitoring

def monitor_transaction_status(self, txid, timeout_seconds=300):
    """Monitor transaction confirmation status"""
    start_time = time.time()
    
    while time.time() - start_time < timeout_seconds:
        try:
            # Get transaction details
            tx_info = self.send_request('blockchain.transaction.get', [txid, True])
            
            if 'confirmations' in tx_info:
                confirmations = tx_info['confirmations']
                if confirmations > 0:
                    return {
                        'status': 'confirmed',
                        'confirmations': confirmations,
                        'block_height': tx_info.get('height', 0)
                    }
                else:
                    return {
                        'status': 'pending',
                        'confirmations': 0,
                        'in_mempool': True
                    }
            else:
                return {
                    'status': 'not_found',
                    'error': 'Transaction not found in mempool or blockchain'
                }
                
        except Exception as e:
            print(f"Error monitoring transaction: {e}")
            
        # Wait before next check
        time.sleep(10)
    
    return {
        'status': 'timeout',
        'error': 'Monitoring timeout reached'
    }
                        
Broadcasting Security:
  • Never broadcast a transaction multiple times - this can lead to double-spending errors
  • Always validate the transaction format before broadcasting
  • Monitor for confirmation to ensure transaction is accepted by the network
  • Keep the signed transaction hex for potential rebroadcast if needed

Error Handling and Best Practices

Common Error Scenarios
Error Type Cause Solution
Connection Timeout Electrum server unreachable Implement retry logic with exponential backoff
Insufficient Funds UTXOs don't cover amount + fee Reduce amount or wait for more UTXOs
Invalid Signature Signing process error Verify preimage construction and private key
Double Spend UTXOs already used Refresh UTXO list and rebuild transaction
Fee Too Low Network congestion Increase fee rate and rebuild transaction
Security Best Practices
  • Air-Gapped Signing: Perform all signing operations on offline devices
  • Key Management: Never log, store, or transmit private keys
  • Transaction Validation: Always verify transaction details before signing
  • Fee Calculation: Include appropriate fees to ensure timely confirmation
  • UTXO Management: Consider consolidation to reduce future transaction fees
  • Network Monitoring: Monitor transaction status after broadcasting