我的博客

2019年创青春·交子杯新网银行高校金融科技挑战赛-分布式算法赛道 判题代码阅读

目录
  1. 流程图
    1. 主流程(左) & 接收和处理消息 (右)
    2. MessageScheduler::onMessageRecv(Json message, int sourceId)
    3. void Logic::ChannelOp::build(Json message)
  2. 代码解读
    1. main.cpp
    2. GlobalUtil.cpp
    3. conn\Connection.cpp
    4. scheduler\Logic.cpp
    5. scheduler/Judger.cpp
    6. scheduler/LogicMessageGen.cpp
    7. scheduler/MessageScheduler.cpp
    8. scheduler/LogicChannel.cpp
    9. topology/Network.cpp
    10. topology/Channel.cpp

流程图

主流程(左) & 接收和处理消息 (右)

graph TD
    start(开始) --> load_config[载入配置]
    load_config --> load_test_data[载入测试数据]
    load_test_data --> wait_node[等待节点连接]
    wait_node --> init_clock[初始化计时器]
    init_clock --> recv_process[接收和处理消息]
    recv_process --> send[发送到时间的消息]
    send --> sleep[休眠 10 毫秒]
    sleep --> recv_process

    start2(开始) --> recv[遍历socket]
    recv --> afterRecv[Logic::afterRecv]
    afterRecv --> onMessageRecv[scheduler.onMessageRecv]
    onMessageRecv --> tick[scheduler.tick]
    tick --> sleep2[休眠 10 毫秒]
    sleep2 --> check_time{是否到结束时间}
    check_time --否--> recv
    check_time --是--> finsih[结束]

MessageScheduler::onMessageRecv(Json message, int sourceId)

graph TD
    start(开始) --> set_sourceId
    set_sourceId --> after_recving[judger->afterRecvingCheck]
    after_recving --> check_msg_type{判断类型}
    check_msg_type --> 建通道
    建通道 --> build["Logic::ChannelOp::build(message)"]
    check_msg_type --> 销毁通道
    销毁通道 --> des[judger->onChannelDestroy]
    des --> d[ChannelOp::destroy]
    check_msg_type --> 其他
    check_msg_type --> 非法
    非法 --> 返回错误

void MessageScheduler::onMessageRecv(Json message, int sourceId)

  1. 设置 sourceId
  2. 调用 judger->afterRecvingCheck
  3. 对于 CALL_TYPE_CHANNEL_BUILD 执行 Logic::ChannelOp::build
  4. 对于 CALL_TYPE_CHANNEL_DESTROY
    1. 调用 conn::judger->onChannelDestroy(channel, message);
    2. 执行 Logic::ChannelOp::destroy(message);
  5. 对于其他合法的交易类型
    1. 调用 conn::judger->onChannelUsing(channel, message)
    2. 调用 conn::judger->onMessageRecv(message);
    3. 调用 Logic::route(message);
  6. 消息类型非法,返回错误

void Logic::ChannelOp::build(Json message)

graph TD
    start(开始) --> check_msg_type{判断类型}
    check_msg_type --> 通道请求
    check_msg_type --> 同意请求
    check_msg_type --> 拒绝请求
    check_msg_type --> 非法情况

void Logic::ChannelOp::build(Json message)

  1. 收到通道请求时
    1. 调用 conn::judger->onChannelPrepare(message)
    2. 调用 network.prepareToBuild(message);
    3. 调用 Logic::MessageGen::sendBuildMessage
  2. 收到同意通道请求时
    1. 调用 network.recvPrepareMessage(message);
    2. 调用 Logic::MessageGen::sendBuildMessage
  3. 收到通道拒绝时
    1. 调用 network.deletePrepareMessage(message);
    2. 调用Logic::MessageGen::sendBuildMessage 发送拒绝消息
  4. 收到其他(非法情况)
    1. 返回非法消息

代码解读

main.cpp

调用初始化,启动主循环

GlobalUtil.cpp

  1. 初始化方法(计时器、清空算分文件)
  2. 获取当前时间函数
  3. 写入 attach.log 即算分文件(包含时延计算)

conn\Connection.cpp

  1. conn::init(std::string initFilename)

    载入配置文件、加载测试数据(插入到 scheduler 的优先队列里)

  2. conn::waitConns()

    等待客户端连接、分配客户端 ID 和最大通道数

  3. conn::mainloop()

  4. conn::send(Json message)

    1. 调用 judger->beforeSendingCheck 检查消息是否合法
    2. 调用 setLog
  5. conn::setLog(Json message)

    这里有个严重 bug 可以导致任意消息一跳即可被判为送达。这里和 judger->beforeSendingCheck 都没有判断消息的 [“sysMessage”][“target”] 字段是否被篡改。客户端只需要修改此字段,使之与 targetId 一致,就可以骗过判题机,使之认为消息已经送到目的地。但是实际的消息目的地已经被篡改。我通过这个漏洞构造的脚本在判题机上得到 1165.954102 的高分,并第一时间联系了比赛主办方,告知漏洞细节,并申请删除了这个异常分数。

    这个 bug 已于 11 月 5 日被官方修复。

    1. 如果是 send 消息,而且 targetId 与 sysMessage 的 target 一致就调用 GlobalUtil::attach 写入 attach.log 算分文件

scheduler\Logic.cpp

  1. Json Logic::beforeSend(Json message)

    发送之前确保消息只包含指定的键:”callType”,”channelId”,”sysMessage”,”extMessage”,”state”,”errCode”,”channelType”。感觉似乎用处不是很大。

  2. Json Logic::afterRecv(Json message)

    为消息增加接收时间字段,这个函数在 server\conn\Connection.cpp 的 void conn::mainloop() 函数中被调用过,但是并没什么用。

  3. Json Logic::toErrMessage(Json message, int errCode)

    根据错误码构造错误信息

  4. void Logic::route(Json message)

    确认通道存在,根据通道时延把消息加入消息队列(添加 timestamp 属性),这里不会校验 targetId 和 channelId 的关系。

    会抛出 ERR_CODE_NO_SUCH_CHANNEL 错误

scheduler/Judger.cpp

这个对象里存了 每个节点剩余可用通道数、全局剩余通道数。

  1. int Judger::checkChannelCount(Json message)

    用于检查一下错误

    1. ERR_CODE_CHANNEL_BUILD_TOTAL_LIMIT 通道总数达到上限
    2. ERR_CODE_CHANNEL_BUILD_SOURCE_LIMIT 申请建通道者通道数达到上限
    3. ERR_CODE_CHANNEL_BUILD_TARGET_LIMIT 通道目标通道数达到上限
  2. void Judger::onChannelPrepare(Json message)

    调用上面的函数 1,判断通道建立信息是否合法

  3. void Judger::onChannelBuild(Json message, Channel *channel)

    这里还会再调用一次上面的函数 1。以确保通道数量合法。然后会减去通道数量。

  4. void Judger::onChannelDestroy(Channel * channel, Json message)

    增加可用通道数量

  5. void Judger::onMessageRecv(Json message)

    1. 判断是否达到通道接收消息上限
    2. 判断消息 extMessage 大小是否超过限制
    3. 减少通道可发送消息数量(messageCount[channelId])
  6. void Judger::onMessageSend(Json message)

    增加 messageCount[channelId] ,这个函数被 MessageScheduler::onMessageSend 调用 MessageScheduler::onMessageSend 被 conn::send(Json message) 调用,也就是说,发送一个消息后,这个通道上的可用消息数会加一

  7. void Judger::onChannelUsing(Channel *channel, Json message)

    判断是接收发送方和通道是否匹配

  8. bool Judger::beforeSendingCheck(Json message)

    产生 ERR_CODE_NO_SUCH_CHANNEL 异常,判断指定通道是否存在

  9. bool Judger::afterRecvingCheck(Json message)

    产生 ERR_CODE_NO_SUCH_CHANNEL 异常,判断指定通道是否存在

scheduler/LogicMessageGen.cpp

  1. void Logic::MessageGen::sendBuildMessage

    发送通道创建相关消息

  2. void Logic::MessageGen::sendDestroyMessage

    发送通道销毁相关消息

scheduler/MessageScheduler.cpp

  1. MessageScheduler::MessageScheduler()

    就一句话 network = new Network();

  2. void MessageScheduler::addMessage(Json message)

    把消息加入时间队列

  3. void MessageScheduler::onMessageRecv(Json message, int sourceId)

    1. 设置 sourceId
    2. 调用 judger->afterRecvingCheck
    3. 对于 CALL_TYPE_CHANNEL_BUILD 执行 Logic::ChannelOp::build
    4. 对于 CALL_TYPE_CHANNEL_DESTROY
      1. 调用 conn::judger->onChannelDestroy(channel, message);
      2. 执行 Logic::ChannelOp::destroy(message);
    5. 对于其他合法的交易类型
      1. 调用 conn::judger->onChannelUsing(channel, message)
      2. 调用 conn::judger->onMessageRecv(message);
      3. 调用 Logic::route(message);
    6. 消息类型非法,返回错误
  4. void MessageScheduler::onMessageSend(Json message, int targetId)

    调用 conn::judger->onMessageSend(message);

  5. void MessageScheduler::tick()

    调用 conn::send()发送消息队列中到时间的消息

scheduler/LogicChannel.cpp

  1. void Logic::ChannelOp::build(Json message)
    1. 收到通道请求时
      1. 调用 conn::judger->onChannelPrepare(message)
      2. 调用 network.prepareToBuild(message);
      3. 调用 Logic::MessageGen::sendBuildMessage
    2. 收到同意通道请求时
      1. 调用 network.recvPrepareMessage(message);
      2. 调用 Logic::MessageGen::sendBuildMessage
    3. 收到通道拒绝时
      1. 调用 network.deletePrepareMessage(message);
      2. 调用Logic::MessageGen::sendBuildMessage 发送拒绝消息
    4. 收到其他(非法情况)
      1. 返回非法消息
  2. void Logic::ChannelOp::destroy(Json message)
    1. network.getChannel(channelId);
    2. Logic::MessageGen::sendDestroyMessage(channel->getPort(), channelId)
    3. network.destroyChannel(message);

topology/Network.cpp

  1. int Network::addChannel(Json message)

    根据消息创建通道,会调用 conn::judger->onChannelBuild(message, channel)

  2. void Network::destroyChannel(Json message)

    根据消息销毁通道

  3. Channel* Network::getChannel(int channelId)

    从 channelMap 中找到通道

  4. void Network::prepareToBuild(Json message)

    处理创建通道消息

  5. int Network::recvPrepareMessage(Json message)

    调用 deletePrepareMessage 和 addChannel。

  6. void Network::deletePrepareMessage(Json message)

  7. void Network::deletePrepareMessage(int source, int target)

  8. void Network::tick()

    检测创建隧道请求是否超时和发送超时消息

  9. void Network::sendTimeoutMessage(PrepareChannel *channel)

    发送超时消息的实现。

topology/Channel.cpp

  1. Channel::Channel(Json buildMessage)

    根据申请消息创建通道

  2. Json Channel::getConfig(int type)

    获取当前的全局高速或低速通道的配置

  3. Json Channel::getConfig()

    获取当前的通道的种类的通道的全局配置

  4. PrepareChannel::PrepareChannel(int source, int target, float timeout)

    计算超时时间

  5. int PrepareChannel::getHash() const

    通道标识 return source * 10000 + target; 似乎会有重复的

  6. bool PrepareChannel::operator < (const PrepareChannel &channel) const

  7. bool PrepareChannel::isTimeout()

    判断请求消息是否超时

  8. int PrepareChannel::getSource() { return source; }

  9. int PrepareChannel::getTarget() { return target; }

  10. float PrepareChannel::getTimeout() { return timeout; }

评论无需登录,可以匿名,欢迎评论!