分析基于NounsDAO的ERC721Checkpointable.sol,他是基于CompoundGovernance代码修改的。数据结构1.投票权结构///@noticeAcheckpointformarkingnumberofvotesfromagiven
分析基于NounsDAO 的ERC721Checkpointable.sol, 他是基于Compound Governance代码修改的。
/// @notice A checkpoint for marking number of votes from a given block
//当前blockNumber具有多少投票权
struct Checkpoint {
uint32 fromBlock;
uint96 votes;
}
/// @notice 用户投票权记录, by index,相同的blockNumber肯定是相同的index
mapping(address => mapping(uint32 => Checkpoint)) public checkpoints;
/// @notice 用户投票权变化的索引 index
mapping(address => uint32) public numCheckpoints;
/// @notice A record of each accounts delegate
//只有一个映射记录,那么只能记录当前的委托状态
mapping(address => address) private _delegates;
function _writeCheckpoint(address delegatee,uint32 nCheckpoints,uint96 oldVotes,uint96 newVotes) internal {
uint32 blockNumber = safe32(
block.number,
'ERC721Checkpointable::_writeCheckpoint: block number exceeds 32 bits'
);
//相同区块是同一条记录
if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
} else {
//否则新增一条记录,numCheckpoints保存了该地址有多少条记录
checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
numCheckpoints[delegatee] = nCheckpoints + 1;
}
emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
}
function _moveDelegates(address srcRep,address dstRep,uint96 amount
) internal {
if (srcRep != dstRep && amount > 0) {
if (srcRep != address(0)) {
uint32 srcRepNum = numCheckpoints[srcRep];
uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
uint96 srcRepNew = sub96(srcRepOld, amount, 'ERC721Checkpointable::_moveDelegates: amount underflows');
_writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
}
if (dstRep != address(0)) {
uint32 dstRepNum = numCheckpoints[dstRep];
uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
uint96 dstRepNew = add96(dstRepOld, amount, 'ERC721Checkpointable::_moveDelegates: amount overflows');
_writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
}
}
}
function _beforeTokenTransfer(address from,address to,uint256 tokenId) internal override {
super._beforeTokenTransfer(from, to, tokenId);
/// @notice Differs from `_transferTokens()` to use `delegates` override method to simulate auto-delegation
_moveDelegates(delegates(from), delegates(to), 1);
}
//委托给自己,并不会改变存储状态
function delegate(address delegatee) public {
if (delegatee == address(0)) delegatee = msg.sender;
return _delegate(msg.sender, delegatee);
}
//一次委托,终身有效,除非重新委托回来自己——通过一个空地址中转一下(委托一个一个空地址,空地址再委托给他)
function _delegate(address delegator, address delegatee) internal {
/// @notice differs from `_delegate()` in `Comp.sol` to use `delegates` override method to simulate auto-delegation
address currentDelegate = delegates(delegator);
_delegates[delegator] = delegatee;
emit DelegateChanged(delegator, currentDelegate, delegatee);
uint96 amount = votesToDelegate(delegator);
_moveDelegates(currentDelegate, delegatee, amount);
}
function _moveDelegates(address srcRep,address dstRep,uint96 amount
) internal {
//关键在这里,委托给自己,并不会改变存储状态
if (srcRep != dstRep && amount > 0) {
if (srcRep != address(0)) {
uint32 srcRepNum = numCheckpoints[srcRep];
uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
uint96 srcRepNew = sub96(srcRepOld, amount, 'ERC721Checkpointable::_moveDelegates: amount underflows');
_writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
}
if (dstRep != address(0)) {
uint32 dstRepNum = numCheckpoints[dstRep];
uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
uint96 dstRepNew = add96(dstRepOld, amount, 'ERC721Checkpointable::_moveDelegates: amount overflows');
_writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
}
}
}
function getPriorVotes(address account, uint256 blockNumber) public view returns (uint96) {
require(blockNumber < block.number, 'ERC721Checkpointable::getPriorVotes: not yet determined');
uint32 nCheckpoints = numCheckpoints[account];
if (nCheckpoints == 0) {
return 0;
}
// First check most recent balance
if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
return checkpoints[account][nCheckpoints - 1].votes;
}
// Next check implicit zero balance
if (checkpoints[account][0].fromBlock > blockNumber) {
return 0;
}
uint32 lower = 0;
uint32 upper = nCheckpoints - 1;
while (upper > lower) {
uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
Checkpoint memory cp = checkpoints[account][center];
if (cp.fromBlock == blockNumber) {
return cp.votes;
} else if (cp.fromBlock < blockNumber) {
lower = center;
} else {
upper = center - 1;
}
}
return checkpoints[account][lower].votes;
}
1、闪电贷借NFT ——通过指定区块为上一个区块的投票数,来规避。 但是仍然可以通过闪电借出NFT,委托为攻击者来投票,实现投票操控 2、NFT在多个地址中转投票 ——通过指定区块为上一个区块的投票数,来规避。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!