从校招上岸到在家撸链:一个普通CS本科生的技术探索实录
哈喽大家好,我是小林,普通一本CS专业大四老油条一枚。去年秋招靠死磕算法题+几段勉强拿得出手的项目,侥幸拿下了一家Web3初创公司的offer,现在正远程居家办公,一边等入职流程走完,一边在公司测试网里“搬砖”。说真的,刚收到offer那会儿我还以为自己终于能躺平了,结果没想到,入职前就被安排了个“热身任务”——重构公司内部的一个轻量级区块链存证模块。
今天这篇水文,就来聊聊我在搞这个项目过程中踩过的坑、学到的招,顺便吐个槽:原来不是所有“区块链项目”都像白皮书里写的那么高大上,有时候它就是你和一堆奇怪报错搏斗到凌晨三点的日常。
起因:被“信任”逼出来的技术债
事情是这样的。我们团队做的其实是个面向中小企业的电子合同平台,核心需求之一是“防篡改+可追溯”。早期为了赶MVP(没错,又是那个万恶的MVP),后端直接用MySQL加时间戳+SHA256搞了个“伪不可篡改”方案。结果去年双11期间,有个客户较真了:“你们这玩意儿能上链吗?我们审计要的是真正的区块链存证。”
老板一听,立马拍板:“上!必须上!” 于是锅就落到了我头上——毕竟我是团队里唯一简历上写过“熟悉Solidity”的人(其实只是课程设计做过一个玩具DApp)。
🤡 自嘲时刻:面试时吹过的牛,终究要用加班来还。
技术选型:别一上来就ETH主网
刚开始我也热血沸腾,想直接梭哈以太坊。但一算Gas费,瞬间冷静——客户合同动辄成千上万份,每份存一次就得花几刀?怕不是要被财务拉去喝茶。
后来和架构师对齐了一下,决定采用 私有联盟链 + IPFS 内容寻址 的混合方案:
- 共识层:用 Hyperledger Fabric(公司已有K8s集群,部署方便)
- 存储层:合同原文扔 IPFS,只把 CID(Content Identifier)和元数据写链
- 验证层:提供 REST API 供前端调用,返回 Merkle Proof
这样既能满足“不可篡改”的合规要求,又能控制成本。最关键的是——不用给用户钱包充ETH,产品经理听了直呼内行。
开发过程:从“Hello World”到线上P0事故
坑点1:Fabric 链码调试比找对象还难
第一次写 Go 链码(Chaincode),我以为和写普通微服务差不多。结果本地 peer chaincode invoke 成功,一上测试网就报:
Error: endorsement failure during invoke. response: status:500 message:"failed to execute transaction: timeout"
查了一晚上,才发现是 Docker 容器网络隔离 导致链码容器无法访问 CouchDB。解决方案是在 docker-compose.yaml 里显式声明 network_mode: "host"。这种玄学问题,文档里根本不会提,全靠 GitHub issue 里老哥们的血泪经验。
坑点2:IPFS 的“永久存储”是个美丽的误会
我们原以为 IPFS 能自动持久化内容,结果发现节点重启后,某些 CID 就 404 了。后来才明白:IPFS 默认只缓存最近访问的内容,除非你 pin 它。
于是在服务启动时加了段逻辑:
// 启动时自动 pin 所有已存证的 CID
func PinAllExistingCIDs(ipfsClient *shell.Shell) {
contracts, _ := db.FindAllContractMeta()
for _, c := range contracts {
if err := ipfsClient.Pin(c.CID); err != nil {
log.Errorf("Failed to pin CID %s: %v", c.CID, err)
// 这里不能panic,否则服务起不来
}
}
}
虽然有点 dirty,但至少保住了数据不丢。运维大哥看到这段代码后默默给我点了杯奶茶,大概意思是“谢天谢地你没让客户数据蒸发”。
代码结构:可读性比炫技更重要
作为一个即将转正的萌新,我深知:能跑的代码不等于好代码。这次我特意做了几件事:
- 链码函数命名清晰:比如
StoreEvidence而不是doIt - 错误统一包装:所有链码错误返回自定义 code(如
ErrInvalidSignature = 51001) - 关键操作加日志上下文:用
context.WithValue(ctx, "tx_id", txID)串起全链路
下面是一段简化后的存证逻辑:
// StoreEvidence 存证合同哈希及元数据
func (s *SmartContract) StoreEvidence(ctx contractapi.TransactionContextInterface,
contractHash string, signerPubKey string) error {
// 1. 验签(省略细节)
if !verifySignature(contractHash, signerPubKey) {
return fmt.Errorf("ErrInvalidSignature: signature mismatch")
}
// 2. 构建存证记录
evidence := Evidence{
Hash: contractHash,
Timestamp: time.Now().Unix(),
Signer: signerPubKey,
Version: "v1",
}
// 3. 写入世界状态
evidenceBytes, _ := json.Marshal(evidence)
return ctx.GetStub().PutState(contractHash, evidenceBytes)
}
别笑,就因为这段代码加了注释和错误码,上周五Code Review时,隔壁组的老哥居然夸我“有工程sense”——要知道他平时连 console.log 都懒得删的人啊!
性能与成本对比:现实很骨感
上线前我们做了压测,结果如下(测试环境:4核8G x3 节点):
| 方案 | TPS | 平均延迟 | 单次成本估算 |
|---|---|---|---|
| 原MySQL方案 | 1200 | 8ms | $0.0001 |
| Fabric+IPFS | 45 | 220ms | $0.003 |
| ETH主网(模拟) | ~15 | 15s+ | $2.5+ |
说实话,性能掉得有点惨。但客户愿意为“合规”买单,而且我们通过批量提交(把一天的存证打包成一个交易)把成本压下来了。这也让我意识到:技术方案没有银弹,只有权衡(trade-off)。
求职启示:项目深度 > 技术广度
回头想想,如果我没在简历里写那个玩具DApp,可能根本拿不到这个offer。但真正让我站稳脚跟的,不是“会Solidity”,而是解决问题的能力:
- 遇到Fabric超时,知道从网络层查起
- 发现IPFS丢数据,立刻想到pin机制
- 被测试怼“接口没做幂等”,连夜加上防重token
这些经验,远比背八股文有用。所以给还在求职的学弟学妹一句真心话:别堆砌技术栈,深挖一个项目,讲清楚你解决了什么、为什么这么解、有没有更好的解——面试官真的会眼前一亮。
写在最后:代码之外,还有生活
上周终于搞定验收,我瘫在椅子上刷知乎,看到有人问:“区块链是不是凉了?” 我笑了笑,关掉网页,去阳台浇了浇我妈养的绿萝。
技术永远在变,ETH 2.0、Move语言、零知识证明……追不完的热点。但不变的是:用技术解决真实问题的踏实感。哪怕你现在写的只是公司内部的一个小工具,只要它让某个流程少出一次错、让某个同事少加一次班,那就值得骄傲。
对了,下周正式入职,据说团建要去露营(远程团队居然要线下团建?)。希望别让我在帐篷里 debug 智能合约吧……
🚀 共勉:愿你的commit都有意义,你的merge都不冲突,你的工资准时到账。

评论 0