
ビットコインのマイニングが不公平である理由と量子コンピュータ問題
ビットコインは総発行量の25%は5-10人程度によって採掘されている。ビットコインは最初の2年間(2009-2010年)で総発行量2100万枚の25%、つまり525万枚が採掘されています。この525万枚のBTCは何人によって採掘されたのでしょうか? もちろん、正確な数字は分かりません。1人が多数の採掘アドレスを使っているからです。 しかし、開発者のサトシ・ナカモトがこの2年間で110万枚、つまり当時の採掘量の1/5を彼1人で採掘していると推定されています。 当時はCPU、つまり一般的な家庭用PCでマイニングしていた時代ですから、サトシも例外ではなかったはずです。つまり、他のマイナーもサトシ同様の計算力で同期間マイニングしていたとすると、サトシを含めてたったの5人しかいなかったことになります。 実際には、サトシは最初から最も長く採掘していたと考えられるので、他の参加者は遅れて参加しているからもう少し採掘量は少なかったと考えると、人数はもう少し増えるでしょう。 それでも、25%の大半は5-10人くらいに初期分配されたと考えるのが自然なのです。 恐らく、ビットコインの開発に関わった少人...

マイニングの参加条件
参加条件ビットコいぬのマイニングは、ブロック(4日)ごとにXでポストした参加者を募集し、抽選によって選ばれた当選者に新規発行されたBTCuが割り当てられます。 抽選アルゴリズムと割り当てのルールについては他の記事で説明していますが、ここでは参加するために満たすべき最低条件を説明します。 ビットコいぬのマイニングに応募するためには、以下の条件を満たす必要があります。 抽選後に当選者についてのみ確認が行われます。 これらの参加条件を1つでも満たしていないと、当選しても割り当ては0になります。1ブロックにつき、1アカウント1つのポストを応募できる。応募できるポストは応募者本人のアカウントが投稿したポストに限られる。保護された非公開アカウントではなく、公開されているアカウントであること。非承認アカウントは開設から1年以上経過していなくてはならない。(承認アカウントはこの制約はない)参加したブロックの期間中に投稿されたポストであること。参加したブロックの終了時点から3日後までポストが削除されていないこと。参加したブロックの終了時点から3日後にインプレッションが50以上あること。ハッシュタグ...

ビットコインの発行上限2100万枚は維持できず、崩壊するという話
発行上限という神話の崩壊「ビットコインの総発行量は2100万枚で固定されていて、誰もこれを変更することはできない」 この仕様はビットコインがデジタルゴールドと呼ばれる様になった理由であり、ビットコインというブランド価値の中核といえます。 しかし、誰もが信じるこの仕様は、実は保証されているものではありません。 それどころか、この仕様のせいでビットコインは崩壊するリスクさえあるのです。BTCの新規発行は、セキュリティコストの大部分を占めているあらゆるブロックチェーンのセキュリティは、以下2つの財源によって守られています。 ①コインの新規発行 ②取引手数料 ビットコインは新規発行を半減期によって減少させてゆき、最終的には0にして2100万枚の供給量に固定するプログラムになってます。 つまり、最終的には取引手数料だけで、セキュリティ維持に必要な財源を賄うという計画なのです。 しかし現在、取引手数料は全体のわずか1%程度に過ぎないのです。 セキュリティはほとんどが新規発行によって維持されているのです。 このまま取引手数料が大きく増えなければ、半減期によってセキュリティはどんどん減少してゆく...

Subscribe to Bitcoinu

ビットコインのマイニングが不公平である理由と量子コンピュータ問題
ビットコインは総発行量の25%は5-10人程度によって採掘されている。ビットコインは最初の2年間(2009-2010年)で総発行量2100万枚の25%、つまり525万枚が採掘されています。この525万枚のBTCは何人によって採掘されたのでしょうか? もちろん、正確な数字は分かりません。1人が多数の採掘アドレスを使っているからです。 しかし、開発者のサトシ・ナカモトがこの2年間で110万枚、つまり当時の採掘量の1/5を彼1人で採掘していると推定されています。 当時はCPU、つまり一般的な家庭用PCでマイニングしていた時代ですから、サトシも例外ではなかったはずです。つまり、他のマイナーもサトシ同様の計算力で同期間マイニングしていたとすると、サトシを含めてたったの5人しかいなかったことになります。 実際には、サトシは最初から最も長く採掘していたと考えられるので、他の参加者は遅れて参加しているからもう少し採掘量は少なかったと考えると、人数はもう少し増えるでしょう。 それでも、25%の大半は5-10人くらいに初期分配されたと考えるのが自然なのです。 恐らく、ビットコインの開発に関わった少人...

マイニングの参加条件
参加条件ビットコいぬのマイニングは、ブロック(4日)ごとにXでポストした参加者を募集し、抽選によって選ばれた当選者に新規発行されたBTCuが割り当てられます。 抽選アルゴリズムと割り当てのルールについては他の記事で説明していますが、ここでは参加するために満たすべき最低条件を説明します。 ビットコいぬのマイニングに応募するためには、以下の条件を満たす必要があります。 抽選後に当選者についてのみ確認が行われます。 これらの参加条件を1つでも満たしていないと、当選しても割り当ては0になります。1ブロックにつき、1アカウント1つのポストを応募できる。応募できるポストは応募者本人のアカウントが投稿したポストに限られる。保護された非公開アカウントではなく、公開されているアカウントであること。非承認アカウントは開設から1年以上経過していなくてはならない。(承認アカウントはこの制約はない)参加したブロックの期間中に投稿されたポストであること。参加したブロックの終了時点から3日後までポストが削除されていないこと。参加したブロックの終了時点から3日後にインプレッションが50以上あること。ハッシュタグ...

ビットコインの発行上限2100万枚は維持できず、崩壊するという話
発行上限という神話の崩壊「ビットコインの総発行量は2100万枚で固定されていて、誰もこれを変更することはできない」 この仕様はビットコインがデジタルゴールドと呼ばれる様になった理由であり、ビットコインというブランド価値の中核といえます。 しかし、誰もが信じるこの仕様は、実は保証されているものではありません。 それどころか、この仕様のせいでビットコインは崩壊するリスクさえあるのです。BTCの新規発行は、セキュリティコストの大部分を占めているあらゆるブロックチェーンのセキュリティは、以下2つの財源によって守られています。 ①コインの新規発行 ②取引手数料 ビットコインは新規発行を半減期によって減少させてゆき、最終的には0にして2100万枚の供給量に固定するプログラムになってます。 つまり、最終的には取引手数料だけで、セキュリティ維持に必要な財源を賄うという計画なのです。 しかし現在、取引手数料は全体のわずか1%程度に過ぎないのです。 セキュリティはほとんどが新規発行によって維持されているのです。 このまま取引手数料が大きく増えなければ、半減期によってセキュリティはどんどん減少してゆく...
<100 subscribers
<100 subscribers


ビットコいぬのマイニング報酬は、4日を1ブロックとし、ブロックごとに定められた報酬が当選者に分配されます。
そのブロック報酬は、91ブロック(=364日)ごとに半減していき、5年間で2100万枚全てのビットコいぬが採掘されます。
詳細は以下の通り。

5年目は半減せずに4年目と同量のブロック報酬が採掘され、5年間合計で2100万枚が採掘されます。
総採掘量の割合は以下の通り。

ビットコインでは4年に1度の半減期で、100年以上に渡って2100万枚を採掘するスケジュールとなっています。
それと比較すると、ビットコいぬの半減期は1年に1度で、5年で採掘完了と非常に短いスケジュールとなっています。
これは2つ理由があります。
認知拡大が速い
ビットコいぬのPoWマイニングは、暗号計算によってセキュリティに貢献するのではなく、Xでのポストによって認知拡大に貢献することで採掘報酬がもらえます。これにより、ビットコインよりも遥かに速い認知拡大が得られ、短い期間の分配であっても十分に分散された分配が可能です。
Xや運営への依存を早期に終わらせる
ビットコいぬのマイニングはXや運営システムに依存するため、極小化されてはいるものの中央集権的なリスク要素があります(Xによるインプレッションの操作や運営のシステムエラー等)。これを可能な限り早期に終わらせて完全に分散化された資産にすることを目指します。
ブロック番号を判定する時間は、イーサリアムメインネットのタイムスタンプを用います。
実際にマイニングが実行されるのはレイヤー2であるBaseですが、メインネット(L1)のタイムスタンプを読み込んで使用します。
これは、メインネットのタイムスタンプは分散化されたバリデータによって検証されるため、操作が非常に困難でありセキュリティが高いためです。
以下はそのメインネットからBaseにタイムスタンプを送るスマートコントラクトです。
Baseで受け取るスマートコントラクトは以下です。
上記の時間を用いて、採掘コントラクトは以下の2機能を実装しています。
ブロックのアンロック上記のメインネットの時間に応じて、採掘可能なブロックをアンロックする。また、10ブロック経過したブロックについてはロックし、採掘を不可とする。
採掘ユーザーは管理者の署名をもって、採掘可能なブロックから管理者が許可した量のBTCuを取得することができる。
ビットコいぬのマイニング報酬は、4日を1ブロックとし、ブロックごとに定められた報酬が当選者に分配されます。
そのブロック報酬は、91ブロック(=364日)ごとに半減していき、5年間で2100万枚全てのビットコいぬが採掘されます。
詳細は以下の通り。

5年目は半減せずに4年目と同量のブロック報酬が採掘され、5年間合計で2100万枚が採掘されます。
総採掘量の割合は以下の通り。

ビットコインでは4年に1度の半減期で、100年以上に渡って2100万枚を採掘するスケジュールとなっています。
それと比較すると、ビットコいぬの半減期は1年に1度で、5年で採掘完了と非常に短いスケジュールとなっています。
これは2つ理由があります。
認知拡大が速い
ビットコいぬのPoWマイニングは、暗号計算によってセキュリティに貢献するのではなく、Xでのポストによって認知拡大に貢献することで採掘報酬がもらえます。これにより、ビットコインよりも遥かに速い認知拡大が得られ、短い期間の分配であっても十分に分散された分配が可能です。
Xや運営への依存を早期に終わらせる
ビットコいぬのマイニングはXや運営システムに依存するため、極小化されてはいるものの中央集権的なリスク要素があります(Xによるインプレッションの操作や運営のシステムエラー等)。これを可能な限り早期に終わらせて完全に分散化された資産にすることを目指します。
ブロック番号を判定する時間は、イーサリアムメインネットのタイムスタンプを用います。
実際にマイニングが実行されるのはレイヤー2であるBaseですが、メインネット(L1)のタイムスタンプを読み込んで使用します。
これは、メインネットのタイムスタンプは分散化されたバリデータによって検証されるため、操作が非常に困難でありセキュリティが高いためです。
以下はそのメインネットからBaseにタイムスタンプを送るスマートコントラクトです。
Baseで受け取るスマートコントラクトは以下です。
上記の時間を用いて、採掘コントラクトは以下の2機能を実装しています。
ブロックのアンロック上記のメインネットの時間に応じて、採掘可能なブロックをアンロックする。また、10ブロック経過したブロックについてはロックし、採掘を不可とする。
採掘ユーザーは管理者の署名をもって、採掘可能なブロックから管理者が許可した量のBTCuを取得することができる。
// SPDX-License-Identifier: MIT
import "@base-org/contracts/contracts/BridgeAdapter.sol";
/**
* @title BlockTimeOracle
* @dev イーサリアムメインネット上のブロックタイムをBaseレイヤー2に送信するコントラクト
* 2段階の権限管理(Admin, SuperAdmin)を実装
*/
contract BlockTimeOracle {
// Baseネットワーク上のターゲットコントラクトのアドレス
address public baseReceiverAddress;
// Base Bridgeアダプタのインスタンス
BridgeAdapter public bridgeAdapter;
// 権限管理用アドレス
address public admin;
address public superAdmin;
// 修飾子
modifier onlyAdmin() {
require(msg.sender == admin, "Caller is not the admin");
_;
}
modifier onlySuperAdmin() {
require(msg.sender == superAdmin, "Caller is not the super admin");
_;
}
/**
* @dev コンストラクタ
* @param _bridgeAdapterAddress Base Bridgeアダプタのアドレス
* @param _baseReceiverAddress Baseネットワーク上のレシーバーコントラクトのアドレス
* @param _admin 初期Admin
* @param _superAdmin 初期SuperAdmin
*/
constructor(
address _bridgeAdapterAddress,
address _baseReceiverAddress,
address _admin,
address _superAdmin
) {
bridgeAdapter = BridgeAdapter(_bridgeAdapterAddress);
baseReceiverAddress = _baseReceiverAddress;
admin = _admin;
superAdmin = _superAdmin;
}
/**
* @dev 現在のブロックタイムをBaseに送信する
* 誰でも呼び出し可能
*/
function sendBlockTimeToBase() external {
// 現在のブロックタイムを取得
uint256 currentBlockTime = block.timestamp;
// Base上のコントラクトに呼び出すための関数シグネチャとパラメータを準備
bytes memory callData = abi.encodeWithSignature(
"receiveBlockTime(uint256)",
currentBlockTime
);
// Base Bridgeを通じてメッセージを送信
bridgeAdapter.send(baseReceiverAddress, callData);
// イベントを発行
emit BlockTimeSent(currentBlockTime);
}
/**
* @dev Baseレシーバーアドレスを更新する(Admin権限が必要)
* @param _baseReceiverAddress 新しいBaseレシーバーアドレス
*/
function updateReceiverAddress(address _baseReceiverAddress) external onlyAdmin {
baseReceiverAddress = _baseReceiverAddress;
emit ReceiverAddressUpdated(_baseReceiverAddress);
}
/**
* @dev Adminを変更する(SuperAdmin権限が必要)
* @param _newAdmin 新しいAdmin
*/
function changeAdmin(address _newAdmin) external onlySuperAdmin {
require(_newAdmin != address(0), "New admin cannot be zero address");
address oldAdmin = admin;
admin = _newAdmin;
emit AdminChanged(oldAdmin, _newAdmin);
}
/**
* @dev SuperAdminを変更する(現SuperAdmin権限が必要)
* @param _newSuperAdmin 新しいSuperAdmin
*/
function changeSuperAdmin(address _newSuperAdmin) external onlySuperAdmin {
require(_newSuperAdmin != address(0), "New super admin cannot be zero address");
address oldSuperAdmin = superAdmin;
superAdmin = _newSuperAdmin;
emit SuperAdminChanged(oldSuperAdmin, _newSuperAdmin);
}
// イベント
event BlockTimeSent(uint256 blockTime);
event ReceiverAddressUpdated(address baseReceiverAddress);
event AdminChanged(address oldAdmin, address newAdmin);
event SuperAdminChanged(address oldSuperAdmin, address newSuperAdmin);
}
// SPDX-License-Identifier: MIT
/**
* @title BlockTimeReceiver
* @dev Baseレイヤー2上でイーサリアムメインネットからのブロックタイムを受け取り保存するコントラクト
* 最新のブロックタイムのみを保持する簡素化バージョン
* 2段階の権限管理(Admin, SuperAdmin)を実装
*/
contract BlockTimeReceiver {
// イーサリアムメインネット上のオラクルコントラクトアドレス
address public mainnetOracleAddress;
// 最後に受信したブロックタイム
uint256 public lastReceivedBlockTime;
// 権限管理用アドレス
address public admin;
address public superAdmin;
// 修飾子
modifier onlyAdmin() {
require(msg.sender == admin, "Caller is not the admin");
_;
}
modifier onlySuperAdmin() {
require(msg.sender == superAdmin, "Caller is not the super admin");
_;
}
/**
* @dev コンストラクタ
* @param _mainnetOracleAddress イーサリアムメインネット上のオラクルアドレス
* @param _admin 初期Admin
* @param _superAdmin 初期SuperAdmin
*/
constructor(
address _mainnetOracleAddress,
address _admin,
address _superAdmin
) {
mainnetOracleAddress = _mainnetOracleAddress;
admin = _admin;
superAdmin = _superAdmin;
lastReceivedBlockTime = 0;
}
/**
* @dev メインネットからブロックタイムを受け取る関数
* @param _blockTime 受信したブロックタイム
*/
function receiveBlockTime(uint256 _blockTime) external {
// 送信元がメインネットのオラクルアドレスであることを確認
// Base Bridgeからのメッセージは特定のリレイヤーから送信される
// 実際の実装では、Base Bridgeのメッセージ認証システムに従う必要があります
require(
_isValidRelayer(msg.sender),
"Caller is not an authorized relayer"
);
// 新しいブロックタイムが前回より大きいことを確認
require(
_blockTime > lastReceivedBlockTime,
"New block time must be greater than the last one"
);
// ブロックタイムを更新
lastReceivedBlockTime = _blockTime;
// イベントを発行
emit BlockTimeReceived(_blockTime);
}
/**
* @dev 有効なリレイヤーかどうかを確認する内部関数
* メインネットオラクルコントラクトのエイリアスアドレスのみを許可
*/
function _isValidRelayer(address _sender) internal view returns (bool) {
// メインネットオラクルコントラクトのL2エイリアスアドレスを計算
address expectedSender = _getL2AliasOfL1ContractAddress(mainnetOracleAddress);
// 送信者がエイリアスアドレスと一致するか確認
return _sender == expectedSender;
}
/**
* @dev L1コントラクトアドレスからL2上でのエイリアスアドレスを計算する
* @param _l1Address L1コントラクトアドレス
* @return L2上でのエイリアスアドレス
*/
function _getL2AliasOfL1ContractAddress(address _l1Address) internal pure returns (address) {
// OP StackのアドレスエイリアシングルールをImplementation
// L1アドレスの上位20ビットを0x1111...(Optimismの予約プレフィックス)で置き換える
return address(
uint160(
uint160(0x1111000000000000000000000000000000001111) |
(uint160(_l1Address) & uint160(0x00ffffffffffffffffffffffffffffffffffffffff))
)
);
}
/**
* @dev 最後に受信したブロックタイムを取得する関数(読み取り専用)
*/
function getLatestBlockTime() external view returns (uint256) {
return lastReceivedBlockTime;
}
/**
* @dev メインネットオラクルアドレスを更新する(Admin権限が必要)
* @param _mainnetOracleAddress 新しいメインネットオラクルアドレス
*/
function updateOracleAddress(address _mainnetOracleAddress) external onlyAdmin {
mainnetOracleAddress = _mainnetOracleAddress;
emit OracleAddressUpdated(_mainnetOracleAddress);
}
/**
* @dev Adminを変更する(SuperAdmin権限が必要)
* @param _newAdmin 新しいAdmin
*/
function changeAdmin(address _newAdmin) external onlySuperAdmin {
require(_newAdmin != address(0), "New admin cannot be zero address");
address oldAdmin = admin;
admin = _newAdmin;
emit AdminChanged(oldAdmin, _newAdmin);
}
/**
* @dev SuperAdminを変更する(現SuperAdmin権限が必要)
* @param _newSuperAdmin 新しいSuperAdmin
*/
function changeSuperAdmin(address _newSuperAdmin) external onlySuperAdmin {
require(_newSuperAdmin != address(0), "New super admin cannot be zero address");
address oldSuperAdmin = superAdmin;
superAdmin = _newSuperAdmin;
emit SuperAdminChanged(oldSuperAdmin, _newSuperAdmin);
}
// イベント
event BlockTimeReceived(uint256 blockTime);
event OracleAddressUpdated(address mainnetOracleAddress);
event AdminChanged(address oldAdmin, address newAdmin);
event SuperAdminChanged(address oldSuperAdmin, address newSuperAdmin);
}
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
interface IBlockTimeReceiver {
function getLatestBlockTime() external view returns (uint256);
}
/**
* @title BitcoinuBlockRewardManager
* @dev Baseブロックチェーン上でBitcoinuトークンの4日ブロックベース解放を管理するコントラクト
*/
contract BitcoinuBlockRewardManager {
using ECDSA for bytes32;
// ブロックのステータスを表す列挙型
enum BlockStatus {
Inactive, // 未アンロック
Active, // アンロック済み、採掘可能
Locked // ロック済み、採掘不可(10ブロック以上前)
}
// ブロック情報の構造体
struct BlockInfo {
uint256 totalReleaseAmount; // ブロックの解放総量(satoshi単位)
uint256 releasedAmount; // 実際に解放済みの量(satoshi単位)
uint256 endTime; // ブロックの終了時間(タイムスタンプ)
BlockStatus status; // ブロックのステータス
}
// 定数
uint256 public constant BLOCK_DURATION = 4 days; // ブロック期間(秒)- 4日 = 345,600秒
// イミュータブル変数(コンストラクタでのみ設定)
IBlockTimeReceiver public immutable blockTimeReceiver; // BlockTimeReceiverコントラクト
IERC20 public immutable bitcoinuToken; // Bitcoinuトークンコントラクト
uint256 public immutable startTimestamp; // スタート時点のタイムスタンプ
// 状態変数
address public admin; // 管理者アドレス
address public superAdmin; // スーパー管理者アドレス
BlockInfo[] public blocks; // ブロック情報の配列
uint256[] public blockReleaseAmounts; // ブロック番号ごとの解放量テーブル(satoshi単位)
// ユーザーID => 採掘済みブロック番号 => 既に採掘したかどうか
mapping(uint256 => mapping(uint256 => bool)) public userMinedBlocks;
// 修飾子
modifier onlyAdmin() {
require(msg.sender == admin, "Caller is not an admin");
_;
}
modifier onlySuperAdmin() {
require(msg.sender == superAdmin, "Caller is not a super admin");
_;
}
// イベント定義
event BlockUnlocked(uint256 indexed blockNumber, uint256 amount);
event BlocksMined(uint256 indexed blockNumber, uint256 userId, address userAddress, uint256 amount);
event BlocksLocked(uint256 fromBlock, uint256 toBlock);
event AdminChanged(address oldAdmin, address newAdmin);
event SuperAdminChanged(address oldSuperAdmin, address newSuperAdmin);
/**
* @dev コンストラクタ
* @param _blockTimeReceiver BlockTimeReceiverコントラクトのアドレス
* @param _bitcoinuToken Bitcoinuトークンのアドレス
* @param _admin 管理者アドレス
* @param _superAdmin スーパー管理者アドレス
*/
constructor(
address _blockTimeReceiver,
address _bitcoinuToken,
address _admin,
address _superAdmin
) {
require(_admin != address(0), "Admin cannot be zero address");
require(_superAdmin != address(0), "SuperAdmin cannot be zero address");
blockTimeReceiver = IBlockTimeReceiver(_blockTimeReceiver);
bitcoinuToken = IERC20(_bitcoinuToken);
admin = _admin;
superAdmin = _superAdmin;
// スタート時点を現在のブロックタイムに設定
startTimestamp = blockTimeReceiver.getLatestBlockTime();
// 各ブロック範囲の解放量を設定(satoshi単位)
blockReleaseAmounts.push(11538461538461); // ブロック 0-90
blockReleaseAmounts.push(5769230769231); // ブロック 91-181
blockReleaseAmounts.push(2884615384615); // ブロック 182-272
blockReleaseAmounts.push(1442307692308); // ブロック 273-363
blockReleaseAmounts.push(1442307692308); // ブロック 364-454
}
// === 読み取り専用関数(view/pure) ===
/**
* @dev 現在のブロック番号を取得
* @return 現在のブロック番号
*/
function getCurrentBlockNumber() public view returns (uint256) {
uint256 currentTime = blockTimeReceiver.getLatestBlockTime();
if (currentTime <= startTimestamp) {
return 0;
}
uint256 elapsedTime = currentTime - startTimestamp;
return elapsedTime / BLOCK_DURATION;
}
/**
* @dev ブロック番号から解放量を取得
* @param blockNumber ブロック番号
* @return 解放量(satoshi単位)
*/
function getReleaseAmountForBlock(uint256 blockNumber) public view returns (uint256) {
if (blockNumber <= 90) {
return blockReleaseAmounts[0];
} else if (blockNumber <= 181) {
return blockReleaseAmounts[1];
} else if (blockNumber <= 272) {
return blockReleaseAmounts[2];
} else if (blockNumber <= 363) {
return blockReleaseAmounts[3];
} else if (blockNumber <= 454) {
return blockReleaseAmounts[4];
} else {
return 0; // 455以降は解放なし
}
}
/**
* @dev 現在までに作成されたブロック数を取得
* @return ブロック配列の長さ
*/
function getBlockCount() public view returns (uint256) {
return blocks.length;
}
/**
* @dev すべてのブロック情報を一括で取得
* @return ブロック情報の配列
*/
function getAllBlocks() external view returns (BlockInfo[] memory) {
BlockInfo[] memory result = new BlockInfoUnsupported embed;
for (uint256 i = 0; i < blocks.length; i++) {
result[i] = blocks[i];
}
return result;
}
// === 状態変更関数 ===
/**
* @dev ブロックをアンロックする(Admin権限が必要)
* @param blockNumber アンロックするブロック番号
*/
function unlock(uint256 blockNumber) external onlyAdmin {
require(blockNumber <= getCurrentBlockNumber(), "Block not reached yet");
// 解放量を計算
uint256 releaseAmount = getReleaseAmountForBlock(blockNumber);
require(releaseAmount > 0, "No tokens to release for this block");
// ブロック終了時間を計算
uint256 blockEndTime = startTimestamp + ((blockNumber + 1) * BLOCK_DURATION);
// 必要に応じてブロック配列を拡張
while (blocks.length <= blockNumber) {
blocks.push(BlockInfo(0, 0, 0, BlockStatus.Inactive));
}
require(blocks[blockNumber].status == BlockStatus.Inactive, "Block already unlocked or locked");
// ブロック情報を更新
blocks[blockNumber].totalReleaseAmount = releaseAmount;
blocks[blockNumber].endTime = blockEndTime;
blocks[blockNumber].status = BlockStatus.Active;
// 10ブロック以上前のブロックをロック
if (blockNumber >= 10) {
uint256 lockFromBlock = blockNumber - 10;
// lockFromBlockだけをロック(それ以前のブロックは既に以前のunlockでロック済みのはず)
if (blocks[lockFromBlock].status == BlockStatus.Active) {
blocks[lockFromBlock].status = BlockStatus.Locked;
emit BlocksLocked(lockFromBlock, lockFromBlock);
}
}
emit BlockUnlocked(blockNumber, releaseAmount);
}
/**
* @dev 署名を検証してブロック報酬を採掘
* @param blockNumber 採掘するブロック番号
* @param userId ユーザーID
* @param minedAmount 採掘する量(satoshi単位)
* @param userAddress ユーザーのウォレットアドレス
* @param signature 管理者の署名
*/
function mine(
uint256 blockNumber,
uint256 userId,
uint256 minedAmount,
address userAddress,
bytes memory signature
) external {
// 呼び出し元がuserAddressであることを確認
require(msg.sender == userAddress, "Caller must be the claimed address");
// ブロック番号が有効範囲内かチェック
require(blockNumber < blocks.length, "Block does not exist");
// ブロックがアクティブであることを確認
require(blocks[blockNumber].status == BlockStatus.Active, "Block not active for mining");
// ユーザーがこのブロックをまだ採掘していないことを確認
require(!userMinedBlocks[userId][blockNumber], "User already mined this block");
// 採掘可能な残量を確認
uint256 availableAmount = blocks[blockNumber].totalReleaseAmount - blocks[blockNumber].releasedAmount;
require(availableAmount >= minedAmount, "Not enough tokens left in this block");
// 署名を検証
bytes32 messageHash = keccak256(abi.encodePacked(
blockNumber,
userId,
minedAmount,
userAddress
));
bytes32 ethSignedMessageHash = messageHash.toEthSignedMessageHash();
address signer = ethSignedMessageHash.recover(signature);
require(signer == admin, "Invalid signature");
// ユーザーを採掘済みとしてマーク
userMinedBlocks[userId][blockNumber] = true;
// ブロックの解放済み量を更新
blocks[blockNumber].releasedAmount += minedAmount;
// トークンを送信
require(bitcoinuToken.transfer(userAddress, minedAmount), "Token transfer failed");
emit BlocksMined(blockNumber, userId, userAddress, minedAmount);
}
// === 管理者機能 ===
/**
* @dev Adminを変更する(SuperAdmin権限が必要)
* @param _newAdmin 新しいAdmin
*/
function changeAdmin(address _newAdmin) external onlySuperAdmin {
require(_newAdmin != address(0), "New admin cannot be zero address");
address oldAdmin = admin;
admin = _newAdmin;
emit AdminChanged(oldAdmin, _newAdmin);
}
/**
* @dev SuperAdminを変更する(現SuperAdmin権限が必要)
* @param _newSuperAdmin 新しいSuperAdmin
*/
function changeSuperAdmin(address _newSuperAdmin) external onlySuperAdmin {
require(_newSuperAdmin != address(0), "New super admin cannot be zero address");
address oldSuperAdmin = superAdmin;
superAdmin = _newSuperAdmin;
emit SuperAdminChanged(oldSuperAdmin, _newSuperAdmin);
}
}
// SPDX-License-Identifier: MIT
import "@base-org/contracts/contracts/BridgeAdapter.sol";
/**
* @title BlockTimeOracle
* @dev イーサリアムメインネット上のブロックタイムをBaseレイヤー2に送信するコントラクト
* 2段階の権限管理(Admin, SuperAdmin)を実装
*/
contract BlockTimeOracle {
// Baseネットワーク上のターゲットコントラクトのアドレス
address public baseReceiverAddress;
// Base Bridgeアダプタのインスタンス
BridgeAdapter public bridgeAdapter;
// 権限管理用アドレス
address public admin;
address public superAdmin;
// 修飾子
modifier onlyAdmin() {
require(msg.sender == admin, "Caller is not the admin");
_;
}
modifier onlySuperAdmin() {
require(msg.sender == superAdmin, "Caller is not the super admin");
_;
}
/**
* @dev コンストラクタ
* @param _bridgeAdapterAddress Base Bridgeアダプタのアドレス
* @param _baseReceiverAddress Baseネットワーク上のレシーバーコントラクトのアドレス
* @param _admin 初期Admin
* @param _superAdmin 初期SuperAdmin
*/
constructor(
address _bridgeAdapterAddress,
address _baseReceiverAddress,
address _admin,
address _superAdmin
) {
bridgeAdapter = BridgeAdapter(_bridgeAdapterAddress);
baseReceiverAddress = _baseReceiverAddress;
admin = _admin;
superAdmin = _superAdmin;
}
/**
* @dev 現在のブロックタイムをBaseに送信する
* 誰でも呼び出し可能
*/
function sendBlockTimeToBase() external {
// 現在のブロックタイムを取得
uint256 currentBlockTime = block.timestamp;
// Base上のコントラクトに呼び出すための関数シグネチャとパラメータを準備
bytes memory callData = abi.encodeWithSignature(
"receiveBlockTime(uint256)",
currentBlockTime
);
// Base Bridgeを通じてメッセージを送信
bridgeAdapter.send(baseReceiverAddress, callData);
// イベントを発行
emit BlockTimeSent(currentBlockTime);
}
/**
* @dev Baseレシーバーアドレスを更新する(Admin権限が必要)
* @param _baseReceiverAddress 新しいBaseレシーバーアドレス
*/
function updateReceiverAddress(address _baseReceiverAddress) external onlyAdmin {
baseReceiverAddress = _baseReceiverAddress;
emit ReceiverAddressUpdated(_baseReceiverAddress);
}
/**
* @dev Adminを変更する(SuperAdmin権限が必要)
* @param _newAdmin 新しいAdmin
*/
function changeAdmin(address _newAdmin) external onlySuperAdmin {
require(_newAdmin != address(0), "New admin cannot be zero address");
address oldAdmin = admin;
admin = _newAdmin;
emit AdminChanged(oldAdmin, _newAdmin);
}
/**
* @dev SuperAdminを変更する(現SuperAdmin権限が必要)
* @param _newSuperAdmin 新しいSuperAdmin
*/
function changeSuperAdmin(address _newSuperAdmin) external onlySuperAdmin {
require(_newSuperAdmin != address(0), "New super admin cannot be zero address");
address oldSuperAdmin = superAdmin;
superAdmin = _newSuperAdmin;
emit SuperAdminChanged(oldSuperAdmin, _newSuperAdmin);
}
// イベント
event BlockTimeSent(uint256 blockTime);
event ReceiverAddressUpdated(address baseReceiverAddress);
event AdminChanged(address oldAdmin, address newAdmin);
event SuperAdminChanged(address oldSuperAdmin, address newSuperAdmin);
}
// SPDX-License-Identifier: MIT
/**
* @title BlockTimeReceiver
* @dev Baseレイヤー2上でイーサリアムメインネットからのブロックタイムを受け取り保存するコントラクト
* 最新のブロックタイムのみを保持する簡素化バージョン
* 2段階の権限管理(Admin, SuperAdmin)を実装
*/
contract BlockTimeReceiver {
// イーサリアムメインネット上のオラクルコントラクトアドレス
address public mainnetOracleAddress;
// 最後に受信したブロックタイム
uint256 public lastReceivedBlockTime;
// 権限管理用アドレス
address public admin;
address public superAdmin;
// 修飾子
modifier onlyAdmin() {
require(msg.sender == admin, "Caller is not the admin");
_;
}
modifier onlySuperAdmin() {
require(msg.sender == superAdmin, "Caller is not the super admin");
_;
}
/**
* @dev コンストラクタ
* @param _mainnetOracleAddress イーサリアムメインネット上のオラクルアドレス
* @param _admin 初期Admin
* @param _superAdmin 初期SuperAdmin
*/
constructor(
address _mainnetOracleAddress,
address _admin,
address _superAdmin
) {
mainnetOracleAddress = _mainnetOracleAddress;
admin = _admin;
superAdmin = _superAdmin;
lastReceivedBlockTime = 0;
}
/**
* @dev メインネットからブロックタイムを受け取る関数
* @param _blockTime 受信したブロックタイム
*/
function receiveBlockTime(uint256 _blockTime) external {
// 送信元がメインネットのオラクルアドレスであることを確認
// Base Bridgeからのメッセージは特定のリレイヤーから送信される
// 実際の実装では、Base Bridgeのメッセージ認証システムに従う必要があります
require(
_isValidRelayer(msg.sender),
"Caller is not an authorized relayer"
);
// 新しいブロックタイムが前回より大きいことを確認
require(
_blockTime > lastReceivedBlockTime,
"New block time must be greater than the last one"
);
// ブロックタイムを更新
lastReceivedBlockTime = _blockTime;
// イベントを発行
emit BlockTimeReceived(_blockTime);
}
/**
* @dev 有効なリレイヤーかどうかを確認する内部関数
* メインネットオラクルコントラクトのエイリアスアドレスのみを許可
*/
function _isValidRelayer(address _sender) internal view returns (bool) {
// メインネットオラクルコントラクトのL2エイリアスアドレスを計算
address expectedSender = _getL2AliasOfL1ContractAddress(mainnetOracleAddress);
// 送信者がエイリアスアドレスと一致するか確認
return _sender == expectedSender;
}
/**
* @dev L1コントラクトアドレスからL2上でのエイリアスアドレスを計算する
* @param _l1Address L1コントラクトアドレス
* @return L2上でのエイリアスアドレス
*/
function _getL2AliasOfL1ContractAddress(address _l1Address) internal pure returns (address) {
// OP StackのアドレスエイリアシングルールをImplementation
// L1アドレスの上位20ビットを0x1111...(Optimismの予約プレフィックス)で置き換える
return address(
uint160(
uint160(0x1111000000000000000000000000000000001111) |
(uint160(_l1Address) & uint160(0x00ffffffffffffffffffffffffffffffffffffffff))
)
);
}
/**
* @dev 最後に受信したブロックタイムを取得する関数(読み取り専用)
*/
function getLatestBlockTime() external view returns (uint256) {
return lastReceivedBlockTime;
}
/**
* @dev メインネットオラクルアドレスを更新する(Admin権限が必要)
* @param _mainnetOracleAddress 新しいメインネットオラクルアドレス
*/
function updateOracleAddress(address _mainnetOracleAddress) external onlyAdmin {
mainnetOracleAddress = _mainnetOracleAddress;
emit OracleAddressUpdated(_mainnetOracleAddress);
}
/**
* @dev Adminを変更する(SuperAdmin権限が必要)
* @param _newAdmin 新しいAdmin
*/
function changeAdmin(address _newAdmin) external onlySuperAdmin {
require(_newAdmin != address(0), "New admin cannot be zero address");
address oldAdmin = admin;
admin = _newAdmin;
emit AdminChanged(oldAdmin, _newAdmin);
}
/**
* @dev SuperAdminを変更する(現SuperAdmin権限が必要)
* @param _newSuperAdmin 新しいSuperAdmin
*/
function changeSuperAdmin(address _newSuperAdmin) external onlySuperAdmin {
require(_newSuperAdmin != address(0), "New super admin cannot be zero address");
address oldSuperAdmin = superAdmin;
superAdmin = _newSuperAdmin;
emit SuperAdminChanged(oldSuperAdmin, _newSuperAdmin);
}
// イベント
event BlockTimeReceived(uint256 blockTime);
event OracleAddressUpdated(address mainnetOracleAddress);
event AdminChanged(address oldAdmin, address newAdmin);
event SuperAdminChanged(address oldSuperAdmin, address newSuperAdmin);
}
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
interface IBlockTimeReceiver {
function getLatestBlockTime() external view returns (uint256);
}
/**
* @title BitcoinuBlockRewardManager
* @dev Baseブロックチェーン上でBitcoinuトークンの4日ブロックベース解放を管理するコントラクト
*/
contract BitcoinuBlockRewardManager {
using ECDSA for bytes32;
// ブロックのステータスを表す列挙型
enum BlockStatus {
Inactive, // 未アンロック
Active, // アンロック済み、採掘可能
Locked // ロック済み、採掘不可(10ブロック以上前)
}
// ブロック情報の構造体
struct BlockInfo {
uint256 totalReleaseAmount; // ブロックの解放総量(satoshi単位)
uint256 releasedAmount; // 実際に解放済みの量(satoshi単位)
uint256 endTime; // ブロックの終了時間(タイムスタンプ)
BlockStatus status; // ブロックのステータス
}
// 定数
uint256 public constant BLOCK_DURATION = 4 days; // ブロック期間(秒)- 4日 = 345,600秒
// イミュータブル変数(コンストラクタでのみ設定)
IBlockTimeReceiver public immutable blockTimeReceiver; // BlockTimeReceiverコントラクト
IERC20 public immutable bitcoinuToken; // Bitcoinuトークンコントラクト
uint256 public immutable startTimestamp; // スタート時点のタイムスタンプ
// 状態変数
address public admin; // 管理者アドレス
address public superAdmin; // スーパー管理者アドレス
BlockInfo[] public blocks; // ブロック情報の配列
uint256[] public blockReleaseAmounts; // ブロック番号ごとの解放量テーブル(satoshi単位)
// ユーザーID => 採掘済みブロック番号 => 既に採掘したかどうか
mapping(uint256 => mapping(uint256 => bool)) public userMinedBlocks;
// 修飾子
modifier onlyAdmin() {
require(msg.sender == admin, "Caller is not an admin");
_;
}
modifier onlySuperAdmin() {
require(msg.sender == superAdmin, "Caller is not a super admin");
_;
}
// イベント定義
event BlockUnlocked(uint256 indexed blockNumber, uint256 amount);
event BlocksMined(uint256 indexed blockNumber, uint256 userId, address userAddress, uint256 amount);
event BlocksLocked(uint256 fromBlock, uint256 toBlock);
event AdminChanged(address oldAdmin, address newAdmin);
event SuperAdminChanged(address oldSuperAdmin, address newSuperAdmin);
/**
* @dev コンストラクタ
* @param _blockTimeReceiver BlockTimeReceiverコントラクトのアドレス
* @param _bitcoinuToken Bitcoinuトークンのアドレス
* @param _admin 管理者アドレス
* @param _superAdmin スーパー管理者アドレス
*/
constructor(
address _blockTimeReceiver,
address _bitcoinuToken,
address _admin,
address _superAdmin
) {
require(_admin != address(0), "Admin cannot be zero address");
require(_superAdmin != address(0), "SuperAdmin cannot be zero address");
blockTimeReceiver = IBlockTimeReceiver(_blockTimeReceiver);
bitcoinuToken = IERC20(_bitcoinuToken);
admin = _admin;
superAdmin = _superAdmin;
// スタート時点を現在のブロックタイムに設定
startTimestamp = blockTimeReceiver.getLatestBlockTime();
// 各ブロック範囲の解放量を設定(satoshi単位)
blockReleaseAmounts.push(11538461538461); // ブロック 0-90
blockReleaseAmounts.push(5769230769231); // ブロック 91-181
blockReleaseAmounts.push(2884615384615); // ブロック 182-272
blockReleaseAmounts.push(1442307692308); // ブロック 273-363
blockReleaseAmounts.push(1442307692308); // ブロック 364-454
}
// === 読み取り専用関数(view/pure) ===
/**
* @dev 現在のブロック番号を取得
* @return 現在のブロック番号
*/
function getCurrentBlockNumber() public view returns (uint256) {
uint256 currentTime = blockTimeReceiver.getLatestBlockTime();
if (currentTime <= startTimestamp) {
return 0;
}
uint256 elapsedTime = currentTime - startTimestamp;
return elapsedTime / BLOCK_DURATION;
}
/**
* @dev ブロック番号から解放量を取得
* @param blockNumber ブロック番号
* @return 解放量(satoshi単位)
*/
function getReleaseAmountForBlock(uint256 blockNumber) public view returns (uint256) {
if (blockNumber <= 90) {
return blockReleaseAmounts[0];
} else if (blockNumber <= 181) {
return blockReleaseAmounts[1];
} else if (blockNumber <= 272) {
return blockReleaseAmounts[2];
} else if (blockNumber <= 363) {
return blockReleaseAmounts[3];
} else if (blockNumber <= 454) {
return blockReleaseAmounts[4];
} else {
return 0; // 455以降は解放なし
}
}
/**
* @dev 現在までに作成されたブロック数を取得
* @return ブロック配列の長さ
*/
function getBlockCount() public view returns (uint256) {
return blocks.length;
}
/**
* @dev すべてのブロック情報を一括で取得
* @return ブロック情報の配列
*/
function getAllBlocks() external view returns (BlockInfo[] memory) {
BlockInfo[] memory result = new BlockInfoUnsupported embed;
for (uint256 i = 0; i < blocks.length; i++) {
result[i] = blocks[i];
}
return result;
}
// === 状態変更関数 ===
/**
* @dev ブロックをアンロックする(Admin権限が必要)
* @param blockNumber アンロックするブロック番号
*/
function unlock(uint256 blockNumber) external onlyAdmin {
require(blockNumber <= getCurrentBlockNumber(), "Block not reached yet");
// 解放量を計算
uint256 releaseAmount = getReleaseAmountForBlock(blockNumber);
require(releaseAmount > 0, "No tokens to release for this block");
// ブロック終了時間を計算
uint256 blockEndTime = startTimestamp + ((blockNumber + 1) * BLOCK_DURATION);
// 必要に応じてブロック配列を拡張
while (blocks.length <= blockNumber) {
blocks.push(BlockInfo(0, 0, 0, BlockStatus.Inactive));
}
require(blocks[blockNumber].status == BlockStatus.Inactive, "Block already unlocked or locked");
// ブロック情報を更新
blocks[blockNumber].totalReleaseAmount = releaseAmount;
blocks[blockNumber].endTime = blockEndTime;
blocks[blockNumber].status = BlockStatus.Active;
// 10ブロック以上前のブロックをロック
if (blockNumber >= 10) {
uint256 lockFromBlock = blockNumber - 10;
// lockFromBlockだけをロック(それ以前のブロックは既に以前のunlockでロック済みのはず)
if (blocks[lockFromBlock].status == BlockStatus.Active) {
blocks[lockFromBlock].status = BlockStatus.Locked;
emit BlocksLocked(lockFromBlock, lockFromBlock);
}
}
emit BlockUnlocked(blockNumber, releaseAmount);
}
/**
* @dev 署名を検証してブロック報酬を採掘
* @param blockNumber 採掘するブロック番号
* @param userId ユーザーID
* @param minedAmount 採掘する量(satoshi単位)
* @param userAddress ユーザーのウォレットアドレス
* @param signature 管理者の署名
*/
function mine(
uint256 blockNumber,
uint256 userId,
uint256 minedAmount,
address userAddress,
bytes memory signature
) external {
// 呼び出し元がuserAddressであることを確認
require(msg.sender == userAddress, "Caller must be the claimed address");
// ブロック番号が有効範囲内かチェック
require(blockNumber < blocks.length, "Block does not exist");
// ブロックがアクティブであることを確認
require(blocks[blockNumber].status == BlockStatus.Active, "Block not active for mining");
// ユーザーがこのブロックをまだ採掘していないことを確認
require(!userMinedBlocks[userId][blockNumber], "User already mined this block");
// 採掘可能な残量を確認
uint256 availableAmount = blocks[blockNumber].totalReleaseAmount - blocks[blockNumber].releasedAmount;
require(availableAmount >= minedAmount, "Not enough tokens left in this block");
// 署名を検証
bytes32 messageHash = keccak256(abi.encodePacked(
blockNumber,
userId,
minedAmount,
userAddress
));
bytes32 ethSignedMessageHash = messageHash.toEthSignedMessageHash();
address signer = ethSignedMessageHash.recover(signature);
require(signer == admin, "Invalid signature");
// ユーザーを採掘済みとしてマーク
userMinedBlocks[userId][blockNumber] = true;
// ブロックの解放済み量を更新
blocks[blockNumber].releasedAmount += minedAmount;
// トークンを送信
require(bitcoinuToken.transfer(userAddress, minedAmount), "Token transfer failed");
emit BlocksMined(blockNumber, userId, userAddress, minedAmount);
}
// === 管理者機能 ===
/**
* @dev Adminを変更する(SuperAdmin権限が必要)
* @param _newAdmin 新しいAdmin
*/
function changeAdmin(address _newAdmin) external onlySuperAdmin {
require(_newAdmin != address(0), "New admin cannot be zero address");
address oldAdmin = admin;
admin = _newAdmin;
emit AdminChanged(oldAdmin, _newAdmin);
}
/**
* @dev SuperAdminを変更する(現SuperAdmin権限が必要)
* @param _newSuperAdmin 新しいSuperAdmin
*/
function changeSuperAdmin(address _newSuperAdmin) external onlySuperAdmin {
require(_newSuperAdmin != address(0), "New super admin cannot be zero address");
address oldSuperAdmin = superAdmin;
superAdmin = _newSuperAdmin;
emit SuperAdminChanged(oldSuperAdmin, _newSuperAdmin);
}
}
Share Dialog
Share Dialog
No activity yet