如何用Node.js实现DeMarriage婚姻登记合约?

来源:网络学院作者:马来西亚程序员头衔:程序员
导读:本期聚焦于马来西亚程序员创作的《如何用Node.js实现DeMarriage婚姻登记合约?》,敬请观看详情。如何把婚姻状态登记到区块链上并用Node.js完成日常调用?DeMarriage是一个用Solidity编写的婚姻登记合约,记录配偶双方地址、登记时间与状态。本文从合约数据结构出发,逐步演示用Node.js通过Web3.js连接到以太坊节点,完成账户解锁、合约部署、登记结婚与解除婚姻等操作。你会看到状态变量、映射、事件和修饰器如何配合使用,以及Node.js脚本如何封装交易发送和回执等待。由于区块链交易不可篡改,每次状态变更都会留下可追溯的日志,这也让合约具备类似公证的能力。即使没有太多Solidity经验,也可以按文中的代码搭建一个本地测试环境,把婚姻登记流程完整跑通。

在以太坊这类支持智能合约的平台上,婚姻登记可以被建模为一种地址映射关系:两个地址通过一笔交易建立配偶关系,也可以通过另一笔交易解除。Node.js在这一流程里扮演链下脚本和后端角色,负责生成交易数据、签名或发送到节点、读取事件日志。本文以一个简化版DeMarriage合约为例,展示从合约编写到Node.js调用完整链路。

如何用Node.js实现DeMarriage婚姻登记合约?

一、DeMarriage合约的状态与约束设计

合约核心是保存每个地址的婚姻状态。Solidity中的映射类型很适合这种一一对应的关系,用mapping(address => Marriage)记录配偶地址、结婚时间和状态枚举。枚举值分成未结婚、已婚和已离婚,这样查询接口可以返回可读的数字状态,而不是只靠地址是否为零来判断。

登记函数需要完成双向写入:调用者与配偶地址都要指向彼此,并记录同一个时间戳。状态判断放在修饰器中,如果一个地址已经处于已婚状态,再次调用登记就会直接回滚。离婚函数只让已婚地址调用,通过配偶字段找到另一方,再清除双方记录并触发事件。这样既保持了数据一致性,也避免出现单边删除导致错误状态。

事件是链上日志的重要部分。婚姻登记和解除都会写入Married和Divorced事件,Node.js端可以订阅这些事件来做实时提醒或数据同步。由于事件参数带有indexed标记,按地址过滤历史婚姻记录会比遍历映射方便很多。下面是一个可运行的合约示例:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract DeMarriage {
    enum Status { Single, Married, Divorced }

    struct Marriage {
        address partner;
        uint256 since;
        Status status;
    }

    mapping(address=>Marriage) public marriages;

    event Married(address indexed left, address indexed right, uint256 at);
    event Divorced(address indexed left, address indexed right, uint256 at);

    modifier onlySingle() {
        require(marriages[msg.sender].status == Status.Single, "Already married");
        _;
    }

    function marry(address partner) external onlySingle {
        require(partner != address(0), "Invalid partner");
        require(marriages[partner].status == Status.Single, "Partner not single");
        marriages[msg.sender] = Marriage(partner, block.timestamp, Status.Married);
        marriages[partner] = Marriage(msg.sender, block.timestamp, Status.Married);
        emit Married(msg.sender, partner, block.timestamp);
    }

    function divorce() external {
        Marriage storage mine = marriages[msg.sender];
        require(mine.status == Status.Married, "Not married");
        address partner = mine.partner;
        marriages[partner] = Marriage(address(0), block.timestamp, Status.Divorced);
        delete marriages[msg.sender];
        emit Divorced(msg.sender, partner, block.timestamp);
    }
}

二、用Node.js部署合约前的准备

部署合约需要一个可用的以太坊节点。本地开发通常使用Ganache,它会开放http://127.0.0.1:8545这个JSON-RPC地址,同时准备若干测试账户和私钥。Node.js端安装web3库,并用solc编译合约得到ABI和字节码。实际项目中可以在编译后把ABI保存成JSON,让部署脚本读取。

下面先准备依赖:

  • Node.js 18或更高版本
  • 本地以太坊节点,如Ganache
  • web3.js库
  • Solidity编译器solc

部署脚本的核心逻辑是:读取ABI和字节码,创建合约对象,再调用deploy方法发送交易。注意gas不能设置得太低,否则合约部署会因资源不足失败。交易发出后可以等待回执得到合约地址,这个地址后续查询和调用时都要使用。

const Web3 = require('web3');
const fs = require('fs');

const web3 = new Web3('http://127.0.0.1:8545');
const abi = JSON.parse(fs.readFileSync('./DeMarriage.json', 'utf8')).abi;
const bytecode = JSON.parse(fs.readFileSync('./DeMarriage.json', 'utf8')).bytecode;

async function deploy() {
  const accounts = await web3.eth.getAccounts();
  const from = accounts[0];
  const contract = new web3.eth.Contract(abi);

  const instance = await contract.deploy({ data: bytecode })
    .send({ from, gas: 2000000 });

  console.log('Deployed at:', instance.options.address);
  fs.writeFileSync('./contract-address.txt', instance.options.address);
  return instance.options.address;
}

deploy().catch(console.error);

本地测试节点通常会解锁账户,因此脚本可以直接使用from发送交易。生产环境如果接入真实节点,最好不要长期解锁账户,改用私钥签名交易或借助钱包服务。部署完成后,把地址写入文件是为了让后续脚本不用重新部署。

三、登记结婚与离婚函数的Node.js调用

有了合约地址和ABI之后,就可以在Node.js中构造合约实例。登记结婚需要两个不同账户,分别代表夫妻双方。调用marry方法时,交易从一方账户发出,把另一方地址作为参数传入。发送成功后返回回执,回执的events属性中包含Married事件的参数。

查询婚姻状态属于只读操作,不需要消耗gas,所以用call方法而不是send。因为marriages被声明为public,Solidity会自动生成对应读函数,Node.js可以直接调用contract.methods.marriages(account).call()。返回对象的partner和status字段可以用于界面展示。

离婚调用只需从已婚账户发起,不需要额外参数。合约会根据msg.sender找到配偶地址,然后清除双方记录并写入事件。以下是完整调用脚本:

const Web3 = require('web3');
const fs = require('fs');

const web3 = new Web3('http://127.0.0.1:8545');
const abi = JSON.parse(fs.readFileSync('./DeMarriage.json', 'utf8')).abi;
const address = fs.readFileSync('./contract-address.txt', 'utf8').trim();

async function main() {
  const accounts = await web3.eth.getAccounts();
  const a = accounts[0];
  const b = accounts[1];
  const contract = new web3.eth.Contract(abi, address);

  const receipt = await contract.methods.marry(b)
    .send({ from: a, gas: 200000 });

  console.log('Married event:', receipt.events.Married.returnValues);

  const mine = await contract.methods.marriages(a).call();
  console.log('Partner:', mine.partner, 'Status:', mine.status);

  const divorceReceipt = await contract.methods.divorce()
    .send({ from: a, gas: 200000 });

  console.log('Divorced event:', divorceReceipt.events.Divorced.returnValues);
}

main().catch(console.error);

如果账户顺序不确定,可以先从节点获取账户列表,再把第一个和第二个分别作为双方。测试时若账户未解锁,发送交易会报错,这是链下环境最常见的配置问题。

四、事件监听与错误排查方向

除了等待回执里的events,还可以用contract.events.Married()创建一个订阅,当新婚姻登记上链时收到通知。事件订阅通常从最新区块开始,如果需要回放历史记录,可以调用getPastEvents按过滤条件查询。

async function listen() {
  const contract = new web3.eth.Contract(abi, address);
  contract.events.Married({ fromBlock: 'latest' })
    .on('data', (event) => {
      console.log('New marriage:', event.returnValues.left, event.returnValues.right);
    })
    .on('error', console.error);
}

开发中常见的问题包括gas上限不足、ABI与合约字节码不匹配、nonce冲突等。node端如果直接复用同一个nonce发送多笔交易,容易出现替换交易或报错。建议每个交易发出后等待回执,再继续下一条,本地测试阶段尤其要注意。

DeMarriage这类合约还可以扩展成双方共同确认解除、设置冷静期、加入证人地址等更贴近现实婚姻登记的机制。Node.js端只需要根据新增的方法调整参数和事件名称,整体调用模式仍然一致。把状态、事件、链下脚本三者分开理解,后续开发其他业务合约也会顺手很多。

Node.jsDeMarriage智能合约修改时间:2026-09-30 15:43:48

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0930/63877.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。