我们的DeFi项目线上出了一次故障,差点造成用户资金损失,整个过程惊心动魄。本文是这次故障的完整复盘,包括故障发生的经过、应急响应过程、根因分析、影响评估、以及改进措施。如果你在做DeFi项目,或者对区块链安全感兴趣,希望这次复盘能给你一些警示和参考。
一、项目背景
先说说我们的项目情况。
我们的项目是一个DeFi借贷协议,用户可以存入资产作为抵押,借出其他资产。协议部署在以太坊上,智能合约用Solidity编写,经过了第三方安全审计。项目上线了几个月,运行一直很稳定,锁仓量(TVL)大概在5000万美元左右。
团队有5个人,2个智能合约开发,2个前端开发,1个运维。因为是小团队,每个人都身兼数职,没有专门的安全人员。
这次故障发生在一个周末的晚上,现在回想起来,整个过程就像一场噩梦。
二、故障发生
故障发生在周六晚上11点多。
我当时已经准备睡觉了,突然手机收到了一条告警,说我们的借贷协议的清算功能出现了异常,有几笔清算交易失败了。
我一开始没太在意,以为是网络拥堵导致的交易失败。但是过了几分钟,又收到了几条告警,而且用户在Discord和Telegram群里开始提问,说清算功能用不了了。
我赶紧打开电脑,查看链上数据。发现确实有问题:我们的清算合约调用一直失败,而且失败的原因都是同一个:"revert"。
这时候我意识到问题可能比较严重了。清算功能是DeFi借贷协议的核心功能,如果清算不能正常进行,当抵押品价格下跌的时候,就无法及时清算,可能会造成坏账,也就是用户借的钱还不上,协议会蒙受损失。
我赶紧叫醒了另外两个开发,开了个紧急会议,开始排查问题。
三、应急响应
我们的应急响应过程大概是这样的。
第一步:确认问题
我们首先确认了问题确实存在。在测试环境复现了清算失败的问题,确认不是网络问题,而是合约本身的问题。
我们查了一下最近的部署记录,发现当天下午我们部署了一个新的合约版本,做了一个小的优化。问题很可能出在这次部署上。
第二步:暂停服务
确认问题之后,我们立刻决定暂停协议的新借款功能,防止问题扩大。我们的合约有暂停功能(Pause),可以在紧急情况下暂停某些功能。
我们通过多签钱包调用了暂停函数,暂停了新借款和新存款功能。已经存在的借款和存款不受影响,还款和取款功能依然正常。
暂停之后,我们在官方渠道发布了公告,告诉用户协议暂时暂停了部分功能,我们正在排查问题,会尽快恢复。
第三步:回滚合约
因为问题很可能是下午的新部署导致的,我们决定回滚到上一个稳定版本。
回滚的过程比较复杂,因为DeFi合约有代理模式,逻辑合约可以升级,但是存储不能丢。我们需要把代理合约指向旧的逻辑合约,同时确保用户的资金和数据不受影响。
我们在测试环境反复验证了回滚的流程,确认没问题之后,在主网执行了回滚。回滚之后,清算功能恢复了正常。
第四步:验证和监控
回滚之后,我们在测试环境和主网都做了充分的验证,确认所有功能都正常。然后恢复了暂停的功能,重新开放了借款和存款。
之后我们安排了人24小时监控,密切关注协议的运行情况,确保没有其他问题。
整个应急响应过程从晚上11点多到凌晨3点多,持续了4个多小时。好在发现及时,处理得当,没有造成用户资金损失。
四、根因分析
故障处理完之后,我们做了详细的根因分析。
直接原因
直接原因是下午部署的新合约版本中,有一个函数的逻辑写错了。我们在优化清算函数的时候,修改了一个条件判断,把>=写成了>。这个小小的改动,导致当抵押品价值刚好等于清算阈值的时候,清算函数会revert,无法执行清算。
这个问题在测试的时候没有发现,因为我们的测试用例都是抵押品价值明显低于阈值的情况,没有测试刚好等于阈值的边界情况。
深层原因
除了直接的代码错误,我们还分析了一些深层的原因:
- 测试不充分:我们的单元测试和集成测试没有覆盖边界情况,特别是等于阈值的情况。而且没有做模糊测试和形式化验证。
- 代码审查不严格:这次改动很小,只有一行代码的修改,代码审查的时候没有仔细看,觉得是小改动不会有问题。
- 部署流程不规范:部署是在周五下午做的,而且部署之后没有充分的验证就直接上线了。周末人少,出了问题响应不及时。
- 监控不完善:我们的监控只监控了交易失败率,没有监控具体的失败原因。如果能更早地发现清算函数的revert原因,就能更早地发现问题。
- 没有专门的安全人员:团队小,没有专门的安全人员,安全工作都是开发兼职做的,不够专业和系统。
五、影响评估
我们对这次故障的影响做了评估。
资金影响
好消息是,这次故障没有造成用户资金损失。因为问题只影响清算功能,而且只在抵押品价值刚好等于阈值的时候才会触发,实际遇到这种情况的交易很少。而且我们发现及时,很快就回滚了,没有造成坏账。
用户影响
有少数用户的清算交易失败了,但是回滚之后都可以正常执行了。暂停服务的几个小时里,有一些用户想借款但是借不了,但是时间不长,影响不大。
我们在官方渠道做了详细的说明和道歉,大部分用户都表示理解。
声誉影响
这次故障对项目的声誉有一定的影响,毕竟DeFi项目出故障是很敏感的事情。有一些用户表示了担忧,也有一些媒体做了报道。但是因为我们处理及时、透明,而且没有造成资金损失,大部分用户还是信任我们的。
团队影响
这次故障对团队的触动很大。大家都意识到了DeFi安全的重要性,也意识到了我们在流程和规范上的不足。故障之后,团队做了很多改进,安全意识大大提升。
六、改进措施
故障之后,我们做了一系列的改进措施。
1. 加强测试
- 增加了边界情况的测试用例,特别是等于、小于、大于阈值的情况
- 引入了模糊测试(Fuzz Testing),用随机输入来发现隐藏的bug
- 对核心合约做形式化验证,用数学方法证明合约的正确性
- 增加了集成测试和端到端测试,覆盖完整的用户流程
2. 严格代码审查
- 所有代码改动,不管多小,都必须经过至少两个人的审查
- 核心合约的改动,需要有安全经验的人审查
- 代码审查要有checklist,逐项检查,不能走过场
- 引入了静态代码分析工具,自动发现常见的安全问题
3. 规范部署流程
- 部署只能在工作日的上午做,不能在周五下午或者周末做
- 部署之前必须有完整的测试报告和代码审查记录
- 部署之后要有充分的验证时间,在测试环境和主网都要验证
- 部署之后24小时内要有专人监控,确保没有问题
- 制定了详细的回滚预案,出了问题可以快速回滚
4. 完善监控和告警
- 增加了对合约函数调用的监控,特别是核心函数
- 监控具体的失败原因,不只是失败率
- 设置了更灵敏的告警阈值,异常情况及时通知
- 建立了仪表盘,可以实时查看协议的运行状态
- 增加了链上数据的监控,及时发现异常的链上活动
5. 加强安全建设
- 招聘了专门的安全人员,负责安全审计和漏洞响应
- 建立了漏洞赏金计划,鼓励白帽子发现和报告漏洞
- 定期做安全审计,每次大的改动都要做第三方审计
- 建立了应急响应预案,明确了故障发生时的处理流程和责任人
- 团队定期做安全培训,提升安全意识
6. 提升透明度
- 故障之后,我们发布了详细的复盘报告,向社区公开故障的原因、影响和改进措施
- 建立了定期的透明度报告制度,定期向社区公布协议的运行情况和安全状况
- 设立了社区监督机制,让社区可以参与协议的治理和监督
七、经验总结
这次故障给了我们很多教训,也总结了一些经验。
1. DeFi安全无小事
DeFi项目管理着用户的真金白银,安全是第一位的。任何小小的代码错误,都可能造成巨大的资金损失。不能因为改动小就掉以轻心,每一行代码都要认真对待。
2. 测试是安全的基础
充分的测试是发现bug的最有效手段。单元测试、集成测试、模糊测试、形式化验证,各种测试手段都要用起来。特别是边界情况和异常情况的测试,最容易出问题的地方就是边界。
3. 流程比人更可靠
不能依赖人的细心和经验,要靠流程和规范来保证质量。代码审查、部署流程、监控告警,这些流程要严格执行,不能因为忙或者觉得没问题就跳过。
4. 应急响应能力很重要
不管做得多好,故障还是可能发生。重要的是要有快速响应和处理故障的能力。监控要灵敏,响应要及时,处理要果断,回滚要熟练。这样才能把故障的影响降到最低。
5. 透明度建立信任
出了故障不要藏着掖着,要及时、透明地向社区公布情况。用户最担心的不是出问题,而是出了问题不知道。坦诚地面对问题,详细地说明原因和改进措施,反而能赢得用户的信任。
6. 小团队更要重视安全
小团队人少,资源有限,更容易在安全上投入不足。但是正因为小,出了问题更难承受,所以更要重视安全。可以从最基础的做起,比如加强测试、严格代码审查、规范部署流程,这些不需要很多资源,但是能大大提升安全性。
八、写在最后
这次DeFi故障是一次惊心动魄的经历,现在回想起来还有点后怕。如果发现得晚一点,如果回滚不顺利,如果造成了用户资金损失,后果不堪设想。
但是这次故障也给了我们很大的成长。团队的安全意识大大提升,流程和规范更加完善,项目的安全性也大大提高。从某种意义上说,这次故障是一次宝贵的教训,让我们在还没有造成大损失的时候就发现了问题,及时改进。
DeFi是一个新兴的领域,发展很快,但是安全问题也很突出。每年都有很多DeFi项目因为安全问题被黑客攻击,造成用户资金损失。作为DeFi开发者,我们有责任把安全放在第一位,保护好用户的资金。
希望我们的这次复盘能给其他DeFi项目一些警示和参考。不要等出了问题才重视安全,要在平时就把安全工作做好。安全是DeFi的生命线,怎么重视都不为过。
最后用一句话结束本文:"安全无小事,责任大于天。"愿每一个DeFi项目都能安全运行,每一个用户的资金都能得到保护。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录