深入研究审计竞赛分析和经济学

morpheus

版主

morpheus Rep
0
0
0
Rep
0
morpheus Vouches
Vouches
0
8 MONTHS
8 8 MONTHS OF SERVICE
morpheus 获得
0
0
0
获得
0
morpheus 凭证
0
0
0
凭证
0
主题
15
点赞
0
HakB
8
在社区成长 8 月
LEVEL 1 1 XP
 
自Code4rena 将竞争性审计引入智能合约安全领域以来已经有几年了,而且审计竞赛似乎将继续存在。与此同时,其他几个具有相同论坛的平台也出现了。
审计竞赛很简单。一个项目发布了一组他们希望对其进行审计的智能合约,并承诺为安全相关的发现提供奖金池。竞赛会持续几周,在此期间参与者可以提交他们的发现。然后根据调查结果的严重性和独特性分配奖金池。
审计竞赛在各个方面都取得了巨大的成功。除了从代码竞赛报告中受益的许多项目之外,他们还为新的安全研究人员提供了培训和证明自己的机会。许多独立审核员都是通过参加审核竞赛开始他们的职业生涯的。
这篇文章最好的部分之一: 所有结果都可用,并且已经积累了足够的数据来进行一些有趣的分析并回答一些紧迫的问题。
让我们深入研究一下代码竞赛数据的一些分析!

问题​

我们可以问的问题太多了,在这篇文章中我们将重点讨论两个主要问题:
(覆盖率集)我们实际上需要多少人才能找到所有错误?
(边际价值)每增加一个参与者,我们就能增加多少价值?什么是最佳点?
这两个问题都旨在让我们深入了解审计竞赛模型的经济效率。

数据​

我们提取了c4 最近3 年的审计报告。因此,您将在本文中读到的结果主要适用于code4rena。也就是说,替代代码竞赛平台是相似的,因此从这些数据中学到的知识可能仍然适用。
# block-description: 我们是来自c4 的数据。它包含所有竞赛的所有结果以及每个竞赛的奖励分配。
导入数学
进口熊猫
将pandas 导入为pd
导入mplcatpuccin
将matplotlib 导入为mpl
mpl.style.use('摩卡')
data=pandas.read_json('reports.json',typ='series')
Normalized_base=pd.json_normalize(数据)
# 过滤没有结果的行(不是列表,空列表)
Normalized_base=Normalized_base[normalized_base['findings'].apply(lambda x: type(x) 是列表且len(x) 0)]

覆盖范围集​

让我们从第一个问题开始: 我们实际上需要多少人才能找到所有错误?或者换句话说,所有参与者中最少有多少人能够发现比赛中的所有错误。这个问题将使我们初步了解竞赛模型的效率。
不相信?
审计竞赛的核心是资源分配过程。它采用激励措施让审计师花时间在一个项目上,并遵循“更多的人更多的发现”的核心假设。
“更多的人有更多的发现”固然很好,但如果我们能用更少的人找到相同数量的错误呢?我们会节省多少钱?在审计中发现所有错误的最小审计员群体就是我们所说的“覆盖范围”。
示例场景: 有100 名参与者,我们知道只需要10 名参与者(覆盖集)即可找到所有错误。我们可以跳过比赛,直接支付10 美元。
尽管在比赛开始之前不可能知道覆盖率集,但历史覆盖率集的大小可以很好地指示比赛模型的最佳效率。

计算覆盖率集​

计算覆盖范围很容易!我们可以将此问题编码为优化问题,并让求解器(cvxpy) 找到us: 的解决方案
# block-description: 一种查找作者最小覆盖率集的算法
将cvxpy 导入为cp
将numpy 导入为np
defsmallest_coverage_set(set_of_sets):
# 展平并识别集合中的所有独特元素
all_elements=set().union(*set_of_sets)
element_to_index={element: i for i,枚举中的元素(all_elements)}
n_elements=len(所有_elements)
# 为通用集中的每个元素创建一个二进制变量
x=cp.Variable(n_elements, boolean=True)
# Constraints: 确保至少选择每组中的一个元素
约束=[]
对于set_of_sets: 中的s
indexs=[element_to_index[element] for s 中的元素]
constraints.append(cp.sum(x[索引])=1)
# Objective: 最小化二元变量的总和(最小化选择的元素数量)
目标=cp.Minimize(cp.sum(x))
#解决问题
问题=cp.Problem(目标, 约束)
问题.解决()
#检索解决方案
解决方案=np.round(x.value).astype(bool)
selected_elements=[元素对元素,如果选择则在zip(all_elements, 解决方案) 中选择]
返回选定的元素
# block-description: 一个简单的实用函数,用于从标题中提取日期
def extract_date(标题):
# 标题格式: yyyy-mm-title,取年份和月份并返回日期时间
日期=title.split('-')[:2]
返回pd.to_datetime('-'.join(date))
# block-description: 测量覆盖集分析
结果=[]
对于索引,在normalized_base.iterrows(): 中报告
如果report.keys()中没有“发现”:
继续
标题=报告['标题']
if type(report['findings']) 不是list:
继续
调查结果=pd.json_normalize(报告['调查结果'])
如果结果.empty:
继续
set_of_authors=调查结果[调查结果['作者'].notna()]
set_of_authors=set_of_authors.explode('作者')
set_of_authors=set_of_authors['作者'].unique()
# set_of_authors.count()
# 自己发现问题的人
调查结果['author_count']=调查结果['作者'].apply(lambda x: len(x) 如果x 不是None else 0)
single_author_findings=调查结果[调查结果['author_count']==1]
unique_finders=set(single_author_findings.explode('authors')['authors'].unique())
author_tgts=[ e for e in discovery['authors'][findings['authors'].notna()].tolist() if e]
如果不是author_tgts:
继续
最小覆盖率=最小覆盖率集(author_tgts)
result.append({'title': 标题,'日期': extract_date(title),'authors': set_of_authors,'coverage_set':smallest_coverage})
结果=pd.DataFrame(结果)
# block-description: 绘制作者数量、覆盖集中的作者数量以及两者之间的百分比差异
将matplotlib.pyplot 导入为plt
结果['n_authors']=结果['作者'].apply(lambda x: len(x))
结果['n_coverage']=结果['coverage_set'].apply(lambda x: len(x))
结果['coverage_diff']=结果['n_authors'] - 结果['n_coverage']
结果['coverage_diff_pct']=结果['coverage_diff']/结果['n_authors'] * 100
# 结果['日期']=pd.to_datetime(结果['日期'])
# 只选择日期、n_authors、n_coverage、coverage_diff
plot_data=结果[['日期', 'n_authors', 'n_coverage', 'coverage_diff', 'coverage_diff_pct']]
plot_data=plot_data.sort_values(by='日期')
plot_data=plot_data.groupby('日期').mean()
# 删除最后一个不完整的条目
绘图数据=绘图数据[:-1]
# n_authors、n_coverage、coverage_diff 的人类可读标签
plot_data=plot_data.rename(columns={'n_authors': '作者数量', 'n_coverage': '覆盖范围内的作者数量', 'coverage_diff_pct': '覆盖范围差异%'})
从statsmodels.tsa.stattools 导入adfuller
打印('''
以下是增强迪基-富勒测试的结果。原假设是数据是非平稳的。如果p 值小于0.05,我们拒绝原假设并得出数据平稳的结论。
''')
result=adfuller(plot_data['覆盖范围内的作者数量'])
print(f'覆盖集中的作者数量为{'not' if result[1] 0.05 else ''}stationary.p-value: {result[1]}')
结果=adfuller(plot_data['作者数量'])
print(f'作者数量为{'not' if result[1] 0.05 else ''}stationary.p-value: {result[1]}')
如果结果[1] 0.05:
print(f'作者数量的趋势为每月{plot_data['Number of Authors'].diff().mean()}')
以下是增强迪基-富勒测试的结果。原假设是数据是非平稳的。如果p 值小于0.05,我们拒绝原假设并得出数据平稳的结论。
覆盖范围内的作者数量是固定的。 p 值: 0.03455077312769427
作者的数量并不是固定的。 p 值: 0.2169667120943446
作者数量趋势为每月1.1870967741935483

覆盖结果​

在下图中,我们可以比较每次竞赛的作者数量以及覆盖范围的大小。
我们可以观察到两件事:
作者的数量随着时间的推移而不断增长。
覆盖范围内的作者数量保持不变。
这很有趣!作者的数量几乎翻了一番,但我们的覆盖范围保持不变。这强烈表明存在更有效的资源分配的机会。根据我们的迪基马云惹不起马云富勒测试的结果,我们可以看到这些观察结果具有统计显着性。
请注意,我们不考虑那些没有归因于自己的问题的作者,当作者只提交低严重性或非关键问题时,通常会出现这种情况。这是我们数据集的一个产物,并不总是将低度和非关键问题归为作者。从我们的分析中省略低级和非关键问题实际上可能是一件好事。这就是数据集为我们提供的!
plot_data.plot(y=['作者数量', '覆盖范围内的作者数量', '覆盖范围S 的差异
 
顶部