兄弟公司区块链教程btcpool矿池源代码分析StratumServer模块分析。2018年下半年,区块链行业正逐渐褪去浮躁,回归发展之初的理性。表面上看,相关人才的需求和价值似乎在回落。但事实上,正是最初泡沫的逐渐消退,才让人们更加关注区块链的真正技术。
# btcpool矿池-StratumServer模块分析
# #核心机制概述
*接收到的延迟超过60秒的作业将被丢弃。
*如果作业中的prevHash与本地作业中的prevHash不同,则已生成新块,并且作业中的isClean状态将设置为true。
* true要求采掘机立即切换作业。
*在三种情况下,一个新的作业将被发布给矿机:
*收到新高度的作业
*过去的工作是新的高度和空的工作,最新的工作是非空的工作。
*达到30秒的预定时间间隔。
*最后一次发出作业的时间将被写入文件(由file_last_notify_time指定)。
*本地作业的有效期为300秒。
*每10秒拉一次新用户列表(由list_id_api_url指定),用户将其写入本地映射。
* s服务器的最大可用SessionId是16777214。
* btcpool支持BtcAgent扩展协议和Stratum协议,以magic_number(0x7F)区分。
*处理地层协议:
* suggest_target相当于suggest _ difference,用来设置初始挖掘难度。它需要在订阅前请求。
*使用sessionID作为extraNonce1_以确保矿机任务不重复。
*授权前15秒、授权后10分钟以及10分钟未提交将断开连接。
*初始难度为16384,或者从suggest _ difference中指定。下次调整到位10s,提交分享。
*每个会话将维护一个队列长度为10的本地作业队列。
*共享被拒绝的几种情况:
* JOB_NOT_FOUND,localJobs_ queue中的作业已被挤出。
* DUPLICATE_SHARE,共享已提交,提交的共享核算包含在submitShares中_
* JOB_NOT_FOUND,jobRepository_中的作业不存在,即作业已过期(过期时间为300秒)
* JOB_NOT_FOUND,jobRepository_中作业的状态是陈旧的,即该作业是旧的而不是新的。
* TIME_TOO_OLD,共享中提交的时间小于作业指定的时间。
* TIME_TOO_OLD,提交的共享时间比当前时间大10分钟。
* LOW _ cavity,share中提交的hash不符合难度目标。
*处理BtcAgent扩展协议:
*代理下矿机默认难度也是16384。
*使用Agent sessionID作为extraNonce2_的前半部分,保证Agent下的挖机任务不重复。
*当矿池发布新任务时,如BtcAgent for session,将为代理下的所有矿机计算难度。
*如果难度发生变化,将根据难度的不同构造多个CMD_MINING_SET_DIFF指令,并一起发出和处理。
## StratumServer命令使用
“贝壳
sserver -c sserver.cfg -l log_dir
#-c指定服务器配置文件
#-l指定日志目录
“`
## sserver.cfg配置文件
“贝壳
//是否使用testnet
testnet=true
//kafka集群
卡夫卡={
经纪人=\’1.1.1.1:9092,2.2.2:9092,3 . 3 . 3:9092 \’;
};
//服务器配置
sserver={
//IP和端口
IP=\’ 0 . 0 . 0 . 0 \’;
端口=3333;
//服务器id,全局唯一,取值范围[1,255]
id=1;
//最后一次挖掘通知时间写入文件进行监控。
file _ last _ notify _ time=\’/work/XXX/s server _ last notify time . txt \’;
//如果模拟器被启用,所有共享都被接受进行测试。
enable _ simulator=false
//如果启用,所有共享都将被分块并提交进行测试。
enable _ submit _ invalid _ block=false;
//两次股份提交之间的时间间隔
share _ avg _ seconds=10
};
用户={
//用户列表api
list _ id _ API _ URL=\’ example . com/get _ user _ id _ list \’;
};
“`
##已解决_分享消息
` ` c
if(isSubmitInvalidBlock _==true | | bnBlockHash=bnNetworkTarget){
//
//生成找到的块
//
FoundBlock foundBlock
found block . jobid _=share . jobid _;
foundblock . workerid _=share . worker hashid _;
found block . userid _=share . userid _;
foundblock . height _=sjob-height _;
memcpy(foundBlock.header80_,(const uint8_t *)header,sizeof(CBlockHeader));
snprintf(foundblock . worker full name _,sizeof(foundblock . worker full name _),
“%s”,work full name . c _ str());
//发送
sendsolvedshare 2卡夫卡(foundBlock,coin basebin);
//将作业标记为过时
作业存储库_-markAllJobsAsStale();
\”日志(信息)\”找到一个新块:“blkHash .ToString()
,jobId: \’ share.jobId_ \’,userId: \’ share.userId_
,由:\”工作全名\”;
}
“`
## 计算挖矿难度
` ` c
//构造函数
//kMinDiff_为最小难度,静态常数uint64 kMinDiff _=64
//kMaxDiff_为最大难度,静态常数uint 64 kMaxDiff _=4611686018427387904 ull;
//kDefaultDiff_为默认初始难度,静态常量uint64 kDefaultDiff _=16384
//kDiffWindow_为普通个分享的时间窗口,静态常数time _ t kDiffWindow _=900
//kRecordSeconds_为一个分享的时间,静态常数time _ t kRecordSeconds _=10
//sharesNum_和股份_初始值均为90
差分控制器(const int 32 _ t shareAvgSeconds):
startTime_(0),
minDiff_(kMinDiff_),curDiff_(kDefaultDiff_),curHashRateLevel_(0),
股份数量_(kDiffWindow _/kRecordSeconds _),/*每隔普通秒作为一条记录*/
shares _(kDiffWindow _/kRecordSeconds _)
{
if(shareAvgSeconds=1 shareAvgSeconds=60){
shareAvgSeconds _=shareAvgSeconds;
}否则{
shareAvgSeconds _=8;
}
}
//代码btcpool/src/StratumSession.h
//计算挖矿难度
//不低于最小难度64
uint 64差分控制器:calcCurDiff(){
uint 64 diff=_ calcCurDiff();
if (diff minDiff_) {
diff=minDiff _;
}
返回差异;
}
uint 64差分控制器:_ calcCurDiff(){
const time _ t now=time(nullptr);
const int 64k=now/kRecordSeconds _;
const double股数=(double)股数\\总和(k)和:
如果(startTime_==0) { //第一次,我们设置开始时间
开始时间_=时间(空指针);
}
常数double kRateHigh=1.40
常数double kRateLow=0.40
//时间窗口(900秒)内预期的分享数
double预期计数=round(kDiffWindow _/(double)shareAvgSeconds _);
//return now=start time _ kDiffWindow _;
if (isFullWindow(now)) { /*现在有一个完整的窗口*/
//大型矿商有较大预期股份数,使其看起来更平稳。
期望计数*=miner系数(现在,k);
}
if (expectedCount kDiffWindow_) {
//最多一秒提交一个,预期分享数最大为900
预期计数=kDiffWindow _;//每股一秒就够了
}
//这是用于非常低哈希速率的矿工,例如通用串行总线矿工
//应该每60秒收到至少一个共享
//非完整时间窗口、且时间已超过60年代提交的分享数小于(60秒一个)、当前难度大于或等于2倍最小难度
//此时降低难度为之前1/2
如果(!isFullWindow(now)now=开始时间_ 60
股份计数=(int 32 _ t)((现在开始时间_)/60.0)
curDiff_=minDiff_*2) {
setCurDiff(curDiff _/2);
sharesNum_ .地图乘法(2.0);
返回curDiff _;
}
//太快了
//如果提交分享数超过预期数的1.4倍时
if(股份计数预期计数* krate高){
//如果分享数大于预期分享数,且当前难度最大难度时,提升难度为原来2倍
while (sharesNum_ .总和(千)预期计数
curDiff_ kMaxDiff_) {
setCurDiff(curDiff _ * 2);
sharesNum_ .mapDivide(2.0);//共享数/2
}
返回curDiff _;
}
//太慢
//如果是完整时间窗口,且当前难度大于或等于2被最小难度
if(is full window(now)curDiff _=minDiff _ * 2){
//如果分享数低于预期数的0.4,且当前难度大于或等于2倍最小难度,降低难度为原来的1/2
while (sharesNum_ .sum(k) expectedCount * kRateLow
curDiff_=minDiff_*2) {
setCurDiff(curDiff _/2);
sharesNum_ .地图乘法(2.0);//共享数乘2
}
assert(curDiff _=minDiff _);
返回curDiff _;
}
返回curDiff _;
}
“`
##服务器校验分享的机制
“贝壳
//本地工作列表本地作业_最多保留最近10条任务,如有新任务,将挤出一条老任务。如果分享所对应的工作未在本地列表中,将StratumError:JOB_NOT_FOUND
//本地分享列表提交股份_中,如果已有本条分享,即重复,将StratumError:DUPLICATE_SHARE
//校验分享不通过
//作业列表exJobs_没有找到作业,exJobs_中工作有300秒过期时间,过期将删除,报StratumError:JOB_NOT_FOUND
//共享中nTime小于工作的最小时间,过老,报StratumError:TIME_TOO_OLD
//共享中nTime超过工作中的时间10分钟分钟,过新,报StratumError:TIME_TOO_NEW
//区块哈希工作难度目标,不合格,报StratumError:低_难度
“`
##服务器下发新工作的机制
1、如果收到新高度状态作业,将立即下发新工作
` ` c
bool isClean=false
if (latestPrevBlockHash_!=sjob-prevHash_) {
isClean=true
latestPrevBlockHash _=sjob-prev hash _;
日志(信息)\”收到新的高度状态作业,高度:\” sjob-height_
,prevhash: \’ sjob-prevHash_ .ToString();
}
shared _ ptrStratumJobEx job=STD:make _ sharedStratumJobEx(sjob,is clean);
{
范围锁sl(lock _);
if (isClean) {
//将所有作业标记为过时,应该在插入新作业之前这样做
for (auto it : exJobs_) {
它。second-mark stale();
}
}
//插入新作业
ex jobs _[sjob-jobId _]=ex job;
}
if (isClean) {
sendMiningNotify(ex job);
返回;
}
“`
2、如果过去一个工作为新高度且为空块工作,并且最新工作非空块工作,将尽快下发新工作
“`
if (isClean==false exJobs_ .size()=2) {
auto itr=exJobs_ .Rb egin();
shared _ ptrStratumJobEx job 1=itr-second;
itr
shared _ ptrStratumJobEx job 2=itr-second;
if (exJob2-isClean_==true
exJob2-sjob_-merkleBranch_ .size()==0
exJob1-sjob_-merkleBranch_ .size()!=0) {
sendMiningNotify(ex job);
}
}
“`
3、每超过一定时间间隔(30秒),将下发新工作
“`
void作业资料档案库:checkAndSendMiningNotify(){
//上一个作业已\”完成\”,发送一个新作业
if (exJobs_ .大小()
lastJobSendTime _ kminingotifyinterval _=time(空指针))
{
shared _ ptrStratumJobEx job=ex jobs _ .Rb egin()-秒;
sendMiningNotify(ex job);
}
}
job repository:job repository(const char * kafkaBrokers,
常量字符串fileLastNotifyTime,
服务器*服务器):
跑步_(真),
kafkaConsumer_(kafkaBrokers,KAFKA_TOPIC_STRATUM_JOB,0/* partition */),
服务器_(服务器),文件最后通知时间_(文件最后通知时间),
kMaxJobsLifeTime_(300),
kminingotifyinterval _(30),//TODO: make as config arg
lastJobSendTime_(0)
{
assert(kminingotifyinterval _ kMaxJobsLifeTime _);
}
“`