<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chinonso Amadi</title>
    <description>The latest articles on DEV Community by Chinonso Amadi (@nonsoamadi10).</description>
    <link>https://dev.to/nonsoamadi10</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F154261%2F64a58b55-9322-4d18-8d7b-5dcdd550fb7b.jpg</url>
      <title>DEV Community: Chinonso Amadi</title>
      <link>https://dev.to/nonsoamadi10</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nonsoamadi10"/>
    <language>en</language>
    <item>
      <title>Moving My Technical Essays to Medium</title>
      <dc:creator>Chinonso Amadi</dc:creator>
      <pubDate>Tue, 23 Dec 2025 21:40:51 +0000</pubDate>
      <link>https://dev.to/nonsoamadi10/moving-my-technical-essays-to-medium-k04</link>
      <guid>https://dev.to/nonsoamadi10/moving-my-technical-essays-to-medium-k04</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;After a great run sharing tutorials, tips, and lessons on dev.to, I’m shifting my long-form technical writing to Medium.&lt;/p&gt;

&lt;p&gt;Why Medium? I want to explore deeper dives into Bitcoin infrastructure, Lightning Network operations, custody security, and high-scale system engineering—topics that benefit from longer narratives, detailed diagrams, and real-world lessons from production.&lt;/p&gt;

&lt;p&gt;If you’ve enjoyed my posts here, you’ll find the Medium articles even more in-depth, practical, and actionable. I’ll continue sharing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Lessons from running Bitcoin Lightning nodes in production&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Strategies for building scarce, high-value skills in infrastructure and security&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hands-on guides for Rust, cryptography, and distributed systems&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can follow me on Medium here: &lt;a href="https://medium.com/@jackhoudini" rel="noopener noreferrer"&gt;Wartime Engineer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you to the dev.to community for all the engagement and feedback—it’s been invaluable. I hope to see you over on Medium for the next chapter.&lt;/p&gt;

&lt;p&gt;— Chinonso&lt;/p&gt;

</description>
      <category>programming</category>
      <category>security</category>
      <category>bitcoin</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>Designing Bitcoin Infrastructure Under Adversarial Assumptions</title>
      <dc:creator>Chinonso Amadi</dc:creator>
      <pubDate>Fri, 19 Dec 2025 00:17:33 +0000</pubDate>
      <link>https://dev.to/nonsoamadi10/designing-bitcoin-infrastructure-under-adversarial-assumptions-5820</link>
      <guid>https://dev.to/nonsoamadi10/designing-bitcoin-infrastructure-under-adversarial-assumptions-5820</guid>
      <description>&lt;p&gt;Scaling Bitcoin infrastructure exposes a fundamental tension: critical operations often run on environments we do not control. In building signing and execution services, I encountered this directly. Assumptions that the cloud provider is "mostly honest" work for standard workloads but fail when private keys, signing logic, or protocol-critical operations are involved. This is not paranoia; it is the reality of operational security in systems where value is at stake. Single trust assumptions cascade into systemic fragility, a problem engineers sense but rarely discuss.&lt;/p&gt;

&lt;p&gt;Typical Bitcoin deployments rely on virtual machines, containerized services, environment-variable secrets, and IAM policies. This setup suffices for stateless applications but is brittle for high-trust components. Custody services expose keys to host memory; consensus-critical nodes assume the runtime is benign; signing services for multisig or threshold schemes treat the host OS as cooperative. Application-specific processors inherit the same blind spots. Administrative boundaries, plaintext secrets, and mutable build artifacts combine into an implicit trust model that cannot withstand adversarial conditions. These weaknesses are structural, not accidental.&lt;/p&gt;

&lt;p&gt;This is an execution problem under adversarial assumptions, not a cloud problem. Operators may be hostile, hosts compromised, builds tampered with, or configurations misapplied. Traditional security focuses on perimeter hardening; adversarial infrastructure requires minimizing trust in the operator itself. The right primitives are isolated execution, reproducibility, and attestation.&lt;/p&gt;

&lt;p&gt;Trusted execution environments provide hardware-backed isolation, cryptographic measurement, and remote attestation. Secrets are released only when verifiable conditions are met. This allows a trust model that is instance-specific and conditional, shifting security from hope to verifiable constraints. Confidential computing is not a panacea: it does not prevent application-level bugs, side channels, or misconfigured network I/O. Its value lies in reducing operator trust in a measurable, enforceable way. Confidential computing only becomes meaningful once it survives contact with real infrastructure.&lt;/p&gt;

&lt;p&gt;To pressure-test these ideas, I developed a Terraform reference implementation (&lt;a href="https://github.com/NonsoAmadi10/terraform-aws-bitcoin-enclave-node" rel="noopener noreferrer"&gt;here&lt;/a&gt;) for deploying Bitcoin-sensitive workloads inside AWS Nitro Enclaves. The goal was not production readiness but to force clarity in the architecture and ask the harder question: What happens when adversarial assumptions are enforced at the infrastructure layer, not just in application code?&lt;/p&gt;

&lt;p&gt;Terraform quickly became a forcing function. Encoding enclave hosts, IAM boundaries, networking constraints, and attestation flows as declarative infrastructure made implicit trust decisions explicit. Every resource definition represented a concrete choice about who is trusted, when, and under what conditions. There was no room for abstraction or hand-waving. Either the enclave could be launched reproducibly and attested, or the system refused to start.&lt;/p&gt;

&lt;p&gt;Supply-chain risk became impossible to ignore. Enclave images had to be built deterministically in CI, measured, and referenced immutably. Secret provisioning could not be deferred or assumed safe. It had to be cryptographically gated on successful attestation, or the deployment failed by design. What began as infrastructure code evolved into a way to encode security posture directly, not merely provision resources. This exercise surfaced an uncomfortable truth: most Bitcoin infrastructure are fragile not because engineers are careless, but because our tooling encourages deferring trust decisions until runtime. Terraform, when combined with enclaves, reverses that dynamic. You are forced to decide what is trusted before the system ever boots.&lt;/p&gt;

&lt;p&gt;A minimal parent process runs on a Nitro-enabled EC2 instance as an untrusted broker. The enclave application performs signing operations in a cryptographically measured, isolated environment. Terraform codifies the host, IAM boundaries, networking, and enclave lifecycle. Bootstrapping ensures enclave images are built deterministically, measured against expected PCR(Platform Configuration Results) values, and refused if integrity checks fail. Secrets are provisioned only after successful attestation.&lt;/p&gt;

&lt;p&gt;This experience surfaced key engineering truths: enclave boundaries enforce modularization, attestation becomes a protocol, and reproducibility is necessary to catch subtle drift. Infrastructure code encodes security posture explicitly, rather than deferring trust decisions to runtime. Designing under these constraints shifts priorities from secure deployment to verifiable execution. Only the minimum logic resides in the enclave. Data flows are explicit. Trust is earned per instance. Reproducibility and attestation replace assumptions and hope.&lt;/p&gt;

&lt;p&gt;Ongoing work includes attested secret brokers, deterministic CI-built enclave artifacts, cross-TEE comparisons, and scaling enclave orchestration without introducing implicit trust. Lifecycle management, failure recovery, and observability remain practical challenges, but the foundation is now principled.&lt;/p&gt;

&lt;p&gt;Bitcoin infrastructure deserves trust only when it earns it. Assuming cooperative operators is convenient but unsafe. Confidential computing allows us to design systems that are resilient to adversarial environments, with trust enforced by hardware and verified through reproducible, measured execution.&lt;/p&gt;

&lt;p&gt;Confidential computing for Bitcoin infrastructure is still early, and many of the hard problems are operational rather than theoretical. Attestation workflows, deterministic builds, enclave lifecycle management, observability, and failure recovery all expose sharp edges once systems move beyond prototypes.&lt;/p&gt;

&lt;p&gt;I am continuing to explore these areas, particularly where confidential execution intersects with real-world infrastructure constraints. If you are working on trusted execution environments, enclave orchestration, reproducible build systems, or Bitcoin infrastructure under adversarial assumptions, I would be interested in comparing notes.&lt;/p&gt;

&lt;p&gt;These systems will not mature through isolated implementations. They will mature through shared threat models, concrete failures, and careful engineering discussion.&lt;/p&gt;

</description>
      <category>bitcoin</category>
      <category>infrastructureascode</category>
      <category>security</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>Beyond Cost: Lessons from our Migration to Hetzner</title>
      <dc:creator>Chinonso Amadi</dc:creator>
      <pubDate>Wed, 08 Oct 2025 22:46:38 +0000</pubDate>
      <link>https://dev.to/nonsoamadi10/beyond-cost-lessons-from-our-migration-to-hetzner-4pcd</link>
      <guid>https://dev.to/nonsoamadi10/beyond-cost-lessons-from-our-migration-to-hetzner-4pcd</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;I. The Strategic Imperative: Architectural Debt and Regulatory Constraints&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When engineering teams discuss infrastructure migration, the conversation often starts and often terminates with cost reduction. However, a significant architectural decision requires a synthesis of financial prudence, regulatory adherence, and operational excellence. The transition of our Kubernetes staging environment from DigitalOcean to Hetzner was not merely a cloud hop, it was a deliberate, strategic refactoring of our entire infrastructure foundation, driven by mounting technical debt and the non-negotiable demands of PCI-DSS compliance.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tipping Point: Trading Simplicity for Control
&lt;/h3&gt;

&lt;p&gt;In the early stages of our startup lifecycle, DigitalOcean’s simplicity and user-friendly control panel were highly effective for rapid prototyping and initial speed. This high-abstraction environment was invaluable. As our fintech product lines expanded, the staging environment grew commensurately, needing to precisely mirror production to enable rigorous testing of complex financial features, such as payment flows and third-party integrations. This demand for high-fidelity staging quickly pushed us against a wall of unsustainable costs.&lt;/p&gt;

&lt;p&gt;The staging environment alone escalated to a consumption rate of $1,200 monthly on DigitalOcean. While the pricing was transparent, the underlying rigid model capped our ability to experiment affordably at scale. This moment represented a pivotal shift: the company was moving past the initial growth phase, necessitating the exchange of abstraction convenience for the long-term benefits of high-control, raw performance, and a lower cost-per-unit infrastructure. The core objective became leveraging infrastructure optimization as a financial catalyst for sustained growth, much like scaling companies utilize elasticity to fund expansion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining Success: The Three Pillars of the Migration
&lt;/h3&gt;

&lt;p&gt;To ensure this complex migration served the long-term strategic needs of the organization, we defined three non-negotiable success metrics. First, &lt;strong&gt;Cost Efficiency:&lt;/strong&gt; achieve substantial, verifiable savings without sacrificing production-grade reliability or performance. Second, &lt;strong&gt;PCI-DSS Compliance:&lt;/strong&gt; refactor the network architecture to meet stringent regulatory isolation requirements explicitly demanded of a modern fintech operation. Third, &lt;strong&gt;Developer Velocity:&lt;/strong&gt; use the infrastructure shift as a mandate to modernize Continuous Delivery, enhancing security while simultaneously accelerating deployment speed. Achieving these three goals simultaneously demanded a fundamentally different infrastructure philosophy.&lt;/p&gt;

&lt;h2&gt;
  
  
  II. Strategic Infrastructure Refactoring: Building the Compliant Foundation
&lt;/h2&gt;

&lt;p&gt;The technical execution of the migration centered on acquiring superior control over two core functions: cost management and network topology. The choice of Hetzner was validated through rigorous benchmarking, confirming that the cost savings were paired with sufficient technical capability to satisfy stringent compliance demands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quantifying Savings and Benchmarking Performance
&lt;/h3&gt;

&lt;p&gt;The financial catalyst for the move was profound. The previous monthly expenditure of $1,200 for our staging workload on DigitalOcean was reduced to approximately €224, translating to roughly $260 on Hetzner. This achieved a near 80% reduction in monthly infrastructure costs for the non-production environment.&lt;/p&gt;

&lt;p&gt;To quantify these savings rigorously, we leveraged infrastructure-as-code principles. Terraform was used to simulate and estimate costs across potential architectures on Hetzner Cloud, specifically benchmarking the CX-series servers against DigitalOcean’s droplets. &lt;/p&gt;

&lt;p&gt;This process confirmed that Hetzner delivered equivalent or superior compute density and raw performance-per-dollar, eliminating the concern that cost efficiency would lead to performance trade-offs. This monthly saving, approximately €940, could then be redirected toward core business activities, such as hiring and feature development, turning infrastructure optimization into a powerful lever for the business.&lt;/p&gt;

&lt;p&gt;The table below summarizes the technical trade-offs and strategic impacts of moving from a high-abstraction platform to an environment offering bare-metal control and cost-efficiency.&lt;/p&gt;

&lt;p&gt;Table 1: Infrastructure and Compliance Comparison: DigitalOcean versus Hetzner&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;DigitalOcean(previous state)&lt;/th&gt;
&lt;th&gt;Hetzner(current state)&lt;/th&gt;
&lt;th&gt;Strategic Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Monthly Cost&lt;/td&gt;
&lt;td&gt;$1200&lt;/td&gt;
&lt;td&gt;€224($260)&lt;/td&gt;
&lt;td&gt;80% Cost Reduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Networking Model&lt;/td&gt;
&lt;td&gt;High-Abstraction VPC&lt;/td&gt;
&lt;td&gt;Bare-Metal Private Network, VLAN-level control&lt;/td&gt;
&lt;td&gt;Enable strict PCI-DSS segmentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment Isolation&lt;/td&gt;
&lt;td&gt;Public IPs, citing risk&lt;/td&gt;
&lt;td&gt;Zero-Trust, VPN-Only Access&lt;/td&gt;
&lt;td&gt;Secured environment per PCI-DSS Requirement 6.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute Density&lt;/td&gt;
&lt;td&gt;Standard Virtualized Droplets&lt;/td&gt;
&lt;td&gt;Dedicated Cores, CX Series&lt;/td&gt;
&lt;td&gt;Improved Performance-per-Dollar&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Compliance as the Primary Design Constraint, PCI-DSS 6.4
&lt;/h3&gt;

&lt;p&gt;In regulated industries like fintech, staging environments must be treated with production-grade discipline because compliance failures frequently originate in non-production settings. PCI-DSS Requirement 6.4 strictly mandates explicit logical or physical separation between preproduction, development, and production environments.&lt;/p&gt;

&lt;p&gt;Under DigitalOcean, achieving strict segmentation proved operationally challenging. The platform’s high-level networking abstractions and the automatic assignment of public IP addresses, even within VPCs, made auditable network segmentation clunky. &lt;/p&gt;

&lt;p&gt;Although DigitalOcean offers VPCs for private network interfaces, the abstracted nature of the service often lacked the granularity required to satisfy auditors focused on layer 2 and layer 3 control. This configuration frequently resulted in compliance flags regarding insufficient segmentation, despite the use of mock cardholder data.&lt;/p&gt;

&lt;p&gt;The migration to Hetzner solved this regulatory challenge by offering a closer equivalent to bare-metal network primitives. Hetzner's private networking functionality allowed the engineering team to construct robust, auditable VLAN-like isolation. This shift provided the granular L2, L3 control necessary to restrict access to staging clusters entirely: ingress and egress access were strictly limited to connections originating from the corporate WireGuard VPN. This enforcement of a Zero-Trust architecture at the network perimeter, paired with manual configuration of robust audit logs and access controls, ensured complete traceability, thus satisfying granular PCI-DSS requirements for non-production environments. The architectural choice of Hetzner, prioritizing lower-level network control, was entirely dictated by the regulatory requirement for infrastructure primitives that enable audit-ready segmentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enhanced Security and Granular Network Control
&lt;/h3&gt;

&lt;p&gt;The move to Hetzner established a truly secure sandbox for developers. Previously, developers testing APIs and microservices risked accidental exposure to the public internet, potentially leading to data leaks or exposure to DDoS threats. The architectural pivot to Hetzner’s private network facilitated the full implementation of a Zero Trust model: staging clusters are now completely isolated, with traffic routed exclusively via the WireGuard VPN. This VPN is integrated with the Single Sign-On, using role-based keys to ensure only authorized engineers can access the staging environment, effectively prioritizing security early in the deployment process.&lt;/p&gt;

&lt;p&gt;This capability to enforce deep segmentation underscores a critical difference: while DigitalOcean’s user-friendly networking provides abstraction, it lacks the necessary granularity needed for specialized regulatory compliance environments, particularly compared to Hetzner's offering, which allows for segmentation closer to a physical bare-metal setup. This granular control was paramount to achieving the necessary compliance posture.&lt;/p&gt;

&lt;h2&gt;
  
  
  III. Modernizing Continuous Delivery: The GitOps Mandate
&lt;/h2&gt;

&lt;p&gt;The strategic infrastructure move created the perfect opportunity to address compounding issues within the Continuous Delivery (CD) pipeline, where security liabilities had begun to directly impact auditability and velocity.&lt;/p&gt;

&lt;h3&gt;
  
  
  The DevSecOps Nightmare of Imperative CI/CD
&lt;/h3&gt;

&lt;p&gt;Our previous CD pipeline, reliant entirely on GitHub Actions within the DigitalOcean ecosystem, was an architectural liability masquerading as a convenience. Kubernetes configurations, including sensitive kubeconfigs and application manifests, were often baked directly into the workflow files.&lt;/p&gt;

&lt;p&gt;This configuration created an inherent security flaw: sensitive data, even when obfuscated using base64 encoding, meant that a single repository breach or slipped secret could grant unauthorized access to cluster keys, constituting a blatant violation of the principle of least privilege. Furthermore, the entire system was imperative and scattered, making change management and security auditing extremely difficult. Auditing changes felt like chasing ghosts, a serious compliance vulnerability in a PCI-DSS environment where traceability is non-negotiable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Embracing Declarative State with ArgoCD
&lt;/h3&gt;

&lt;p&gt;The migration served as the mandate needed to adopt full GitOps principles using ArgoCD. The team transitioned from an imperative system, where scripts executed commands, to a declarative flow, where the Git repository serves as the single source of truth for the desired state.&lt;/p&gt;

&lt;p&gt;This declarative approach immediately yielded massive security and auditability benefits. GitOps ensures that the Git commit history provides a natural, immutable audit log of every application configuration change, satisfying the rigorous traceability demands of PCI-DSS auditors. By pairing ArgoCD with tools like HashiCorp Vault, secrets are kept locked away, never exposed within CI logs, reinforcing the principle of least privilege.&lt;/p&gt;

&lt;p&gt;We leveraged Helm charts extensively, making applications versioned, parameterized, and significantly easier to manage across multiple non-production environments. This professionalization of configuration management fundamentally eliminated configuration drift, improving reliability across all testing environments.&lt;/p&gt;

&lt;p&gt;Table 2 details this necessary security and operational shift.&lt;br&gt;
Table 2: DevOps Maturity Shift: From Imperative CI to Declarative GitOps&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pipeline Aspect&lt;/th&gt;
&lt;th&gt;Github Actions(Imperative)&lt;/th&gt;
&lt;th&gt;ArgoCD/GitOps(Declarative)&lt;/th&gt;
&lt;th&gt;Engineering Outcome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Configuration Management&lt;/td&gt;
&lt;td&gt;Kubeconfigs/Manifests baked into workflows&lt;/td&gt;
&lt;td&gt;Helm Charts, Git is Single Source of Truth&lt;/td&gt;
&lt;td&gt;Enhanced security, secrets isolation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit Trail&lt;/td&gt;
&lt;td&gt;Scattered CI/CD Logs, Manual Checks&lt;/td&gt;
&lt;td&gt;Immutable Git History and ArgoCD Events&lt;/td&gt;
&lt;td&gt;Instantaneous, tamper-proof auditability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment Velocity&lt;/td&gt;
&lt;td&gt;Slow, Sequential Checks&lt;/td&gt;
&lt;td&gt;50% Faster, Automated Sync&lt;/td&gt;
&lt;td&gt;Increased DevX and iteration speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compliance Risk, Drift&lt;/td&gt;
&lt;td&gt;High Risk of Configuration Drift&lt;/td&gt;
&lt;td&gt;Near Zero Drift Risk&lt;/td&gt;
&lt;td&gt;Ensures consistency between Git and Cluster State&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Measurable Velocity Payoff
&lt;/h3&gt;

&lt;p&gt;The causal relationship between security constraints and velocity improvement is clearly demonstrated here. The need for auditability and least privilege, driven by regulatory compliance, forced the adoption of GitOps, which proved to be a superior operational methodology.&lt;/p&gt;

&lt;p&gt;This transformation delivered immediate, quantifiable results: deployment times were halved, representing a 50% reduction in cycle time. The declarative consistency vastly improved reliability. Developers now operate with increased confidence, testing in a secure, production-mirrored sandbox without fear of external exposure. Furthermore, the ArgoCD UI provides a centralized dashboard for monitoring application sync status, offering developers self-service monitoring capabilities that reduce operational reliance on the core infrastructure team. The adoption of sophisticated deployment strategies, such as canary releases for critical updates, became reliable and routine.&lt;/p&gt;

&lt;h2&gt;
  
  
  IV. Navigating the Implementation Friction: Challenges and Solutions
&lt;/h2&gt;

&lt;p&gt;A complex migration of this nature inevitably introduces friction. A high-caliber engineering story acknowledges these difficulties, as they highlight the resilience and ingenuity of the team involved. Our lean team faced three major hurdles that tested our migration limits, demonstrating the hidden operational cost inherent in shifting to a higher-control, lower-cost environment like Hetzner.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refactoring Imperative Logic into Declarative Helm
&lt;/h3&gt;

&lt;p&gt;The greatest initial challenge was the sheer volume of manual effort required to transition the legacy, imperative logic embedded within GitHub Actions into robust, reusable Helm charts. This involved meticulously extracting encoded environment variables and translating step-by-step CI/CD pipeline logic into standardized, parameterized &lt;code&gt;values.yaml&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;Compounding this task was the steep learning curve associated with ArgoCD itself. The team faced significant difficulty debugging application synchronization issues, particularly those caused by Custom Resource Definition, CRD, mismatches or complex network configurations interacting unexpectedly with the new cluster architecture. Successfully managing this architectural shift required significant dedication to pair-programming and meticulous documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Integrity During Persistent Volume Migration
&lt;/h3&gt;

&lt;p&gt;Migrating persistent data, particularly sensitive application data, required careful planning to ensure integrity and minimize downtime. DigitalOcean provides a seamless block storage abstraction, making volume management largely transparent. Moving to Hetzner’s storage backend, which requires a greater degree of manual provisioning and control, necessitated a robust, verified migration process.&lt;/p&gt;

&lt;p&gt;The team elected to use &lt;code&gt;rsync&lt;/code&gt; over SSH tunnels for data transfer to Hetzner’s storage. This choice was deliberate: &lt;code&gt;rsync&lt;/code&gt; ensures data integrity verification and is highly efficient for differential transfers. It achieves this by utilizing client/server hashing to compare files and only transferring the differences, significantly minimizing the amount of data sent over the network pipe. Despite the technical soundness of the approach, the manual nature of coordinating data movement across different cloud storage primitives demanded intense focus and robust integrity checks upon completion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Team Resilience and Shared Ownership
&lt;/h3&gt;

&lt;p&gt;This complex refactoring was executed by a lean team of four engineers, demanding ruthless focus on three core operational principles: audit, automate, and iterate. The scope of the work, encompassing cost modeling, PCI-DSS network redesign, Kubernetes cluster provisioning, GitOps implementation, and data migration, risked creating significant burnout and knowledge silos.&lt;/p&gt;

&lt;p&gt;The countermeasure to this constraint was a cultural commitment to shared ownership. The team enforced mandatory pair-programming sessions for complex refactoring tasks and meticulously logged every configuration, debugging session, and architectural decision into a shared Git repository. This commitment ensured that organizational knowledge was distributed and collective ownership mitigated the intensity of the implementation friction.&lt;/p&gt;

&lt;h2&gt;
  
  
  V. Outcomes and Lessons for Engineering Leaders
&lt;/h2&gt;

&lt;p&gt;This infrastructure transformation validates that constraints, especially regulatory ones, can serve as powerful accelerants for strategic modernization.&lt;/p&gt;

&lt;h3&gt;
  
  
  The True Return on Investment, ROI
&lt;/h3&gt;

&lt;p&gt;Defining the success of this project requires moving beyond a simple financial metric. While the infrastructural savings, roughly €940 monthly, are materially significant, the true ROI calculation must be holistic, measuring strategic gains against initial effort.&lt;/p&gt;

&lt;p&gt;The critical value metrics achieved include &lt;strong&gt;Reduced Risk&lt;/strong&gt;, secured through an audit-ready compliance posture owing to true network segmentation and verifiable GitOps history. This is coupled with &lt;strong&gt;Increased Velocity&lt;/strong&gt;, defined by 50% faster, more reliable deployments powered by a declarative CD model. Finally, the project fostered a positive &lt;strong&gt;Cultural Shift&lt;/strong&gt;, leading to enhanced developer confidence and satisfaction, DevX, by providing a safe, production-mirrored sandbox.&lt;/p&gt;

&lt;h3&gt;
  
  
  Five Foundational Lessons for High-Growth Teams
&lt;/h3&gt;

&lt;p&gt;The experience yielded critical, transferable lessons for engineering leaders navigating scale, regulation, and architectural debt:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Staging Is Not an Afterthought, It Is a Security Mandate:&lt;/strong&gt; In regulated sectors, security vulnerabilities often begin in non-production environments. Treat staging with the same discipline, security, and audit rigor applied to production systems, viewing it as a critical component of the Cardholder Data Environment, CDE.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ROI Goes Beyond the Invoice:&lt;/strong&gt; Financial savings are a welcome catalyst, but success must be measured by the long-term security posture, velocity gains, and cultural agility the migration unlocks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Constraint Drives Innovation:&lt;/strong&gt; Regulatory requirements, such as PCI-DSS, should not be viewed solely as burdens. In this case, the need for auditable segmentation and least privilege forced the adoption of superior technologies, GitOps and Zero Trust networking, accelerating modernization across the board.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compliance and Velocity Can Coexist:&lt;/strong&gt; Strategic tooling, ArgoCD and Helm, combined with strict architectural control, VPN-based access and detailed audit logging, allows teams to satisfy auditors while simultaneously empowering developers to move faster and with greater confidence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Staging Is Your Innovation Engine:&lt;/strong&gt; A robust, secure, and cost-effective staging environment sets the pace for safe, fast iteration and experimentation. Infrastructure that encourages this behavior should be treated as a strategic asset, not merely a cost center.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  VI. Final Reflection: Infrastructure Shapes Culture
&lt;/h2&gt;

&lt;p&gt;The migration from DigitalOcean to Hetzner was far more than optimizing a billing statement. It was a deliberate choice to sharpen our compliance edge, accelerate development cycles, and establish a framework for scaling smarter. By acquiring the network control and cost-efficiency required for robust regulatory adherence, the team successfully turned a compliance constraint into an architectural advantage.&lt;/p&gt;

&lt;p&gt;For technical leaders, the enduring lesson is clear: infrastructure decisions fundamentally shape a team’s culture, determine the company’s agility, and define its capacity to meet future regulatory and market demands head-on. We successfully shifted from consuming an abstracted cloud service to mastering the underlying primitives, a foundational step toward becoming a mature, enterprise-grade fintech operation.&lt;/p&gt;

&lt;p&gt;How do you balance compliance and velocity in your infrastructure decisions? I’d love to hear how other teams are turning constraints into advantages&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>architecture</category>
      <category>cloud</category>
    </item>
    <item>
      <title>How Pandas Simplifies ETL Data Cleaning</title>
      <dc:creator>Chinonso Amadi</dc:creator>
      <pubDate>Fri, 11 Apr 2025 21:30:59 +0000</pubDate>
      <link>https://dev.to/nonsoamadi10/how-pandas-simplifies-etl-data-cleaning-54jc</link>
      <guid>https://dev.to/nonsoamadi10/how-pandas-simplifies-etl-data-cleaning-54jc</guid>
      <description>&lt;p&gt;When discussing the "T" in the ETL (Extract, Transform, Load) process, a huge part of the work involves cleaning the data and ensuring it is ready for analysis. &lt;/p&gt;

&lt;p&gt;If you're a data engineer, you've probably stared down the barrel of messy spreadsheets more times than you can count, columns riddled with null values, dates being formatted every which way, inconsistent text, and those delightful outliers that make no sense. Our job is to wrangle this chaos into clean, reliable data pipelines. &lt;/p&gt;

&lt;p&gt;In this article, we will see how Pandas, a data manipulation library written in Python, can help address these challenges and simplify the data cleaning process in the context of ETL pipelines. &lt;/p&gt;

&lt;p&gt;We will be using a real-world example of banking data stored in a CSV file to see exactly how Pandas deals with common data issues and converts messy, raw information into a format that works perfectly for analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Pandas?
&lt;/h2&gt;

&lt;p&gt;Pandas is a Python library for data manipulation and analysis. It acts like a Swiss Army Knife for data wrangling providing versatile tools to handle messy data of any size and suitable for both quick analyses and complex data pipelines. The library's foundation rests on two primary data structures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;DataFrame: A 2D, table-like structure (akin to a spreadsheet) for holding labeled, structured data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Series: A 1D labeled array, effectively a single column from a DataFrame.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ability to efficiently and intuitively address real-world data challenges is what makes Pandas so powerful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Hurdles in Data Quality
&lt;/h2&gt;

&lt;p&gt;Data entering the ETL processes is often dirty and failure to identify and correct common issues can seriously undermine analytics and machine learning models, leading to flawed insights. Before exploring Pandas' solutions, let's recognize these typical problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Missing Data: Records may be incomplete (null or blank values) due to collection errors or omissions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inconsistent Formatting: Data representation can vary widely (e.g., different date styles, mixed types in one column, inconsistent labels).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Duplication: Redundant entries of the same information can bloat datasets and skew analysis.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Outliers: Atypical, extreme values may distort statistical summaries and model performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Incorrect Data Types: Some Columns might contain unexpected data types. A good example is a text data type appearing where numbers are expected.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Addressing these challenges is a critical step, highlighting the need for robust data cleaning tools like Pandas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Cleaning with Pandas
&lt;/h2&gt;

&lt;p&gt;Pandas equips data engineers with a robust toolkit, making the often tedious task of data cleaning significantly easier. Its functions streamline handling missing values, standardizing formats, integrating data sources, and more. Key capabilities include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Addressing Missing Values&lt;/strong&gt;: Use functions like &lt;code&gt;isna()&lt;/code&gt; to detect nulls, &lt;code&gt;fillna()&lt;/code&gt; to impute them (with mean, median, mode, etc.), or &lt;code&gt;dropna()&lt;/code&gt; to remove them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Transforming and Standardizing Data&lt;/strong&gt;: Easily rename columns, apply custom functions (&lt;code&gt;apply()&lt;/code&gt;, &lt;code&gt;map()&lt;/code&gt;), perform string manipulations, and ensure consistent formatting across datasets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Combining Data Sources&lt;/strong&gt;: Integrate data from various tables or files using high-performance merging (&lt;code&gt;merge()&lt;/code&gt;, &lt;code&gt;join()&lt;/code&gt;) and concatenation (&lt;code&gt;concat()&lt;/code&gt;) operations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Eliminating Redundancy&lt;/strong&gt;: Identify and remove duplicate rows efficiently with &lt;code&gt;drop_duplicates()&lt;/code&gt; to maintain data uniqueness.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Correcting Data Types&lt;/strong&gt;: Convert columns to their appropriate types (numeric, string, boolean, datetime) using &lt;code&gt;astype()&lt;/code&gt; or specialized converters like &lt;code&gt;to_datetime()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These features allow for systematic and efficient data preparation within ETL pipelines&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Example: Cleaning Banking Marketing Data
&lt;/h2&gt;

&lt;p&gt;Let’s take a look at a real-world example of how Pandas can help clean data. Imagine you’re working with data from a recent marketing campaign for a bank, where the goal is to identify potential clients for personal loans. &lt;/p&gt;

&lt;p&gt;The raw data likely contains common issues: missing values, inconsistent text formats (like &lt;code&gt;'yes'/'no'&lt;/code&gt; variations), incorrect data types, and maybe some outliers. These need fixing before the data can be reliably used for analysis or loaded into a data lake or a data warehouse&lt;/p&gt;

&lt;p&gt;Here’s a breakdown of the types of data involved and typical cleaning tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Client Data: Contains details like job type, marital status, education level, and mortgage status. Cleaning might involve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replacing inconsistent text (e.g., standardizing job or education entries containing '.') with consistent values.&lt;/li&gt;
&lt;li&gt;Handling missing values (e.g., replacing 'unknown' education levels with &lt;code&gt;NaN&lt;/code&gt; - Not a Number).&lt;/li&gt;
&lt;li&gt;Converting &lt;code&gt;'yes'/'no'&lt;/code&gt; fields (like &lt;code&gt;credit_default&lt;/code&gt; or &lt;code&gt;mortgage&lt;/code&gt;) into Boolean (&lt;code&gt;True/False&lt;/code&gt;) types.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Campaign Data: Contains information on contact history, such as the number of contacts, duration, outcomes of previous campaigns, and last contact date. Cleaning often requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Converting outcome descriptions (e.g., 'success', 'failure', 'nonexistent') into Boolean or numerical flags.&lt;/li&gt;
&lt;li&gt;Parsing date components (like day and month) into a standard datetime format.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Economic Indicators: Contextual data like consumer price index or interest rates (&lt;code&gt;EURIBOR&lt;/code&gt;). Cleaning typically focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensuring these columns have the correct numerical data type.&lt;/li&gt;
&lt;li&gt;Addressing any missing numerical values appropriately (e.g., filling or removing).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Here’s a code snippet illustrating how Pandas tackles some of these cleaning steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt; &lt;span class="c1"&gt;# Often needed for handling NaN
&lt;/span&gt;
&lt;span class="c1"&gt;# Let's pretend we loaded the messy data
# df = pd.read_csv('bank_marketing_messy.csv') # Placeholder for loading
&lt;/span&gt;
&lt;span class="c1"&gt;# --- Client Data Cleaning ---
&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;client_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;age&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;job&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;marital&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;education&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credit_default&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mortgage&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]].&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# Use .copy() to avoid SettingWithCopyWarning
&lt;/span&gt;
&lt;span class="c1"&gt;# Clean up text inconsistencies (e.g., '.' shouldn't be in job/education)
&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;job&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;job&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;education&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;education&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Handle 'unknown' consistently - replace with NaN for better handling later
&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;education&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;education&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NaN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Convert yes/no/unknown to more usable types (e.g., boolean or numerical)
# Assuming 'unknown' in credit_default means 'no' for this example
&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credit_default&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credit_default&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;no&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mortgage&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mortgage&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;no&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Assuming 'unknown' means False here too
&lt;/span&gt;
&lt;span class="c1"&gt;# --- Campaign Data Cleaning ---
&lt;/span&gt;&lt;span class="n"&gt;campaign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;client_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;number_contacts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;contact_duration&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;previous_campaign_contacts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;previous_outcome&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;campaign_outcome&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;day&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;month&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]].&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Map outcomes to boolean (or 1/0)
&lt;/span&gt;&lt;span class="n"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;previous_outcome&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;previous_outcome&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;failure&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nonexistent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;campaign_outcome&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;campaign_outcome&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;no&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create a proper datetime column from day/month (assuming year 2022 for context)
# Note: This requires careful handling of potential errors/formats
&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Build a date string: '5 May 2022'
&lt;/span&gt;    &lt;span class="n"&gt;date_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;day&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;month&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2022&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;last_contact_date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%d %b %Y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;coerce&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# errors='coerce' turns bad dates into NaT (Not a Time)
&lt;/span&gt;&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Date conversion error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Handle error - maybe create an empty date column or log issues
&lt;/span&gt;    &lt;span class="n"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;last_contact_date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NaT&lt;/span&gt;

&lt;span class="c1"&gt;# Drop original day/month columns if no longer needed
&lt;/span&gt;&lt;span class="n"&gt;campaign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;day&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;month&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;


&lt;span class="c1"&gt;# --- Economics Data Cleaning ---
&lt;/span&gt;&lt;span class="n"&gt;economics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;client_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cons_price_idx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;euribor_three_months&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]].&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Main task here is often ensuring correct numeric type and handling NaNs
# Example: fill missing economic indicators with the mean/median if appropriate
&lt;/span&gt;&lt;span class="n"&gt;economics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;euribor_three_months&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;economics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;euribor_three_months&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;economics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;euribor_three_months&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="c1"&gt;# Ensure data types are correct (they often load as object/string if NaNs were present)
&lt;/span&gt;&lt;span class="n"&gt;economics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cons_price_idx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;economics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cons_price_idx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;coerce&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;economics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;euribor_three_months&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;economics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;euribor_three_months&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;coerce&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# Now you'd save these cleaned dataframes
&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;client_cleaned.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;campaign_cleaned.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;economics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;economics_cleaned.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Data cleaning steps applied (example).&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Pandas is a Valuable Tool for Data Engineers
&lt;/h2&gt;

&lt;p&gt;For data engineers, tools that offer efficiency and maintainability are crucial. Pandas provides several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Efficient Data Handling: It's designed for good performance on in-memory datasets, allowing for rapid cleaning and manipulation often faster than iterating manually or using overly complex SQL for transformations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Readable and Reusable Code: The straightforward syntax makes writing, understanding, and maintaining data cleaning scripts easier. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These scripts can often be adapted for different projects or integrated into automated data pipelines.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strong Integration: Pandas works well with other core Python libraries (like NumPy, Scikit-learn) and data sources/sinks (databases, CSV, Parquet, JSON), facilitating its use in comprehensive data processing workflows.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Ultimately, Pandas is a fundamental part of a data engineer's toolkit. Whether dealing with marketing data like the example above or preparing datasets for other purposes, Pandas helps simplify and standardize the transformation ('T') stage of ETL. &lt;/p&gt;

&lt;p&gt;Using (replaced "Leveraging") its powerful features saves time, reduces potential errors, and ensures data is correctly structured for loading and subsequent analysis."&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgements
&lt;/h2&gt;

&lt;p&gt;The real-world banking marketing data cleaning example discussed in this article draws inspiration from the practical exercises and datasets provided in DataCamp's Data Engineering track.&lt;/p&gt;

</description>
      <category>dataengineering</category>
      <category>pandas</category>
      <category>datascience</category>
    </item>
    <item>
      <title>What a Time to Be Alive</title>
      <dc:creator>Chinonso Amadi</dc:creator>
      <pubDate>Tue, 31 Dec 2024 21:20:56 +0000</pubDate>
      <link>https://dev.to/nonsoamadi10/what-a-time-to-be-alive-2mdc</link>
      <guid>https://dev.to/nonsoamadi10/what-a-time-to-be-alive-2mdc</guid>
      <description>&lt;p&gt;&lt;strong&gt;Man is a mystery. It needs to be unraveled, and if you spend your whole life unraveling it, don’t say that you’ve wasted your time.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Dostoevsky’s words resonate deeply with me as I reflect on 2024.&lt;/p&gt;

&lt;p&gt;I’ve never written a year in review before. Each time I’ve reached my goals in the past, I’ve felt an emptiness—a wave of nothingness washing over me. I used to think it was cool not to reflect on how far I’d come, but I’ve realized it was just a way to avoid the existential discomfort of questioning my value or purpose.  &lt;/p&gt;

&lt;p&gt;This year, however, has been nothing short of amazing. It’s been a year of growth, grit, and intentionality—a year where I made it my mission to let my work speak for itself, take charge of my future, and show up fully in every area of my life.  &lt;/p&gt;

&lt;p&gt;As I look back on the past 12 months, I see big decisions made, challenges overcome, wins celebrated, and foundations built for an even bigger year ahead. Life didn’t always unfold the way I imagined, but every twist and turn brought me closer to the person I want to be in the years to come.  &lt;/p&gt;

&lt;p&gt;Let me take you through my year—the highs, the lows, and the exciting plans for 2025.  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Owning My Work&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If I had to summarize my career this year, it would be about quiet power—the kind of impact that doesn’t always grab headlines but changes the game for the people and systems that depend on me.  &lt;/p&gt;

&lt;p&gt;As a Site Reliability Engineer (SRE), much of my work happens behind the scenes, ensuring things run smoothly so others can shine. Let’s be honest: it can feel like a thankless job at times. But this year, I made it my mission to ensure that my impact wasn’t just seen but felt. I leveled up in every way possible—diving deep into tools, processes, and strategies that elevated both the company and myself as a professional.  &lt;/p&gt;

&lt;p&gt;One of my proudest achievements was driving my team’s efforts to secure a PCI-DSS license. This wasn’t just another compliance checkbox—it was a massive undertaking that showcased our commitment to security and reliability. My role involved ensuring our systems were robust, scalable, and fully compliant. The result? We didn’t just secure the license; we paved the way for future certifications that will unlock even more opportunities. Knowing that my work played such a pivotal role has been deeply fulfilling.  &lt;/p&gt;

&lt;p&gt;Another career highlight was leading our integration with Lightspark. This wasn’t just about technical execution; it was about fostering collaboration and unlocking new potential for growth. It proved how impactful strong partnerships can be when backed by focused effort and clear vision.  &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Tackling the Black Tax – Building a Stronger Foundation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;There’s a difference between obligation and purpose. For a long time, I thought supporting my family was just my cross to bear. But this year, I realized it’s deeper than that. It’s about creating opportunities that ripple outward.&lt;/p&gt;

&lt;p&gt;I took deliberate steps to ensure my siblings were set up for success, whether through education, career opportunities, or side hustles. By helping them build their own paths, I’ve significantly reduced the financial burden on myself and created a sense of shared empowerment within my family.  &lt;/p&gt;

&lt;p&gt;This achievement wasn’t just about money—it was about creating space for my own growth and freedom. Knowing my loved ones are thriving has been one of the most rewarding parts of my year.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Small Wins, Big Gratitude
&lt;/h2&gt;

&lt;p&gt;Sometimes, the quiet wins hit the hardest. Here are a few that stood out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;African Bitcoin Conference: Standing on that stage felt surreal. Sharing my story, my insights, and seeing people resonate with it—it was humbling. It’s moments like that where you realize the work you’re doing matters.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Billie: She’s been my peace in the noise. From deep talks to quiet evenings, she’s reminded me what it means to feel seen, understood, and cared for.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Health Reset: This year, I got intentional about taking care of myself. Intermittent fasting, daily movement, and real rest—it wasn’t about aesthetics; it was about feeling strong and staying focused.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Lessons Learned – The School of Life&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This year wasn’t without its challenges, but each one taught me something valuable.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Boundaries Are Self-Respect&lt;/strong&gt;: As someone who gives freely, it hasn’t always been easy to say no. This year, I prioritized my peace, even when it felt uncomfortable. I’ve learned not everyone deserves access to my energy, and that’s okay.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Letting Go&lt;/strong&gt;: I had to walk away from relationships that no longer aligned with who I am or where I’m going. It wasn’t easy and definitely hurt at times, but it made space for better connections and a lighter heart.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leaning on Others&lt;/strong&gt;: Whether it was collaborating on big projects or asking for help during personal challenges, I saw how community and teamwork can make all the difference.  &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Looking Ahead – Europe and Bigger Dreams&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;2024 laid the groundwork, but 2025 is where I build the vision.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Travel: Europe is calling, and I’m ready to step out of my comfort zone and into new experiences.&lt;/li&gt;
&lt;li&gt;Skills: Whether it’s Rust for blockchain development or leveling up in MLOps, I’m here for the challenge. Growth isn’t just a goal; it’s the standard.&lt;/li&gt;
&lt;li&gt;Relationships: Love, family, work—I’m doubling down on the people and connections that matter most.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Gratitude and Ambition
&lt;/h2&gt;

&lt;p&gt;Here’s the thing: I’m proud of how far I’ve come, but I know there’s more ahead. 2024 wasn’t just a chapter—it was a prologue. The story I’m writing is bigger than what I’ve done so far, and I’m stepping into 2025 with clarity, drive, and the energy to make it happen.&lt;/p&gt;

&lt;p&gt;As Drake says, &lt;em&gt;“When writing the story of your life, don’t let anyone else hold the pen.”&lt;/em&gt; The seeds I’ve planted this year will bear fruit, and I’m ready to embrace whatever comes next.  &lt;/p&gt;

&lt;p&gt;This isn’t about boasting. It’s about acknowledging the work, the lessons, and the blessings. It’s about being grateful for what I’ve achieved and staying hungry for what’s next.&lt;/p&gt;

&lt;p&gt;Here’s to progress, purpose, and the moments that remind us why we keep moving forward.&lt;/p&gt;

&lt;p&gt;Here’s to the journey—what a time to be alive.  &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Software Migration Strategies: The Strangler Fig Pattern</title>
      <dc:creator>Chinonso Amadi</dc:creator>
      <pubDate>Tue, 05 Nov 2024 13:33:14 +0000</pubDate>
      <link>https://dev.to/nonsoamadi10/software-migration-strategies-the-strangler-fig-pattern-9m9</link>
      <guid>https://dev.to/nonsoamadi10/software-migration-strategies-the-strangler-fig-pattern-9m9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Migrating from legacy systems to modern systems is no doubt a daunting task, this can be due to the risks of disruption, long project timelines, and potential failures. However one practical and risk-averse solution engineering teams can adopt to help with such risk is the &lt;strong&gt;Strangler Fig Pattern&lt;/strong&gt;. Drawing inspiration from a naturally existing strangler fig tree, this pattern allows for a gradual and specific replacement of old systems with minimal disruption. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Strangler Fig Tree Pattern?
&lt;/h2&gt;

&lt;p&gt;A strangler fig tree is a tropical plant that begins its life by latching on to the body of an already existing tree (Host Tree) and overtime grows around such tree until it eventually replaces the host. This pattern perfectly encapsulates the phased approach at which old software systems are ported to modern software systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does the strangler fig pattern work?
&lt;/h2&gt;

&lt;p&gt;The pattern’s success lies in its methodical approach to system migration. Rather than attempting a risky "big bang" replacement, the strangler fig pattern encourages that systems are gradually migrated piece by piece.  Here is  a detailed look at how this works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Incremental Migration:&lt;/strong&gt; The key concept behind this pattern is incremental change. Rather than trying to rewrite entire systems all at once-a daunting and risky task-organisations move parts of the system step by step. This phased approach simplifies the process, allowing the engineering team to tackle each part one at a time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Coexistence of the systems:&lt;/strong&gt; During the migration, the legacy systems and the new systems run side by side. This allows the system to maintain uninterrupted service while the new components are tested and refined in real-time. By having both systems coexist, there is reduced pressure on how soon the migration should be completed, allowing for better-quality development and iterative improvements.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Phased Replacement:&lt;/strong&gt; The migration is done in phases, where traffic is gradually shifted from the old system to the new system in a green/blue deployment strategy as new features or improvements are implemented. Over time, these new components take on more responsibility, reducing the reliance on the legacy system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Minimal Disruption:&lt;/strong&gt; A gradual transition means that users and stakeholders experience minimal disruption. Changes can be rolled out, tested, and refined, all while business operations continue smoothly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Continuous Delivery:&lt;/strong&gt; The Strangler Fig Pattern aligns well with continuous delivery principles, enabling frequent, small updates. This provides faster feedback cycles, allowing teams to adapt and improve with each phase.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Implementation Strategy
&lt;/h2&gt;

&lt;p&gt;The diagram below illustrates the methodical steps to which the strangler fig pattern can be implemented:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frpjs1bwvd8ya2pilidny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frpjs1bwvd8ya2pilidny.png" alt="image explaining strangler fig pattern" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a facade layer:&lt;/strong&gt; Develop a new application layer or interface that can intercept and redirect requests between the legacy system and new modules. This layer acts as a gateway and allows you to control traffic flow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Develop New Features:&lt;/strong&gt; Begin implementing new functionality or improved versions of existing features within the modernised application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Route Traffic Gradually:&lt;/strong&gt; Slowly shift user traffic to the new components while monitoring performance and functionality. This step is crucial for identifying issues early and making necessary adjustments without impacting the entire system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decommission Legacy Components:&lt;/strong&gt; As more features are migrated, progressively disable and remove unused parts of the legacy system, reducing its footprint until it can be entirely retired.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ensure Consistent Testing and Monitoring:&lt;/strong&gt; Continuous testing and monitoring throughout this process ensure that the migration is smooth, performance metrics are met, and any issues are addressed immediately.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Advantages of the Strangler Fig Pattern&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced Risk:&lt;/strong&gt; Unlike a complete system overhaul, incremental changes reduce the risk of failure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexibility:&lt;/strong&gt; Teams can prioritize specific features or modules for migration based on business needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Better Adaptability:&lt;/strong&gt; Each phase allows for learning and adjustments along the way.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced Downtime:&lt;/strong&gt; Users typically don’t experience service disruptions, making this approach perfect for mission-critical applications.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The Strangler Fig Pattern offers a strategic approach to modernizing legacy systems by enabling new architectures to take root and thrive alongside existing structures. Like the strangler fig tree slowly replacing its host, this pattern helps phase out outdated systems smoothly, creating a stronger and more sustainable software architecture.  By embracing incremental change and phased development, businesses can steadily migrate to modern systems while maintaining stability and continuous improvement. &lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>architecture</category>
      <category>microservices</category>
      <category>eventdriven</category>
    </item>
    <item>
      <title>Improving Compiler Performance with Profile Guided Optimization</title>
      <dc:creator>Chinonso Amadi</dc:creator>
      <pubDate>Thu, 07 Dec 2023 22:43:15 +0000</pubDate>
      <link>https://dev.to/nonsoamadi10/improving-compiler-performance-with-profile-guided-optimization-1b2p</link>
      <guid>https://dev.to/nonsoamadi10/improving-compiler-performance-with-profile-guided-optimization-1b2p</guid>
      <description>&lt;p&gt;Since the rise of compilers, building software has been an ever evolving journey. Developers have to navigate between writing clean and maintainable code and delivering high-performance applications. This has given rise to different performance tuning techniques and one of such is the Profile Guided Optimization(PGO) which is also known as the Feedback-Driven Optimization(FDO).&lt;/p&gt;

&lt;p&gt;In this article we would look at what PGO means, the benefits it offers, and the different steps on  how it can be implemented.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is PGO?
&lt;/h2&gt;

&lt;p&gt;Profile Guided Optimization is a compiler optimization technique that improves the final compiled binary of a piece of software by using profile data hints and compiling the code based on those profiles to improve the runtime performance.&lt;/p&gt;

&lt;p&gt;At its core, this technique transcends the traditional confines of static analysis. Rather than relying solely on predefined rules to enhance code performance, It introduces an element of adaptability by leveraging insights from actual program execution. It's akin to having a seasoned guide direct you through a maze, showing you the most frequented paths and shortcuts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Implementing PGO
&lt;/h2&gt;

&lt;p&gt;PGO’s brilliance lies in its ability to optimize code based on genuine usage patterns. As software architects, developers, and engineers, we craft programs with expectations of how they will be used. PGO aligns with this human-centric approach by observing and learning from the runtime behavior of our applications.&lt;/p&gt;

&lt;p&gt;Consider this analogy: if writing code is akin to composing a symphony, then PGO is the dynamic conductor adjusting the tempo based on the audience's response. It's not merely about creating an optimized performance; it's about crafting an experience tailored to the nuances of how users interact with our software.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it Work?
&lt;/h2&gt;

&lt;p&gt;Implementing PGO involves a series of well-defined steps, each contributing to the creation of highly optimized, performance-centric binaries as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa35o1a3brfsncb1itg6h.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa35o1a3brfsncb1itg6h.jpg" alt="Steps PGO undergoes" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Instrumentation:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first thing we do is to instrument our code. Think of this as attaching a profiler to our application, allowing it to collect data on which parts of the code are traversed most frequently.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Profile Execution:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this point we run the instrumented binary on representative input datasets, allowing the program to collect profiling data that mirrors its actual runtime behavior.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Recompile with Profile Data:&lt;br&gt;
Armed with the collected profile data, the code is recompiled. The compiler utilizes this invaluable information to guide its optimization decisions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate Optimized Binary:&lt;br&gt;
The compiler, now armed with runtime insights, applies targeted optimizations based on the collected profile data. The result is a finely tuned binary designed to excel in the specific scenarios observed during profiling.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Actual Code Implementation
&lt;/h3&gt;

&lt;p&gt;At this point the profile-guided optimization technique will be implemented using the Go language. From golang version 1.21 and above it provides a pprof CPU profile which can be collected using the &lt;code&gt;runtime/pprof&lt;/code&gt; and &lt;code&gt;net/http/pprof&lt;/code&gt; packages which can be used to instrument our code. &lt;/p&gt;

&lt;p&gt;Secondly, the standard approach is to store a pprof CPU profile with the filename &lt;code&gt;default.pgo&lt;/code&gt; in the main package directory of the profiled binary. This profile is automatically detected by the go build command, enabling PGO optimizations during the build.&lt;/p&gt;

&lt;p&gt;Let’s build a simple application and see how it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "log"

    "net/http"

    _ "net/http/pprof"

    "os"

    jsoniter "github.com/json-iterator/go"
)

type Data struct {
    Quiz struct {
        Sport struct {
            Q1 struct {
                Question string `json:"question"`

                Options []string `json:"options"`

                Answer string `json:"answer"`
            } `json:"q1"`
        } `json:"sport"`

        Maths struct {
            Q1 struct {
                Question string `json:"question"`

                Options []string `json:"options"`

                Answer string `json:"answer"`
            } `json:"q1"`

            Q2 struct {
                Question string `json:"question"`

                Options []string `json:"options"`

                Answer string `json:"answer"`
            } `json:"q2"`
        } `json:"maths"`
    } `json:"quiz"`
}

func main() {

    http.HandleFunc("/", handler)

    log.Fatal(http.ListenAndServe(":8080", nil))

}

func handler(w http.ResponseWriter, r *http.Request) {

    var json = jsoniter.ConfigCompatibleWithStandardLibrary

    file, _ := os.ReadFile("./data.json")

    data := Data{}

    json.Unmarshal(file, &amp;amp;data)

    d, _ := json.Marshal(data)

    w.Write(d)

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a simple endpoint that reads a .json file and unmarshal and marshalagain and then write it to response. The piece of code has also been instrumented with the &lt;code&gt;net/http/pprof&lt;/code&gt; package to prepare it for PGO.&lt;/p&gt;

&lt;p&gt;Then next we build the instrumented binary code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go build -o nonPGOBinary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we proceed, we want to test the performance of this binary, for this I would be using &lt;code&gt;github.com/tsliwowicz/go-wrk&lt;/code&gt; to create traffic that sends requests to this endpoint.&lt;/p&gt;

&lt;p&gt;Start the server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./nonPGOBinary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open another terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; go-wrk -d 20 http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results are as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F49vsjb830u4qtp9em77w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F49vsjb830u4qtp9em77w.png" alt="Instrument Binary Result" width="800" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take note of how many requests it is able to serve without Optimization.&lt;/p&gt;

&lt;p&gt;Next, we gather profile data from the server that is running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -o default.pgo "http://localhost:8080/debug/pprof/profile?seconds=10"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s recompile our code with the profile data that we have and then run the benchmark again and compare the results. &lt;/p&gt;

&lt;p&gt;Stop the current server and build again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go build -pgo=auto -gcflags -m -o PGOBinary main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the Optimized Binary and Create Traffic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./PGOBinary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go-wrk -d 20 http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffhz2zs5u6swf3eyhfxp7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffhz2zs5u6swf3eyhfxp7.png" alt="PGO Optimized Result" width="800" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that there is an increase in the number of requests our server can handle by 5,000.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this illustrative example, our exploration of Profile-Guided Optimization (PGO) included the crucial step of collecting a profile and subsequently rebuilding our server. It's essential to note that, in a real-world scenario, software development is an ongoing journey, marked by continuous enhancements and refinements.&lt;/p&gt;

&lt;p&gt;In practice, the ability to collect a profile from a production environment running last week's code and seamlessly apply it to build with today's source code is a testament to the robust flexibility of PGO in Go. Unlike some optimization techniques that might stumble upon encountering code evolution, PGO gracefully handles such scenarios. Its adaptability shines, allowing developers to harness its runtime insights even in the midst of an ever-evolving codebase.&lt;/p&gt;

&lt;p&gt;Happy coding! 🚀&lt;/p&gt;

</description>
      <category>go</category>
      <category>compilers</category>
      <category>softwareengineering</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Silent Payments: Empowering Africa's Financial Ecosystem</title>
      <dc:creator>Chinonso Amadi</dc:creator>
      <pubDate>Mon, 16 Oct 2023 23:31:56 +0000</pubDate>
      <link>https://dev.to/nonsoamadi10/bitcoin-cores-silent-payments-empowering-africas-financial-ecosystem-18ee</link>
      <guid>https://dev.to/nonsoamadi10/bitcoin-cores-silent-payments-empowering-africas-financial-ecosystem-18ee</guid>
      <description>&lt;p&gt;One of the things I love about bitcoin is its transparency and decentralised nature. While it is often regarded as the one true ultrasound money, it remains at the forefront of innovation.  One of the most exciting developments on the Bitcoin Protocol is the proposal for "Silent Payments." &lt;/p&gt;

&lt;p&gt;Spearheaded by Ruben Somsen, this groundbreaking feature has the potential to revolutionise financial transactions, particularly in the African continent. &lt;br&gt;
In this article, we will delve into what Silent Payments are, their potential impact on Africa, and how African developers can leverage this feature to build a more robust and private financial ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Silent Payments
&lt;/h2&gt;

&lt;p&gt;Silent Payments, as proposed by Ruben Somsen, are a novel concept that allows individuals to make payments to a public identifier (commonly referred to as an "address") without revealing that identifier on the blockchain. This means that Alice, for instance, can create a public key identifier and publish it on her website. When Bob wishes to send her Bitcoin, he can use one of his private keys to derive a new Bitcoin address. This address is used for the transaction, but only Alice and Bob possess the information required to link it to her. Importantly, only Alice holds the private key necessary to spend the funds received at this address.&lt;/p&gt;

&lt;p&gt;The beauty of Silent Payments lies in its privacy-preserving nature. If a third party, like Carol, comes across Alice's public identifier and decides to send Bitcoin, they will derive a different address for Alice. Neither Bob nor any other third party can directly determine that this address belongs to Alice. This fundamental feature enhances privacy and security in Bitcoin transactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Empowering Africa and its developers
&lt;/h2&gt;

&lt;p&gt;Silent Payments can offer a range of benefits including enhanced privacy and security, financial autonomy and user trust. These benefits can open up various opportunities for developers to build interesting solutions and applications around them. Many of which include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Human Rights Protection&lt;/strong&gt;: In countries with political instability and human rights concerns, silent payments solutions can help protect individuals from government surveillance and censorship, empowering citizens to engage in secure and private financial transactions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhanced Security&lt;/strong&gt;: Since silent payments is an improvement on privacy, it prevents unauthorized access to transaction data, reducing the risk of fraud, identity theft, and malicious attacks. Solutions can be built to include privacy-focused wallets and encryption techniques to secure user data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Philanthropy and Charitable Giving:&lt;/strong&gt; Anonymous donations can encourage philanthropy by allowing individuals and organizations to contribute to causes without revealing their identities. This can be especially useful in Africa, where charitable contributions play a vital role in addressing social and humanitarian challenges&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Business Confidentiality&lt;/strong&gt;: Enterprises can use this feature to protect sensitive business data, transaction details, and intellectual property.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Legal and Compliance&lt;/strong&gt;: Solutions that combine privacy and compliance can help businesses and individuals navigate the regulatory landscape, ensuring that privacy and legal obligations are balanced.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Challenges and Trade-offs
&lt;/h2&gt;

&lt;p&gt;While Silent Payments hold great promise, they are not without challenges. Checking for newly-received transactions with Silent Payments requires scanning every transaction in every new block, which can be resource-intensive. This may pose difficulties for light wallets that aim to receive payments. However, the scheme is well-suited for users who prioritize enhanced privacy and are willing to bear the associated resource costs.&lt;/p&gt;

&lt;p&gt;Silent Payments also require maintaining information about many transaction's parent transactions, a task that many full nodes do not currently perform. This may necessitate additional I/O operations and CPU resources. Nonetheless, these challenges are not insurmountable, and Somsen has actively sought feedback to improve the proposal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Silent Payments represent a significant step forward in enhancing privacy and security in Bitcoin transactions. For the African continent, where financial inclusion and privacy are paramount concerns, this feature holds tremendous potential. African developers are in a prime position to harness Silent Payments' capabilities to build a more robust and private financial ecosystem that empowers the continent's residents. As Ruben Somsen actively seeks feedback and improvements for the proposal, the future of Silent Payments in Africa looks bright, promising greater financial freedom and security for all.&lt;/p&gt;

</description>
      <category>bitcoin</category>
      <category>engineering</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Exploring Go's Unique Approach to Object-Oriented Programming</title>
      <dc:creator>Chinonso Amadi</dc:creator>
      <pubDate>Thu, 15 Jun 2023 21:53:09 +0000</pubDate>
      <link>https://dev.to/nonsoamadi10/exploring-gos-unique-approach-to-object-oriented-programming-1l5i</link>
      <guid>https://dev.to/nonsoamadi10/exploring-gos-unique-approach-to-object-oriented-programming-1l5i</guid>
      <description>&lt;p&gt;Unlike other languages, when it comes to the concept of object oriented programming(OOP), Golang takes a unique yet intuitive approach to modelling objects that contain data and methods. Rather than following concepts such as Encapsulation, Inheritance, and Polymorphism, The Go language focuses on composition, interfaces, and naming convention. In this article we would see how these principles contribute to the flexibility, maintainability and extensibility of Object Oriented Programming in the Go language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Composition: Building Blocks Of Functionality&lt;/strong&gt;&lt;br&gt;
In Go, composition is favored over inheritance when it comes to creating complex functionality. Composition allows developers to combine smaller types to form more specialized and comprehensive ones. By using fields or struct embedding, Go promotes code reuse and modularity. Take a look at this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Person struct {
    Name string
    Age  int
}

type Employee struct {
    Person
    EmployeeID string
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code snippet, the &lt;code&gt;Employee&lt;/code&gt; type includes a &lt;code&gt;Person&lt;/code&gt; type as a field. This composition enables an &lt;code&gt;Employee&lt;/code&gt; object to inherit the &lt;code&gt;Name&lt;/code&gt; and &lt;code&gt;Age&lt;/code&gt; fields from the &lt;code&gt;Person&lt;/code&gt; object. With composition, Go encourages the creation of flexible and customizable types by combining smaller building blocks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interfaces: Contracts for Behavior&lt;/strong&gt;&lt;br&gt;
Go relies on interfaces to achieve polymorphism and code flexibility. Instead of explicit class inheritance, Go uses interfaces to define contracts for behavior. Any type that implements the methods defined in an interface is said to implicitly satisfy that interface. This allows different types to be used interchangeably as long as they adhere to the specified interface. Look at this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Shape interface {
    Area() float64
    Perimeter() float64
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;Shape&lt;/code&gt; interface defines the behavior of any shape by specifying the &lt;code&gt;Area()&lt;/code&gt; and &lt;code&gt;Perimeter()&lt;/code&gt; methods. The &lt;code&gt;Circle&lt;/code&gt; struct satisfies the &lt;code&gt;Shape&lt;/code&gt; interface by implementing these methods. By utilizing interfaces, Go enables polymorphism, allowing different types to be treated uniformly based on shared behaviors rather than hierarchical relationships.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Naming Conventions: Clarity and Intent&lt;/strong&gt;&lt;br&gt;
Naming conventions play a crucial role in code readability and conveying intent. Unlike some other languages where naming conventions are merely recommendations, Go enforces specific patterns to ensure consistent and understandable code. Adhering to these conventions aids in comprehending the purpose and functionality of code components. Here are a few common naming conventions it uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Capitalizing initial letters for exported (public) identifiers&lt;/li&gt;
&lt;li&gt;Using mixedCase or camelCase for local (private) identifiers&lt;/li&gt;
&lt;li&gt;Choosing concise and descriptive names for variables, functions, and types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following these naming conventions, Go code becomes more self-explanatory, promoting collaboration among developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;br&gt;
Go's approach to object-oriented programming sets it apart from traditional languages that heavily rely on encapsulation, inheritance, and polymorphism. By embracing composition, interfaces, and strict naming conventions, Go promotes code reusability, modularity, and extensibility. Through the examples and explanations provided, you now have a solid understanding of Go's unique programming paradigm. By applying these principles, you can write clean, flexible, and maintainable Go code while embracing the simplicity and efficiency that the language offers.&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building a Bitcoin P2P Network Analyzer with Golang and Fiber Framework</title>
      <dc:creator>Chinonso Amadi</dc:creator>
      <pubDate>Wed, 07 Jun 2023 21:20:31 +0000</pubDate>
      <link>https://dev.to/nonsoamadi10/building-a-bitcoin-p2p-network-analyzer-with-golang-and-fiber-framework-269p</link>
      <guid>https://dev.to/nonsoamadi10/building-a-bitcoin-p2p-network-analyzer-with-golang-and-fiber-framework-269p</guid>
      <description>&lt;p&gt;In the world of Bitcoin, understanding the dynamics of the peer-to-peer (P2P) network is crucial. Monitoring node information, connection metrics, and P2P traffic can provide valuable insights into the network's health, performance, and behavior. In this article, we will explore how to build a Bitcoin P2P network analyzer using Golang and the Fiber framework. We'll cover fetching node information, tracking connection metrics, and exposing relevant information through APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along with this tutorial,the necessary dependencies installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Golang 1.17 or later&lt;/li&gt;
&lt;li&gt;SQLite&lt;/li&gt;
&lt;li&gt;Btcd ( a bitcoin node implementation written in golang )&lt;/li&gt;
&lt;li&gt;A running testnet bitcoin node daemon &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, a basic understanding of Bitcoin, P2P networks, and the Golang Fiber framework will be helpful.&lt;/p&gt;

&lt;p&gt;Setting Up the Project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a new folder and create a new Go module for our project:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go mod init bitcoin-p2p-analyzer

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Install the required dependencies:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go get -u github.com/gofiber/fiber/v2
go get -u github.com/lncm/lnd-rpc/v0.10.0/lnrpc
go get -u github.com/btcsuite/btcd
go get -u gorm.io/gorm
go get -u github.com/joho/godotenv

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would be using this folder format during the course of this tutorial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📁 app
📁 bitcoin
📁 controllers
📁 db
📁 lightning
📁 services
📁 utils
go.mod
go.sum
main.go 
.env

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;app&lt;/code&gt; directory will house the configuration and initialization of the fiber application which will run in the &lt;code&gt;main.go&lt;/code&gt;. The &lt;code&gt;bitcoin&lt;/code&gt; and &lt;code&gt;lightning&lt;/code&gt; folder will contain configuration of our bitcoin and lightning clients respectively. The controllers will contain function handlers for our api endpoints, while the &lt;code&gt;db&lt;/code&gt; will hold our connection to our sqlite database using gorm. The services and utils fodler will contain helper methods for our application. &lt;br&gt;
It is important to note that all our implementation will focus on the bitcoin test network(testnet).&lt;/p&gt;
&lt;h2&gt;
  
  
  Node Information Monitoring
&lt;/h2&gt;

&lt;p&gt;The first step in our network analyzer is to fetch and monitor node information such as user agent, version, and services. We can use the Lightning Network Daemon (LND) client and Bitcoin Client to retrieve this information. Let us setup our Lightning Client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// lightning/client.go&lt;/span&gt;


&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;lightning&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/hex"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"io/ioutil"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"os/user"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="s"&gt;"google.golang.org/grpc"&lt;/span&gt;
    &lt;span class="s"&gt;"google.golang.org/grpc/credentials"&lt;/span&gt;
    &lt;span class="s"&gt;"gopkg.in/macaroon.v2"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/lncm/lnd-rpc/v0.10.0/lnrpc"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;rpcCreds&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;rpcCreds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;RequireTransportSecurity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;rpcCreds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetRequestMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;newCreds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rpcCreds&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;creds&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"macaroon"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EncodeToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;creds&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tlsFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;macaroonFile&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;lnrpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LightningClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;macaroonBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ioutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;macaroonFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cannot read macaroon file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;mac&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;macaroon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Macaroon&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnmarshalBinary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;macaroonBytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cannot unmarshal macaroon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;transportCredentials&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClientTLSFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tlsFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;fullHostname&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s:%d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DialContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fullHostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DialOption&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithBlock&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTransportCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transportCredentials&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithPerRPCCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newCreds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;macaroonBytes&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unable to connect to %s: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fullHostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lnrpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewLightningClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;lnrpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LightningClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;homeDir&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HomeDir&lt;/span&gt;
    &lt;span class="n"&gt;lndDir&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s/app_container/lightning"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;homeDir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;hostname&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"localhost"&lt;/span&gt;
        &lt;span class="n"&gt;port&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10009&lt;/span&gt;
        &lt;span class="n"&gt;tlsFile&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s/tls.cert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lndDir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;macaroonFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s/data/chain/bitcoin/testnet/admin.macaroon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lndDir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tlsFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;macaroonFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Bitcoin Client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;bitcoin&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="s"&gt;"bitcoin-p2p-analyzer/utils"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/btcsuite/btcd/rpcclient"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rpcclient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c"&gt;// Connect to a running Bitcoin Core node via RPC&lt;/span&gt;
    &lt;span class="n"&gt;connCfg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;rpcclient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnConfig&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BTC_HOST"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BTC_USER"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Pass&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BTC_PASS"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;HTTPPostMode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DisableTLS&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rpcclient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connCfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error connecting to bitcoind:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// Get the current block count&lt;/span&gt;
    &lt;span class="n"&gt;blockCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetBlockCount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error getting block count:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Current block count:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blockCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we go ahead and create our function in the &lt;code&gt;services&lt;/code&gt; directory that displays our node information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// services/lightning.go&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="s"&gt;"bitcoin-p2p-analyzer/lightning"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/lncm/lnd-rpc/v0.10.0/lnrpc"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;LNodeMetrics&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;PubKey&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"pub_key"`&lt;/span&gt;
    &lt;span class="n"&gt;UserAgent&lt;/span&gt;   &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"user_agent"`&lt;/span&gt;
    &lt;span class="n"&gt;Alias&lt;/span&gt;       &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"alias"`&lt;/span&gt;
    &lt;span class="n"&gt;NetCapacity&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;    &lt;span class="s"&gt;`json:"network_capacity"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetNodeInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;LNodeMetrics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;lightning&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;infoReq&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lnrpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetInfoRequest&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;infoReq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error getting node info: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;moreInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetNetworkInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lnrpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NetworkInfoRequest&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;LNodeMetrics&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;UserAgent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Alias&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Alias&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;NetCapacity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;moreInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalNetworkCapacity&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;PubKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IdentityPubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bitcoin Node Info:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;
&lt;span class="c"&gt;// services/bitcoin.go&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"math"&lt;/span&gt;

    &lt;span class="s"&gt;"bitcoin-p2p-analyzer/bitcoin"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/btcsuite/btcd/chaincfg/chainhash"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;NodeMetrics&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Difficulty&lt;/span&gt;    &lt;span class="kt"&gt;float64&lt;/span&gt;     &lt;span class="s"&gt;`json:"difficulty"`&lt;/span&gt;
    &lt;span class="n"&gt;Version&lt;/span&gt;       &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="s"&gt;`json:"version"`&lt;/span&gt;
    &lt;span class="n"&gt;Chain&lt;/span&gt;         &lt;span class="kt"&gt;string&lt;/span&gt;      &lt;span class="s"&gt;`json:"chain"`&lt;/span&gt;
    &lt;span class="n"&gt;Blocks&lt;/span&gt;        &lt;span class="kt"&gt;int32&lt;/span&gt;       &lt;span class="s"&gt;`json:"no_of_blocks"`&lt;/span&gt;
    &lt;span class="n"&gt;BestBlockHash&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;      &lt;span class="s"&gt;`json:"bestblockhash"`&lt;/span&gt;
    &lt;span class="n"&gt;UserAgent&lt;/span&gt;     &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="s"&gt;`json:"user_agent"`&lt;/span&gt;
    &lt;span class="n"&gt;HashRate&lt;/span&gt;      &lt;span class="kt"&gt;float64&lt;/span&gt;     &lt;span class="s"&gt;`json:"hash_rate"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;NodeMetrics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bitcoin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shutdown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetBlockChainInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;networkInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetNetworkInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;lastBlockHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;chainhash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewHashFromStr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BestBlockHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;lastBlock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lastBlockHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;timeToFindBlock&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;lastBlock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timestamp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unix&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lastBlock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrevBlock&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lastBlock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrevBlock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;hashrate&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Difficulty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeToFindBlock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;metrics&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;NodeMetrics&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Difficulty&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Difficulty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;networkInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Chain&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Blocks&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Blocks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;BestBlockHash&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BestBlockHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;UserAgent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;networkInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;HashRate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;hashrate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we create an api function handler in our controllers folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// controllers/controller.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;controllers&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"bitcoin-p2p-analyzer/services"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gofiber/fiber/v2"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Success&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;        &lt;span class="s"&gt;`json:"success"`&lt;/span&gt;
    &lt;span class="n"&gt;Data&lt;/span&gt;    &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="s"&gt;`json:"data"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetMetrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;NodeResponse&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Lightning&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="s"&gt;`json:"lightning"`&lt;/span&gt;
        &lt;span class="n"&gt;Bitcoin&lt;/span&gt;   &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="s"&gt;`json:"bitcoin"`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;bitcoin&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;lightning&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetNodeInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;NodeResponse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Bitcoin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;bitcoin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Lightning&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;lightning&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we update the &lt;code&gt;app&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// app/app.go&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"bitcoin-p2p-analyzer/controllers"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gofiber/fiber/v2"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gofiber/fiber/v2/middleware/cors"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;AllowOrigins&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;AllowHeaders&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Origin, Content-Type, Accept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, World!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/node-info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controllers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetMetrics&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;update the main.go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// main.go&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="s"&gt;"bitcoin-p2p-analyzer/app"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:1700"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file and run the app in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your postman on the following address &lt;code&gt;http://127.0.0.1:1700/node-info&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Connection Metrics Monitoring
&lt;/h2&gt;

&lt;p&gt;Next, we'll focus on tracking connection metrics, such as uptime and churn, for both on-premises servers and EC2 instances running Bitcoin Core (btcd). We would be implementing in a time-series manner where by we can compare the metrics overtime and to get insights on the connection performance. We would use goroutines to scrape the metrics every 3 minutes and save in a sqlite database. We'll use the btcd and LND clients to fetch the necessary information. Here's an example code snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// db/database.go&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="s"&gt;"gorm.io/driver/sqlite"&lt;/span&gt;
    &lt;span class="s"&gt;"gorm.io/gorm"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sqlite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"metrics.db"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to open database: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// services/metrics.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="s"&gt;"bitcoin-p2p-analyzer/bitcoin"&lt;/span&gt;
    &lt;span class="s"&gt;"bitcoin-p2p-analyzer/db"&lt;/span&gt;
    &lt;span class="s"&gt;"bitcoin-p2p-analyzer/lightning"&lt;/span&gt;
    &lt;span class="s"&gt;"bitcoin-p2p-analyzer/utils"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/lncm/lnd-rpc/v0.10.0/lnrpc"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;ConnectionMetrics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Create the metrics table if it doesn't already exist&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoMigrate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionMetrics&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;

    &lt;span class="c"&gt;// Get Bitcoin Client&lt;/span&gt;
    &lt;span class="n"&gt;bitcoin&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bitcoin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;bitcoin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shutdown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Get Lightning Client&lt;/span&gt;
    &lt;span class="n"&gt;lnd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;lightning&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;//Get Bitcoin Peer Info&lt;/span&gt;

        &lt;span class="n"&gt;peerInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bitcoin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetPeerInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to fetch btcd peer info: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;infoReq&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lnrpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetInfoRequest&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

        &lt;span class="n"&gt;lndInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;lnd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;infoReq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to fetch lnd info: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c"&gt;// Calculate the incoming and outgoing bandwidth for the btcd node&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;btcdBandwidthIn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;btcdBandwidthOut&lt;/span&gt; &lt;span class="kt"&gt;uint64&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;peerInfo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;btcdBandwidthIn&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BytesRecv&lt;/span&gt;
            &lt;span class="n"&gt;btcdBandwidthOut&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BytesSent&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;metrics&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionMetrics&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Timestamp&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;           &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;NumBTCPeers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="kt"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peerInfo&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="n"&gt;NumLNDPeers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="kt"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lndInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NumPeers&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;NumActiveChannels&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="kt"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lndInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NumActiveChannels&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;NumPendingChannels&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="kt"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lndInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NumPendingChannels&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;NumInactiveChannels&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lndInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NumInactiveChannels&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;BtcdBandwidthIn&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;btcdBandwidthIn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;BtcdBandwidthOut&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;btcdBandwidthOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;BlockHeight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lndInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BlockHeight&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;BlockHash&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;           &lt;span class="n"&gt;lndInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BlockHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;BestHeaderAge&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;lndInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BestHeaderTimestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;SyncedToChain&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;lndInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SyncedToChain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;// Wait for 3 minute before fetching the next set of connection metrics&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Minute&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fetching Metrics:&lt;br&gt;
To retrieve the stored connection metrics, we can create an API endpoint using the Fiber framework:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="c"&gt;//services/metrics.go&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;FetchMetrics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionMetrics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;allMetrics&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionMetrics&lt;/span&gt;

    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;//fetch all metrics&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;allMetrics&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;allMetrics&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// controllers/controller.go&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetConnMetrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;metrics&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FetchMetrics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you update the &lt;code&gt;app.go&lt;/code&gt; with the required endpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;
&lt;span class="c"&gt;// app/app.go&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/conn-metrics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controllers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetConnMetrics&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionMetrics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// run the function as a goroutine&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your postman on &lt;code&gt;http://127.0.0.1:1700/conn-metrics&lt;/code&gt; and test after 3 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we explored how to build a Bitcoin P2P network analyzer using Golang and the Fiber framework. We learned how to fetch node information, track connection metrics, and expose the relevant information through an API endpoint. By monitoring node information, connection metrics, and P2P traffic, we can gain valuable insights into the behaviour, performance, and bandwidth usage of the Bitcoin network. For example, analysing node information allows us to identify the distribution of different node implementations, such as Bitcoin Core, btcd, or other client software versions. This insight can help us understand the level of network diversity and the adoption rate of software updates within the Bitcoin ecosystem.&lt;/p&gt;

&lt;p&gt;Connection metrics provide visibility into the connectivity and network topology of the Bitcoin network. By monitoring connection counts and peer relationships, we can identify well-connected nodes, influential network participants, and potential bottlenecks. This information helps us evaluate the resilience and decentralisation of the network, and it can guide decisions related to optimising node connections for better performance and redundancy.&lt;/p&gt;

&lt;p&gt;P2P traffic analysis allows us to examine the flow of data within the Bitcoin network. By studying message types, transaction propagation, and block dissemination, we can gain insights into the efficiency and effectiveness of the network's information dissemination protocols. This analysis can reveal potential delays or inefficiencies in block propagation, highlighting areas for optimisation to enhance the overall network throughput and reduce confirmation times.&lt;/p&gt;

&lt;p&gt;Furthermore, by extending the functionality of a Bitcoin P2P network analyser, we can unlock even more valuable insights. For instance, incorporating transaction analysis capabilities can enable the identification of transaction patterns, fee dynamics, and network congestion levels. By visualising these metrics, we can better understand the factors influencing transaction confirmation times and fee market dynamics.&lt;/p&gt;

&lt;p&gt;Additionally, integrating data from Lightning Network nodes and channels can provide insights into the growth, liquidity distribution, and routing efficiency of the Layer 2 network. This information can help evaluate the scalability and usability of the Lightning Network, as well as identify areas for improvement.&lt;/p&gt;

&lt;p&gt;By leveraging the data collected and analyzed through a Bitcoin network monitoring tool, researchers, developers, and network participants can make informed decisions to enhance the security, efficiency, and scalability of the Bitcoin network.&lt;/p&gt;

&lt;p&gt;Note: This article provides a high-level overview of the implementation process. Please refer to the official documentation and relevant libraries' documentation for detailed instructions and best practices.&lt;/p&gt;

&lt;p&gt;Happy coding and exploring the fascinating world of Bitcoin P2P networks!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>UTXO Consolidation: Maximising Your Bitcoin Wallet's Potential</title>
      <dc:creator>Chinonso Amadi</dc:creator>
      <pubDate>Mon, 15 May 2023 15:37:59 +0000</pubDate>
      <link>https://dev.to/nonsoamadi10/utxo-consolidation-maximising-your-bitcoin-wallets-potential-3k13</link>
      <guid>https://dev.to/nonsoamadi10/utxo-consolidation-maximising-your-bitcoin-wallets-potential-3k13</guid>
      <description>&lt;p&gt;The Unspent Transaction Output (UTXO) is a vital element of how transactions are conducted on the Bitcoin blockchain. It denotes the funds that are still available to spend for a specific Bitcoin address. When completing a transaction, users need to utilize Unspent Transaction Outputs (UTXOs), which can result in the accumulation of a significant number of UTXOs. Managing such a large quantity can become challenging. In this article, we will explore the process of consolidating such transaction output and offer practical advice on how to implement this consolidation using a real-world Bitcoin wallet&lt;/p&gt;

&lt;h2&gt;
  
  
  What is UTXO Consolidation?
&lt;/h2&gt;

&lt;p&gt;When someone sends bitcoin, the process involves moving funds from different sources, called transaction inputs or UTXOs, to create a new output UTXO that represents the updated balance after the transaction. However, this approach can lead to challenges in handling funds effectively and may result in increased transaction fees when users accumulate multiple UTXOs over a period of time. To streamline the management of funds and minimise fees, a technique called UTXO consolidation can be employed. This technique involves merging multiple UTXOs into a single one, offering benefits such as improved privacy and reduced transaction size on the blockchain..&lt;/p&gt;

&lt;p&gt;There are two primary approaches to consolidate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Manual consolidation&lt;/li&gt;
&lt;li&gt;Automatic consolidation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Manual Consolidation&lt;/strong&gt;: This involves creating a new transaction with your desired UTXOs as inputs and then sending the entire amount to yourself in a single output. &lt;/p&gt;

&lt;p&gt;Consolidating manually offers users a higher degree of control during the process. They can handpick particular UTXOs based on several factors such as age, size, and transaction fees. However, this method can be tedious and requires active management of UTXOs by the user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automatic Consolidation&lt;/strong&gt;: Bitcoin wallets can merge multiple unspent transaction outputs (UTXOs) into a single output periodically. Users can set a threshold based on the number or total value of UTXOs to automatically trigger this process at regular intervals.&lt;/p&gt;

&lt;p&gt;A Bitcoin wallet can automatically merge unspent transaction outputs based on specific conditions, like having more than 100 UTXOs or a total value exceeding 0.1 BTC. The wallet will handle the selection of the appropriate UTXOs and begin the consolidation process without requiring any action from the user&lt;/p&gt;

&lt;p&gt;Consolidating UTXOs manually offers users greater control and visibility compared to automated consolidation, even though the latter makes it easier to handle UTXO management and decreases the chance of UTXO scattered into tiny bits that are expensive to manage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing UTXO Consolidation
&lt;/h2&gt;

&lt;p&gt;To implement UTXO consolidation, we need bitcoin node implementation running on our computer. Our demonstration will center around using the &lt;a href="https://github.com/bitcoin/bitcoin" rel="noopener noreferrer"&gt;bitcon-cli&lt;/a&gt; and &lt;a href="https://github.com/btcsuite/btcd" rel="noopener noreferrer"&gt;btcd&lt;/a&gt;. These implementations provide an interface for interacting with the Bitcoin network via the JSON-RPC API. The following steps outline the process of implementing UTXO consolidation manually using bitcoin-cli:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Determine the UTXOs to Consolidate:
The first step involves  searching the network for all UTXOs linked to a particular address. One way to do this is by utilising the &lt;code&gt;listunspent&lt;/code&gt; command via bitcoin-cli, which provides a list of UTXOs linked to a given address. For instance, to obtain a list of all unspent transaction outputs connected to the address &lt;code&gt;tb1qreze397npdx93rdqzmjhclc2qk3d9x9vc8ylm6&lt;/code&gt;, use the following command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bitcoin-cli listunspent 0 999999 "[\"tb1qreze397npdx93rdqzmjhclc2qk3d9x9vc8ylm6\"]"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you use this command, you'll receive a comprehensive list of all UTXOs linked to the provided address that haven't been spent yet.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Determine the overall worth of UTXOs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After identifying the UTXOs to merge, the subsequent crucial phase is to compute their overall value. It's important to make sure there are enough funds to cover the transaction fee when consolidating. To calculate the total value of UTXOs, simply add up the value of each transaction output listed in Step 1.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Transaction with Consolidated UTXOs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After identifying the UTXOs to consolidate and totaling their value, the next step is to generate a new transaction that merges them into one UTXO. The command &lt;code&gt;createrawtransaction&lt;/code&gt; can be used to accomplish this. For instance, if you want to consolidate two UTXOs linked to the address &lt;code&gt;tb1qreze397npdx93rdqzmjhclc2qk3d9x9vc8ylm6&lt;/code&gt;, use the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bitcoin-cli createrawtransaction '[{"txid":"&amp;lt;id1&amp;gt;","vout":&amp;lt;number1&amp;gt;},{"txid":"&amp;lt;id2&amp;gt;","vout":&amp;lt;number2&amp;gt;}}]'


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Sign the Transaction&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After creating a new transaction, the subsequent step involves signing it with the private key linked to the address holding the UTXOs. To sign, you can employ the &lt;code&gt;signrawtransactionwithwallet&lt;/code&gt; command. For instance, if you want to sign the transaction generated in Step 3, use the subsequent command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bitcoin-cli signrawtransactionwithwallet &amp;lt;rawtransaction&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will sign the transaction with the private key associated with the address that holds the UTXOs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Broadcast the Transaction:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After the transaction has been signed, the last step is to send it to the Bitcoin network by executing the &lt;code&gt;sendrawtransaction&lt;/code&gt; command to broadcast the transaction made in Step 4:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bitcoin-cli sendrawtransaction &amp;lt;signedtransaction&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once executed, the transaction will be disseminated throughout the Bitcoin network, and upon confirmation, the Unspent Transaction Outputs will merge into a solitary UTXO.&lt;/p&gt;

&lt;p&gt;Below are the steps to implement automatic UTXO consolidation using the btcd package in Golang. Although these steps can be applied to any programming language supporting Bitcoin node implementation, we will focus on Golang's btcd for this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"os/signal"&lt;/span&gt;
    &lt;span class="s"&gt;"syscall"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/btcsuite/btcd/chaincfg"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/btcsuite/btcd/rpcclient"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;rpcUser&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"your_rpc_username"&lt;/span&gt;
    &lt;span class="n"&gt;rpcPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"your_rpc_password"&lt;/span&gt;
    &lt;span class="n"&gt;rpcHost&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"localhost"&lt;/span&gt;
    &lt;span class="n"&gt;rpcPort&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"8332"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Connect to the Bitcoin node&lt;/span&gt;
    &lt;span class="n"&gt;connCfg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;rpcclient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnConfig&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;rpcHost&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;":"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rpcPort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;rpcUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Pass&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;rpcPassword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;HTTPPostMode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DisableTLS&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rpcclient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connCfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;chaincfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MainNetParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shutdown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Configure consolidation threshold&lt;/span&gt;
    &lt;span class="n"&gt;consolidationThreshold&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="c"&gt;// Number of UTXOs or total UTXO value threshold&lt;/span&gt;

    &lt;span class="c"&gt;// Start the consolidation process&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;consolidateUTXOs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consolidationThreshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Wait for interrupt signal to gracefully exit&lt;/span&gt;
    &lt;span class="n"&gt;waitForExitSignal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;consolidateUTXOs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rpcclient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Retrieve UTXOs from the wallet&lt;/span&gt;
        &lt;span class="n"&gt;unspentList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListUnspentMinMaxAddresses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error retrieving UTXOs:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c"&gt;// Check if consolidation is necessary&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unspentList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c"&gt;// Create the consolidation transaction&lt;/span&gt;
            &lt;span class="n"&gt;txInputs&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;rpcclient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TransactionInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;totalValue&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;utxo&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;unspentList&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;txInputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txInputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rpcclient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TransactionInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;Txid&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;utxo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TxID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Vout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;utxo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Vout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="n"&gt;totalValue&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;utxo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;txOutput&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;txOutput&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"your_destination_address"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;totalValue&lt;/span&gt;

            &lt;span class="n"&gt;consolidationTxID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateRawTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txInputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;txOutput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error creating consolidation transaction:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c"&gt;// Sign and send the consolidation transaction&lt;/span&gt;
            &lt;span class="n"&gt;signedTx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SignRawTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;consolidationTxID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error signing consolidation transaction:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;txHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendRawTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signedTx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error sending consolidation transaction:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Consolidation transaction sent, TxID: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;txHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c"&gt;// Wait for some time before checking again&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Minute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;waitForExitSignal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;interruptChannel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interruptChannel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Interrupt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGTERM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;interruptChannel&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exiting..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;UTXO consolidation is a useful process that can bring various benefits to Bitcoin users. When users merge several UTXOs into one transaction output, it reduces the number of inputs needed for a transaction, leading to faster transaction times and reduced transaction fees and improves privacy. Consolidating UTXOs also simplifies Bitcoin holdings management for users&lt;/p&gt;

</description>
      <category>bitcoin</category>
      <category>blockchain</category>
      <category>programming</category>
      <category>go</category>
    </item>
    <item>
      <title>Bitcoin Fee and Optimisation Strategies</title>
      <dc:creator>Chinonso Amadi</dc:creator>
      <pubDate>Mon, 24 Apr 2023 15:15:21 +0000</pubDate>
      <link>https://dev.to/nonsoamadi10/bitcoin-fee-and-optimisation-strategies-341e</link>
      <guid>https://dev.to/nonsoamadi10/bitcoin-fee-and-optimisation-strategies-341e</guid>
      <description>&lt;p&gt;One thing that makes bitcoin fascinating is that it is not controlled by a central entity or government. This makes it a sought-after choice for those seeking a secure and private way to transfer funds. However, with the growing acceptance of Bitcoin, transaction fees have risen, resulting in user dissatisfaction. Consequently, users are seeking ways to decrease fees and improve their transaction performance. This article discusses Bitcoin fee estimation and optimisation strategies, and why they are important.&lt;/p&gt;

&lt;p&gt;To optimise fee estimation, users can utilize the mempool state. The mempool consists of unconfirmed transactions broadcasted to the network and is maintained by all nodes in the network. It includes information about each transaction such as its fee rate, transaction size, and priority. However, it is important to note that there is no single unified mempool state shared by all nodes. Each node can impact the contents of their own mempool by adjusting their policy rules. By using the mempool state, users can estimate the fee rate required to ensure a prompt transaction confirmation.&lt;/p&gt;

&lt;p&gt;To determine the appropriate fee rate, users should review the current status of the mempool, checking how many transactions are pending confirmation and the corresponding fee rates they offer. Based on this analysis, users can approximate the fee needed to ensure timely confirmation of their transaction.&lt;/p&gt;

&lt;p&gt;For fee estimation, Bitcoin Core offers the &lt;code&gt;estimatesmartfee&lt;/code&gt; tool. It uses current mempool statistics as well as a review of fee rates for transactions that were confirmed in previous blocks and the duration they spent in the mempool before being validated. This tool provides an estimate of the required fee rate, measured in satoshis per byte, for confirmation within a specified number of blocks. The fee rate is updated periodically, typically every few blocks.&lt;/p&gt;

&lt;p&gt;Another tool that utilises mempool state for fee estimation is the &lt;code&gt;mempool.space&lt;/code&gt; web link. This tool provides users with access to real-time information about the state of the mempool, such as the number of unconfirmed transactions, total mempool size, and current fee rates paid by transactions. This information is useful for estimating the required fee rate to ensure timely confirmation of transactions.&lt;/p&gt;

&lt;p&gt;Optimising fee estimation is important for users who want to send transactions quickly without overpaying. By using mempool state, users can estimate the fee rate they need to include to get their transaction confirmed quickly. Tools like estimatefee and mempool.space make it easy for users to access this information and optimize their fee estimation.&lt;/p&gt;

&lt;p&gt;There are multiple fee optimisation strategies available for Bitcoin, and the mempool state is just one of them. One such strategy is to consolidate multiple transactions into a single transaction. This approach reduces the transaction size, resulting in lower fees for the users.&lt;/p&gt;

&lt;p&gt;Using Segregated Witness(SegWit)addresses is another effective fee optimisation strategy as it separates transaction signatures from transaction data which results in smaller transaction sizes. &lt;br&gt;
The Lightning Network allows for instant transactions with low fees as transactions are processed off-chain, reducing the need for miners and resulting in lower fees.&lt;/p&gt;

&lt;p&gt;Users can utilise fee estimation tools that apply different algorithms to determine the best fee for their transaction based on the current network status. Additionally, RBF(replace-by-fee) is a protocol that enables users to replace an unconfirmed transaction with a new transaction that has a higher fee. This feature can be helpful if a user's initial transaction is stuck in the mempool for an extended period without confirmation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is crucial for Bitcoin users to employ fee estimation and optimization strategies. These strategies enable users to confirm their transactions quickly and at a reasonable cost.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
