diff --git a/Cargo.lock b/Cargo.lock index 579680833..889fddf1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2763,6 +2763,27 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "fendermint_actor_activity_tracker" +version = "0.1.0" +dependencies = [ + "anyhow", + "cid", + "fil_actors_evm_shared", + "fil_actors_runtime", + "frc42_dispatch", + "fvm_ipld_blockstore", + "fvm_ipld_encoding", + "fvm_shared", + "hex-literal 0.4.1", + "log", + "multihash 0.18.1", + "num-derive 0.3.3", + "num-traits", + "serde", + "serde_tuple", +] + [[package]] name = "fendermint_actor_chainmetadata" version = "0.1.0" @@ -2829,6 +2850,7 @@ version = "0.1.0" dependencies = [ "anyhow", "cid", + "fendermint_actor_activity_tracker", "fendermint_actor_chainmetadata", "fendermint_actor_eam", "fendermint_actor_gas_market_eip1559", @@ -3313,6 +3335,7 @@ dependencies = [ "fvm_shared", "hex", "ipc-api", + "ipc-types", "multihash 0.18.1", "num-traits", "quickcheck", @@ -3335,6 +3358,7 @@ dependencies = [ "base64 0.21.7", "cid", "ethers", + "fendermint_actor_activity_tracker", "fendermint_actor_chainmetadata", "fendermint_actor_eam", "fendermint_actor_gas_market_eip1559", @@ -3366,6 +3390,7 @@ dependencies = [ "ipc-observability", "ipc_actors_abis", "libipld", + "merkle-tree-rs", "multihash 0.18.1", "num-traits", "pin-project", @@ -5121,6 +5146,7 @@ dependencies = [ "ipc_actors_abis", "lazy_static", "log", + "merkle-tree-rs", "num-traits", "num_enum", "serde", @@ -5222,6 +5248,7 @@ dependencies = [ "ipc_actors_abis", "libsecp256k1", "log", + "merkle-tree-rs", "num-derive 0.3.3", "num-traits", "prometheus", diff --git a/Cargo.toml b/Cargo.toml index c226151a5..30c5cc7d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ members = [ "fendermint/actors", "fendermint/actors/api", "fendermint/actors/chainmetadata", + "fendermint/actors/activity-tracker", "fendermint/actors/eam", "fendermint/actors/gas_market/eip1559", ] @@ -222,6 +223,7 @@ fil_actor_eam = { git = "https://github.com/filecoin-project/builtin-actors", ta fil_actors_runtime = { git = "https://github.com/filecoin-project/builtin-actors", tag = "v15.0.0" } fendermint_actor_eam = { path = "./fendermint/actors/eam" } +fendermint_actor_activity_tracker= { path = "./fendermint/actors/activity-tracker" } cid = { version = "0.10.1", default-features = false, features = [ "serde-codec", diff --git a/contracts/.storage-layouts/GatewayActorModifiers.json b/contracts/.storage-layouts/GatewayActorModifiers.json index c1987575a..b2f59a894 100644 --- a/contracts/.storage-layouts/GatewayActorModifiers.json +++ b/contracts/.storage-layouts/GatewayActorModifiers.json @@ -1,12 +1,12 @@ { "storage": [ { - "astId": 11979, + "astId": 11959, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "s", "offset": 0, "slot": "0", - "type": "t_struct(GatewayActorStorage)11965_storage" + "type": "t_struct(GatewayActorStorage)11945_storage" } ], "types": { @@ -27,14 +27,14 @@ "label": "bytes32[]", "numberOfBytes": "32" }, - "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage": { - "base": "t_struct(IpcEnvelope)18702_storage", + "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage": { + "base": "t_struct(IpcEnvelope)19408_storage", "encoding": "dynamic_array", "label": "struct IpcEnvelope[]", "numberOfBytes": "32" }, - "t_array(t_struct(Validator)18966_storage)dyn_storage": { - "base": "t_struct(Validator)18966_storage", + "t_array(t_struct(Validator)19672_storage)dyn_storage": { + "base": "t_struct(Validator)19672_storage", "encoding": "dynamic_array", "label": "struct Validator[]", "numberOfBytes": "32" @@ -54,22 +54,22 @@ "label": "bytes", "numberOfBytes": "32" }, - "t_enum(IpcMsgKind)18679": { + "t_enum(IpcMsgKind)19385": { "encoding": "inplace", "label": "enum IpcMsgKind", "numberOfBytes": "1" }, - "t_enum(PermissionMode)18912": { + "t_enum(PermissionMode)19618": { "encoding": "inplace", "label": "enum PermissionMode", "numberOfBytes": "1" }, - "t_enum(QuorumObjKind)18748": { + "t_enum(QuorumObjKind)19454": { "encoding": "inplace", "label": "enum QuorumObjKind", "numberOfBytes": "1" }, - "t_enum(StakingOperation)18835": { + "t_enum(StakingOperation)19541": { "encoding": "inplace", "label": "enum StakingOperation", "numberOfBytes": "1" @@ -81,12 +81,12 @@ "numberOfBytes": "32", "value": "t_bytes_storage" }, - "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)": { + "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct ValidatorInfo)", "numberOfBytes": "32", - "value": "t_struct(ValidatorInfo)18907_storage" + "value": "t_struct(ValidatorInfo)19613_storage" }, "t_mapping(t_address,t_uint16)": { "encoding": "mapping", @@ -95,19 +95,19 @@ "numberOfBytes": "32", "value": "t_uint16" }, - "t_mapping(t_bytes32,t_struct(IpcEnvelope)18702_storage)": { + "t_mapping(t_bytes32,t_struct(IpcEnvelope)19408_storage)": { "encoding": "mapping", "key": "t_bytes32", "label": "mapping(bytes32 => struct IpcEnvelope)", "numberOfBytes": "32", - "value": "t_struct(IpcEnvelope)18702_storage" + "value": "t_struct(IpcEnvelope)19408_storage" }, - "t_mapping(t_bytes32,t_struct(Subnet)18829_storage)": { + "t_mapping(t_bytes32,t_struct(Subnet)19535_storage)": { "encoding": "mapping", "key": "t_bytes32", "label": "mapping(bytes32 => struct Subnet)", "numberOfBytes": "32", - "value": "t_struct(Subnet)18829_storage" + "value": "t_struct(Subnet)19535_storage" }, "t_mapping(t_bytes32,t_uint256)": { "encoding": "mapping", @@ -137,40 +137,40 @@ "numberOfBytes": "32", "value": "t_struct(AddressSet)3459_storage" }, - "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)": { + "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct BottomUpCheckpoint)", "numberOfBytes": "32", - "value": "t_struct(BottomUpCheckpoint)18654_storage" + "value": "t_struct(BottomUpCheckpoint)19331_storage" }, - "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)18668_storage)": { + "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)19374_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct BottomUpMsgBatch)", "numberOfBytes": "32", - "value": "t_struct(BottomUpMsgBatch)18668_storage" + "value": "t_struct(BottomUpMsgBatch)19374_storage" }, - "t_mapping(t_uint256,t_struct(ParentFinality)18634_storage)": { + "t_mapping(t_uint256,t_struct(ParentFinality)19311_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct ParentFinality)", "numberOfBytes": "32", - "value": "t_struct(ParentFinality)18634_storage" + "value": "t_struct(ParentFinality)19311_storage" }, - "t_mapping(t_uint256,t_struct(QuorumInfo)18765_storage)": { + "t_mapping(t_uint256,t_struct(QuorumInfo)19471_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct QuorumInfo)", "numberOfBytes": "32", - "value": "t_struct(QuorumInfo)18765_storage" + "value": "t_struct(QuorumInfo)19471_storage" }, - "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)": { + "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)": { "encoding": "mapping", "key": "t_uint64", "label": "mapping(uint64 => struct StakingChange)", "numberOfBytes": "32", - "value": "t_struct(StakingChange)18844_storage" + "value": "t_struct(StakingChange)19550_storage" }, "t_struct(AddressSet)3459_storage": { "encoding": "inplace", @@ -187,20 +187,20 @@ ], "numberOfBytes": "64" }, - "t_struct(BottomUpCheckpoint)18654_storage": { + "t_struct(BottomUpCheckpoint)19331_storage": { "encoding": "inplace", "label": "struct BottomUpCheckpoint", "members": [ { - "astId": 18639, + "astId": 19316, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "subnetID", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18642, + "astId": 19319, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "blockHeight", "offset": 0, @@ -208,7 +208,7 @@ "type": "t_uint256" }, { - "astId": 18645, + "astId": 19322, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "blockHash", "offset": 0, @@ -216,7 +216,7 @@ "type": "t_bytes32" }, { - "astId": 18648, + "astId": 19325, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "nextConfigurationNumber", "offset": 0, @@ -224,30 +224,30 @@ "type": "t_uint64" }, { - "astId": 18653, + "astId": 19330, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "msgs", "offset": 0, "slot": "5", - "type": "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage" + "type": "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage" } ], "numberOfBytes": "192" }, - "t_struct(BottomUpMsgBatch)18668_storage": { + "t_struct(BottomUpMsgBatch)19374_storage": { "encoding": "inplace", "label": "struct BottomUpMsgBatch", "members": [ { - "astId": 18659, + "astId": 19365, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "subnetID", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18662, + "astId": 19368, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "blockHeight", "offset": 0, @@ -255,12 +255,12 @@ "type": "t_uint256" }, { - "astId": 18667, + "astId": 19373, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "msgs", "offset": 0, "slot": "3", - "type": "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage" + "type": "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage" } ], "numberOfBytes": "128" @@ -280,12 +280,12 @@ ], "numberOfBytes": "64" }, - "t_struct(FvmAddress)18733_storage": { + "t_struct(FvmAddress)19439_storage": { "encoding": "inplace", "label": "struct FvmAddress", "members": [ { - "astId": 18730, + "astId": 19436, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "addrType", "offset": 0, @@ -293,7 +293,7 @@ "type": "t_uint8" }, { - "astId": 18732, + "astId": 19438, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "payload", "offset": 0, @@ -303,12 +303,12 @@ ], "numberOfBytes": "64" }, - "t_struct(GatewayActorStorage)11965_storage": { + "t_struct(GatewayActorStorage)11945_storage": { "encoding": "inplace", "label": "struct GatewayActorStorage", "members": [ { - "astId": 11877, + "astId": 11857, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "latestParentHeight", "offset": 0, @@ -316,7 +316,7 @@ "type": "t_uint256" }, { - "astId": 11880, + "astId": 11860, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "bottomUpCheckPeriod", "offset": 0, @@ -324,7 +324,7 @@ "type": "t_uint256" }, { - "astId": 11883, + "astId": 11863, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "bottomUpMsgBatchPeriod", "offset": 0, @@ -332,7 +332,7 @@ "type": "t_uint256" }, { - "astId": 11886, + "astId": 11866, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "bottomUpNonce", "offset": 0, @@ -340,7 +340,7 @@ "type": "t_uint64" }, { - "astId": 11889, + "astId": 11869, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "appliedTopDownNonce", "offset": 8, @@ -348,7 +348,7 @@ "type": "t_uint64" }, { - "astId": 11892, + "astId": 11872, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "totalSubnets", "offset": 16, @@ -356,7 +356,7 @@ "type": "t_uint64" }, { - "astId": 11895, + "astId": 11875, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "maxMsgsPerBottomUpBatch", "offset": 24, @@ -364,7 +364,7 @@ "type": "t_uint64" }, { - "astId": 11898, + "astId": 11878, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "majorityPercentage", "offset": 0, @@ -372,7 +372,7 @@ "type": "t_uint8" }, { - "astId": 11901, + "astId": 11881, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "commitSha", "offset": 0, @@ -380,7 +380,7 @@ "type": "t_bytes32" }, { - "astId": 11904, + "astId": 11884, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "maxTreeDepth", "offset": 0, @@ -388,7 +388,7 @@ "type": "t_uint8" }, { - "astId": 11907, + "astId": 11887, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "generalPurposeCrossMsg", "offset": 1, @@ -396,7 +396,7 @@ "type": "t_bool" }, { - "astId": 11910, + "astId": 11890, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "multiLevelCrossMsg", "offset": 2, @@ -404,87 +404,87 @@ "type": "t_bool" }, { - "astId": 11914, + "astId": 11894, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "currentMembership", "offset": 0, "slot": "7", - "type": "t_struct(Membership)18974_storage" + "type": "t_struct(Membership)19680_storage" }, { - "astId": 11918, + "astId": 11898, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "lastMembership", "offset": 0, "slot": "9", - "type": "t_struct(Membership)18974_storage" + "type": "t_struct(Membership)19680_storage" }, { - "astId": 11922, + "astId": 11902, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "checkpointQuorumMap", "offset": 0, "slot": "11", - "type": "t_struct(QuorumMap)18797_storage" + "type": "t_struct(QuorumMap)19503_storage" }, { - "astId": 11926, + "astId": 11906, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "networkName", "offset": 0, "slot": "18", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 11930, + "astId": 11910, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "validatorsTracker", "offset": 0, "slot": "20", - "type": "t_struct(ParentValidatorsTracker)18950_storage" + "type": "t_struct(ParentValidatorsTracker)19656_storage" }, { - "astId": 11936, + "astId": 11916, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "subnets", "offset": 0, "slot": "31", - "type": "t_mapping(t_bytes32,t_struct(Subnet)18829_storage)" + "type": "t_mapping(t_bytes32,t_struct(Subnet)19535_storage)" }, { - "astId": 11942, + "astId": 11922, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "finalitiesMap", "offset": 0, "slot": "32", - "type": "t_mapping(t_uint256,t_struct(ParentFinality)18634_storage)" + "type": "t_mapping(t_uint256,t_struct(ParentFinality)19311_storage)" }, { - "astId": 11948, + "astId": 11928, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "postbox", "offset": 0, "slot": "33", - "type": "t_mapping(t_bytes32,t_struct(IpcEnvelope)18702_storage)" + "type": "t_mapping(t_bytes32,t_struct(IpcEnvelope)19408_storage)" }, { - "astId": 11954, + "astId": 11934, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "bottomUpCheckpoints", "offset": 0, "slot": "34", - "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)" + "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)" }, { - "astId": 11960, + "astId": 11940, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "bottomUpMsgBatches", "offset": 0, "slot": "35", - "type": "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)18668_storage)" + "type": "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)19374_storage)" }, { - "astId": 11964, + "astId": 11944, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "subnetKeys", "offset": 0, @@ -494,59 +494,59 @@ ], "numberOfBytes": "1216" }, - "t_struct(IPCAddress)18958_storage": { + "t_struct(IPCAddress)19664_storage": { "encoding": "inplace", "label": "struct IPCAddress", "members": [ { - "astId": 18954, + "astId": 19660, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "subnetId", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18957, + "astId": 19663, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "rawAddress", "offset": 0, "slot": "2", - "type": "t_struct(FvmAddress)18733_storage" + "type": "t_struct(FvmAddress)19439_storage" } ], "numberOfBytes": "128" }, - "t_struct(IpcEnvelope)18702_storage": { + "t_struct(IpcEnvelope)19408_storage": { "encoding": "inplace", "label": "struct IpcEnvelope", "members": [ { - "astId": 18684, + "astId": 19390, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "kind", "offset": 0, "slot": "0", - "type": "t_enum(IpcMsgKind)18679" + "type": "t_enum(IpcMsgKind)19385" }, { - "astId": 18688, + "astId": 19394, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "to", "offset": 0, "slot": "1", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18692, + "astId": 19398, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "from", "offset": 0, "slot": "5", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18695, + "astId": 19401, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "nonce", "offset": 0, @@ -554,7 +554,7 @@ "type": "t_uint64" }, { - "astId": 18698, + "astId": 19404, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "value", "offset": 0, @@ -562,7 +562,7 @@ "type": "t_uint256" }, { - "astId": 18701, + "astId": 19407, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "message", "offset": 0, @@ -572,35 +572,35 @@ ], "numberOfBytes": "384" }, - "t_struct(MaxPQ)17125_storage": { + "t_struct(MaxPQ)17052_storage": { "encoding": "inplace", "label": "struct MaxPQ", "members": [ { - "astId": 17124, + "astId": 17051, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(Membership)18974_storage": { + "t_struct(Membership)19680_storage": { "encoding": "inplace", "label": "struct Membership", "members": [ { - "astId": 18971, + "astId": 19677, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "validators", "offset": 0, "slot": "0", - "type": "t_array(t_struct(Validator)18966_storage)dyn_storage" + "type": "t_array(t_struct(Validator)19672_storage)dyn_storage" }, { - "astId": 18973, + "astId": 19679, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "configurationNumber", "offset": 0, @@ -610,27 +610,27 @@ ], "numberOfBytes": "64" }, - "t_struct(MinPQ)17743_storage": { + "t_struct(MinPQ)17670_storage": { "encoding": "inplace", "label": "struct MinPQ", "members": [ { - "astId": 17742, + "astId": 17669, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(PQ)18373_storage": { + "t_struct(PQ)18300_storage": { "encoding": "inplace", "label": "struct PQ", "members": [ { - "astId": 18362, + "astId": 18289, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "size", "offset": 0, @@ -638,7 +638,7 @@ "type": "t_uint16" }, { - "astId": 18367, + "astId": 18294, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "addressToPos", "offset": 0, @@ -646,7 +646,7 @@ "type": "t_mapping(t_address,t_uint16)" }, { - "astId": 18372, + "astId": 18299, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "posToAddress", "offset": 0, @@ -656,12 +656,12 @@ ], "numberOfBytes": "96" }, - "t_struct(ParentFinality)18634_storage": { + "t_struct(ParentFinality)19311_storage": { "encoding": "inplace", "label": "struct ParentFinality", "members": [ { - "astId": 18631, + "astId": 19308, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "height", "offset": 0, @@ -669,7 +669,7 @@ "type": "t_uint256" }, { - "astId": 18633, + "astId": 19310, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "blockHash", "offset": 0, @@ -679,35 +679,35 @@ ], "numberOfBytes": "64" }, - "t_struct(ParentValidatorsTracker)18950_storage": { + "t_struct(ParentValidatorsTracker)19656_storage": { "encoding": "inplace", "label": "struct ParentValidatorsTracker", "members": [ { - "astId": 18946, + "astId": 19652, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "validators", "offset": 0, "slot": "0", - "type": "t_struct(ValidatorSet)18942_storage" + "type": "t_struct(ValidatorSet)19648_storage" }, { - "astId": 18949, + "astId": 19655, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "changes", "offset": 0, "slot": "9", - "type": "t_struct(StakingChangeLog)18865_storage" + "type": "t_struct(StakingChangeLog)19571_storage" } ], "numberOfBytes": "352" }, - "t_struct(QuorumInfo)18765_storage": { + "t_struct(QuorumInfo)19471_storage": { "encoding": "inplace", "label": "struct QuorumInfo", "members": [ { - "astId": 18752, + "astId": 19458, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "hash", "offset": 0, @@ -715,7 +715,7 @@ "type": "t_bytes32" }, { - "astId": 18755, + "astId": 19461, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "rootHash", "offset": 0, @@ -723,7 +723,7 @@ "type": "t_bytes32" }, { - "astId": 18758, + "astId": 19464, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "threshold", "offset": 0, @@ -731,7 +731,7 @@ "type": "t_uint256" }, { - "astId": 18761, + "astId": 19467, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "currentWeight", "offset": 0, @@ -739,7 +739,7 @@ "type": "t_uint256" }, { - "astId": 18764, + "astId": 19470, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "reached", "offset": 0, @@ -749,20 +749,20 @@ ], "numberOfBytes": "160" }, - "t_struct(QuorumMap)18797_storage": { + "t_struct(QuorumMap)19503_storage": { "encoding": "inplace", "label": "struct QuorumMap", "members": [ { - "astId": 18770, + "astId": 19476, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "quorumObjKind", "offset": 0, "slot": "0", - "type": "t_enum(QuorumObjKind)18748" + "type": "t_enum(QuorumObjKind)19454" }, { - "astId": 18773, + "astId": 19479, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "retentionHeight", "offset": 0, @@ -770,15 +770,15 @@ "type": "t_uint256" }, { - "astId": 18779, + "astId": 19485, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "quorumInfo", "offset": 0, "slot": "2", - "type": "t_mapping(t_uint256,t_struct(QuorumInfo)18765_storage)" + "type": "t_mapping(t_uint256,t_struct(QuorumInfo)19471_storage)" }, { - "astId": 18783, + "astId": 19489, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "incompleteQuorums", "offset": 0, @@ -786,7 +786,7 @@ "type": "t_struct(UintSet)3616_storage" }, { - "astId": 18789, + "astId": 19495, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "quorumSignatureSenders", "offset": 0, @@ -794,7 +794,7 @@ "type": "t_mapping(t_uint256,t_struct(AddressSet)3459_storage)" }, { - "astId": 18796, + "astId": 19502, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "quorumSignatures", "offset": 0, @@ -827,20 +827,20 @@ ], "numberOfBytes": "64" }, - "t_struct(StakingChange)18844_storage": { + "t_struct(StakingChange)19550_storage": { "encoding": "inplace", "label": "struct StakingChange", "members": [ { - "astId": 18839, + "astId": 19545, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "op", "offset": 0, "slot": "0", - "type": "t_enum(StakingOperation)18835" + "type": "t_enum(StakingOperation)19541" }, { - "astId": 18841, + "astId": 19547, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "payload", "offset": 0, @@ -848,7 +848,7 @@ "type": "t_bytes_storage" }, { - "astId": 18843, + "astId": 19549, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "validator", "offset": 0, @@ -858,12 +858,12 @@ ], "numberOfBytes": "96" }, - "t_struct(StakingChangeLog)18865_storage": { + "t_struct(StakingChangeLog)19571_storage": { "encoding": "inplace", "label": "struct StakingChangeLog", "members": [ { - "astId": 18855, + "astId": 19561, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "nextConfigurationNumber", "offset": 0, @@ -871,7 +871,7 @@ "type": "t_uint64" }, { - "astId": 18858, + "astId": 19564, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "startConfigurationNumber", "offset": 8, @@ -879,22 +879,22 @@ "type": "t_uint64" }, { - "astId": 18864, + "astId": 19570, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "changes", "offset": 0, "slot": "1", - "type": "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)" + "type": "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)" } ], "numberOfBytes": "64" }, - "t_struct(Subnet)18829_storage": { + "t_struct(Subnet)19535_storage": { "encoding": "inplace", "label": "struct Subnet", "members": [ { - "astId": 18817, + "astId": 19523, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "stake", "offset": 0, @@ -902,7 +902,7 @@ "type": "t_uint256" }, { - "astId": 18819, + "astId": 19525, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "genesisEpoch", "offset": 0, @@ -910,7 +910,7 @@ "type": "t_uint256" }, { - "astId": 18821, + "astId": 19527, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "circSupply", "offset": 0, @@ -918,7 +918,7 @@ "type": "t_uint256" }, { - "astId": 18823, + "astId": 19529, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "topDownNonce", "offset": 0, @@ -926,7 +926,7 @@ "type": "t_uint64" }, { - "astId": 18825, + "astId": 19531, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "appliedBottomUpNonce", "offset": 8, @@ -934,22 +934,22 @@ "type": "t_uint64" }, { - "astId": 18828, + "astId": 19534, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "id", "offset": 0, "slot": "4", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" } ], "numberOfBytes": "192" }, - "t_struct(SubnetID)18814_storage": { + "t_struct(SubnetID)19520_storage": { "encoding": "inplace", "label": "struct SubnetID", "members": [ { - "astId": 18809, + "astId": 19515, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "root", "offset": 0, @@ -957,7 +957,7 @@ "type": "t_uint64" }, { - "astId": 18813, + "astId": 19519, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "route", "offset": 0, @@ -982,12 +982,12 @@ ], "numberOfBytes": "64" }, - "t_struct(Validator)18966_storage": { + "t_struct(Validator)19672_storage": { "encoding": "inplace", "label": "struct Validator", "members": [ { - "astId": 18961, + "astId": 19667, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "weight", "offset": 0, @@ -995,7 +995,7 @@ "type": "t_uint256" }, { - "astId": 18963, + "astId": 19669, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "addr", "offset": 0, @@ -1003,7 +1003,7 @@ "type": "t_address" }, { - "astId": 18965, + "astId": 19671, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "metadata", "offset": 0, @@ -1013,12 +1013,12 @@ ], "numberOfBytes": "96" }, - "t_struct(ValidatorInfo)18907_storage": { + "t_struct(ValidatorInfo)19613_storage": { "encoding": "inplace", "label": "struct ValidatorInfo", "members": [ { - "astId": 18899, + "astId": 19605, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "federatedPower", "offset": 0, @@ -1026,7 +1026,7 @@ "type": "t_uint256" }, { - "astId": 18901, + "astId": 19607, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "confirmedCollateral", "offset": 0, @@ -1034,7 +1034,7 @@ "type": "t_uint256" }, { - "astId": 18903, + "astId": 19609, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "totalCollateral", "offset": 0, @@ -1042,7 +1042,7 @@ "type": "t_uint256" }, { - "astId": 18906, + "astId": 19612, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "metadata", "offset": 0, @@ -1052,20 +1052,20 @@ ], "numberOfBytes": "128" }, - "t_struct(ValidatorSet)18942_storage": { + "t_struct(ValidatorSet)19648_storage": { "encoding": "inplace", "label": "struct ValidatorSet", "members": [ { - "astId": 18921, + "astId": 19627, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "permissionMode", "offset": 0, "slot": "0", - "type": "t_enum(PermissionMode)18912" + "type": "t_enum(PermissionMode)19618" }, { - "astId": 18924, + "astId": 19630, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "activeLimit", "offset": 1, @@ -1073,7 +1073,7 @@ "type": "t_uint16" }, { - "astId": 18927, + "astId": 19633, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "totalConfirmedCollateral", "offset": 0, @@ -1081,28 +1081,28 @@ "type": "t_uint256" }, { - "astId": 18933, + "astId": 19639, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "validators", "offset": 0, "slot": "2", - "type": "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)" + "type": "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)" }, { - "astId": 18937, + "astId": 19643, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "activeValidators", "offset": 0, "slot": "3", - "type": "t_struct(MinPQ)17743_storage" + "type": "t_struct(MinPQ)17670_storage" }, { - "astId": 18941, + "astId": 19647, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "waitingValidators", "offset": 0, "slot": "6", - "type": "t_struct(MaxPQ)17125_storage" + "type": "t_struct(MaxPQ)17052_storage" } ], "numberOfBytes": "288" diff --git a/contracts/.storage-layouts/GatewayDiamond.json b/contracts/.storage-layouts/GatewayDiamond.json index 8d68e6165..a40c2ec87 100644 --- a/contracts/.storage-layouts/GatewayDiamond.json +++ b/contracts/.storage-layouts/GatewayDiamond.json @@ -6,7 +6,7 @@ "label": "s", "offset": 0, "slot": "0", - "type": "t_struct(GatewayActorStorage)11965_storage" + "type": "t_struct(GatewayActorStorage)11945_storage" } ], "types": { @@ -27,14 +27,14 @@ "label": "bytes32[]", "numberOfBytes": "32" }, - "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage": { - "base": "t_struct(IpcEnvelope)18702_storage", + "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage": { + "base": "t_struct(IpcEnvelope)19408_storage", "encoding": "dynamic_array", "label": "struct IpcEnvelope[]", "numberOfBytes": "32" }, - "t_array(t_struct(Validator)18966_storage)dyn_storage": { - "base": "t_struct(Validator)18966_storage", + "t_array(t_struct(Validator)19672_storage)dyn_storage": { + "base": "t_struct(Validator)19672_storage", "encoding": "dynamic_array", "label": "struct Validator[]", "numberOfBytes": "32" @@ -54,22 +54,22 @@ "label": "bytes", "numberOfBytes": "32" }, - "t_enum(IpcMsgKind)18679": { + "t_enum(IpcMsgKind)19385": { "encoding": "inplace", "label": "enum IpcMsgKind", "numberOfBytes": "1" }, - "t_enum(PermissionMode)18912": { + "t_enum(PermissionMode)19618": { "encoding": "inplace", "label": "enum PermissionMode", "numberOfBytes": "1" }, - "t_enum(QuorumObjKind)18748": { + "t_enum(QuorumObjKind)19454": { "encoding": "inplace", "label": "enum QuorumObjKind", "numberOfBytes": "1" }, - "t_enum(StakingOperation)18835": { + "t_enum(StakingOperation)19541": { "encoding": "inplace", "label": "enum StakingOperation", "numberOfBytes": "1" @@ -81,12 +81,12 @@ "numberOfBytes": "32", "value": "t_bytes_storage" }, - "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)": { + "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct ValidatorInfo)", "numberOfBytes": "32", - "value": "t_struct(ValidatorInfo)18907_storage" + "value": "t_struct(ValidatorInfo)19613_storage" }, "t_mapping(t_address,t_uint16)": { "encoding": "mapping", @@ -95,19 +95,19 @@ "numberOfBytes": "32", "value": "t_uint16" }, - "t_mapping(t_bytes32,t_struct(IpcEnvelope)18702_storage)": { + "t_mapping(t_bytes32,t_struct(IpcEnvelope)19408_storage)": { "encoding": "mapping", "key": "t_bytes32", "label": "mapping(bytes32 => struct IpcEnvelope)", "numberOfBytes": "32", - "value": "t_struct(IpcEnvelope)18702_storage" + "value": "t_struct(IpcEnvelope)19408_storage" }, - "t_mapping(t_bytes32,t_struct(Subnet)18829_storage)": { + "t_mapping(t_bytes32,t_struct(Subnet)19535_storage)": { "encoding": "mapping", "key": "t_bytes32", "label": "mapping(bytes32 => struct Subnet)", "numberOfBytes": "32", - "value": "t_struct(Subnet)18829_storage" + "value": "t_struct(Subnet)19535_storage" }, "t_mapping(t_bytes32,t_uint256)": { "encoding": "mapping", @@ -137,40 +137,40 @@ "numberOfBytes": "32", "value": "t_struct(AddressSet)3459_storage" }, - "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)": { + "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct BottomUpCheckpoint)", "numberOfBytes": "32", - "value": "t_struct(BottomUpCheckpoint)18654_storage" + "value": "t_struct(BottomUpCheckpoint)19331_storage" }, - "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)18668_storage)": { + "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)19374_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct BottomUpMsgBatch)", "numberOfBytes": "32", - "value": "t_struct(BottomUpMsgBatch)18668_storage" + "value": "t_struct(BottomUpMsgBatch)19374_storage" }, - "t_mapping(t_uint256,t_struct(ParentFinality)18634_storage)": { + "t_mapping(t_uint256,t_struct(ParentFinality)19311_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct ParentFinality)", "numberOfBytes": "32", - "value": "t_struct(ParentFinality)18634_storage" + "value": "t_struct(ParentFinality)19311_storage" }, - "t_mapping(t_uint256,t_struct(QuorumInfo)18765_storage)": { + "t_mapping(t_uint256,t_struct(QuorumInfo)19471_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct QuorumInfo)", "numberOfBytes": "32", - "value": "t_struct(QuorumInfo)18765_storage" + "value": "t_struct(QuorumInfo)19471_storage" }, - "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)": { + "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)": { "encoding": "mapping", "key": "t_uint64", "label": "mapping(uint64 => struct StakingChange)", "numberOfBytes": "32", - "value": "t_struct(StakingChange)18844_storage" + "value": "t_struct(StakingChange)19550_storage" }, "t_struct(AddressSet)3459_storage": { "encoding": "inplace", @@ -187,20 +187,20 @@ ], "numberOfBytes": "64" }, - "t_struct(BottomUpCheckpoint)18654_storage": { + "t_struct(BottomUpCheckpoint)19331_storage": { "encoding": "inplace", "label": "struct BottomUpCheckpoint", "members": [ { - "astId": 18639, + "astId": 19316, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "subnetID", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18642, + "astId": 19319, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "blockHeight", "offset": 0, @@ -208,7 +208,7 @@ "type": "t_uint256" }, { - "astId": 18645, + "astId": 19322, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "blockHash", "offset": 0, @@ -216,7 +216,7 @@ "type": "t_bytes32" }, { - "astId": 18648, + "astId": 19325, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "nextConfigurationNumber", "offset": 0, @@ -224,30 +224,30 @@ "type": "t_uint64" }, { - "astId": 18653, + "astId": 19330, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "msgs", "offset": 0, "slot": "5", - "type": "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage" + "type": "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage" } ], "numberOfBytes": "192" }, - "t_struct(BottomUpMsgBatch)18668_storage": { + "t_struct(BottomUpMsgBatch)19374_storage": { "encoding": "inplace", "label": "struct BottomUpMsgBatch", "members": [ { - "astId": 18659, + "astId": 19365, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "subnetID", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18662, + "astId": 19368, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "blockHeight", "offset": 0, @@ -255,12 +255,12 @@ "type": "t_uint256" }, { - "astId": 18667, + "astId": 19373, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "msgs", "offset": 0, "slot": "3", - "type": "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage" + "type": "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage" } ], "numberOfBytes": "128" @@ -280,12 +280,12 @@ ], "numberOfBytes": "64" }, - "t_struct(FvmAddress)18733_storage": { + "t_struct(FvmAddress)19439_storage": { "encoding": "inplace", "label": "struct FvmAddress", "members": [ { - "astId": 18730, + "astId": 19436, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "addrType", "offset": 0, @@ -293,7 +293,7 @@ "type": "t_uint8" }, { - "astId": 18732, + "astId": 19438, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "payload", "offset": 0, @@ -303,12 +303,12 @@ ], "numberOfBytes": "64" }, - "t_struct(GatewayActorStorage)11965_storage": { + "t_struct(GatewayActorStorage)11945_storage": { "encoding": "inplace", "label": "struct GatewayActorStorage", "members": [ { - "astId": 11877, + "astId": 11857, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "latestParentHeight", "offset": 0, @@ -316,7 +316,7 @@ "type": "t_uint256" }, { - "astId": 11880, + "astId": 11860, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "bottomUpCheckPeriod", "offset": 0, @@ -324,7 +324,7 @@ "type": "t_uint256" }, { - "astId": 11883, + "astId": 11863, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "bottomUpMsgBatchPeriod", "offset": 0, @@ -332,7 +332,7 @@ "type": "t_uint256" }, { - "astId": 11886, + "astId": 11866, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "bottomUpNonce", "offset": 0, @@ -340,7 +340,7 @@ "type": "t_uint64" }, { - "astId": 11889, + "astId": 11869, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "appliedTopDownNonce", "offset": 8, @@ -348,7 +348,7 @@ "type": "t_uint64" }, { - "astId": 11892, + "astId": 11872, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "totalSubnets", "offset": 16, @@ -356,7 +356,7 @@ "type": "t_uint64" }, { - "astId": 11895, + "astId": 11875, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "maxMsgsPerBottomUpBatch", "offset": 24, @@ -364,7 +364,7 @@ "type": "t_uint64" }, { - "astId": 11898, + "astId": 11878, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "majorityPercentage", "offset": 0, @@ -372,7 +372,7 @@ "type": "t_uint8" }, { - "astId": 11901, + "astId": 11881, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "commitSha", "offset": 0, @@ -380,7 +380,7 @@ "type": "t_bytes32" }, { - "astId": 11904, + "astId": 11884, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "maxTreeDepth", "offset": 0, @@ -388,7 +388,7 @@ "type": "t_uint8" }, { - "astId": 11907, + "astId": 11887, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "generalPurposeCrossMsg", "offset": 1, @@ -396,7 +396,7 @@ "type": "t_bool" }, { - "astId": 11910, + "astId": 11890, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "multiLevelCrossMsg", "offset": 2, @@ -404,87 +404,87 @@ "type": "t_bool" }, { - "astId": 11914, + "astId": 11894, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "currentMembership", "offset": 0, "slot": "7", - "type": "t_struct(Membership)18974_storage" + "type": "t_struct(Membership)19680_storage" }, { - "astId": 11918, + "astId": 11898, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "lastMembership", "offset": 0, "slot": "9", - "type": "t_struct(Membership)18974_storage" + "type": "t_struct(Membership)19680_storage" }, { - "astId": 11922, + "astId": 11902, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "checkpointQuorumMap", "offset": 0, "slot": "11", - "type": "t_struct(QuorumMap)18797_storage" + "type": "t_struct(QuorumMap)19503_storage" }, { - "astId": 11926, + "astId": 11906, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "networkName", "offset": 0, "slot": "18", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 11930, + "astId": 11910, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "validatorsTracker", "offset": 0, "slot": "20", - "type": "t_struct(ParentValidatorsTracker)18950_storage" + "type": "t_struct(ParentValidatorsTracker)19656_storage" }, { - "astId": 11936, + "astId": 11916, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "subnets", "offset": 0, "slot": "31", - "type": "t_mapping(t_bytes32,t_struct(Subnet)18829_storage)" + "type": "t_mapping(t_bytes32,t_struct(Subnet)19535_storage)" }, { - "astId": 11942, + "astId": 11922, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "finalitiesMap", "offset": 0, "slot": "32", - "type": "t_mapping(t_uint256,t_struct(ParentFinality)18634_storage)" + "type": "t_mapping(t_uint256,t_struct(ParentFinality)19311_storage)" }, { - "astId": 11948, + "astId": 11928, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "postbox", "offset": 0, "slot": "33", - "type": "t_mapping(t_bytes32,t_struct(IpcEnvelope)18702_storage)" + "type": "t_mapping(t_bytes32,t_struct(IpcEnvelope)19408_storage)" }, { - "astId": 11954, + "astId": 11934, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "bottomUpCheckpoints", "offset": 0, "slot": "34", - "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)" + "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)" }, { - "astId": 11960, + "astId": 11940, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "bottomUpMsgBatches", "offset": 0, "slot": "35", - "type": "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)18668_storage)" + "type": "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)19374_storage)" }, { - "astId": 11964, + "astId": 11944, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "subnetKeys", "offset": 0, @@ -494,59 +494,59 @@ ], "numberOfBytes": "1216" }, - "t_struct(IPCAddress)18958_storage": { + "t_struct(IPCAddress)19664_storage": { "encoding": "inplace", "label": "struct IPCAddress", "members": [ { - "astId": 18954, + "astId": 19660, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "subnetId", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18957, + "astId": 19663, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "rawAddress", "offset": 0, "slot": "2", - "type": "t_struct(FvmAddress)18733_storage" + "type": "t_struct(FvmAddress)19439_storage" } ], "numberOfBytes": "128" }, - "t_struct(IpcEnvelope)18702_storage": { + "t_struct(IpcEnvelope)19408_storage": { "encoding": "inplace", "label": "struct IpcEnvelope", "members": [ { - "astId": 18684, + "astId": 19390, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "kind", "offset": 0, "slot": "0", - "type": "t_enum(IpcMsgKind)18679" + "type": "t_enum(IpcMsgKind)19385" }, { - "astId": 18688, + "astId": 19394, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "to", "offset": 0, "slot": "1", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18692, + "astId": 19398, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "from", "offset": 0, "slot": "5", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18695, + "astId": 19401, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "nonce", "offset": 0, @@ -554,7 +554,7 @@ "type": "t_uint64" }, { - "astId": 18698, + "astId": 19404, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "value", "offset": 0, @@ -562,7 +562,7 @@ "type": "t_uint256" }, { - "astId": 18701, + "astId": 19407, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "message", "offset": 0, @@ -572,35 +572,35 @@ ], "numberOfBytes": "384" }, - "t_struct(MaxPQ)17125_storage": { + "t_struct(MaxPQ)17052_storage": { "encoding": "inplace", "label": "struct MaxPQ", "members": [ { - "astId": 17124, + "astId": 17051, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(Membership)18974_storage": { + "t_struct(Membership)19680_storage": { "encoding": "inplace", "label": "struct Membership", "members": [ { - "astId": 18971, + "astId": 19677, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "validators", "offset": 0, "slot": "0", - "type": "t_array(t_struct(Validator)18966_storage)dyn_storage" + "type": "t_array(t_struct(Validator)19672_storage)dyn_storage" }, { - "astId": 18973, + "astId": 19679, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "configurationNumber", "offset": 0, @@ -610,27 +610,27 @@ ], "numberOfBytes": "64" }, - "t_struct(MinPQ)17743_storage": { + "t_struct(MinPQ)17670_storage": { "encoding": "inplace", "label": "struct MinPQ", "members": [ { - "astId": 17742, + "astId": 17669, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(PQ)18373_storage": { + "t_struct(PQ)18300_storage": { "encoding": "inplace", "label": "struct PQ", "members": [ { - "astId": 18362, + "astId": 18289, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "size", "offset": 0, @@ -638,7 +638,7 @@ "type": "t_uint16" }, { - "astId": 18367, + "astId": 18294, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "addressToPos", "offset": 0, @@ -646,7 +646,7 @@ "type": "t_mapping(t_address,t_uint16)" }, { - "astId": 18372, + "astId": 18299, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "posToAddress", "offset": 0, @@ -656,12 +656,12 @@ ], "numberOfBytes": "96" }, - "t_struct(ParentFinality)18634_storage": { + "t_struct(ParentFinality)19311_storage": { "encoding": "inplace", "label": "struct ParentFinality", "members": [ { - "astId": 18631, + "astId": 19308, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "height", "offset": 0, @@ -669,7 +669,7 @@ "type": "t_uint256" }, { - "astId": 18633, + "astId": 19310, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "blockHash", "offset": 0, @@ -679,35 +679,35 @@ ], "numberOfBytes": "64" }, - "t_struct(ParentValidatorsTracker)18950_storage": { + "t_struct(ParentValidatorsTracker)19656_storage": { "encoding": "inplace", "label": "struct ParentValidatorsTracker", "members": [ { - "astId": 18946, + "astId": 19652, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "validators", "offset": 0, "slot": "0", - "type": "t_struct(ValidatorSet)18942_storage" + "type": "t_struct(ValidatorSet)19648_storage" }, { - "astId": 18949, + "astId": 19655, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "changes", "offset": 0, "slot": "9", - "type": "t_struct(StakingChangeLog)18865_storage" + "type": "t_struct(StakingChangeLog)19571_storage" } ], "numberOfBytes": "352" }, - "t_struct(QuorumInfo)18765_storage": { + "t_struct(QuorumInfo)19471_storage": { "encoding": "inplace", "label": "struct QuorumInfo", "members": [ { - "astId": 18752, + "astId": 19458, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "hash", "offset": 0, @@ -715,7 +715,7 @@ "type": "t_bytes32" }, { - "astId": 18755, + "astId": 19461, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "rootHash", "offset": 0, @@ -723,7 +723,7 @@ "type": "t_bytes32" }, { - "astId": 18758, + "astId": 19464, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "threshold", "offset": 0, @@ -731,7 +731,7 @@ "type": "t_uint256" }, { - "astId": 18761, + "astId": 19467, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "currentWeight", "offset": 0, @@ -739,7 +739,7 @@ "type": "t_uint256" }, { - "astId": 18764, + "astId": 19470, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "reached", "offset": 0, @@ -749,20 +749,20 @@ ], "numberOfBytes": "160" }, - "t_struct(QuorumMap)18797_storage": { + "t_struct(QuorumMap)19503_storage": { "encoding": "inplace", "label": "struct QuorumMap", "members": [ { - "astId": 18770, + "astId": 19476, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "quorumObjKind", "offset": 0, "slot": "0", - "type": "t_enum(QuorumObjKind)18748" + "type": "t_enum(QuorumObjKind)19454" }, { - "astId": 18773, + "astId": 19479, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "retentionHeight", "offset": 0, @@ -770,15 +770,15 @@ "type": "t_uint256" }, { - "astId": 18779, + "astId": 19485, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "quorumInfo", "offset": 0, "slot": "2", - "type": "t_mapping(t_uint256,t_struct(QuorumInfo)18765_storage)" + "type": "t_mapping(t_uint256,t_struct(QuorumInfo)19471_storage)" }, { - "astId": 18783, + "astId": 19489, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "incompleteQuorums", "offset": 0, @@ -786,7 +786,7 @@ "type": "t_struct(UintSet)3616_storage" }, { - "astId": 18789, + "astId": 19495, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "quorumSignatureSenders", "offset": 0, @@ -794,7 +794,7 @@ "type": "t_mapping(t_uint256,t_struct(AddressSet)3459_storage)" }, { - "astId": 18796, + "astId": 19502, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "quorumSignatures", "offset": 0, @@ -827,20 +827,20 @@ ], "numberOfBytes": "64" }, - "t_struct(StakingChange)18844_storage": { + "t_struct(StakingChange)19550_storage": { "encoding": "inplace", "label": "struct StakingChange", "members": [ { - "astId": 18839, + "astId": 19545, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "op", "offset": 0, "slot": "0", - "type": "t_enum(StakingOperation)18835" + "type": "t_enum(StakingOperation)19541" }, { - "astId": 18841, + "astId": 19547, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "payload", "offset": 0, @@ -848,7 +848,7 @@ "type": "t_bytes_storage" }, { - "astId": 18843, + "astId": 19549, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "validator", "offset": 0, @@ -858,12 +858,12 @@ ], "numberOfBytes": "96" }, - "t_struct(StakingChangeLog)18865_storage": { + "t_struct(StakingChangeLog)19571_storage": { "encoding": "inplace", "label": "struct StakingChangeLog", "members": [ { - "astId": 18855, + "astId": 19561, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "nextConfigurationNumber", "offset": 0, @@ -871,7 +871,7 @@ "type": "t_uint64" }, { - "astId": 18858, + "astId": 19564, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "startConfigurationNumber", "offset": 8, @@ -879,22 +879,22 @@ "type": "t_uint64" }, { - "astId": 18864, + "astId": 19570, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "changes", "offset": 0, "slot": "1", - "type": "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)" + "type": "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)" } ], "numberOfBytes": "64" }, - "t_struct(Subnet)18829_storage": { + "t_struct(Subnet)19535_storage": { "encoding": "inplace", "label": "struct Subnet", "members": [ { - "astId": 18817, + "astId": 19523, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "stake", "offset": 0, @@ -902,7 +902,7 @@ "type": "t_uint256" }, { - "astId": 18819, + "astId": 19525, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "genesisEpoch", "offset": 0, @@ -910,7 +910,7 @@ "type": "t_uint256" }, { - "astId": 18821, + "astId": 19527, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "circSupply", "offset": 0, @@ -918,7 +918,7 @@ "type": "t_uint256" }, { - "astId": 18823, + "astId": 19529, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "topDownNonce", "offset": 0, @@ -926,7 +926,7 @@ "type": "t_uint64" }, { - "astId": 18825, + "astId": 19531, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "appliedBottomUpNonce", "offset": 8, @@ -934,22 +934,22 @@ "type": "t_uint64" }, { - "astId": 18828, + "astId": 19534, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "id", "offset": 0, "slot": "4", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" } ], "numberOfBytes": "192" }, - "t_struct(SubnetID)18814_storage": { + "t_struct(SubnetID)19520_storage": { "encoding": "inplace", "label": "struct SubnetID", "members": [ { - "astId": 18809, + "astId": 19515, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "root", "offset": 0, @@ -957,7 +957,7 @@ "type": "t_uint64" }, { - "astId": 18813, + "astId": 19519, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "route", "offset": 0, @@ -982,12 +982,12 @@ ], "numberOfBytes": "64" }, - "t_struct(Validator)18966_storage": { + "t_struct(Validator)19672_storage": { "encoding": "inplace", "label": "struct Validator", "members": [ { - "astId": 18961, + "astId": 19667, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "weight", "offset": 0, @@ -995,7 +995,7 @@ "type": "t_uint256" }, { - "astId": 18963, + "astId": 19669, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "addr", "offset": 0, @@ -1003,7 +1003,7 @@ "type": "t_address" }, { - "astId": 18965, + "astId": 19671, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "metadata", "offset": 0, @@ -1013,12 +1013,12 @@ ], "numberOfBytes": "96" }, - "t_struct(ValidatorInfo)18907_storage": { + "t_struct(ValidatorInfo)19613_storage": { "encoding": "inplace", "label": "struct ValidatorInfo", "members": [ { - "astId": 18899, + "astId": 19605, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "federatedPower", "offset": 0, @@ -1026,7 +1026,7 @@ "type": "t_uint256" }, { - "astId": 18901, + "astId": 19607, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "confirmedCollateral", "offset": 0, @@ -1034,7 +1034,7 @@ "type": "t_uint256" }, { - "astId": 18903, + "astId": 19609, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "totalCollateral", "offset": 0, @@ -1042,7 +1042,7 @@ "type": "t_uint256" }, { - "astId": 18906, + "astId": 19612, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "metadata", "offset": 0, @@ -1052,20 +1052,20 @@ ], "numberOfBytes": "128" }, - "t_struct(ValidatorSet)18942_storage": { + "t_struct(ValidatorSet)19648_storage": { "encoding": "inplace", "label": "struct ValidatorSet", "members": [ { - "astId": 18921, + "astId": 19627, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "permissionMode", "offset": 0, "slot": "0", - "type": "t_enum(PermissionMode)18912" + "type": "t_enum(PermissionMode)19618" }, { - "astId": 18924, + "astId": 19630, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "activeLimit", "offset": 1, @@ -1073,7 +1073,7 @@ "type": "t_uint16" }, { - "astId": 18927, + "astId": 19633, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "totalConfirmedCollateral", "offset": 0, @@ -1081,28 +1081,28 @@ "type": "t_uint256" }, { - "astId": 18933, + "astId": 19639, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "validators", "offset": 0, "slot": "2", - "type": "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)" + "type": "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)" }, { - "astId": 18937, + "astId": 19643, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "activeValidators", "offset": 0, "slot": "3", - "type": "t_struct(MinPQ)17743_storage" + "type": "t_struct(MinPQ)17670_storage" }, { - "astId": 18941, + "astId": 19647, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "waitingValidators", "offset": 0, "slot": "6", - "type": "t_struct(MaxPQ)17125_storage" + "type": "t_struct(MaxPQ)17052_storage" } ], "numberOfBytes": "288" diff --git a/contracts/.storage-layouts/SubnetActorDiamond.json b/contracts/.storage-layouts/SubnetActorDiamond.json index 1a7511cd6..0d89b7492 100644 --- a/contracts/.storage-layouts/SubnetActorDiamond.json +++ b/contracts/.storage-layouts/SubnetActorDiamond.json @@ -6,7 +6,7 @@ "label": "s", "offset": 0, "slot": "0", - "type": "t_struct(SubnetActorStorage)16379_storage" + "type": "t_struct(SubnetActorStorage)16306_storage" } ], "types": { @@ -27,14 +27,14 @@ "label": "bytes32[]", "numberOfBytes": "32" }, - "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage": { - "base": "t_struct(IpcEnvelope)18702_storage", + "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage": { + "base": "t_struct(IpcEnvelope)19408_storage", "encoding": "dynamic_array", "label": "struct IpcEnvelope[]", "numberOfBytes": "32" }, - "t_array(t_struct(Validator)18966_storage)dyn_storage": { - "base": "t_struct(Validator)18966_storage", + "t_array(t_struct(Validator)19672_storage)dyn_storage": { + "base": "t_struct(Validator)19672_storage", "encoding": "dynamic_array", "label": "struct Validator[]", "numberOfBytes": "32" @@ -54,7 +54,7 @@ "label": "bytes", "numberOfBytes": "32" }, - "t_enum(AssetKind)18987": { + "t_enum(AssetKind)19693": { "encoding": "inplace", "label": "enum AssetKind", "numberOfBytes": "1" @@ -64,17 +64,17 @@ "label": "enum ConsensusType", "numberOfBytes": "1" }, - "t_enum(IpcMsgKind)18679": { + "t_enum(IpcMsgKind)19385": { "encoding": "inplace", "label": "enum IpcMsgKind", "numberOfBytes": "1" }, - "t_enum(PermissionMode)18912": { + "t_enum(PermissionMode)19618": { "encoding": "inplace", "label": "enum PermissionMode", "numberOfBytes": "1" }, - "t_enum(StakingOperation)18835": { + "t_enum(StakingOperation)19541": { "encoding": "inplace", "label": "enum StakingOperation", "numberOfBytes": "1" @@ -91,19 +91,19 @@ "numberOfBytes": "32", "value": "t_string_storage" }, - "t_mapping(t_address,t_struct(AddressStakingReleases)18884_storage)": { + "t_mapping(t_address,t_struct(AddressStakingReleases)19590_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct AddressStakingReleases)", "numberOfBytes": "32", - "value": "t_struct(AddressStakingReleases)18884_storage" + "value": "t_struct(AddressStakingReleases)19590_storage" }, - "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)": { + "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct ValidatorInfo)", "numberOfBytes": "32", - "value": "t_struct(ValidatorInfo)18907_storage" + "value": "t_struct(ValidatorInfo)19613_storage" }, "t_mapping(t_address,t_uint16)": { "encoding": "mapping", @@ -133,26 +133,26 @@ "numberOfBytes": "32", "value": "t_address" }, - "t_mapping(t_uint16,t_struct(StakingRelease)18873_storage)": { + "t_mapping(t_uint16,t_struct(StakingRelease)19579_storage)": { "encoding": "mapping", "key": "t_uint16", "label": "mapping(uint16 => struct StakingRelease)", "numberOfBytes": "32", - "value": "t_struct(StakingRelease)18873_storage" + "value": "t_struct(StakingRelease)19579_storage" }, - "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)": { + "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct BottomUpCheckpoint)", "numberOfBytes": "32", - "value": "t_struct(BottomUpCheckpoint)18654_storage" + "value": "t_struct(BottomUpCheckpoint)19331_storage" }, - "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)": { + "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)": { "encoding": "mapping", "key": "t_uint64", "label": "mapping(uint64 => struct StakingChange)", "numberOfBytes": "32", - "value": "t_struct(StakingChange)18844_storage" + "value": "t_struct(StakingChange)19550_storage" }, "t_string_storage": { "encoding": "bytes", @@ -174,12 +174,12 @@ ], "numberOfBytes": "64" }, - "t_struct(AddressStakingReleases)18884_storage": { + "t_struct(AddressStakingReleases)19590_storage": { "encoding": "inplace", "label": "struct AddressStakingReleases", "members": [ { - "astId": 18876, + "astId": 19582, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "length", "offset": 0, @@ -187,7 +187,7 @@ "type": "t_uint16" }, { - "astId": 18878, + "astId": 19584, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "startIdx", "offset": 2, @@ -195,30 +195,30 @@ "type": "t_uint16" }, { - "astId": 18883, + "astId": 19589, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "releases", "offset": 0, "slot": "1", - "type": "t_mapping(t_uint16,t_struct(StakingRelease)18873_storage)" + "type": "t_mapping(t_uint16,t_struct(StakingRelease)19579_storage)" } ], "numberOfBytes": "64" }, - "t_struct(Asset)18983_storage": { + "t_struct(Asset)19689_storage": { "encoding": "inplace", "label": "struct Asset", "members": [ { - "astId": 18979, + "astId": 19685, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "kind", "offset": 0, "slot": "0", - "type": "t_enum(AssetKind)18987" + "type": "t_enum(AssetKind)19693" }, { - "astId": 18982, + "astId": 19688, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "tokenAddress", "offset": 1, @@ -228,20 +228,20 @@ ], "numberOfBytes": "32" }, - "t_struct(BottomUpCheckpoint)18654_storage": { + "t_struct(BottomUpCheckpoint)19331_storage": { "encoding": "inplace", "label": "struct BottomUpCheckpoint", "members": [ { - "astId": 18639, + "astId": 19316, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "subnetID", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18642, + "astId": 19319, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "blockHeight", "offset": 0, @@ -249,7 +249,7 @@ "type": "t_uint256" }, { - "astId": 18645, + "astId": 19322, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "blockHash", "offset": 0, @@ -257,7 +257,7 @@ "type": "t_bytes32" }, { - "astId": 18648, + "astId": 19325, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "nextConfigurationNumber", "offset": 0, @@ -265,22 +265,22 @@ "type": "t_uint64" }, { - "astId": 18653, + "astId": 19330, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "msgs", "offset": 0, "slot": "5", - "type": "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage" + "type": "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage" } ], "numberOfBytes": "192" }, - "t_struct(FvmAddress)18733_storage": { + "t_struct(FvmAddress)19439_storage": { "encoding": "inplace", "label": "struct FvmAddress", "members": [ { - "astId": 18730, + "astId": 19436, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "addrType", "offset": 0, @@ -288,7 +288,7 @@ "type": "t_uint8" }, { - "astId": 18732, + "astId": 19438, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "payload", "offset": 0, @@ -298,59 +298,59 @@ ], "numberOfBytes": "64" }, - "t_struct(IPCAddress)18958_storage": { + "t_struct(IPCAddress)19664_storage": { "encoding": "inplace", "label": "struct IPCAddress", "members": [ { - "astId": 18954, + "astId": 19660, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "subnetId", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18957, + "astId": 19663, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "rawAddress", "offset": 0, "slot": "2", - "type": "t_struct(FvmAddress)18733_storage" + "type": "t_struct(FvmAddress)19439_storage" } ], "numberOfBytes": "128" }, - "t_struct(IpcEnvelope)18702_storage": { + "t_struct(IpcEnvelope)19408_storage": { "encoding": "inplace", "label": "struct IpcEnvelope", "members": [ { - "astId": 18684, + "astId": 19390, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "kind", "offset": 0, "slot": "0", - "type": "t_enum(IpcMsgKind)18679" + "type": "t_enum(IpcMsgKind)19385" }, { - "astId": 18688, + "astId": 19394, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "to", "offset": 0, "slot": "1", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18692, + "astId": 19398, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "from", "offset": 0, "slot": "5", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18695, + "astId": 19401, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "nonce", "offset": 0, @@ -358,7 +358,7 @@ "type": "t_uint64" }, { - "astId": 18698, + "astId": 19404, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "value", "offset": 0, @@ -366,7 +366,7 @@ "type": "t_uint256" }, { - "astId": 18701, + "astId": 19407, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "message", "offset": 0, @@ -376,42 +376,42 @@ ], "numberOfBytes": "384" }, - "t_struct(MaxPQ)17125_storage": { + "t_struct(MaxPQ)17052_storage": { "encoding": "inplace", "label": "struct MaxPQ", "members": [ { - "astId": 17124, + "astId": 17051, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(MinPQ)17743_storage": { + "t_struct(MinPQ)17670_storage": { "encoding": "inplace", "label": "struct MinPQ", "members": [ { - "astId": 17742, + "astId": 17669, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(PQ)18373_storage": { + "t_struct(PQ)18300_storage": { "encoding": "inplace", "label": "struct PQ", "members": [ { - "astId": 18362, + "astId": 18289, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "size", "offset": 0, @@ -419,7 +419,7 @@ "type": "t_uint16" }, { - "astId": 18367, + "astId": 18294, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "addressToPos", "offset": 0, @@ -427,7 +427,7 @@ "type": "t_mapping(t_address,t_uint16)" }, { - "astId": 18372, + "astId": 18299, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "posToAddress", "offset": 0, @@ -460,20 +460,20 @@ ], "numberOfBytes": "64" }, - "t_struct(StakingChange)18844_storage": { + "t_struct(StakingChange)19550_storage": { "encoding": "inplace", "label": "struct StakingChange", "members": [ { - "astId": 18839, + "astId": 19545, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "op", "offset": 0, "slot": "0", - "type": "t_enum(StakingOperation)18835" + "type": "t_enum(StakingOperation)19541" }, { - "astId": 18841, + "astId": 19547, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "payload", "offset": 0, @@ -481,7 +481,7 @@ "type": "t_bytes_storage" }, { - "astId": 18843, + "astId": 19549, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "validator", "offset": 0, @@ -491,12 +491,12 @@ ], "numberOfBytes": "96" }, - "t_struct(StakingChangeLog)18865_storage": { + "t_struct(StakingChangeLog)19571_storage": { "encoding": "inplace", "label": "struct StakingChangeLog", "members": [ { - "astId": 18855, + "astId": 19561, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "nextConfigurationNumber", "offset": 0, @@ -504,7 +504,7 @@ "type": "t_uint64" }, { - "astId": 18858, + "astId": 19564, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "startConfigurationNumber", "offset": 8, @@ -512,22 +512,22 @@ "type": "t_uint64" }, { - "astId": 18864, + "astId": 19570, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "changes", "offset": 0, "slot": "1", - "type": "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)" + "type": "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)" } ], "numberOfBytes": "64" }, - "t_struct(StakingRelease)18873_storage": { + "t_struct(StakingRelease)19579_storage": { "encoding": "inplace", "label": "struct StakingRelease", "members": [ { - "astId": 18869, + "astId": 19575, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "releaseAt", "offset": 0, @@ -535,7 +535,7 @@ "type": "t_uint256" }, { - "astId": 18872, + "astId": 19578, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "amount", "offset": 0, @@ -545,12 +545,12 @@ ], "numberOfBytes": "64" }, - "t_struct(StakingReleaseQueue)18895_storage": { + "t_struct(StakingReleaseQueue)19601_storage": { "encoding": "inplace", "label": "struct StakingReleaseQueue", "members": [ { - "astId": 18888, + "astId": 19594, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "lockingDuration", "offset": 0, @@ -558,22 +558,22 @@ "type": "t_uint256" }, { - "astId": 18894, + "astId": 19600, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "releases", "offset": 0, "slot": "1", - "type": "t_mapping(t_address,t_struct(AddressStakingReleases)18884_storage)" + "type": "t_mapping(t_address,t_struct(AddressStakingReleases)19590_storage)" } ], "numberOfBytes": "64" }, - "t_struct(SubnetActorStorage)16379_storage": { + "t_struct(SubnetActorStorage)16306_storage": { "encoding": "inplace", "label": "struct SubnetActorStorage", "members": [ { - "astId": 16286, + "astId": 16213, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "genesisCircSupply", "offset": 0, @@ -581,7 +581,7 @@ "type": "t_uint256" }, { - "astId": 16289, + "astId": 16216, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "lastBottomUpCheckpointHeight", "offset": 0, @@ -589,7 +589,7 @@ "type": "t_uint256" }, { - "astId": 16292, + "astId": 16219, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "minActivationCollateral", "offset": 0, @@ -597,7 +597,7 @@ "type": "t_uint256" }, { - "astId": 16295, + "astId": 16222, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "bottomUpCheckPeriod", "offset": 0, @@ -605,7 +605,7 @@ "type": "t_uint256" }, { - "astId": 16297, + "astId": 16224, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "currentSubnetHash", "offset": 0, @@ -613,7 +613,7 @@ "type": "t_bytes32" }, { - "astId": 16300, + "astId": 16227, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "ipcGatewayAddr", "offset": 0, @@ -621,7 +621,7 @@ "type": "t_address" }, { - "astId": 16303, + "astId": 16230, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "maxMsgsPerBottomUpBatch", "offset": 20, @@ -629,7 +629,7 @@ "type": "t_uint64" }, { - "astId": 16306, + "astId": 16233, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "majorityPercentage", "offset": 28, @@ -637,7 +637,7 @@ "type": "t_uint8" }, { - "astId": 16309, + "astId": 16236, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "powerScale", "offset": 29, @@ -645,7 +645,7 @@ "type": "t_int8" }, { - "astId": 16313, + "astId": 16240, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "consensus", "offset": 30, @@ -653,7 +653,7 @@ "type": "t_enum(ConsensusType)5500" }, { - "astId": 16316, + "astId": 16243, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "bootstrapped", "offset": 31, @@ -661,7 +661,7 @@ "type": "t_bool" }, { - "astId": 16319, + "astId": 16246, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "minValidators", "offset": 0, @@ -669,7 +669,7 @@ "type": "t_uint64" }, { - "astId": 16322, + "astId": 16249, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "killed", "offset": 8, @@ -677,55 +677,55 @@ "type": "t_bool" }, { - "astId": 16326, + "astId": 16253, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "supplySource", "offset": 0, "slot": "7", - "type": "t_struct(Asset)18983_storage" + "type": "t_struct(Asset)19689_storage" }, { - "astId": 16330, + "astId": 16257, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "collateralSource", "offset": 0, "slot": "8", - "type": "t_struct(Asset)18983_storage" + "type": "t_struct(Asset)19689_storage" }, { - "astId": 16334, + "astId": 16261, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "parentId", "offset": 0, "slot": "9", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 16338, + "astId": 16265, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "validatorSet", "offset": 0, "slot": "11", - "type": "t_struct(ValidatorSet)18942_storage" + "type": "t_struct(ValidatorSet)19648_storage" }, { - "astId": 16342, + "astId": 16269, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "changeSet", "offset": 0, "slot": "20", - "type": "t_struct(StakingChangeLog)18865_storage" + "type": "t_struct(StakingChangeLog)19571_storage" }, { - "astId": 16346, + "astId": 16273, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "releaseQueue", "offset": 0, "slot": "22", - "type": "t_struct(StakingReleaseQueue)18895_storage" + "type": "t_struct(StakingReleaseQueue)19601_storage" }, { - "astId": 16351, + "astId": 16278, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "bootstrapNodes", "offset": 0, @@ -733,7 +733,7 @@ "type": "t_mapping(t_address,t_string_storage)" }, { - "astId": 16355, + "astId": 16282, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "bootstrapOwners", "offset": 0, @@ -741,23 +741,23 @@ "type": "t_struct(AddressSet)3459_storage" }, { - "astId": 16361, + "astId": 16288, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "committedCheckpoints", "offset": 0, "slot": "27", - "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)" + "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)" }, { - "astId": 16366, + "astId": 16293, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "genesisValidators", "offset": 0, "slot": "28", - "type": "t_array(t_struct(Validator)18966_storage)dyn_storage" + "type": "t_array(t_struct(Validator)19672_storage)dyn_storage" }, { - "astId": 16371, + "astId": 16298, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "genesisBalance", "offset": 0, @@ -765,7 +765,7 @@ "type": "t_mapping(t_address,t_uint256)" }, { - "astId": 16375, + "astId": 16302, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "genesisBalanceKeys", "offset": 0, @@ -773,7 +773,7 @@ "type": "t_array(t_address)dyn_storage" }, { - "astId": 16378, + "astId": 16305, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "validatorGater", "offset": 0, @@ -783,12 +783,12 @@ ], "numberOfBytes": "1024" }, - "t_struct(SubnetID)18814_storage": { + "t_struct(SubnetID)19520_storage": { "encoding": "inplace", "label": "struct SubnetID", "members": [ { - "astId": 18809, + "astId": 19515, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "root", "offset": 0, @@ -796,7 +796,7 @@ "type": "t_uint64" }, { - "astId": 18813, + "astId": 19519, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "route", "offset": 0, @@ -806,12 +806,12 @@ ], "numberOfBytes": "64" }, - "t_struct(Validator)18966_storage": { + "t_struct(Validator)19672_storage": { "encoding": "inplace", "label": "struct Validator", "members": [ { - "astId": 18961, + "astId": 19667, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "weight", "offset": 0, @@ -819,7 +819,7 @@ "type": "t_uint256" }, { - "astId": 18963, + "astId": 19669, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "addr", "offset": 0, @@ -827,7 +827,7 @@ "type": "t_address" }, { - "astId": 18965, + "astId": 19671, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "metadata", "offset": 0, @@ -837,12 +837,12 @@ ], "numberOfBytes": "96" }, - "t_struct(ValidatorInfo)18907_storage": { + "t_struct(ValidatorInfo)19613_storage": { "encoding": "inplace", "label": "struct ValidatorInfo", "members": [ { - "astId": 18899, + "astId": 19605, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "federatedPower", "offset": 0, @@ -850,7 +850,7 @@ "type": "t_uint256" }, { - "astId": 18901, + "astId": 19607, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "confirmedCollateral", "offset": 0, @@ -858,7 +858,7 @@ "type": "t_uint256" }, { - "astId": 18903, + "astId": 19609, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "totalCollateral", "offset": 0, @@ -866,7 +866,7 @@ "type": "t_uint256" }, { - "astId": 18906, + "astId": 19612, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "metadata", "offset": 0, @@ -876,20 +876,20 @@ ], "numberOfBytes": "128" }, - "t_struct(ValidatorSet)18942_storage": { + "t_struct(ValidatorSet)19648_storage": { "encoding": "inplace", "label": "struct ValidatorSet", "members": [ { - "astId": 18921, + "astId": 19627, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "permissionMode", "offset": 0, "slot": "0", - "type": "t_enum(PermissionMode)18912" + "type": "t_enum(PermissionMode)19618" }, { - "astId": 18924, + "astId": 19630, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "activeLimit", "offset": 1, @@ -897,7 +897,7 @@ "type": "t_uint16" }, { - "astId": 18927, + "astId": 19633, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "totalConfirmedCollateral", "offset": 0, @@ -905,28 +905,28 @@ "type": "t_uint256" }, { - "astId": 18933, + "astId": 19639, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "validators", "offset": 0, "slot": "2", - "type": "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)" + "type": "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)" }, { - "astId": 18937, + "astId": 19643, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "activeValidators", "offset": 0, "slot": "3", - "type": "t_struct(MinPQ)17743_storage" + "type": "t_struct(MinPQ)17670_storage" }, { - "astId": 18941, + "astId": 19647, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "waitingValidators", "offset": 0, "slot": "6", - "type": "t_struct(MaxPQ)17125_storage" + "type": "t_struct(MaxPQ)17052_storage" } ], "numberOfBytes": "288" diff --git a/contracts/.storage-layouts/SubnetActorModifiers.json b/contracts/.storage-layouts/SubnetActorModifiers.json index dee728907..baf89be37 100644 --- a/contracts/.storage-layouts/SubnetActorModifiers.json +++ b/contracts/.storage-layouts/SubnetActorModifiers.json @@ -1,12 +1,12 @@ { "storage": [ { - "astId": 16393, + "astId": 16320, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "s", "offset": 0, "slot": "0", - "type": "t_struct(SubnetActorStorage)16379_storage" + "type": "t_struct(SubnetActorStorage)16306_storage" } ], "types": { @@ -27,14 +27,14 @@ "label": "bytes32[]", "numberOfBytes": "32" }, - "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage": { - "base": "t_struct(IpcEnvelope)18702_storage", + "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage": { + "base": "t_struct(IpcEnvelope)19408_storage", "encoding": "dynamic_array", "label": "struct IpcEnvelope[]", "numberOfBytes": "32" }, - "t_array(t_struct(Validator)18966_storage)dyn_storage": { - "base": "t_struct(Validator)18966_storage", + "t_array(t_struct(Validator)19672_storage)dyn_storage": { + "base": "t_struct(Validator)19672_storage", "encoding": "dynamic_array", "label": "struct Validator[]", "numberOfBytes": "32" @@ -54,7 +54,7 @@ "label": "bytes", "numberOfBytes": "32" }, - "t_enum(AssetKind)18987": { + "t_enum(AssetKind)19693": { "encoding": "inplace", "label": "enum AssetKind", "numberOfBytes": "1" @@ -64,17 +64,17 @@ "label": "enum ConsensusType", "numberOfBytes": "1" }, - "t_enum(IpcMsgKind)18679": { + "t_enum(IpcMsgKind)19385": { "encoding": "inplace", "label": "enum IpcMsgKind", "numberOfBytes": "1" }, - "t_enum(PermissionMode)18912": { + "t_enum(PermissionMode)19618": { "encoding": "inplace", "label": "enum PermissionMode", "numberOfBytes": "1" }, - "t_enum(StakingOperation)18835": { + "t_enum(StakingOperation)19541": { "encoding": "inplace", "label": "enum StakingOperation", "numberOfBytes": "1" @@ -91,19 +91,19 @@ "numberOfBytes": "32", "value": "t_string_storage" }, - "t_mapping(t_address,t_struct(AddressStakingReleases)18884_storage)": { + "t_mapping(t_address,t_struct(AddressStakingReleases)19590_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct AddressStakingReleases)", "numberOfBytes": "32", - "value": "t_struct(AddressStakingReleases)18884_storage" + "value": "t_struct(AddressStakingReleases)19590_storage" }, - "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)": { + "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct ValidatorInfo)", "numberOfBytes": "32", - "value": "t_struct(ValidatorInfo)18907_storage" + "value": "t_struct(ValidatorInfo)19613_storage" }, "t_mapping(t_address,t_uint16)": { "encoding": "mapping", @@ -133,26 +133,26 @@ "numberOfBytes": "32", "value": "t_address" }, - "t_mapping(t_uint16,t_struct(StakingRelease)18873_storage)": { + "t_mapping(t_uint16,t_struct(StakingRelease)19579_storage)": { "encoding": "mapping", "key": "t_uint16", "label": "mapping(uint16 => struct StakingRelease)", "numberOfBytes": "32", - "value": "t_struct(StakingRelease)18873_storage" + "value": "t_struct(StakingRelease)19579_storage" }, - "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)": { + "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct BottomUpCheckpoint)", "numberOfBytes": "32", - "value": "t_struct(BottomUpCheckpoint)18654_storage" + "value": "t_struct(BottomUpCheckpoint)19331_storage" }, - "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)": { + "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)": { "encoding": "mapping", "key": "t_uint64", "label": "mapping(uint64 => struct StakingChange)", "numberOfBytes": "32", - "value": "t_struct(StakingChange)18844_storage" + "value": "t_struct(StakingChange)19550_storage" }, "t_string_storage": { "encoding": "bytes", @@ -174,12 +174,12 @@ ], "numberOfBytes": "64" }, - "t_struct(AddressStakingReleases)18884_storage": { + "t_struct(AddressStakingReleases)19590_storage": { "encoding": "inplace", "label": "struct AddressStakingReleases", "members": [ { - "astId": 18876, + "astId": 19582, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "length", "offset": 0, @@ -187,7 +187,7 @@ "type": "t_uint16" }, { - "astId": 18878, + "astId": 19584, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "startIdx", "offset": 2, @@ -195,30 +195,30 @@ "type": "t_uint16" }, { - "astId": 18883, + "astId": 19589, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "releases", "offset": 0, "slot": "1", - "type": "t_mapping(t_uint16,t_struct(StakingRelease)18873_storage)" + "type": "t_mapping(t_uint16,t_struct(StakingRelease)19579_storage)" } ], "numberOfBytes": "64" }, - "t_struct(Asset)18983_storage": { + "t_struct(Asset)19689_storage": { "encoding": "inplace", "label": "struct Asset", "members": [ { - "astId": 18979, + "astId": 19685, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "kind", "offset": 0, "slot": "0", - "type": "t_enum(AssetKind)18987" + "type": "t_enum(AssetKind)19693" }, { - "astId": 18982, + "astId": 19688, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "tokenAddress", "offset": 1, @@ -228,20 +228,20 @@ ], "numberOfBytes": "32" }, - "t_struct(BottomUpCheckpoint)18654_storage": { + "t_struct(BottomUpCheckpoint)19331_storage": { "encoding": "inplace", "label": "struct BottomUpCheckpoint", "members": [ { - "astId": 18639, + "astId": 19316, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "subnetID", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18642, + "astId": 19319, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "blockHeight", "offset": 0, @@ -249,7 +249,7 @@ "type": "t_uint256" }, { - "astId": 18645, + "astId": 19322, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "blockHash", "offset": 0, @@ -257,7 +257,7 @@ "type": "t_bytes32" }, { - "astId": 18648, + "astId": 19325, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "nextConfigurationNumber", "offset": 0, @@ -265,22 +265,22 @@ "type": "t_uint64" }, { - "astId": 18653, + "astId": 19330, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "msgs", "offset": 0, "slot": "5", - "type": "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage" + "type": "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage" } ], "numberOfBytes": "192" }, - "t_struct(FvmAddress)18733_storage": { + "t_struct(FvmAddress)19439_storage": { "encoding": "inplace", "label": "struct FvmAddress", "members": [ { - "astId": 18730, + "astId": 19436, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "addrType", "offset": 0, @@ -288,7 +288,7 @@ "type": "t_uint8" }, { - "astId": 18732, + "astId": 19438, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "payload", "offset": 0, @@ -298,59 +298,59 @@ ], "numberOfBytes": "64" }, - "t_struct(IPCAddress)18958_storage": { + "t_struct(IPCAddress)19664_storage": { "encoding": "inplace", "label": "struct IPCAddress", "members": [ { - "astId": 18954, + "astId": 19660, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "subnetId", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18957, + "astId": 19663, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "rawAddress", "offset": 0, "slot": "2", - "type": "t_struct(FvmAddress)18733_storage" + "type": "t_struct(FvmAddress)19439_storage" } ], "numberOfBytes": "128" }, - "t_struct(IpcEnvelope)18702_storage": { + "t_struct(IpcEnvelope)19408_storage": { "encoding": "inplace", "label": "struct IpcEnvelope", "members": [ { - "astId": 18684, + "astId": 19390, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "kind", "offset": 0, "slot": "0", - "type": "t_enum(IpcMsgKind)18679" + "type": "t_enum(IpcMsgKind)19385" }, { - "astId": 18688, + "astId": 19394, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "to", "offset": 0, "slot": "1", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18692, + "astId": 19398, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "from", "offset": 0, "slot": "5", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18695, + "astId": 19401, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "nonce", "offset": 0, @@ -358,7 +358,7 @@ "type": "t_uint64" }, { - "astId": 18698, + "astId": 19404, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "value", "offset": 0, @@ -366,7 +366,7 @@ "type": "t_uint256" }, { - "astId": 18701, + "astId": 19407, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "message", "offset": 0, @@ -376,42 +376,42 @@ ], "numberOfBytes": "384" }, - "t_struct(MaxPQ)17125_storage": { + "t_struct(MaxPQ)17052_storage": { "encoding": "inplace", "label": "struct MaxPQ", "members": [ { - "astId": 17124, + "astId": 17051, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(MinPQ)17743_storage": { + "t_struct(MinPQ)17670_storage": { "encoding": "inplace", "label": "struct MinPQ", "members": [ { - "astId": 17742, + "astId": 17669, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(PQ)18373_storage": { + "t_struct(PQ)18300_storage": { "encoding": "inplace", "label": "struct PQ", "members": [ { - "astId": 18362, + "astId": 18289, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "size", "offset": 0, @@ -419,7 +419,7 @@ "type": "t_uint16" }, { - "astId": 18367, + "astId": 18294, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "addressToPos", "offset": 0, @@ -427,7 +427,7 @@ "type": "t_mapping(t_address,t_uint16)" }, { - "astId": 18372, + "astId": 18299, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "posToAddress", "offset": 0, @@ -460,20 +460,20 @@ ], "numberOfBytes": "64" }, - "t_struct(StakingChange)18844_storage": { + "t_struct(StakingChange)19550_storage": { "encoding": "inplace", "label": "struct StakingChange", "members": [ { - "astId": 18839, + "astId": 19545, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "op", "offset": 0, "slot": "0", - "type": "t_enum(StakingOperation)18835" + "type": "t_enum(StakingOperation)19541" }, { - "astId": 18841, + "astId": 19547, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "payload", "offset": 0, @@ -481,7 +481,7 @@ "type": "t_bytes_storage" }, { - "astId": 18843, + "astId": 19549, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "validator", "offset": 0, @@ -491,12 +491,12 @@ ], "numberOfBytes": "96" }, - "t_struct(StakingChangeLog)18865_storage": { + "t_struct(StakingChangeLog)19571_storage": { "encoding": "inplace", "label": "struct StakingChangeLog", "members": [ { - "astId": 18855, + "astId": 19561, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "nextConfigurationNumber", "offset": 0, @@ -504,7 +504,7 @@ "type": "t_uint64" }, { - "astId": 18858, + "astId": 19564, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "startConfigurationNumber", "offset": 8, @@ -512,22 +512,22 @@ "type": "t_uint64" }, { - "astId": 18864, + "astId": 19570, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "changes", "offset": 0, "slot": "1", - "type": "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)" + "type": "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)" } ], "numberOfBytes": "64" }, - "t_struct(StakingRelease)18873_storage": { + "t_struct(StakingRelease)19579_storage": { "encoding": "inplace", "label": "struct StakingRelease", "members": [ { - "astId": 18869, + "astId": 19575, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "releaseAt", "offset": 0, @@ -535,7 +535,7 @@ "type": "t_uint256" }, { - "astId": 18872, + "astId": 19578, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "amount", "offset": 0, @@ -545,12 +545,12 @@ ], "numberOfBytes": "64" }, - "t_struct(StakingReleaseQueue)18895_storage": { + "t_struct(StakingReleaseQueue)19601_storage": { "encoding": "inplace", "label": "struct StakingReleaseQueue", "members": [ { - "astId": 18888, + "astId": 19594, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "lockingDuration", "offset": 0, @@ -558,22 +558,22 @@ "type": "t_uint256" }, { - "astId": 18894, + "astId": 19600, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "releases", "offset": 0, "slot": "1", - "type": "t_mapping(t_address,t_struct(AddressStakingReleases)18884_storage)" + "type": "t_mapping(t_address,t_struct(AddressStakingReleases)19590_storage)" } ], "numberOfBytes": "64" }, - "t_struct(SubnetActorStorage)16379_storage": { + "t_struct(SubnetActorStorage)16306_storage": { "encoding": "inplace", "label": "struct SubnetActorStorage", "members": [ { - "astId": 16286, + "astId": 16213, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "genesisCircSupply", "offset": 0, @@ -581,7 +581,7 @@ "type": "t_uint256" }, { - "astId": 16289, + "astId": 16216, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "lastBottomUpCheckpointHeight", "offset": 0, @@ -589,7 +589,7 @@ "type": "t_uint256" }, { - "astId": 16292, + "astId": 16219, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "minActivationCollateral", "offset": 0, @@ -597,7 +597,7 @@ "type": "t_uint256" }, { - "astId": 16295, + "astId": 16222, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "bottomUpCheckPeriod", "offset": 0, @@ -605,7 +605,7 @@ "type": "t_uint256" }, { - "astId": 16297, + "astId": 16224, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "currentSubnetHash", "offset": 0, @@ -613,7 +613,7 @@ "type": "t_bytes32" }, { - "astId": 16300, + "astId": 16227, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "ipcGatewayAddr", "offset": 0, @@ -621,7 +621,7 @@ "type": "t_address" }, { - "astId": 16303, + "astId": 16230, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "maxMsgsPerBottomUpBatch", "offset": 20, @@ -629,7 +629,7 @@ "type": "t_uint64" }, { - "astId": 16306, + "astId": 16233, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "majorityPercentage", "offset": 28, @@ -637,7 +637,7 @@ "type": "t_uint8" }, { - "astId": 16309, + "astId": 16236, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "powerScale", "offset": 29, @@ -645,7 +645,7 @@ "type": "t_int8" }, { - "astId": 16313, + "astId": 16240, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "consensus", "offset": 30, @@ -653,7 +653,7 @@ "type": "t_enum(ConsensusType)5500" }, { - "astId": 16316, + "astId": 16243, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "bootstrapped", "offset": 31, @@ -661,7 +661,7 @@ "type": "t_bool" }, { - "astId": 16319, + "astId": 16246, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "minValidators", "offset": 0, @@ -669,7 +669,7 @@ "type": "t_uint64" }, { - "astId": 16322, + "astId": 16249, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "killed", "offset": 8, @@ -677,55 +677,55 @@ "type": "t_bool" }, { - "astId": 16326, + "astId": 16253, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "supplySource", "offset": 0, "slot": "7", - "type": "t_struct(Asset)18983_storage" + "type": "t_struct(Asset)19689_storage" }, { - "astId": 16330, + "astId": 16257, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "collateralSource", "offset": 0, "slot": "8", - "type": "t_struct(Asset)18983_storage" + "type": "t_struct(Asset)19689_storage" }, { - "astId": 16334, + "astId": 16261, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "parentId", "offset": 0, "slot": "9", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 16338, + "astId": 16265, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "validatorSet", "offset": 0, "slot": "11", - "type": "t_struct(ValidatorSet)18942_storage" + "type": "t_struct(ValidatorSet)19648_storage" }, { - "astId": 16342, + "astId": 16269, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "changeSet", "offset": 0, "slot": "20", - "type": "t_struct(StakingChangeLog)18865_storage" + "type": "t_struct(StakingChangeLog)19571_storage" }, { - "astId": 16346, + "astId": 16273, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "releaseQueue", "offset": 0, "slot": "22", - "type": "t_struct(StakingReleaseQueue)18895_storage" + "type": "t_struct(StakingReleaseQueue)19601_storage" }, { - "astId": 16351, + "astId": 16278, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "bootstrapNodes", "offset": 0, @@ -733,7 +733,7 @@ "type": "t_mapping(t_address,t_string_storage)" }, { - "astId": 16355, + "astId": 16282, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "bootstrapOwners", "offset": 0, @@ -741,23 +741,23 @@ "type": "t_struct(AddressSet)3459_storage" }, { - "astId": 16361, + "astId": 16288, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "committedCheckpoints", "offset": 0, "slot": "27", - "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)" + "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)" }, { - "astId": 16366, + "astId": 16293, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "genesisValidators", "offset": 0, "slot": "28", - "type": "t_array(t_struct(Validator)18966_storage)dyn_storage" + "type": "t_array(t_struct(Validator)19672_storage)dyn_storage" }, { - "astId": 16371, + "astId": 16298, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "genesisBalance", "offset": 0, @@ -765,7 +765,7 @@ "type": "t_mapping(t_address,t_uint256)" }, { - "astId": 16375, + "astId": 16302, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "genesisBalanceKeys", "offset": 0, @@ -773,7 +773,7 @@ "type": "t_array(t_address)dyn_storage" }, { - "astId": 16378, + "astId": 16305, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "validatorGater", "offset": 0, @@ -783,12 +783,12 @@ ], "numberOfBytes": "1024" }, - "t_struct(SubnetID)18814_storage": { + "t_struct(SubnetID)19520_storage": { "encoding": "inplace", "label": "struct SubnetID", "members": [ { - "astId": 18809, + "astId": 19515, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "root", "offset": 0, @@ -796,7 +796,7 @@ "type": "t_uint64" }, { - "astId": 18813, + "astId": 19519, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "route", "offset": 0, @@ -806,12 +806,12 @@ ], "numberOfBytes": "64" }, - "t_struct(Validator)18966_storage": { + "t_struct(Validator)19672_storage": { "encoding": "inplace", "label": "struct Validator", "members": [ { - "astId": 18961, + "astId": 19667, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "weight", "offset": 0, @@ -819,7 +819,7 @@ "type": "t_uint256" }, { - "astId": 18963, + "astId": 19669, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "addr", "offset": 0, @@ -827,7 +827,7 @@ "type": "t_address" }, { - "astId": 18965, + "astId": 19671, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "metadata", "offset": 0, @@ -837,12 +837,12 @@ ], "numberOfBytes": "96" }, - "t_struct(ValidatorInfo)18907_storage": { + "t_struct(ValidatorInfo)19613_storage": { "encoding": "inplace", "label": "struct ValidatorInfo", "members": [ { - "astId": 18899, + "astId": 19605, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "federatedPower", "offset": 0, @@ -850,7 +850,7 @@ "type": "t_uint256" }, { - "astId": 18901, + "astId": 19607, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "confirmedCollateral", "offset": 0, @@ -858,7 +858,7 @@ "type": "t_uint256" }, { - "astId": 18903, + "astId": 19609, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "totalCollateral", "offset": 0, @@ -866,7 +866,7 @@ "type": "t_uint256" }, { - "astId": 18906, + "astId": 19612, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "metadata", "offset": 0, @@ -876,20 +876,20 @@ ], "numberOfBytes": "128" }, - "t_struct(ValidatorSet)18942_storage": { + "t_struct(ValidatorSet)19648_storage": { "encoding": "inplace", "label": "struct ValidatorSet", "members": [ { - "astId": 18921, + "astId": 19627, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "permissionMode", "offset": 0, "slot": "0", - "type": "t_enum(PermissionMode)18912" + "type": "t_enum(PermissionMode)19618" }, { - "astId": 18924, + "astId": 19630, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "activeLimit", "offset": 1, @@ -897,7 +897,7 @@ "type": "t_uint16" }, { - "astId": 18927, + "astId": 19633, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "totalConfirmedCollateral", "offset": 0, @@ -905,28 +905,28 @@ "type": "t_uint256" }, { - "astId": 18933, + "astId": 19639, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "validators", "offset": 0, "slot": "2", - "type": "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)" + "type": "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)" }, { - "astId": 18937, + "astId": 19643, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "activeValidators", "offset": 0, "slot": "3", - "type": "t_struct(MinPQ)17743_storage" + "type": "t_struct(MinPQ)17670_storage" }, { - "astId": 18941, + "astId": 19647, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "waitingValidators", "offset": 0, "slot": "6", - "type": "t_struct(MaxPQ)17125_storage" + "type": "t_struct(MaxPQ)17052_storage" } ], "numberOfBytes": "288" diff --git a/contracts/binding/Cargo.toml b/contracts/binding/Cargo.toml index fc82cff9b..e3a27d90d 100644 --- a/contracts/binding/Cargo.toml +++ b/contracts/binding/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] ethers = { workspace = true, features = ["abigen", "ws"] } -fvm_shared = { workspace = true, features = ["crypto"] } +fvm_shared = { workspace = true } anyhow = { workspace = true } [build-dependencies] diff --git a/contracts/binding/build.rs b/contracts/binding/build.rs index 75d56a51a..63de37f86 100644 --- a/contracts/binding/build.rs +++ b/contracts/binding/build.rs @@ -47,6 +47,7 @@ fn main() { "TopDownFinalityFacet", "XnetMessagingFacet", "GatewayMessengerFacet", + "SubnetActorActivityFacet", "SubnetActorCheckpointingFacet", "SubnetActorDiamond", "SubnetActorGetterFacet", @@ -91,6 +92,7 @@ fn main() { "SubnetActorCheckpointingFacet", "SubnetActorGetterFacet", "LibGateway", + "CheckpointingFacet", ]; let modules = fvm_address_conversion.into_iter().map(camel_to_snake); diff --git a/contracts/binding/src/lib.rs b/contracts/binding/src/lib.rs index dd59524fb..bbc78042b 100644 --- a/contracts/binding/src/lib.rs +++ b/contracts/binding/src/lib.rs @@ -30,6 +30,8 @@ pub mod ownership_facet; #[allow(clippy::all)] pub mod register_subnet_facet; #[allow(clippy::all)] +pub mod subnet_actor_activity_facet; +#[allow(clippy::all)] pub mod subnet_actor_checkpointing_facet; #[allow(clippy::all)] pub mod subnet_actor_diamond; @@ -58,6 +60,7 @@ fvm_address_conversion!(gateway_messenger_facet); fvm_address_conversion!(subnet_actor_checkpointing_facet); fvm_address_conversion!(subnet_actor_getter_facet); fvm_address_conversion!(lib_gateway); +fvm_address_conversion!(checkpointing_facet); // The list of contracts that need to convert common types between each other common_type_conversion!(subnet_actor_getter_facet, checkpointing_facet); diff --git a/contracts/contracts/SubnetActorDiamond.sol b/contracts/contracts/SubnetActorDiamond.sol index 5abb6cfb3..cf4ac475b 100644 --- a/contracts/contracts/SubnetActorDiamond.sol +++ b/contracts/contracts/SubnetActorDiamond.sol @@ -15,6 +15,8 @@ import {SubnetIDHelper} from "./lib/SubnetIDHelper.sol"; import {LibStaking} from "./lib/LibStaking.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {AssetHelper} from "./lib/AssetHelper.sol"; +import {LibActivity} from "./lib/LibActivity.sol"; + error FunctionNotFound(bytes4 _functionSelector); contract SubnetActorDiamond { @@ -37,6 +39,7 @@ contract SubnetActorDiamond { Asset collateralSource; SubnetID parentId; address validatorGater; + address validatorRewarder; } constructor(IDiamond.FacetCut[] memory _diamondCut, ConstructorParams memory params, address owner) { @@ -102,6 +105,10 @@ contract SubnetActorDiamond { if (params.validatorGater != address(0)) { s.validatorGater = params.validatorGater; } + + if (params.validatorRewarder != address(0)) { + LibActivity.setRewarder(params.validatorRewarder); + } } function _fallback() internal { diff --git a/contracts/contracts/SubnetRegistryDiamond.sol b/contracts/contracts/SubnetRegistryDiamond.sol index bd57a65b9..871d67a06 100644 --- a/contracts/contracts/SubnetRegistryDiamond.sol +++ b/contracts/contracts/SubnetRegistryDiamond.sol @@ -25,6 +25,7 @@ contract SubnetRegistryDiamond { address diamondCutFacet; address diamondLoupeFacet; address ownershipFacet; + address activityFacet; bytes4[] subnetActorGetterSelectors; bytes4[] subnetActorManagerSelectors; bytes4[] subnetActorRewarderSelectors; @@ -33,6 +34,7 @@ contract SubnetRegistryDiamond { bytes4[] subnetActorDiamondCutSelectors; bytes4[] subnetActorDiamondLoupeSelectors; bytes4[] subnetActorOwnershipSelectors; + bytes4[] subnetActorActivitySelectors; SubnetCreationPrivileges creationPrivileges; } @@ -64,6 +66,9 @@ contract SubnetRegistryDiamond { if (params.ownershipFacet == address(0)) { revert FacetCannotBeZero(); } + if (params.activityFacet == address(0)) { + revert FacetCannotBeZero(); + } LibDiamond.setContractOwner(msg.sender); LibDiamond.diamondCut({_diamondCut: _diamondCut, _init: address(0), _calldata: new bytes(0)}); @@ -83,6 +88,7 @@ contract SubnetRegistryDiamond { s.SUBNET_ACTOR_DIAMOND_CUT_FACET = params.diamondCutFacet; s.SUBNET_ACTOR_LOUPE_FACET = params.diamondLoupeFacet; s.SUBNET_ACTOR_OWNERSHIP_FACET = params.ownershipFacet; + s.VALIDATOR_REWARD_FACET = params.activityFacet; s.subnetActorGetterSelectors = params.subnetActorGetterSelectors; s.subnetActorManagerSelectors = params.subnetActorManagerSelectors; @@ -92,6 +98,7 @@ contract SubnetRegistryDiamond { s.subnetActorDiamondCutSelectors = params.subnetActorDiamondCutSelectors; s.subnetActorDiamondLoupeSelectors = params.subnetActorDiamondLoupeSelectors; s.subnetActorOwnershipSelectors = params.subnetActorOwnershipSelectors; + s.subnetActorActivitySelectors = params.subnetActorActivitySelectors; s.creationPrivileges = params.creationPrivileges; } diff --git a/contracts/contracts/errors/IPCErrors.sol b/contracts/contracts/errors/IPCErrors.sol index 7f838e1a4..7d73a8df4 100644 --- a/contracts/contracts/errors/IPCErrors.sol +++ b/contracts/contracts/errors/IPCErrors.sol @@ -80,6 +80,10 @@ error InvalidFederationPayload(); error DuplicatedGenesisValidator(); error NotEnoughGenesisValidators(); error ValidatorPowerChangeDenied(); +error MissingActivityCommitment(); +error ValidatorAlreadyClaimed(); +error InvalidActivityProof(); +error NotOwner(); enum InvalidXnetMessageReason { Sender, diff --git a/contracts/contracts/examples/ValidatorRewarderMap.sol b/contracts/contracts/examples/ValidatorRewarderMap.sol new file mode 100644 index 000000000..98d1384f8 --- /dev/null +++ b/contracts/contracts/examples/ValidatorRewarderMap.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.23; + +import {IValidatorRewarder} from "../interfaces/IValidatorRewarder.sol"; +import {Consensus} from "../structs/Activity.sol"; +import {SubnetID} from "../structs/Subnet.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; + +/// An example validator rewarder implementation that only tracks the cumulative number of +/// blocks committed by each validator. +contract ValidatorRewarderMap is IValidatorRewarder, Ownable { + SubnetID public subnetId; + + mapping(address => uint64) public blocksCommitted; + + constructor() Ownable(msg.sender) {} + + function setSubnet(SubnetID calldata id) external onlyOwner { + require(id.route.length > 0, "root not allowed"); + subnetId = id; + } + + function notifyValidClaim(SubnetID calldata id, uint64, Consensus.ValidatorData calldata data) external { + require(keccak256(abi.encode(id)) == keccak256(abi.encode(subnetId)), "not my subnet"); + + address actor = id.route[id.route.length - 1]; + require(actor == msg.sender, "not from subnet"); + + blocksCommitted[data.validator] += data.blocksCommitted; + } +} diff --git a/contracts/contracts/gateway/router/CheckpointingFacet.sol b/contracts/contracts/gateway/router/CheckpointingFacet.sol index a49780cc8..54e2b1e52 100644 --- a/contracts/contracts/gateway/router/CheckpointingFacet.sol +++ b/contracts/contracts/gateway/router/CheckpointingFacet.sol @@ -17,6 +17,8 @@ import {CrossMsgHelper} from "../../lib/CrossMsgHelper.sol"; import {IpcEnvelope, SubnetID} from "../../structs/CrossNet.sol"; import {SubnetIDHelper} from "../../lib/SubnetIDHelper.sol"; +import {ActivityRollupRecorded, FullActivityRollup} from "../../structs/Activity.sol"; + contract CheckpointingFacet is GatewayActorModifiers { using SubnetIDHelper for SubnetID; using CrossMsgHelper for IpcEnvelope; @@ -47,10 +49,12 @@ contract CheckpointingFacet is GatewayActorModifiers { /// @param checkpoint - a bottom-up checkpoint /// @param membershipRootHash - a root hash of the Merkle tree built from the validator public keys and their weight /// @param membershipWeight - the total weight of the membership + /// @param activity - the full activity rollup function createBottomUpCheckpoint( BottomUpCheckpoint calldata checkpoint, bytes32 membershipRootHash, - uint256 membershipWeight + uint256 membershipWeight, + FullActivityRollup calldata activity ) external systemActorOnly { if (LibGateway.bottomUpCheckpointExists(checkpoint.blockHeight)) { revert CheckpointAlreadyExists(); @@ -64,7 +68,10 @@ contract CheckpointingFacet is GatewayActorModifiers { membershipWeight: membershipWeight, majorityPercentage: s.majorityPercentage }); + LibGateway.storeBottomUpCheckpoint(checkpoint); + + emit ActivityRollupRecorded(uint64(checkpoint.blockHeight), activity); } /// @notice Set a new checkpoint retention height and garbage collect all checkpoints in range [`retentionHeight`, `newRetentionHeight`) diff --git a/contracts/contracts/interfaces/IGateway.sol b/contracts/contracts/interfaces/IGateway.sol index 9d820dff6..1c807ea81 100644 --- a/contracts/contracts/interfaces/IGateway.sol +++ b/contracts/contracts/interfaces/IGateway.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.23; import {BottomUpCheckpoint, BottomUpMsgBatch, IpcEnvelope, ParentFinality} from "../structs/CrossNet.sol"; +import {FullActivityRollup} from "../structs/Activity.sol"; import {SubnetID} from "../structs/Subnet.sol"; import {FvmAddress} from "../structs/FvmAddress.sol"; @@ -71,6 +72,7 @@ interface IGateway { function createBottomUpCheckpoint( BottomUpCheckpoint calldata checkpoint, bytes32 membershipRootHash, - uint256 membershipWeight + uint256 membershipWeight, + FullActivityRollup calldata activity ) external; } diff --git a/contracts/contracts/interfaces/IValidatorRewarder.sol b/contracts/contracts/interfaces/IValidatorRewarder.sol new file mode 100644 index 000000000..55a7877ea --- /dev/null +++ b/contracts/contracts/interfaces/IValidatorRewarder.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.23; + +import {SubnetID} from "../structs/Subnet.sol"; +import {Consensus} from "../structs/Activity.sol"; + +/// @title ValidatorRewarder interface. +/// +/// @dev Implement this interface and supply the address of the implementation contract at subnet creation to process +/// consensus activity summaries at this level, and disburse rewards to validators based on their block production +/// activities inside the subnet. +/// +/// This interface will be called by the subnet actor when a validator presents a _valid_ proof of consensus activity, +/// via the SubnetActivityActivityFacet#claim method. +interface IValidatorRewarder { + /// Called by the subnet actor when a validator presents a _valid_ proof of consensus activity, via + /// SubnetActorActivityFacet#claim() or its batch equivalents. + /// @dev This method should revert if the summary is invalid; this will cause the claim submission to be rejected. + function notifyValidClaim( + SubnetID calldata id, + uint64 checkpointHeight, + Consensus.ValidatorData calldata validatedData + ) external; +} diff --git a/contracts/contracts/lib/LibActivity.sol b/contracts/contracts/lib/LibActivity.sol new file mode 100644 index 000000000..ac2c736c3 --- /dev/null +++ b/contracts/contracts/lib/LibActivity.sol @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.23; + +import {IValidatorRewarder} from "../interfaces/IValidatorRewarder.sol"; +import {Consensus, CompressedActivityRollup} from "../structs/Activity.sol"; +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; +import {SubnetID} from "../structs/Subnet.sol"; +import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol"; +import {InvalidActivityProof, MissingActivityCommitment, ValidatorAlreadyClaimed} from "../errors/IPCErrors.sol"; + +/// Library to handle activity rollups. +library LibActivity { + bytes32 private constant CONSENSUS_NAMESPACE = keccak256("activity.consensus"); + + using SubnetIDHelper for SubnetID; + using EnumerableSet for EnumerableSet.AddressSet; + using EnumerableSet for EnumerableSet.Bytes32Set; + + // Newtype for extra safety. + type SubnetKey is bytes32; + + /// An object to track consensus-related activity submissions from subnets. + struct ConsensusTracker { + /// An enumeration of checkpoint heights that are pending. + EnumerableSet.Bytes32Set pendingHeights; + /// The pending summaries for each checkpoint height, including information about the validators that have already claimed rewards. + mapping(uint64 => ConsensusPendingAtHeight) pending; + } + + struct ConsensusPendingAtHeight { + /// The original compressed summary submitted for this height. + /// We store the summary in full, or we relay it upwards if there is no validator rewarder configured. + Consensus.CompressedSummary summary; + /// Tracks validators have already claimed rewards for this height. + EnumerableSet.AddressSet claimed; + } + + /// Used by the SubnetActor to track the rewards for each validator + struct ConsensusStorage { + /// @notice The contract address for the validator rewarder. + address validatorRewarder; + /// @notice Tracks pending summaries to be processed. + /// If the validator rewarder is non-zero, these denote summaries presentable at this level. + /// If the validator rewarder is zero, these summaries must be relayed upwards in the next bottom-up checkpoint. + /// Partitioned by subnet ID (hash) then by checkpoint block height in the child subnet to the commitment + mapping(SubnetKey => ConsensusTracker) tracker; + } + + /// Return type for the list commitments view method. + struct ListPendingReturnEntry { + uint64 height; + Consensus.CompressedSummary summary; + address[] claimed; + } + + function ensureValidProof( + Consensus.MerkleHash commitment, + Consensus.ValidatorData calldata detail, + Consensus.MerkleHash[] calldata proof + ) internal pure { + // Constructing leaf: https://github.com/OpenZeppelin/merkle-tree#leaf-hash + bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(detail.validator, detail.blocksCommitted)))); + // converting proof to bytes32[] + bytes32[] memory proofBytes = new bytes32[](proof.length); + for (uint256 i = 0; i < proof.length; i++) { + proofBytes[i] = Consensus.MerkleHash.unwrap(proof[i]); + } + bool valid = MerkleProof.verify({proof: proofBytes, root: Consensus.MerkleHash.unwrap(commitment), leaf: leaf}); + if (!valid) { + revert InvalidActivityProof(); + } + } + + // =========== External library functions ============= + + function recordActivityRollup( + SubnetID calldata subnet, + uint64 checkpointHeight, + CompressedActivityRollup calldata activity + ) internal { + ConsensusStorage storage s = consensusStorage(); + + SubnetKey subnetKey = SubnetKey.wrap(subnet.toHash()); + + ConsensusTracker storage tracker = s.tracker[subnetKey]; + bool added = tracker.pendingHeights.add(bytes32(uint256(checkpointHeight))); + require(added, "duplicate checkpoint height"); + + ConsensusPendingAtHeight storage pending = tracker.pending[checkpointHeight]; + pending.summary = activity.consensus; + } + + /// A view accessor to query the pending consensus summaries for a given subnet. + function listPendingConsensus( + SubnetID calldata subnet + ) internal view returns (ListPendingReturnEntry[] memory result) { + ConsensusStorage storage s = consensusStorage(); + + SubnetKey subnetKey = SubnetKey.wrap(subnet.toHash()); + + uint256 size = s.tracker[subnetKey].pendingHeights.length(); + result = new ListPendingReturnEntry[](size); + + // Ok to not optimize with unchecked increments, since we expect this to be used off-chain only, for introspection. + for (uint256 i = 0; i < size; i++) { + ConsensusTracker storage tracker = s.tracker[subnetKey]; + bytes32[] memory heights = tracker.pendingHeights.values(); + + for (uint256 j = 0; j < heights.length; j++) { + uint64 height = uint64(uint256(heights[j]) << 192 >> 192); + ConsensusPendingAtHeight storage pending = tracker.pending[height]; + result[i] = ListPendingReturnEntry({ + height: height, + summary: pending.summary, + claimed: pending.claimed.values() + }); + } + } + + return result; + } + + function processConsensusClaim( + SubnetID calldata subnet, + uint64 checkpointHeight, + Consensus.ValidatorData calldata data, + Consensus.MerkleHash[] calldata proof + ) internal { + ConsensusStorage storage s = consensusStorage(); + + SubnetKey subnetKey = SubnetKey.wrap(subnet.toHash()); + + // Check the validity of the proof. + ConsensusPendingAtHeight storage pending = s.tracker[subnetKey].pending[checkpointHeight]; + Consensus.MerkleHash commitment = pending.summary.dataRootCommitment; + if (Consensus.MerkleHash.unwrap(commitment) == bytes32(0)) { + revert MissingActivityCommitment(); + } + ensureValidProof(commitment, data, proof); + + // Mark the validator as claimed. + bool added = pending.claimed.add(data.validator); + if (!added) { + revert ValidatorAlreadyClaimed(); + } + + // Notify the validator rewarder of a valid claim. + IValidatorRewarder(s.validatorRewarder).notifyValidClaim(subnet, checkpointHeight, data); + + // Prune state for this height if all validators have claimed. + if (pending.claimed.length() == pending.summary.stats.totalActiveValidators) { + ConsensusTracker storage tracker = s.tracker[subnetKey]; + tracker.pendingHeights.remove(bytes32(uint256(checkpointHeight))); + delete tracker.pending[checkpointHeight]; + } + } + + function setRewarder(address rewarder) internal { + ConsensusStorage storage ds = consensusStorage(); + ds.validatorRewarder = rewarder; + } + + // ============ Internal library functions ============ + + function consensusStorage() internal pure returns (ConsensusStorage storage ds) { + bytes32 position = CONSENSUS_NAMESPACE; + assembly { + ds.slot := position + } + return ds; + } +} diff --git a/contracts/contracts/lib/LibDiamond.sol b/contracts/contracts/lib/LibDiamond.sol index e60f520c2..476086d12 100644 --- a/contracts/contracts/lib/LibDiamond.sol +++ b/contracts/contracts/lib/LibDiamond.sol @@ -3,12 +3,12 @@ pragma solidity ^0.8.23; import {IDiamondCut} from "../interfaces/IDiamondCut.sol"; import {IDiamond} from "../interfaces/IDiamond.sol"; +import {NotOwner} from "../errors/IPCErrors.sol"; library LibDiamond { bytes32 public constant DIAMOND_STORAGE_POSITION = keccak256("libdiamond.lib.diamond.storage"); error InvalidAddress(); - error NotOwner(); error NoBytecodeAtAddress(address _contractAddress, string _message); error IncorrectFacetCutAction(IDiamondCut.FacetCutAction _action); error NoSelectorsProvidedForFacetForCut(address _facetAddress); diff --git a/contracts/contracts/lib/LibGateway.sol b/contracts/contracts/lib/LibGateway.sol index f7c3e84ad..e48c0dcee 100644 --- a/contracts/contracts/lib/LibGateway.sol +++ b/contracts/contracts/lib/LibGateway.sol @@ -82,6 +82,7 @@ library LibGateway { b.subnetID = checkpoint.subnetID; b.nextConfigurationNumber = checkpoint.nextConfigurationNumber; b.blockHeight = checkpoint.blockHeight; + b.activity = checkpoint.activity; uint256 msgLength = checkpoint.msgs.length; for (uint256 i; i < msgLength; ) { diff --git a/contracts/contracts/lib/LibSubnetRegistryStorage.sol b/contracts/contracts/lib/LibSubnetRegistryStorage.sol index fed633a02..4d3a380aa 100644 --- a/contracts/contracts/lib/LibSubnetRegistryStorage.sol +++ b/contracts/contracts/lib/LibSubnetRegistryStorage.sol @@ -11,6 +11,7 @@ struct SubnetRegistryActorStorage { address SUBNET_ACTOR_GETTER_FACET; // solhint-disable-next-line var-name-mixedcase address SUBNET_ACTOR_MANAGER_FACET; + /// TODO: see https://github.com/consensus-shipyard/ipc/issues/1217 // solhint-disable-next-line var-name-mixedcase address SUBNET_ACTOR_REWARD_FACET; // solhint-disable-next-line var-name-mixedcase @@ -23,11 +24,14 @@ struct SubnetRegistryActorStorage { address SUBNET_ACTOR_LOUPE_FACET; // solhint-disable-next-line var-name-mixedcase address SUBNET_ACTOR_OWNERSHIP_FACET; + // solhint-disable-next-line var-name-mixedcase + address VALIDATOR_REWARD_FACET; /// The subnet actor getter facet functions selectors bytes4[] subnetActorGetterSelectors; /// The subnet actor manager facet functions selectors bytes4[] subnetActorManagerSelectors; /// The subnet actor reward facet functions selectors + /// TODO: see https://github.com/consensus-shipyard/ipc/issues/1217 bytes4[] subnetActorRewarderSelectors; /// The subnet actor checkpointing facet functions selectors bytes4[] subnetActorCheckpointerSelectors; @@ -39,6 +43,8 @@ struct SubnetRegistryActorStorage { bytes4[] subnetActorDiamondLoupeSelectors; /// The subnet actor ownership facet functions selectors bytes4[] subnetActorOwnershipSelectors; + /// The validator reward facet functions selectors + bytes4[] subnetActorActivitySelectors; /// @notice Mapping that tracks the deployed subnet actors per user. /// Key is the hash of Subnet ID, values are addresses. /// mapping owner => nonce => subnet diff --git a/contracts/contracts/structs/Activity.sol b/contracts/contracts/structs/Activity.sol new file mode 100644 index 000000000..cdb6982dc --- /dev/null +++ b/contracts/contracts/structs/Activity.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.23; + +import {SubnetID} from "../structs/Subnet.sol"; + +// Event to be emitted within the subnet when a new activity summary has been recorded. +event ActivityRollupRecorded(uint64 checkpointHeight, FullActivityRollup rollup); + +// Carries a set of reports summarising various aspects of the activity that took place in the subnet between the +// previous checkpoint and the checkpoint this summary is committed into. If this is the first checkpoint, the summary +// contains information about the subnet's activity since genesis. +// In the future we'll be having more kinds of activity reports here. +struct FullActivityRollup { + /// A report of consensus-level activity that took place in the subnet between the previous checkpoint + /// and the checkpoint this summary is committed into. + /// @dev If there is a configuration change applied at this checkpoint, this carries information + /// about the _old_ validator set. + Consensus.FullSummary consensus; +} + +// Compressed representation of the activity summary that can be embedded in checkpoints to propagate up the hierarchy. +struct CompressedActivityRollup { + Consensus.CompressedSummary consensus; +} + +/// Namespace for consensus-level activity summaries. +library Consensus { + type MerkleHash is bytes32; + + // Aggregated stats for consensus-level activity. + struct AggregatedStats { + /// The total number of unique validators that have mined within this period. + uint64 totalActiveValidators; + /// The total number of blocks committed by all validators during this period. + uint64 totalNumBlocksCommitted; + } + + // The full activity summary for consensus-level activity. + struct FullSummary { + AggregatedStats stats; + /// The breakdown of activity per validator. + ValidatorData[] data; + } + + // The compresed representation of the activity summary for consensus-level activity suitable for embedding in a checkpoint. + struct CompressedSummary { + AggregatedStats stats; + /// The commitment for the validator details, so that we don't have to transmit them in full. + MerkleHash dataRootCommitment; + } + + struct ValidatorData { + /// @dev The validator whose activity we're reporting about, identified by the Ethereum address corresponding + /// to its secp256k1 pubkey. + address validator; + /// @dev The number of blocks committed by this validator during the summarised period. + uint64 blocksCommitted; + } + + /// The payload for validators to claim rewards + struct ValidatorClaim { + ValidatorData data; + MerkleHash[] proof; + } +} diff --git a/contracts/contracts/structs/CrossNet.sol b/contracts/contracts/structs/CrossNet.sol index 368554b60..1f5962346 100644 --- a/contracts/contracts/structs/CrossNet.sol +++ b/contracts/contracts/structs/CrossNet.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.23; import {SubnetID, IPCAddress} from "./Subnet.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {CompressedActivityRollup} from "../structs/Activity.sol"; uint64 constant MAX_MSGS_PER_BATCH = 10; uint256 constant BATCH_PERIOD = 100; @@ -29,6 +30,8 @@ struct BottomUpCheckpoint { uint64 nextConfigurationNumber; /// @dev Batch of messages to execute. IpcEnvelope[] msgs; + /// @dev The activity rollup from child subnet to parent subnet. + CompressedActivityRollup activity; } /// @notice A batch of bottom-up messages for execution. diff --git a/contracts/contracts/subnet/SubnetActorActivityFacet.sol b/contracts/contracts/subnet/SubnetActorActivityFacet.sol new file mode 100644 index 000000000..49cd64b71 --- /dev/null +++ b/contracts/contracts/subnet/SubnetActorActivityFacet.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.23; + +import {Consensus} from "../structs/Activity.sol"; +import {LibActivity} from "../lib/LibActivity.sol"; +import {LibDiamond} from "../lib/LibDiamond.sol"; +import {NotAuthorized} from "../errors/IPCErrors.sol"; +import {Pausable} from "../lib/LibPausable.sol"; +import {ReentrancyGuard} from "../lib/LibReentrancyGuard.sol"; +import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol"; +import {SubnetID} from "../structs/Subnet.sol"; + +/// The validator reward facet for the parent subnet, i.e. for validators in the child subnet +/// to claim their reward in the parent subnet, which should be the current subnet this facet +/// is deployed. +contract SubnetActorActivityFacet is ReentrancyGuard, Pausable { + // Entrypoint for validators to batch claim rewards in the parent subnet, for a given subnet, + // against multiple checkpoints at once. Atomically succeeds or reverts. + function batchSubnetClaim( + SubnetID calldata subnet, + uint64[] calldata checkpointHeights, + Consensus.ValidatorClaim[] calldata claims + ) external nonReentrant whenNotPaused { + require(checkpointHeights.length == claims.length, "length mismatch"); + uint256 len = claims.length; + for (uint256 i = 0; i < len; ) { + _claim(subnet, checkpointHeights[i], claims[i].data, claims[i].proof); + unchecked { + i++; + } + } + } + + /// Entrypoint for validators to claim their reward for doing work in the child subnet. + function claim( + SubnetID calldata subnet, + uint64 checkpointHeight, + Consensus.ValidatorData calldata data, + Consensus.MerkleHash[] calldata proof + ) external nonReentrant whenNotPaused { + _claim(subnet, checkpointHeight, data, proof); + } + + // ======== Internal functions =========== + + function _claim( + SubnetID calldata subnetId, + uint64 checkpointHeight, + Consensus.ValidatorData calldata detail, + Consensus.MerkleHash[] calldata proof + ) internal { + // Note: No need to check if the subnet is active. If the subnet is not active, the checkpointHeight + // will never exist. + if (msg.sender != detail.validator) { + revert NotAuthorized(msg.sender); + } + + LibActivity.processConsensusClaim(subnetId, checkpointHeight, detail, proof); + } +} diff --git a/contracts/contracts/subnet/SubnetActorCheckpointingFacet.sol b/contracts/contracts/subnet/SubnetActorCheckpointingFacet.sol index 667433d8b..160ab9e1d 100644 --- a/contracts/contracts/subnet/SubnetActorCheckpointingFacet.sol +++ b/contracts/contracts/subnet/SubnetActorCheckpointingFacet.sol @@ -13,6 +13,7 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet import {LibSubnetActor} from "../lib/LibSubnetActor.sol"; import {Pausable} from "../lib/LibPausable.sol"; import {LibGateway} from "../lib/LibGateway.sol"; +import {LibActivity} from "../lib/LibActivity.sol"; contract SubnetActorCheckpointingFacet is SubnetActorModifiers, ReentrancyGuard, Pausable { using EnumerableSet for EnumerableSet.AddressSet; @@ -45,6 +46,8 @@ contract SubnetActorCheckpointingFacet is SubnetActorModifiers, ReentrancyGuard, // Commit in gateway to distribute rewards IGateway(s.ipcGatewayAddr).commitCheckpoint(checkpoint); + LibActivity.recordActivityRollup(checkpoint.subnetID, uint64(checkpoint.blockHeight), checkpoint.activity); + // confirming the changes in membership in the child LibStaking.confirmChange(checkpoint.nextConfigurationNumber); } diff --git a/contracts/contracts/subnetregistry/RegisterSubnetFacet.sol b/contracts/contracts/subnetregistry/RegisterSubnetFacet.sol index 30babb8f7..ed9cb4f25 100644 --- a/contracts/contracts/subnetregistry/RegisterSubnetFacet.sol +++ b/contracts/contracts/subnetregistry/RegisterSubnetFacet.sol @@ -28,7 +28,7 @@ contract RegisterSubnetFacet is ReentrancyGuard { ensurePrivileges(); - IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](8); + IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](9); // set the diamond cut for subnet getter diamondCut[0] = IDiamond.FacetCut({ @@ -80,6 +80,12 @@ contract RegisterSubnetFacet is ReentrancyGuard { functionSelectors: s.subnetActorOwnershipSelectors }); + diamondCut[8] = IDiamond.FacetCut({ + facetAddress: s.VALIDATOR_REWARD_FACET, + action: IDiamond.FacetCutAction.Add, + functionSelectors: s.subnetActorActivitySelectors + }); + // slither-disable-next-line reentrancy-benign subnetAddr = address(new SubnetActorDiamond(diamondCut, _params, msg.sender)); diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 2d7761d10..eb7626a0e 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -16,6 +16,7 @@ remappings = [ "murky/=lib/murky/src/", ] allow_paths = ["../node_modules"] +solc = "0.8.23" [fuzz] runs = 512 diff --git a/contracts/tasks/deploy-gateway.ts b/contracts/tasks/deploy-gateway.ts index d8971e82d..de2226d1b 100644 --- a/contracts/tasks/deploy-gateway.ts +++ b/contracts/tasks/deploy-gateway.ts @@ -80,6 +80,8 @@ async function deployGatewayDiamond( gatewayConstructorParams.networkName.root = await hre.getChainId() gatewayConstructorParams.commitSha = hre.ethers.utils.formatBytes32String(gitCommitSha()) + console.log('deploy gateway with params', gatewayConstructorParams) + const deployments = await Deployments.deploy(hre, deployer, { name: 'GatewayDiamond', args: [facets.asFacetCuts(), gatewayConstructorParams], diff --git a/contracts/tasks/deploy-registry.ts b/contracts/tasks/deploy-registry.ts index 348035dd1..14a6ac1f7 100644 --- a/contracts/tasks/deploy-registry.ts +++ b/contracts/tasks/deploy-registry.ts @@ -37,10 +37,17 @@ task('deploy-registry') }, { name: 'SubnetActorPauseFacet' }, { name: 'SubnetActorRewardFacet' }, - { name: 'SubnetActorCheckpointingFacet' }, + { + name: 'SubnetActorCheckpointingFacet', + libraries: ['SubnetIDHelper'], + }, { name: 'DiamondCutFacet' }, { name: 'DiamondLoupeFacet' }, { name: 'OwnershipFacet' }, + { + name: 'SubnetActorActivityFacet', + libraries: ['SubnetIDHelper'], + }, ) const registryFacets = await Deployments.deploy( @@ -85,6 +92,7 @@ task('deploy-registry') diamondCutFacet: subnetActorFacets.addresses['DiamondCutFacet'], diamondLoupeFacet: subnetActorFacets.addresses['DiamondLoupeFacet'], ownershipFacet: subnetActorFacets.addresses['OwnershipFacet'], + activityFacet: subnetActorFacets.addresses['SubnetActorActivityFacet'], subnetActorGetterSelectors: selectors(subnetActorFacets.contracts['SubnetActorGetterFacet']), subnetActorManagerSelectors: selectors(subnetActorFacets.contracts['SubnetActorManagerFacet']), @@ -94,6 +102,8 @@ task('deploy-registry') subnetActorDiamondCutSelectors: selectors(subnetActorFacets.contracts['DiamondCutFacet']), subnetActorDiamondLoupeSelectors: selectors(subnetActorFacets.contracts['DiamondLoupeFacet']), subnetActorOwnershipSelectors: selectors(subnetActorFacets.contracts['OwnershipFacet']), + subnetActorActivitySelectors: selectors(subnetActorFacets.contracts['SubnetActorActivityFacet']), + creationPrivileges: Number(mode), } diff --git a/contracts/tasks/gen-selector-library.ts b/contracts/tasks/gen-selector-library.ts index 31a4e045b..a55db8ddb 100644 --- a/contracts/tasks/gen-selector-library.ts +++ b/contracts/tasks/gen-selector-library.ts @@ -6,14 +6,18 @@ import { selectors } from './lib' task('gen-selector-library', 'Generates a Solidity library with contract selectors for tests').setAction( async (_args, hre: HardhatRuntimeEnvironment) => { - await hre.run('compile') - // ridiculously, this appears to be the only way to get hardhat to compile a specific subtree // we are only updating the in-memory representation of the config, so it won't write this value out to disk // be careful if you compose this task with other tasks in larger scripts! + console.log('compiling mocks...') + const oldSources = hre.config.paths.sources hre.config.paths.sources = './test/mocks' await hre.run('compile') + hre.config.paths.sources = oldSources + console.log('compiling contracts...') + await hre.run('compile') + const contracts: string[] = [ 'OwnershipFacet', 'DiamondCutFacet', @@ -32,6 +36,7 @@ task('gen-selector-library', 'Generates a Solidity library with contract selecto 'RegisterSubnetFacet', 'SubnetGetterFacet', 'SubnetActorMock', + 'SubnetActorActivityFacet', ] const resolveSelectors = async (contractName: string) => { diff --git a/contracts/tasks/index.ts b/contracts/tasks/index.ts index 901e611bc..d0ab1c692 100644 --- a/contracts/tasks/index.ts +++ b/contracts/tasks/index.ts @@ -5,4 +5,5 @@ import './deploy-registry' import './deploy' import './upgrade' import './validator-gater' +import './validator-rewarder' import './gen-selector-library' diff --git a/contracts/tasks/validator-rewarder.ts b/contracts/tasks/validator-rewarder.ts new file mode 100644 index 000000000..095ffa935 --- /dev/null +++ b/contracts/tasks/validator-rewarder.ts @@ -0,0 +1,47 @@ +import { task } from 'hardhat/config' +import { HardhatRuntimeEnvironment, TaskArguments } from 'hardhat/types' +import { Deployments } from './lib' +import { artifacts, ethers } from 'hardhat' + +// step 1. deploy the validator rewarder +// sample command: pnpm exec hardhat validator-rewarder-deploy --network calibrationnet +task('validator-rewarder-deploy') + .setDescription('Deploy example subnet validator rewarder contract') + .setAction(async (_: TaskArguments, hre: HardhatRuntimeEnvironment) => { + await hre.run('compile') + + const [deployer] = await hre.getUnnamedAccounts() + const balance = await hre.ethers.provider.getBalance(deployer) + console.log( + `Deploying validator rewarder contract with account: ${deployer} and balance: ${hre.ethers.utils.formatEther(balance.toString())}`, + ) + + await Deployments.deploy(hre, deployer, { + name: 'ValidatorRewarderMap', + libraries: [], + }) + }) + +// step 2. set the subnet for the rewarder +// sample command: pnpm exec hardhat validator-rewarder-set-subnet --network calibrationnet 314159 +task('validator-rewarder-set-subnet') + .setDescription('Deploy example subnet validator rewarder contract') + .addPositionalParam('root', 'the chain id of parent subnet') + .addPositionalParam('address', 'the address of the subnet actor contract, L2 only') + .setAction(async (args: TaskArguments, hre: HardhatRuntimeEnvironment) => { + await hre.run('compile') + + const [deployer] = await hre.getUnnamedAccounts() + const balance = await hre.ethers.provider.getBalance(deployer) + console.log( + `Set validator rewarder subnet with account: ${deployer} and balance: ${hre.ethers.utils.formatEther(balance.toString())}`, + ) + + // only L2 for now + const subnetId = { root: args.root, route: [args.address] } + console.log('pointing to', subnetId) + + const contracts = await Deployments.resolve(hre, 'ValidatorRewarderMap') + const contract = contracts.contracts.ValidatorRewarderMap + await contract.setSubnet(subnetId) + }) diff --git a/contracts/test/IntegrationTestBase.sol b/contracts/test/IntegrationTestBase.sol index 84eaf5456..19013b3d7 100644 --- a/contracts/test/IntegrationTestBase.sol +++ b/contracts/test/IntegrationTestBase.sol @@ -46,6 +46,10 @@ import {GatewayFacetsHelper} from "./helpers/GatewayFacetsHelper.sol"; import {SubnetActorFacetsHelper} from "./helpers/SubnetActorFacetsHelper.sol"; import {DiamondFacetsHelper} from "./helpers/DiamondFacetsHelper.sol"; +import {FullActivityRollup, CompressedActivityRollup, Consensus} from "../contracts/structs/Activity.sol"; +import {ValidatorRewarderMap} from "../contracts/examples/ValidatorRewarderMap.sol"; +import {SubnetActorActivityFacet} from "../contracts/subnet/SubnetActorActivityFacet.sol"; + struct TestSubnetDefinition { GatewayDiamond gateway; address gatewayAddr; @@ -163,6 +167,7 @@ contract TestSubnetActor is Test, TestParams { bytes4[] saCutterSelectors; bytes4[] saLouperSelectors; bytes4[] saOwnershipSelectors; + bytes4[] saActivitySelectors; SubnetActorDiamond saDiamond; SubnetActorMock saMock; @@ -177,6 +182,7 @@ contract TestSubnetActor is Test, TestParams { saCutterSelectors = SelectorLibrary.resolveSelectors("DiamondCutFacet"); saLouperSelectors = SelectorLibrary.resolveSelectors("DiamondLoupeFacet"); saOwnershipSelectors = SelectorLibrary.resolveSelectors("OwnershipFacet"); + saActivitySelectors = SelectorLibrary.resolveSelectors("SubnetActorActivityFacet"); } function defaultSubnetActorParamsWith( @@ -205,7 +211,8 @@ contract TestSubnetActor is Test, TestParams { permissionMode: PermissionMode.Collateral, supplySource: source, collateralSource: AssetHelper.native(), - validatorGater: address(0) + validatorGater: address(0), + validatorRewarder: address(0) }); return params; } @@ -229,7 +236,8 @@ contract TestSubnetActor is Test, TestParams { permissionMode: PermissionMode.Collateral, supplySource: source, collateralSource: collateral, - validatorGater: address(0) + validatorGater: address(0), + validatorRewarder: address(0) }); return params; } @@ -263,7 +271,8 @@ contract TestSubnetActor is Test, TestParams { permissionMode: PermissionMode.Collateral, supplySource: Asset({kind: AssetKind.ERC20, tokenAddress: tokenAddress}), collateralSource: AssetHelper.native(), - validatorGater: address(0) + validatorGater: address(0), + validatorRewarder: address(0) }); return params; } @@ -412,6 +421,7 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, functionSelectors: gwOwnershipSelectors }) ); + gatewayDiamond = new GatewayDiamond(gwDiamondCut, params); return gatewayDiamond; @@ -489,8 +499,9 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, DiamondLoupeFacet louper = new DiamondLoupeFacet(); DiamondCutFacet cutter = new DiamondCutFacet(); OwnershipFacet ownership = new OwnershipFacet(); + SubnetActorActivityFacet activity = new SubnetActorActivityFacet(); - IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](8); + IDiamond.FacetCut[] memory diamondCut = new IDiamond.FacetCut[](9); diamondCut[0] = ( IDiamond.FacetCut({ @@ -556,6 +567,13 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, }) ); + diamondCut[8] = ( + IDiamond.FacetCut({ + facetAddress: address(activity), + action: IDiamond.FacetCutAction.Add, + functionSelectors: saActivitySelectors + }) + ); SubnetActorDiamond diamond = new SubnetActorDiamond(diamondCut, params, address(this)); return diamond; @@ -606,7 +624,8 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, permissionMode: _permissionMode, supplySource: AssetHelper.native(), collateralSource: AssetHelper.native(), - validatorGater: address(0) + validatorGater: address(0), + validatorRewarder: address(new ValidatorRewarderMap()) }); saDiamond = createSubnetActor(params); } @@ -637,7 +656,8 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, permissionMode: _permissionMode, supplySource: AssetHelper.native(), collateralSource: AssetHelper.native(), - validatorGater: _validatorGater + validatorGater: _validatorGater, + validatorRewarder: address(new ValidatorRewarderMap()) }); saDiamond = createSubnetActor(params); } @@ -913,7 +933,51 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, blockHeight: h, blockHash: keccak256(abi.encode(h)), nextConfigurationNumber: nextConfigNum - 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: CompressedActivityRollup({ + consensus: Consensus.CompressedSummary({ + stats: Consensus.AggregatedStats({ + totalActiveValidators: uint64(validators.length), + totalNumBlocksCommitted: 3 + }), + dataRootCommitment: Consensus.MerkleHash.wrap(bytes32(uint256(nextConfigNum))) + }) + }) + }); + + vm.deal(address(saDiamond), 100 ether); + + bytes32 hash = keccak256(abi.encode(checkpoint)); + + for (uint256 i = 0; i < n; i++) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privKeys[i], hash); + signatures[i] = abi.encodePacked(r, s, v); + } + + vm.prank(validators[0]); + saDiamond.checkpointer().submitCheckpoint(checkpoint, validators, signatures); + } + + function confirmChange( + address[] memory validators, + uint256[] memory privKeys, + CompressedActivityRollup memory activities + ) internal { + uint256 n = validators.length; + + bytes[] memory signatures = new bytes[](n); + + (uint64 nextConfigNum, ) = saDiamond.getter().getConfigurationNumbers(); + + uint256 h = saDiamond.getter().lastBottomUpCheckpointHeight() + saDiamond.getter().bottomUpCheckPeriod(); + + BottomUpCheckpoint memory checkpoint = BottomUpCheckpoint({ + subnetID: saDiamond.getter().getParent().createSubnetId(address(saDiamond)), + blockHeight: h, + blockHash: keccak256(abi.encode(h)), + nextConfigurationNumber: nextConfigNum - 1, + msgs: new IpcEnvelope[](0), + activity: activities }); vm.deal(address(saDiamond), 100 ether); diff --git a/contracts/test/helpers/ActivityHelper.sol b/contracts/test/helpers/ActivityHelper.sol new file mode 100644 index 000000000..d4bda988c --- /dev/null +++ b/contracts/test/helpers/ActivityHelper.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.23; + +import {Consensus, CompressedActivityRollup, FullActivityRollup} from "../../contracts/structs/Activity.sol"; + +library ActivityHelper { + function newCompressedActivityRollup( + uint64 totalActiveValidators, + uint64 totalNumBlocksCommitted, + bytes32 detailsRootCommitment + ) internal pure returns (CompressedActivityRollup memory compressed) { + Consensus.CompressedSummary memory summary = Consensus.CompressedSummary({ + stats: Consensus.AggregatedStats({ + totalActiveValidators: totalActiveValidators, + totalNumBlocksCommitted: totalNumBlocksCommitted + }), + dataRootCommitment: Consensus.MerkleHash.wrap(detailsRootCommitment) + }); + compressed.consensus = summary; + return compressed; + } + + function wrapBytes32Array(bytes32[] memory data) internal pure returns (Consensus.MerkleHash[] memory wrapped) { + uint256 length = data.length; + + if (length == 0) { + return wrapped; + } + + wrapped = new Consensus.MerkleHash[](data.length); + for (uint256 i = 0; i < length; ) { + wrapped[i] = Consensus.MerkleHash.wrap(data[i]); + unchecked { + i++; + } + } + + return wrapped; + } + + function dummyActivityRollup() internal pure returns (FullActivityRollup memory rollup) { + Consensus.ValidatorData[] memory data = new Consensus.ValidatorData[](0); + rollup = FullActivityRollup({ + consensus: Consensus.FullSummary({ + stats: Consensus.AggregatedStats({totalActiveValidators: 0, totalNumBlocksCommitted: 0}), + data: data + }) + }); + return rollup; + } +} diff --git a/contracts/test/helpers/GatewayFacetsHelper.sol b/contracts/test/helpers/GatewayFacetsHelper.sol index d35889134..5034bb350 100644 --- a/contracts/test/helpers/GatewayFacetsHelper.sol +++ b/contracts/test/helpers/GatewayFacetsHelper.sol @@ -48,7 +48,6 @@ library GatewayFacetsHelper { return facet; } - // function ownership(GatewayDiamond gw) internal pure returns (OwnershipFacet) { OwnershipFacet facet = OwnershipFacet(address(gw)); return facet; diff --git a/contracts/test/helpers/MerkleTreeHelper.sol b/contracts/test/helpers/MerkleTreeHelper.sol index 0307d7090..c2789fb62 100644 --- a/contracts/test/helpers/MerkleTreeHelper.sol +++ b/contracts/test/helpers/MerkleTreeHelper.sol @@ -31,4 +31,33 @@ library MerkleTreeHelper { return (root, proofs); } + + function createMerkleProofsForConsensusActivity( + address[] memory addrs, + uint64[] memory blocksMined + ) internal returns (bytes32, bytes32[][] memory) { + Merkle merkleTree = new Merkle(); + + if (addrs.length != blocksMined.length) { + revert("different array lengths btw blocks mined and addrs"); + } + + uint256 len = addrs.length; + + bytes32 root; + bytes32[][] memory proofs = new bytes32[][](len); + bytes32[] memory data = new bytes32[](len); + for (uint256 i = 0; i < len; i++) { + data[i] = keccak256(bytes.concat(keccak256(abi.encode(addrs[i], blocksMined[i])))); + } + + root = merkleTree.getRoot(data); + // get proof + for (uint256 i = 0; i < len; i++) { + bytes32[] memory proof = merkleTree.getProof(data, i); + proofs[i] = proof; + } + + return (root, proofs); + } } diff --git a/contracts/test/helpers/SelectorLibrary.sol b/contracts/test/helpers/SelectorLibrary.sol index adcdc5e96..ae80a4482 100644 --- a/contracts/test/helpers/SelectorLibrary.sol +++ b/contracts/test/helpers/SelectorLibrary.sol @@ -48,7 +48,7 @@ library SelectorLibrary { if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("CheckpointingFacet"))) { return abi.decode( - hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000453b4e7bf00000000000000000000000000000000000000000000000000000000fba0fa4d00000000000000000000000000000000000000000000000000000000dc749b0500000000000000000000000000000000000000000000000000000000ac81837900000000000000000000000000000000000000000000000000000000", + hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000453b4e7bf00000000000000000000000000000000000000000000000000000000a031667200000000000000000000000000000000000000000000000000000000d8b4602500000000000000000000000000000000000000000000000000000000ac81837900000000000000000000000000000000000000000000000000000000", (bytes4[]) ); } @@ -97,14 +97,14 @@ library SelectorLibrary { if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("SubnetActorCheckpointingFacet"))) { return abi.decode( - hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000279979f5700000000000000000000000000000000000000000000000000000000cc2dc2b900000000000000000000000000000000000000000000000000000000", + hex"00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002dcab3d7800000000000000000000000000000000000000000000000000000000cc2dc2b900000000000000000000000000000000000000000000000000000000", (bytes4[]) ); } if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("RegisterSubnetFacet"))) { return abi.decode( - hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000018471614600000000000000000000000000000000000000000000000000000000", + hex"00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001611941f900000000000000000000000000000000000000000000000000000000", (bytes4[]) ); } @@ -118,7 +118,14 @@ library SelectorLibrary { if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("SubnetActorMock"))) { return abi.decode( - hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001210fd4261000000000000000000000000000000000000000000000000000000004e71d92d00000000000000000000000000000000000000000000000000000000350a14bf00000000000000000000000000000000000000000000000000000000c7ebdaef000000000000000000000000000000000000000000000000000000003ae247130000000000000000000000000000000000000000000000000000000041c0e1b500000000000000000000000000000000000000000000000000000000d66d9e19000000000000000000000000000000000000000000000000000000008456cb59000000000000000000000000000000000000000000000000000000005c975abb000000000000000000000000000000000000000000000000000000004d9013a10000000000000000000000000000000000000000000000000000000066783c9b00000000000000000000000000000000000000000000000000000000da5d09ee00000000000000000000000000000000000000000000000000000000dcda897300000000000000000000000000000000000000000000000000000000a694fc3a0000000000000000000000000000000000000000000000000000000079979f57000000000000000000000000000000000000000000000000000000003f4ba83a000000000000000000000000000000000000000000000000000000002e17de7800000000000000000000000000000000000000000000000000000000cc2dc2b900000000000000000000000000000000000000000000000000000000", + hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d10fd4261000000000000000000000000000000000000000000000000000000004e71d92d00000000000000000000000000000000000000000000000000000000350a14bf00000000000000000000000000000000000000000000000000000000c7ebdaef000000000000000000000000000000000000000000000000000000003ae247130000000000000000000000000000000000000000000000000000000041c0e1b500000000000000000000000000000000000000000000000000000000d66d9e19000000000000000000000000000000000000000000000000000000004d9013a10000000000000000000000000000000000000000000000000000000066783c9b00000000000000000000000000000000000000000000000000000000da5d09ee00000000000000000000000000000000000000000000000000000000dcda897300000000000000000000000000000000000000000000000000000000a694fc3a000000000000000000000000000000000000000000000000000000002e17de7800000000000000000000000000000000000000000000000000000000", + (bytes4[]) + ); + } + if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("SubnetActorActivityFacet"))) { + return + abi.decode( + hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000202eca6eb00000000000000000000000000000000000000000000000000000000f9d3434c00000000000000000000000000000000000000000000000000000000", (bytes4[]) ); } diff --git a/contracts/test/helpers/SubnetActorFacetsHelper.sol b/contracts/test/helpers/SubnetActorFacetsHelper.sol index 4d7a24002..595b0ba93 100644 --- a/contracts/test/helpers/SubnetActorFacetsHelper.sol +++ b/contracts/test/helpers/SubnetActorFacetsHelper.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.23; import {SubnetActorManagerFacet} from "../../contracts/subnet/SubnetActorManagerFacet.sol"; import {SubnetActorPauseFacet} from "../../contracts/subnet/SubnetActorPauseFacet.sol"; import {SubnetActorCheckpointingFacet} from "../../contracts/subnet/SubnetActorCheckpointingFacet.sol"; +import {SubnetActorActivityFacet} from "../../contracts/subnet/SubnetActorActivityFacet.sol"; import {SubnetActorRewardFacet} from "../../contracts/subnet/SubnetActorRewardFacet.sol"; import {SubnetActorGetterFacet} from "../../contracts/subnet/SubnetActorGetterFacet.sol"; import {SubnetActorDiamond} from "../../contracts/SubnetActorDiamond.sol"; @@ -46,7 +47,10 @@ library SubnetActorFacetsHelper { return facet; } - // + function activity(SubnetActorDiamond sa) internal pure returns (SubnetActorActivityFacet) { + SubnetActorActivityFacet facet = SubnetActorActivityFacet(address(sa)); + return facet; + } function manager(SubnetActorDiamond sa) internal pure returns (SubnetActorManagerFacet) { SubnetActorManagerFacet facet = SubnetActorManagerFacet(address(sa)); diff --git a/contracts/test/integration/GatewayDiamond.t.sol b/contracts/test/integration/GatewayDiamond.t.sol index 0759b98f0..6a96a6c3b 100644 --- a/contracts/test/integration/GatewayDiamond.t.sol +++ b/contracts/test/integration/GatewayDiamond.t.sol @@ -39,6 +39,9 @@ import {GatewayFacetsHelper} from "../helpers/GatewayFacetsHelper.sol"; import {SubnetActorDiamond} from "../../contracts/SubnetActorDiamond.sol"; import {SubnetActorFacetsHelper} from "../helpers/SubnetActorFacetsHelper.sol"; +import {FullActivityRollup, Consensus} from "../../contracts/structs/Activity.sol"; +import {ActivityHelper} from "../helpers/ActivityHelper.sol"; + contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeTokenMock { using SubnetIDHelper for SubnetID; using CrossMsgHelper for IpcEnvelope; @@ -62,7 +65,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT require(owner != newOwner, "ownership should be updated"); require(newOwner == address(1), "new owner not address 1"); - vm.expectRevert(LibDiamond.NotOwner.selector); + vm.expectRevert(NotOwner.selector); gatewayDiamond.ownership().transferOwnership(address(1)); } @@ -140,7 +143,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT ); //test that other user cannot call diamondcut to add function vm.prank(0x1234567890123456789012345678901234567890); - vm.expectRevert(LibDiamond.NotOwner.selector); + vm.expectRevert(NotOwner.selector); gwDiamondCutter.diamondCut(gwDiamondCut, address(0), new bytes(0)); gwDiamondCutter.diamondCut(gwDiamondCut, address(0), new bytes(0)); @@ -161,7 +164,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT //test that other user cannot call diamondcut to replace function vm.prank(0x1234567890123456789012345678901234567890); - vm.expectRevert(LibDiamond.NotOwner.selector); + vm.expectRevert(NotOwner.selector); gwDiamondCutter.diamondCut(gwDiamondCut, address(0), new bytes(0)); gwDiamondCutter.diamondCut(gwDiamondCut, address(0), new bytes(0)); @@ -179,7 +182,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT //test that other user cannot call diamondcut to remove function vm.prank(0x1234567890123456789012345678901234567890); - vm.expectRevert(LibDiamond.NotOwner.selector); + vm.expectRevert(NotOwner.selector); gwDiamondCutter.diamondCut(gwDiamondCut, address(0), new bytes(0)); gwDiamondCutter.diamondCut(gwDiamondCut, address(0), new bytes(0)); @@ -1067,7 +1070,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: 0, blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); BottomUpCheckpoint memory checkpoint = BottomUpCheckpoint({ @@ -1075,13 +1079,19 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); // failed to create a checkpoint with zero membership weight vm.startPrank(FilAddress.SYSTEM_ACTOR); vm.expectRevert(ZeroMembershipWeight.selector); - gatewayDiamond.checkpointer().createBottomUpCheckpoint(checkpoint, membershipRoot, 0); + gatewayDiamond.checkpointer().createBottomUpCheckpoint( + checkpoint, + membershipRoot, + 0, + ActivityHelper.dummyActivityRollup() + ); vm.stopPrank(); // failed create a processed checkpoint @@ -1090,7 +1100,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT gatewayDiamond.checkpointer().createBottomUpCheckpoint( old, membershipRoot, - weights[0] + weights[1] + weights[2] + weights[0] + weights[1] + weights[2], + ActivityHelper.dummyActivityRollup() ); vm.stopPrank(); @@ -1099,7 +1110,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT gatewayDiamond.checkpointer().createBottomUpCheckpoint( checkpoint, membershipRoot, - weights[0] + weights[1] + weights[2] + weights[0] + weights[1] + weights[2], + ActivityHelper.dummyActivityRollup() ); vm.stopPrank(); @@ -1116,7 +1128,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: d, blockHash: keccak256("block"), nextConfigurationNumber: 2, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.startPrank(FilAddress.SYSTEM_ACTOR); @@ -1124,7 +1137,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT gatewayDiamond.checkpointer().createBottomUpCheckpoint( checkpoint, membershipRoot, - weights[0] + weights[1] + weights[2] + weights[0] + weights[1] + weights[2], + ActivityHelper.dummyActivityRollup() ); vm.stopPrank(); @@ -1139,7 +1153,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.expectRevert(InvalidCheckpointSource.selector); @@ -1160,7 +1175,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.prank(caller); @@ -1206,7 +1222,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.prank(caller); @@ -1226,7 +1243,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); BottomUpCheckpoint memory checkpoint2 = BottomUpCheckpoint({ @@ -1234,7 +1252,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: 2 * gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block2"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); // create a checkpoint @@ -1242,12 +1261,14 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT gatewayDiamond.checkpointer().createBottomUpCheckpoint( checkpoint1, membershipRoot, - weights[0] + weights[1] + weights[2] + weights[0] + weights[1] + weights[2], + ActivityHelper.dummyActivityRollup() ); gatewayDiamond.checkpointer().createBottomUpCheckpoint( checkpoint2, membershipRoot, - weights[0] + weights[1] + weights[2] + weights[0] + weights[1] + weights[2], + ActivityHelper.dummyActivityRollup() ); vm.stopPrank(); @@ -1298,7 +1319,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); // create a checkpoint @@ -1306,7 +1328,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT gatewayDiamond.checkpointer().createBottomUpCheckpoint( checkpoint, membershipRoot, - weights[0] + weights[1] + weights[2] + weights[0] + weights[1] + weights[2], + ActivityHelper.dummyActivityRollup() ); vm.stopPrank(); @@ -1359,7 +1382,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); // create a checkpoint @@ -1367,7 +1391,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT gatewayDiamond.checkpointer().createBottomUpCheckpoint( checkpoint, membershipRoot, - weights[0] + weights[1] + weights[2] + weights[0] + weights[1] + weights[2], + ActivityHelper.dummyActivityRollup() ); vm.stopPrank(); @@ -1442,12 +1467,18 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); // create a checkpoint vm.startPrank(FilAddress.SYSTEM_ACTOR); - gatewayDiamond.checkpointer().createBottomUpCheckpoint(checkpoint, membershipRoot, 10); + gatewayDiamond.checkpointer().createBottomUpCheckpoint( + checkpoint, + membershipRoot, + 10, + ActivityHelper.dummyActivityRollup() + ); vm.stopPrank(); uint8 v; @@ -1476,12 +1507,18 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); // create a checkpoint vm.startPrank(FilAddress.SYSTEM_ACTOR); - gatewayDiamond.checkpointer().createBottomUpCheckpoint(checkpoint, membershipRoot, 10); + gatewayDiamond.checkpointer().createBottomUpCheckpoint( + checkpoint, + membershipRoot, + 10, + ActivityHelper.dummyActivityRollup() + ); vm.stopPrank(); uint8 v; @@ -1520,12 +1557,18 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); // create a checkpoint vm.startPrank(FilAddress.SYSTEM_ACTOR); - gatewayDiamond.checkpointer().createBottomUpCheckpoint(checkpoint, membershipRoot, 10); + gatewayDiamond.checkpointer().createBottomUpCheckpoint( + checkpoint, + membershipRoot, + 10, + ActivityHelper.dummyActivityRollup() + ); vm.stopPrank(); uint8 v; @@ -1568,10 +1611,16 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: i * gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); - gatewayDiamond.checkpointer().createBottomUpCheckpoint(checkpoint, membershipRoot, 10); + gatewayDiamond.checkpointer().createBottomUpCheckpoint( + checkpoint, + membershipRoot, + 10, + ActivityHelper.dummyActivityRollup() + ); } vm.stopPrank(); @@ -1631,7 +1680,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.prank(caller); diff --git a/contracts/test/integration/GatewayDiamondToken.t.sol b/contracts/test/integration/GatewayDiamondToken.t.sol index 74f6223cf..b1d01eb35 100644 --- a/contracts/test/integration/GatewayDiamondToken.t.sol +++ b/contracts/test/integration/GatewayDiamondToken.t.sol @@ -33,6 +33,9 @@ import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.so import {GatewayFacetsHelper} from "../helpers/GatewayFacetsHelper.sol"; +import {FullActivityRollup, Consensus} from "../../contracts/structs/Activity.sol"; +import {ActivityHelper} from "../helpers/ActivityHelper.sol"; + contract GatewayDiamondTokenTest is Test, IntegrationTestBase { using SubnetIDHelper for SubnetID; using CrossMsgHelper for IpcEnvelope; @@ -163,7 +166,8 @@ contract GatewayDiamondTokenTest is Test, IntegrationTestBase { blockHash: blockhash(block.number), blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.prank(address(saDiamond)); @@ -221,7 +225,8 @@ contract GatewayDiamondTokenTest is Test, IntegrationTestBase { blockHash: blockhash(block.number), blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); // Verify that we received the call and that the recipient has the tokens. diff --git a/contracts/test/integration/MultiSubnet.t.sol b/contracts/test/integration/MultiSubnet.t.sol index 088f828fb..53654136d 100644 --- a/contracts/test/integration/MultiSubnet.t.sol +++ b/contracts/test/integration/MultiSubnet.t.sol @@ -45,6 +45,9 @@ import {SubnetActorFacetsHelper} from "../helpers/SubnetActorFacetsHelper.sol"; import "forge-std/console.sol"; +import {FullActivityRollup, Consensus} from "../../contracts/structs/Activity.sol"; +import {ActivityHelper} from "../helpers/ActivityHelper.sol"; + contract MultiSubnetTest is Test, IntegrationTestBase { using SubnetIDHelper for SubnetID; using CrossMsgHelper for IpcEnvelope; @@ -1348,11 +1351,17 @@ contract MultiSubnetTest is Test, IntegrationTestBase { blockHeight: batch.blockHeight, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: batch.msgs + msgs: batch.msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.startPrank(FilAddress.SYSTEM_ACTOR); - checkpointer.createBottomUpCheckpoint(checkpoint, membershipRoot, weights[0] + weights[1] + weights[2]); + checkpointer.createBottomUpCheckpoint( + checkpoint, + membershipRoot, + weights[0] + weights[1] + weights[2], + ActivityHelper.dummyActivityRollup() + ); vm.stopPrank(); return checkpoint; @@ -1377,11 +1386,17 @@ contract MultiSubnetTest is Test, IntegrationTestBase { blockHeight: e, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.startPrank(FilAddress.SYSTEM_ACTOR); - checkpointer.createBottomUpCheckpoint(checkpoint, membershipRoot, weights[0] + weights[1] + weights[2]); + checkpointer.createBottomUpCheckpoint( + checkpoint, + membershipRoot, + weights[0] + weights[1] + weights[2], + ActivityHelper.dummyActivityRollup() + ); vm.stopPrank(); return checkpoint; diff --git a/contracts/test/integration/SubnetActorDiamond.t.sol b/contracts/test/integration/SubnetActorDiamond.t.sol index 85f57e135..5ead392fb 100644 --- a/contracts/test/integration/SubnetActorDiamond.t.sol +++ b/contracts/test/integration/SubnetActorDiamond.t.sol @@ -43,6 +43,11 @@ import {GatewayFacetsHelper} from "../helpers/GatewayFacetsHelper.sol"; import {ERC20PresetFixedSupply} from "../helpers/ERC20PresetFixedSupply.sol"; import {SubnetValidatorGater} from "../../contracts/examples/SubnetValidatorGater.sol"; +import {FullActivityRollup, Consensus} from "../../contracts/structs/Activity.sol"; +import {ValidatorRewarderMap} from "../../contracts/examples/ValidatorRewarderMap.sol"; +import {MerkleTreeHelper} from "../helpers/MerkleTreeHelper.sol"; +import {ActivityHelper} from "../helpers/ActivityHelper.sol"; + contract SubnetActorDiamondTest is Test, IntegrationTestBase { using SubnetIDHelper for SubnetID; using FilAddress for address; @@ -78,7 +83,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { } function testSubnetActorDiamondReal_LoupeFunction() public view { - require(saDiamond.diamondLouper().facets().length == 8, "unexpected length"); + require(saDiamond.diamondLouper().facets().length == 9, "unexpected length"); require( saDiamond.diamondLouper().supportsInterface(type(IERC165).interfaceId) == true, "IERC165 not supported" @@ -340,7 +345,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { permissionMode: PermissionMode.Collateral, supplySource: native, collateralSource: AssetHelper.native(), - validatorGater: address(0) + validatorGater: address(0), + validatorRewarder: address(0) }), address(saDupGetterFaucet), address(saDupMangerFaucet), @@ -688,7 +694,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); BottomUpCheckpoint memory checkpointWithIncorrectHeight = BottomUpCheckpoint({ @@ -696,7 +703,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 1, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.deal(address(saDiamond), 100 ether); @@ -796,7 +804,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 1, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); BottomUpCheckpoint memory checkpointWithIncorrectHeight = BottomUpCheckpoint({ @@ -804,7 +813,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 1, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.deal(address(saDiamond), 100 ether); @@ -833,6 +843,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { // submit another again checkpoint.blockHeight = 2; + checkpoint.activity = ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))); hash = keccak256(abi.encode(checkpoint)); for (uint256 i = 0; i < 3; i++) { @@ -888,7 +899,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 1, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require(saDiamond.getter().lastBottomUpCheckpointHeight() == 1, " checkpoint height incorrect"); @@ -900,7 +912,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 3, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require(saDiamond.getter().lastBottomUpCheckpointHeight() == 3, " checkpoint height incorrect"); @@ -911,7 +924,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 2, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.expectRevert(BottomUpCheckpointAlreadySubmitted.selector); submitCheckpointInternal(checkpoint, validators, signatures, keys); @@ -922,7 +936,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod() + 1, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.expectRevert(CannotSubmitFutureCheckpoint.selector); submitCheckpointInternal(checkpoint, validators, signatures, keys); @@ -932,7 +947,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -945,7 +961,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod() + 1, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -958,7 +975,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod() + 2, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -971,7 +989,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod() + 3, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.expectRevert(InvalidCheckpointEpoch.selector); submitCheckpointInternal(checkpoint, validators, signatures, keys); @@ -981,7 +1000,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod() * 2, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -994,7 +1014,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod() * 3, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -1035,7 +1056,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); vm.deal(address(saDiamond), 100 ether); @@ -1078,7 +1100,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 2 * saDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + activity: ActivityHelper.newCompressedActivityRollup(1, 3, bytes32(uint256(0))) }); hash = keccak256(abi.encode(checkpoint)); @@ -1116,7 +1139,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { ); //test that other user cannot call diamondcut to add function vm.prank(0x1234567890123456789012345678901234567890); - vm.expectRevert(LibDiamond.NotOwner.selector); + vm.expectRevert(NotOwner.selector); saDiamondCutter.diamondCut(saDiamondCut, address(0), new bytes(0)); saDiamondCutter.diamondCut(saDiamondCut, address(0), new bytes(0)); @@ -1136,7 +1159,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { //test that other user cannot call diamondcut to replace function vm.prank(0x1234567890123456789012345678901234567890); - vm.expectRevert(LibDiamond.NotOwner.selector); + vm.expectRevert(NotOwner.selector); saDiamondCutter.diamondCut(saDiamondCut, address(0), new bytes(0)); saDiamondCutter.diamondCut(saDiamondCut, address(0), new bytes(0)); @@ -1154,7 +1177,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { //test that other user cannot call diamondcut to remove function vm.prank(0x1234567890123456789012345678901234567890); - vm.expectRevert(LibDiamond.NotOwner.selector); + vm.expectRevert(NotOwner.selector); saDiamondCutter.diamondCut(saDiamondCut, address(0), new bytes(0)); saDiamondCutter.diamondCut(saDiamondCut, address(0), new bytes(0)); @@ -1773,14 +1796,14 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { function testSubnetActorDiamond_PauseUnpause_NotOwner() public { vm.prank(vm.addr(1)); - vm.expectRevert(LibDiamond.NotOwner.selector); + vm.expectRevert(NotOwner.selector); saDiamond.pauser().pause(); saDiamond.pauser().pause(); require(saDiamond.pauser().paused(), "not paused"); vm.prank(vm.addr(1)); - vm.expectRevert(LibDiamond.NotOwner.selector); + vm.expectRevert(NotOwner.selector); saDiamond.pauser().unpause(); saDiamond.pauser().unpause(); @@ -2310,6 +2333,251 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { require(address(gatewayAddress).balance == DEFAULT_MIN_VALIDATOR_STAKE, "gateway post claim balance wrong"); } + // ============== Test Activities =============== + function testSubnetActor_ValidatorClaimMiningReward_Works() public { + gatewayAddress = address(gatewayDiamond); + + Asset memory source = Asset({kind: AssetKind.Native, tokenAddress: address(0)}); + + SubnetActorDiamond.ConstructorParams memory params = defaultSubnetActorParamsWith( + gatewayAddress, + SubnetID(ROOTNET_CHAINID, new address[](0)), + source, + AssetHelper.native() + ); + ValidatorRewarderMap m = new ValidatorRewarderMap(); + params.validatorRewarder = address(m); + params.minValidators = 2; + params.permissionMode = PermissionMode.Federated; + + saDiamond = createSubnetActor(params); + + SubnetID memory subnetId = SubnetID(ROOTNET_CHAINID, new address[](1)); + subnetId.route[0] = address(saDiamond); + m.setSubnet(subnetId); + + (address[] memory addrs, uint256[] memory privKeys, bytes[] memory pubkeys) = TestUtils.newValidators(4); + + uint256[] memory powers = new uint256[](4); + powers[0] = 10000; + powers[1] = 10000; + powers[2] = 10000; + powers[3] = 10000; + saDiamond.manager().setFederatedPower(addrs, pubkeys, powers); + + uint64[] memory blocksMined = new uint64[](addrs.length); + + blocksMined[0] = 1; + blocksMined[1] = 2; + + (bytes32 activityRoot, bytes32[][] memory proofs) = MerkleTreeHelper.createMerkleProofsForConsensusActivity( + addrs, + blocksMined + ); + + confirmChange(addrs, privKeys, ActivityHelper.newCompressedActivityRollup(2, 3, activityRoot)); + + uint64 bottomUpCheckPeriod = uint64(gatewayDiamond.getter().bottomUpCheckPeriod()); + + vm.startPrank(addrs[0]); + vm.deal(addrs[0], 1 ether); + saDiamond.activity().claim( + subnetId, + bottomUpCheckPeriod, + Consensus.ValidatorData({validator: addrs[0], blocksCommitted: blocksMined[0]}), + ActivityHelper.wrapBytes32Array(proofs[0]) + ); + + vm.startPrank(addrs[1]); + vm.deal(addrs[1], 1 ether); + saDiamond.activity().claim( + subnetId, + bottomUpCheckPeriod, + Consensus.ValidatorData({validator: addrs[1], blocksCommitted: blocksMined[1]}), + ActivityHelper.wrapBytes32Array(proofs[1]) + ); + + // These validators have no claims; they were inactive, so the pending activity should've been removed + // and as a result, the claim should fail. + + vm.startPrank(addrs[2]); + vm.deal(addrs[2], 1 ether); + vm.expectRevert(MissingActivityCommitment.selector); + saDiamond.activity().claim( + subnetId, + bottomUpCheckPeriod, + Consensus.ValidatorData({validator: addrs[2], blocksCommitted: blocksMined[2]}), + ActivityHelper.wrapBytes32Array(proofs[2]) + ); + + vm.startPrank(addrs[3]); + vm.deal(addrs[3], 1 ether); + vm.expectRevert(MissingActivityCommitment.selector); + saDiamond.activity().claim( + subnetId, + bottomUpCheckPeriod, + Consensus.ValidatorData({validator: addrs[3], blocksCommitted: blocksMined[3]}), + ActivityHelper.wrapBytes32Array(proofs[3]) + ); + + // check + assert(m.blocksCommitted(addrs[0]) == 1); + assert(m.blocksCommitted(addrs[1]) == 2); + assert(m.blocksCommitted(addrs[2]) == 0); + assert(m.blocksCommitted(addrs[3]) == 0); + } + + function testSubnetActor_ValidatorBatchClaimMiningReward_Works() public { + ValidatorRewarderMap m = new ValidatorRewarderMap(); + { + gatewayAddress = address(gatewayDiamond); + + Asset memory source = Asset({kind: AssetKind.Native, tokenAddress: address(0)}); + + SubnetActorDiamond.ConstructorParams memory params = defaultSubnetActorParamsWith( + gatewayAddress, + SubnetID(ROOTNET_CHAINID, new address[](0)), + source, + AssetHelper.native() + ); + params.validatorRewarder = address(m); + params.minValidators = 2; + params.permissionMode = PermissionMode.Federated; + + saDiamond = createSubnetActor(params); + } + + SubnetID memory subnetId = SubnetID(ROOTNET_CHAINID, new address[](1)); + subnetId.route[0] = address(saDiamond); + m.setSubnet(subnetId); + + (address[] memory addrs, uint256[] memory privKeys, bytes[] memory pubkeys) = TestUtils.newValidators(4); + + { + uint256[] memory powers = new uint256[](4); + powers[0] = 10000; + powers[1] = 10000; + powers[2] = 10000; + powers[3] = 10000; + saDiamond.manager().setFederatedPower(addrs, pubkeys, powers); + } + + uint64[] memory blocksMined = new uint64[](addrs.length); + + blocksMined[0] = 1; + blocksMined[1] = 2; + + (bytes32 activityRoot1, bytes32[][] memory proofs1) = MerkleTreeHelper.createMerkleProofsForConsensusActivity( + addrs, + blocksMined + ); + + (bytes32 activityRoot2, bytes32[][] memory proofs2) = MerkleTreeHelper.createMerkleProofsForConsensusActivity( + addrs, + blocksMined + ); + + confirmChange(addrs, privKeys, ActivityHelper.newCompressedActivityRollup(2, 3, activityRoot1)); + confirmChange(addrs, privKeys, ActivityHelper.newCompressedActivityRollup(2, 3, activityRoot2)); + + vm.startPrank(addrs[0]); + vm.deal(addrs[0], 1 ether); + + Consensus.ValidatorClaim[] memory claimProofs = new Consensus.ValidatorClaim[](2); + uint64[] memory checkpointHeights = new uint64[](2); + + checkpointHeights[0] = uint64(gatewayDiamond.getter().bottomUpCheckPeriod()); + checkpointHeights[1] = uint64(gatewayDiamond.getter().bottomUpCheckPeriod()) * 2; + + claimProofs[0] = Consensus.ValidatorClaim({ + data: Consensus.ValidatorData({validator: addrs[0], blocksCommitted: blocksMined[0]}), + proof: ActivityHelper.wrapBytes32Array(proofs1[0]) + }); + claimProofs[1] = Consensus.ValidatorClaim({ + data: Consensus.ValidatorData({validator: addrs[0], blocksCommitted: blocksMined[0]}), + proof: ActivityHelper.wrapBytes32Array(proofs2[0]) + }); + + saDiamond.activity().batchSubnetClaim(subnetId, checkpointHeights, claimProofs); + + // check + assert(m.blocksCommitted(addrs[0]) == 2); + } + + function testSubnetActor_ValidatorBatchClaimMiningReward_NoDoubleClaim() public { + ValidatorRewarderMap m = new ValidatorRewarderMap(); + { + gatewayAddress = address(gatewayDiamond); + + Asset memory source = Asset({kind: AssetKind.Native, tokenAddress: address(0)}); + + SubnetActorDiamond.ConstructorParams memory params = defaultSubnetActorParamsWith( + gatewayAddress, + SubnetID(ROOTNET_CHAINID, new address[](0)), + source, + AssetHelper.native() + ); + params.validatorRewarder = address(m); + params.minValidators = 2; + params.permissionMode = PermissionMode.Federated; + + saDiamond = createSubnetActor(params); + } + + SubnetID memory subnetId = SubnetID(ROOTNET_CHAINID, new address[](1)); + subnetId.route[0] = address(saDiamond); + m.setSubnet(subnetId); + + (address[] memory addrs, uint256[] memory privKeys, bytes[] memory pubkeys) = TestUtils.newValidators(4); + + { + uint256[] memory powers = new uint256[](4); + powers[0] = 10000; + powers[1] = 10000; + powers[2] = 10000; + powers[3] = 10000; + saDiamond.manager().setFederatedPower(addrs, pubkeys, powers); + } + + uint64[] memory blocksMined = new uint64[](addrs.length); + + blocksMined[0] = 1; + blocksMined[1] = 2; + + (bytes32 activityRoot1, bytes32[][] memory proofs1) = MerkleTreeHelper.createMerkleProofsForConsensusActivity( + addrs, + blocksMined + ); + (bytes32 activityRoot2, bytes32[][] memory proofs2) = MerkleTreeHelper.createMerkleProofsForConsensusActivity( + addrs, + blocksMined + ); + + confirmChange(addrs, privKeys, ActivityHelper.newCompressedActivityRollup(2, 3, activityRoot1)); + confirmChange(addrs, privKeys, ActivityHelper.newCompressedActivityRollup(2, 3, activityRoot2)); + + vm.startPrank(addrs[0]); + vm.deal(addrs[0], 1 ether); + + Consensus.ValidatorClaim[] memory claimProofs = new Consensus.ValidatorClaim[](2); + uint64[] memory heights = new uint64[](2); + + heights[0] = uint64(gatewayDiamond.getter().bottomUpCheckPeriod()); + heights[1] = uint64(gatewayDiamond.getter().bottomUpCheckPeriod()); + + claimProofs[0] = Consensus.ValidatorClaim({ + data: Consensus.ValidatorData({validator: addrs[0], blocksCommitted: blocksMined[0]}), + proof: ActivityHelper.wrapBytes32Array(proofs1[0]) + }); + claimProofs[1] = Consensus.ValidatorClaim({ + data: Consensus.ValidatorData({validator: addrs[0], blocksCommitted: blocksMined[0]}), + proof: ActivityHelper.wrapBytes32Array(proofs2[0]) + }); + + vm.expectRevert(ValidatorAlreadyClaimed.selector); + saDiamond.activity().batchSubnetClaim(subnetId, heights, claimProofs); + } + // ----------------------------------------------------------------------------------------------------------------- // Tests for validator gater // ----------------------------------------------------------------------------------------------------------------- diff --git a/contracts/test/integration/SubnetRegistry.t.sol b/contracts/test/integration/SubnetRegistry.t.sol index 95c7d8b87..3703c3fbe 100644 --- a/contracts/test/integration/SubnetRegistry.t.sol +++ b/contracts/test/integration/SubnetRegistry.t.sol @@ -18,6 +18,7 @@ import {SubnetActorPauseFacet} from "../../contracts/subnet/SubnetActorPauseFace import {SubnetActorCheckpointingFacet} from "../../contracts/subnet/SubnetActorCheckpointingFacet.sol"; import {SubnetActorRewardFacet} from "../../contracts/subnet/SubnetActorRewardFacet.sol"; import {SubnetActorDiamond} from "../../contracts/SubnetActorDiamond.sol"; +import {SubnetActorActivityFacet} from "../../contracts/subnet/SubnetActorActivityFacet.sol"; import {SubnetID, PermissionMode, SubnetCreationPrivileges} from "../../contracts/structs/Subnet.sol"; import {SubnetRegistryDiamond} from "../../contracts/SubnetRegistryDiamond.sol"; @@ -29,6 +30,7 @@ import {OwnershipFacet} from "../../contracts/OwnershipFacet.sol"; import {AssetHelper} from "../../contracts/lib/AssetHelper.sol"; import {RegistryFacetsHelper} from "../helpers/RegistryFacetsHelper.sol"; import {DiamondFacetsHelper} from "../helpers/DiamondFacetsHelper.sol"; +import {ValidatorRewarderMap} from "../../contracts/examples/ValidatorRewarderMap.sol"; import {SelectorLibrary} from "../helpers/SelectorLibrary.sol"; @@ -66,6 +68,7 @@ contract SubnetRegistryTest is Test, TestRegistry, IntegrationTestBase { params.diamondCutFacet = address(new DiamondCutFacet()); params.diamondLoupeFacet = address(new DiamondLoupeFacet()); params.ownershipFacet = address(new OwnershipFacet()); + params.activityFacet = address(new SubnetActorActivityFacet()); params.subnetActorGetterSelectors = mockedSelectors; params.subnetActorManagerSelectors = mockedSelectors2; @@ -75,6 +78,7 @@ contract SubnetRegistryTest is Test, TestRegistry, IntegrationTestBase { params.subnetActorDiamondCutSelectors = SelectorLibrary.resolveSelectors("DiamondCutFacet"); params.subnetActorDiamondLoupeSelectors = SelectorLibrary.resolveSelectors("DiamondLoupeFacet"); params.subnetActorOwnershipSelectors = SelectorLibrary.resolveSelectors("OwnershipFacet"); + params.subnetActorActivitySelectors = SelectorLibrary.resolveSelectors("SubnetActorActivityFacet"); params.creationPrivileges = SubnetCreationPrivileges.Unrestricted; @@ -101,7 +105,7 @@ contract SubnetRegistryTest is Test, TestRegistry, IntegrationTestBase { params.permissionMode = PermissionMode.Collateral; vm.prank(address(1)); - vm.expectRevert(LibDiamond.NotOwner.selector); + vm.expectRevert(NotOwner.selector); s.register().newSubnetActor(params); } @@ -170,6 +174,7 @@ contract SubnetRegistryTest is Test, TestRegistry, IntegrationTestBase { new SubnetRegistryDiamond(diamondCut, params); params.ownershipFacet = address(8); + params.activityFacet = address(9); new SubnetRegistryDiamond(diamondCut, params); } @@ -257,7 +262,8 @@ contract SubnetRegistryTest is Test, TestRegistry, IntegrationTestBase { permissionMode: PermissionMode.Collateral, supplySource: AssetHelper.native(), collateralSource: AssetHelper.native(), - validatorGater: address(0) + validatorGater: address(0), + validatorRewarder: address(new ValidatorRewarderMap()) }); registrySubnetFacet.newSubnetActor(params); @@ -308,7 +314,7 @@ contract SubnetRegistryTest is Test, TestRegistry, IntegrationTestBase { // Test only owner can update vm.prank(address(1)); // Set a different address as the sender - vm.expectRevert(abi.encodeWithSelector(LibDiamond.NotOwner.selector)); // Expected revert message + vm.expectRevert(abi.encodeWithSelector(NotOwner.selector)); // Expected revert message registrySubnetGetterFacet.updateReferenceSubnetContract( newGetterFacet, newManagerFacet, diff --git a/contracts/test/invariants/SubnetRegistryInvariants.t.sol b/contracts/test/invariants/SubnetRegistryInvariants.t.sol index 4dd42e247..b6858f132 100644 --- a/contracts/test/invariants/SubnetRegistryInvariants.t.sol +++ b/contracts/test/invariants/SubnetRegistryInvariants.t.sol @@ -18,6 +18,7 @@ import {SubnetGetterFacet} from "../../contracts/subnetregistry/SubnetGetterFace import {DiamondLoupeFacet} from "../../contracts/diamond/DiamondLoupeFacet.sol"; import {DiamondCutFacet} from "../../contracts/diamond/DiamondCutFacet.sol"; import {OwnershipFacet} from "../../contracts/OwnershipFacet.sol"; +import {SubnetActorActivityFacet} from "../../contracts/subnet/SubnetActorActivityFacet.sol"; import {IntegrationTestBase, TestRegistry} from "../IntegrationTestBase.sol"; import {SelectorLibrary} from "../helpers/SelectorLibrary.sol"; @@ -51,6 +52,7 @@ contract SubnetRegistryInvariants is StdInvariant, Test, TestRegistry, Integrati params.diamondCutFacet = address(new DiamondCutFacet()); params.diamondLoupeFacet = address(new DiamondLoupeFacet()); params.ownershipFacet = address(new OwnershipFacet()); + params.activityFacet = address(new SubnetActorActivityFacet()); params.subnetActorGetterSelectors = mockedSelectors; params.subnetActorManagerSelectors = mockedSelectors2; @@ -60,6 +62,7 @@ contract SubnetRegistryInvariants is StdInvariant, Test, TestRegistry, Integrati params.subnetActorDiamondCutSelectors = SelectorLibrary.resolveSelectors("DiamondCutFacet"); params.subnetActorDiamondLoupeSelectors = SelectorLibrary.resolveSelectors("DiamondLoupeFacet"); params.subnetActorOwnershipSelectors = SelectorLibrary.resolveSelectors("OwnershipFacet"); + params.subnetActorActivitySelectors = SelectorLibrary.resolveSelectors("SubnetActorActivityFacet"); registryDiamond = createSubnetRegistry(params); registryHandler = new SubnetRegistryHandler(registryDiamond); diff --git a/contracts/test/invariants/handlers/SubnetRegistryHandler.sol b/contracts/test/invariants/handlers/SubnetRegistryHandler.sol index 688383197..a842c2106 100644 --- a/contracts/test/invariants/handlers/SubnetRegistryHandler.sol +++ b/contracts/test/invariants/handlers/SubnetRegistryHandler.sol @@ -11,6 +11,8 @@ import {SubnetRegistryDiamond} from "../../../contracts/SubnetRegistryDiamond.so import {ConsensusType} from "../../../contracts/enums/ConsensusType.sol"; import {SubnetID, PermissionMode} from "../../../contracts/structs/Subnet.sol"; import {AssetHelper} from "../../../contracts/lib/AssetHelper.sol"; +import {ValidatorRewarderMap} from "../../../contracts/examples/ValidatorRewarderMap.sol"; + import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {RegistryFacetsHelper} from "../../helpers/RegistryFacetsHelper.sol"; @@ -124,7 +126,8 @@ contract SubnetRegistryHandler is CommonBase, StdCheats, StdUtils { permissionMode: PermissionMode.Collateral, supplySource: AssetHelper.native(), collateralSource: AssetHelper.native(), - validatorGater: address(0) + validatorGater: address(0), + validatorRewarder: address(new ValidatorRewarderMap()) }); address owner = getRandomOldAddressOrNewOne(seed); diff --git a/contracts/test/mocks/SubnetActorMock.sol b/contracts/test/mocks/SubnetActorMock.sol index b5ff8a123..152901b1c 100644 --- a/contracts/test/mocks/SubnetActorMock.sol +++ b/contracts/test/mocks/SubnetActorMock.sol @@ -3,16 +3,9 @@ pragma solidity ^0.8.23; import {SubnetActorManagerFacet} from "../../contracts/subnet/SubnetActorManagerFacet.sol"; import {LibStaking} from "../../contracts/lib/LibStaking.sol"; -import {SubnetActorPauseFacet} from "../../contracts/subnet/SubnetActorPauseFacet.sol"; import {SubnetActorRewardFacet} from "../../contracts/subnet/SubnetActorRewardFacet.sol"; -import {SubnetActorCheckpointingFacet} from "../../contracts/subnet/SubnetActorCheckpointingFacet.sol"; -contract SubnetActorMock is - SubnetActorPauseFacet, - SubnetActorManagerFacet, - SubnetActorRewardFacet, - SubnetActorCheckpointingFacet -{ +contract SubnetActorMock is SubnetActorManagerFacet, SubnetActorRewardFacet { function confirmChange(uint64 _configurationNumber) external { LibStaking.confirmChange(_configurationNumber); } diff --git a/extras/axelar-token/foundry.toml b/extras/axelar-token/foundry.toml index bd8ca5d7e..e06f33cc0 100644 --- a/extras/axelar-token/foundry.toml +++ b/extras/axelar-token/foundry.toml @@ -5,4 +5,5 @@ libs = ["node_modules", "lib"] fs_permissions = [{ access = "read-write", path = "./out"}] remappings = [ "@consensus-shipyard/=node_modules/@consensus-shipyard/" -] \ No newline at end of file +] +allow_paths = ["../../contracts"] \ No newline at end of file diff --git a/extras/linked-token/foundry.toml b/extras/linked-token/foundry.toml index 57993af93..4409ba773 100644 --- a/extras/linked-token/foundry.toml +++ b/extras/linked-token/foundry.toml @@ -7,4 +7,4 @@ remappings = [ "@ipc/=node_modules/@consensus-shipyard/ipc-contracts/", ## this murky remapping is only needed transitively for testing; we should try to get rid of this. "murky/=node_modules/@consensus-shipyard/ipc-contracts/lib/murky/src/", -] +] \ No newline at end of file diff --git a/extras/linked-token/test/MultiSubnetTest.t.sol b/extras/linked-token/test/MultiSubnetTest.t.sol index 38143279f..8b421d7e3 100644 --- a/extras/linked-token/test/MultiSubnetTest.t.sol +++ b/extras/linked-token/test/MultiSubnetTest.t.sol @@ -9,6 +9,7 @@ import {TestUtils} from "@ipc/test/helpers/TestUtils.sol"; import {MerkleTreeHelper} from "@ipc/test/helpers/MerkleTreeHelper.sol"; import {GatewayFacetsHelper} from "@ipc/test/helpers/GatewayFacetsHelper.sol"; import {SubnetActorFacetsHelper} from "@ipc/test/helpers/SubnetActorFacetsHelper.sol"; +import {ActivityHelper} from "@ipc/test/helpers/ActivityHelper.sol"; import {LinkedTokenController} from "../contracts/LinkedTokenController.sol"; import {LinkedTokenReplica} from "../contracts/LinkedTokenReplica.sol"; @@ -28,6 +29,7 @@ import {GatewayGetterFacet} from "@ipc/contracts/gateway/GatewayGetterFacet.sol" import {SubnetActorCheckpointingFacet} from "@ipc/contracts/subnet/SubnetActorCheckpointingFacet.sol"; import {CheckpointingFacet} from "@ipc/contracts/gateway/router/CheckpointingFacet.sol"; import {FvmAddressHelper} from "@ipc/contracts/lib/FvmAddressHelper.sol"; +import {Consensus, CompressedActivityRollup} from "@ipc/contracts/structs/Activity.sol"; import {IpcEnvelope, BottomUpMsgBatch, BottomUpCheckpoint, ParentFinality, IpcMsgKind, ResultMsg, CallMsg} from "@ipc/contracts/structs/CrossNet.sol"; import {SubnetIDHelper} from "@ipc/contracts/lib/SubnetIDHelper.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -383,11 +385,20 @@ contract MultiSubnetTest is IntegrationTestBase { blockHeight: batch.blockHeight, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: batch.msgs + msgs: batch.msgs, + activity: CompressedActivityRollup({ + consensus: Consensus.CompressedSummary({ + stats: Consensus.AggregatedStats({ + totalActiveValidators: 1, + totalNumBlocksCommitted: 1 + }), + dataRootCommitment: Consensus.MerkleHash.wrap(bytes32(0)) + }) + }) }); vm.startPrank(FilAddress.SYSTEM_ACTOR); - checkpointer.createBottomUpCheckpoint(checkpoint, membershipRoot, weights[0] + weights[1] + weights[2]); + checkpointer.createBottomUpCheckpoint(checkpoint, membershipRoot, weights[0] + weights[1] + weights[2], ActivityHelper.dummyActivityRollup()); vm.stopPrank(); return checkpoint; diff --git a/fendermint/actors/Cargo.toml b/fendermint/actors/Cargo.toml index 117ad73ea..f697ee7b8 100644 --- a/fendermint/actors/Cargo.toml +++ b/fendermint/actors/Cargo.toml @@ -6,6 +6,7 @@ edition.workspace = true license.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] +fendermint_actor_activity_tracker = { path = "activity-tracker", features = ["fil-actor"] } fendermint_actor_chainmetadata = { path = "chainmetadata", features = ["fil-actor"] } fendermint_actor_gas_market_eip1559 = { path = "gas_market/eip1559", features = ["fil-actor"] } fendermint_actor_eam = { path = "eam", features = ["fil-actor"] } diff --git a/fendermint/actors/activity-tracker/Cargo.toml b/fendermint/actors/activity-tracker/Cargo.toml new file mode 100644 index 000000000..0c06eaa25 --- /dev/null +++ b/fendermint/actors/activity-tracker/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "fendermint_actor_activity_tracker" +description = "Tracks subnet activity and generates rollups to submit to the parent in checkpoints" +license.workspace = true +edition.workspace = true +authors.workspace = true +version = "0.1.0" + +[lib] +## lib is necessary for integration tests +## cdylib is necessary for Wasm build +crate-type = ["cdylib", "lib"] + +[dependencies] +anyhow = { workspace = true } +cid = { workspace = true } +fil_actors_runtime = { workspace = true } +fvm_ipld_blockstore = { workspace = true } +fvm_ipld_encoding = { workspace = true } +fvm_shared = { workspace = true } +log = { workspace = true } +multihash = { workspace = true } +num-derive = { workspace = true } +num-traits = { workspace = true } +serde = { workspace = true } +serde_tuple = { workspace = true } +hex-literal = { workspace = true } +frc42_dispatch = { workspace = true } + +[dev-dependencies] +fil_actors_evm_shared = { workspace = true } +fil_actors_runtime = { workspace = true, features = ["test_utils"] } + +[features] +fil-actor = ["fil_actors_runtime/fil-actor"] diff --git a/fendermint/actors/activity-tracker/src/lib.rs b/fendermint/actors/activity-tracker/src/lib.rs new file mode 100644 index 000000000..f8f84faa9 --- /dev/null +++ b/fendermint/actors/activity-tracker/src/lib.rs @@ -0,0 +1,117 @@ +// Copyright 2021-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use crate::state::ConsensusData; +pub use crate::state::State; +use crate::types::FullActivityRollup; +use fil_actors_runtime::builtin::singletons::SYSTEM_ACTOR_ADDR; +use fil_actors_runtime::runtime::{ActorCode, Runtime}; +use fil_actors_runtime::{actor_dispatch, ActorError, EAM_ACTOR_ID}; +use fil_actors_runtime::{actor_error, DEFAULT_HAMT_CONFIG}; +use fvm_shared::address::{Address, Payload}; +use fvm_shared::METHOD_CONSTRUCTOR; +use num_derive::FromPrimitive; + +mod state; +pub mod types; + +#[cfg(feature = "fil-actor")] +fil_actors_runtime::wasm_trampoline!(ActivityTrackerActor); + +pub const IPC_ACTIVITY_TRACKER_ACTOR_NAME: &str = "activity_tracker"; + +pub struct ActivityTrackerActor; + +#[derive(FromPrimitive)] +#[repr(u64)] +pub enum Method { + Constructor = METHOD_CONSTRUCTOR, + RecordBlockCommitted = frc42_dispatch::method_hash!("RecordBlockCommitted"), + CommitActivity = frc42_dispatch::method_hash!("CommitActivity"), + PendingActivity = frc42_dispatch::method_hash!("PendingActivity"), +} + +trait ActivityTracker { + /// Hook for the consensus layer to report that the validator committed a new block. + fn record_block_committed(rt: &impl Runtime, validator: Address) -> Result<(), ActorError>; + + /// Commits the pending activity into an activity rollup. + /// Currently, this constructs an activity rollup from the internal state, and then resets the internal state. + /// In the future, this might actually write the activity rollup to the gateway directly, instead of relying on the client to move it around. + /// Returns the activity rollup as a Solidity ABI-encoded type, in raw byte form. + fn commit_activity(rt: &impl Runtime) -> Result; + + /// Queries the activity that has been accumulated since the last commit, and is pending a flush. + fn pending_activity(rt: &impl Runtime) -> Result; +} + +impl ActivityTrackerActor { + pub fn constructor(rt: &impl Runtime) -> Result<(), ActorError> { + let st = State::new(rt.store())?; + rt.create(&st)?; + Ok(()) + } +} + +impl ActivityTracker for ActivityTrackerActor { + fn record_block_committed(rt: &impl Runtime, validator: Address) -> Result<(), ActorError> { + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; + + // Reject non-f410 addresses. + if !matches!(validator.payload(), Payload::Delegated(d) if d.namespace() == EAM_ACTOR_ID && d.subaddress().len() == 20) + { + return Err( + actor_error!(illegal_argument; "validator address must be a valid f410 address"), + ); + } + + rt.transaction(|st: &mut State, rt| { + let mut consensus = + ConsensusData::load(rt.store(), &st.consensus, DEFAULT_HAMT_CONFIG, "consensus")?; + + let mut v = consensus.get(&validator)?.cloned().unwrap_or_default(); + v.blocks_committed += 1; + consensus.set(&validator, v)?; + + st.consensus = consensus.flush()?; + + Ok(()) + }) + } + + fn commit_activity(rt: &impl Runtime) -> Result { + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; + + // Obtain the pending rollup from state. + let rollup = rt.state::()?.pending_activity_rollup(rt)?; + + rt.transaction(|st: &mut State, rt| { + st.consensus = ConsensusData::flush_empty(rt.store(), DEFAULT_HAMT_CONFIG)?; + st.tracking_since = rt.curr_epoch(); + Ok(()) + })?; + + Ok(rollup) + } + + fn pending_activity(rt: &impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + + rt.state::()?.pending_activity_rollup(rt) + } +} + +impl ActorCode for ActivityTrackerActor { + type Methods = Method; + + fn name() -> &'static str { + IPC_ACTIVITY_TRACKER_ACTOR_NAME + } + + actor_dispatch! { + Constructor => constructor, + RecordBlockCommitted => record_block_committed, + CommitActivity => commit_activity, + PendingActivity => pending_activity, + } +} diff --git a/fendermint/actors/activity-tracker/src/state.rs b/fendermint/actors/activity-tracker/src/state.rs new file mode 100644 index 000000000..b7ae7b88b --- /dev/null +++ b/fendermint/actors/activity-tracker/src/state.rs @@ -0,0 +1,54 @@ +// Copyright 2021-2023 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use crate::types::{FullActivityRollup, ValidatorStats}; +use cid::Cid; +use fil_actors_runtime::runtime::Runtime; +use fil_actors_runtime::{ActorError, Map2, DEFAULT_HAMT_CONFIG}; +use fvm_ipld_blockstore::Blockstore; +use fvm_shared::address::Address; +use fvm_shared::clock::ChainEpoch; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct State { + pub tracking_since: ChainEpoch, + pub consensus: Cid, // ConsensusData +} + +pub type ConsensusData = Map2; + +impl State { + pub fn new(store: &BS) -> Result { + let state = State { + tracking_since: 0, + consensus: ConsensusData::flush_empty(store, DEFAULT_HAMT_CONFIG)?, + }; + Ok(state) + } + + /// Returns the pending activity rollup. + pub fn pending_activity_rollup( + &self, + rt: &impl Runtime, + ) -> Result { + let consensus = { + let cid = &rt.state::()?.consensus; + ConsensusData::load(rt.store(), cid, DEFAULT_HAMT_CONFIG, "consensus") + }?; + + // Populate the rollup struct. + let mut rollup = FullActivityRollup::default(); + consensus.for_each(|validator_addr, validator_stats| { + rollup.consensus.stats.total_active_validators += 1; + rollup.consensus.stats.total_num_blocks_committed += validator_stats.blocks_committed; + rollup + .consensus + .data + .insert(validator_addr, validator_stats.clone()); + Ok(()) + })?; + + Ok(rollup) + } +} diff --git a/fendermint/actors/activity-tracker/src/types.rs b/fendermint/actors/activity-tracker/src/types.rs new file mode 100644 index 000000000..3ad297d4f --- /dev/null +++ b/fendermint/actors/activity-tracker/src/types.rs @@ -0,0 +1,28 @@ +// Copyright 2021-2023 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use fvm_ipld_encoding::tuple::{Deserialize_tuple, Serialize_tuple}; +use fvm_shared::address::Address; +use std::collections::HashMap; + +#[derive(Deserialize_tuple, Serialize_tuple, Debug, Clone, PartialEq, Eq, Default)] +pub struct AggregatedStats { + pub total_active_validators: u64, + pub total_num_blocks_committed: u64, +} + +#[derive(Deserialize_tuple, Serialize_tuple, Debug, Clone, PartialEq, Eq, Default)] +pub struct FullConsensusSummary { + pub stats: AggregatedStats, + pub data: HashMap, +} + +#[derive(Deserialize_tuple, Serialize_tuple, Debug, Clone, PartialEq, Eq, Default)] +pub struct FullActivityRollup { + pub consensus: FullConsensusSummary, +} + +#[derive(Deserialize_tuple, Serialize_tuple, Debug, Clone, PartialEq, Eq, Default)] +pub struct ValidatorStats { + pub blocks_committed: u64, +} diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index 86e2313da..2b6178fcb 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -22,7 +22,7 @@ use fendermint_vm_interpreter::fvm::state::{ FvmUpdatableParams, }; use fendermint_vm_interpreter::fvm::store::ReadOnlyBlockstore; -use fendermint_vm_interpreter::fvm::{BlockGasLimit, FvmApplyRet, PowerUpdates}; +use fendermint_vm_interpreter::fvm::{EndBlockOutput, FvmApplyRet}; use fendermint_vm_interpreter::genesis::{read_genesis_car, GenesisAppState}; use fendermint_vm_interpreter::signed::InvalidSignature; use fendermint_vm_interpreter::{ @@ -424,7 +424,7 @@ where Message = Vec, BeginOutput = FvmApplyRet, DeliverOutput = BytesMessageApplyRes, - EndOutput = (PowerUpdates, BlockGasLimit), + EndOutput = EndBlockOutput, >, I: CheckInterpreter< State = FvmExecState>, @@ -794,7 +794,11 @@ where tracing::debug!(height = request.height, "end block"); // End the interpreter for this block. - let (power_updates, new_block_gas_limit) = self + let EndBlockOutput { + power_updates, + block_gas_limit: new_block_gas_limit, + events, + } = self .modify_exec_state(|s| self.interpreter.end(s)) .await .context("end failed")?; @@ -823,7 +827,10 @@ where let ret = response::EndBlock { validator_updates, consensus_param_updates, - events: Vec::new(), // TODO: Return events from epoch transitions. + events: events + .into_iter() + .flat_map(|(stamped, emitters)| to_events("event", stamped, emitters)) + .collect::>(), }; Ok(ret) diff --git a/fendermint/eth/api/Cargo.toml b/fendermint/eth/api/Cargo.toml index c1b675bb7..997bb4496 100644 --- a/fendermint/eth/api/Cargo.toml +++ b/fendermint/eth/api/Cargo.toml @@ -32,7 +32,7 @@ tokio = { workspace = true } tower-http = { workspace = true } fil_actors_evm_shared = { workspace = true } -fvm_shared = { workspace = true } +fvm_shared = { workspace = true, features = ["crypto"] } fvm_ipld_encoding = { workspace = true } fendermint_crypto = { path = "../../crypto" } diff --git a/fendermint/eth/api/src/apis/eth.rs b/fendermint/eth/api/src/apis/eth.rs index 8e6b30281..01ba886f8 100644 --- a/fendermint/eth/api/src/apis/eth.rs +++ b/fendermint/eth/api/src/apis/eth.rs @@ -963,9 +963,9 @@ where while height <= to_height { if let Ok(block_results) = data.tm().block_results(height).await { - if let Some(tx_results) = block_results.txs_results { - let block_number = et::U64::from(height.value()); + let block_number = et::U64::from(height.value()); + if let Some(tx_results) = block_results.txs_results { let block = data .block_by_height(et::BlockNumber::Number(block_number)) .await?; @@ -1010,6 +1010,29 @@ where log_index_start += tx_result.events.len(); } } + + if let Some(events) = block_results.end_block_events { + let emitters = from_tm::collect_emitters(&events); + + // Filter by address. + if !addrs.is_empty() && addrs.intersection(&emitters).next().is_none() { + height = height.increment(); + continue; + } + + // all zero indicating it's system contract call + let tx_hash = et::TxHash::zero(); + let tx_idx = et::U64::zero(); + let block_hash = et::H256::zero(); + + let mut tx_logs = + from_tm::to_logs(&events, block_hash, block_number, tx_hash, tx_idx, 0)?; + + // Filter by topic. + tx_logs.retain(|log| matches_topics(&filter, log)); + + logs.append(&mut tx_logs); + } } else { break; } diff --git a/fendermint/testing/contract-test/Cargo.toml b/fendermint/testing/contract-test/Cargo.toml index 405b8c22f..60f82f8a7 100644 --- a/fendermint/testing/contract-test/Cargo.toml +++ b/fendermint/testing/contract-test/Cargo.toml @@ -13,7 +13,7 @@ anyhow = { workspace = true } cid = { workspace = true } ethers = { workspace = true } fvm = { workspace = true } -fvm_shared = { workspace = true } +fvm_shared = { workspace = true, features = ["crypto"] } fvm_ipld_blockstore = { workspace = true } hex = { workspace = true } rand = { workspace = true } diff --git a/fendermint/testing/contract-test/src/lib.rs b/fendermint/testing/contract-test/src/lib.rs index fb7b8841e..8d6e3f9cf 100644 --- a/fendermint/testing/contract-test/src/lib.rs +++ b/fendermint/testing/contract-test/src/lib.rs @@ -9,7 +9,7 @@ use std::{future::Future, sync::Arc}; use fendermint_crypto::PublicKey; use fendermint_vm_genesis::Genesis; -use fendermint_vm_interpreter::fvm::{BlockGasLimit, PowerUpdates}; +use fendermint_vm_interpreter::fvm::EndBlockOutput; use fendermint_vm_interpreter::genesis::{create_test_genesis_state, GenesisOutput}; use fendermint_vm_interpreter::{ fvm::{ @@ -67,7 +67,7 @@ where Message = FvmMessage, BeginOutput = FvmApplyRet, DeliverOutput = FvmApplyRet, - EndOutput = (PowerUpdates, BlockGasLimit), + EndOutput = EndBlockOutput, >, { pub async fn new(interpreter: I, genesis: Genesis) -> anyhow::Result { @@ -142,8 +142,7 @@ where let _res = self .modify_exec_state(|s| self.interpreter.begin(s)) - .await - .unwrap(); + .await?; Ok(()) } diff --git a/fendermint/testing/contract-test/tests/staking/machine.rs b/fendermint/testing/contract-test/tests/staking/machine.rs index 7aed62ea5..b950dd339 100644 --- a/fendermint/testing/contract-test/tests/staking/machine.rs +++ b/fendermint/testing/contract-test/tests/staking/machine.rs @@ -120,6 +120,7 @@ impl StateMachine for StakingMachine { token_address: ethers::types::Address::zero(), }, validator_gater: EthAddress::from(ethers::types::Address::zero()).into(), + validator_rewarder: Default::default(), }; eprintln!("\n> PARENT IPC: {parent_ipc:?}"); @@ -285,6 +286,7 @@ impl StateMachine for StakingMachine { block_hash: *block_hash, next_configuration_number: *next_configuration_number, msgs: Vec::new(), + activity: Default::default(), }; let checkpoint_hash = checkpoint.clone().abi_hash(); diff --git a/fendermint/vm/actor_interface/src/activity.rs b/fendermint/vm/actor_interface/src/activity.rs new file mode 100644 index 000000000..51353cacd --- /dev/null +++ b/fendermint/vm/actor_interface/src/activity.rs @@ -0,0 +1,4 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +define_id!(ACTIVITY_TRACKER { id: 99 }); diff --git a/fendermint/vm/actor_interface/src/gas.rs b/fendermint/vm/actor_interface/src/gas.rs new file mode 100644 index 000000000..ccc6c1f18 --- /dev/null +++ b/fendermint/vm/actor_interface/src/gas.rs @@ -0,0 +1,4 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +define_id!(GAS_MARKET { id: 98 }); diff --git a/fendermint/vm/actor_interface/src/ipc.rs b/fendermint/vm/actor_interface/src/ipc.rs index f57b3c36d..d3f4c18d7 100644 --- a/fendermint/vm/actor_interface/src/ipc.rs +++ b/fendermint/vm/actor_interface/src/ipc.rs @@ -118,6 +118,12 @@ lazy_static! { name: "OwnershipFacet", abi: ia::ownership_facet::OWNERSHIPFACET_ABI.to_owned(), }, + EthFacet { + name: "SubnetActorActivityFacet", + abi: ia::subnet_actor_activity_facet::SUBNETACTORACTIVITYFACET_ABI + .to_owned(), + }, + // ========== IF YOU WANT TO ADD FACET FOR SUBNET, APPEND HERE ========== // The registry has its own facets: // https://github.com/consensus-shipyard/ipc-solidity-actors/blob/b01a2dffe367745f55111a65536a3f6fea9165f5/scripts/deploy-registry.template.ts#L58-L67 EthFacet { @@ -470,6 +476,7 @@ pub mod registry { pub diamond_cut_facet: Address, pub diamond_loupe_facet: Address, pub ownership_facet: Address, + pub activity_facet: Address, pub subnet_getter_selectors: Vec, pub subnet_manager_selectors: Vec, pub subnet_rewarder_selectors: Vec, @@ -478,6 +485,7 @@ pub mod registry { pub subnet_actor_diamond_cut_selectors: Vec, pub subnet_actor_diamond_loupe_selectors: Vec, pub subnet_actor_ownership_selectors: Vec, + pub subnet_actor_activity_selectors: Vec, pub creation_privileges: u8, // 0 = Unrestricted, 1 = Owner. } } @@ -534,12 +542,13 @@ pub mod subnet { ], next_configuration_number: 1, msgs: vec![], + activity: Default::default(), }; let param_type = BottomUpCheckpoint::param_type(); // Captured value of `abi.encode` in Solidity. - let expected_abi: Bytes = "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000156b736f342ab34d9afe4234a92bdb190c35b2e8d822d9601b00b9d7089b190f0100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000abc8e314f58b4de5000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000007b11cf9ca8ccee13bb3d003c97af5c18434067a90000000000000000000000003d9019b8bf3bfd5e979ddc3b2761be54af867c470000000000000000000000000000000000000000000000000000000000000000".parse().unwrap(); + let expected_abi: Bytes = "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000156b736f342ab34d9afe4234a92bdb190c35b2e8d822d9601b00b9d7089b190f01000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000abc8e314f58b4de5000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000007b11cf9ca8ccee13bb3d003c97af5c18434067a90000000000000000000000003d9019b8bf3bfd5e979ddc3b2761be54af867c470000000000000000000000000000000000000000000000000000000000000000".parse().unwrap(); // XXX: It doesn't work with `decode_whole`. let expected_tokens = diff --git a/fendermint/vm/actor_interface/src/lib.rs b/fendermint/vm/actor_interface/src/lib.rs index ca0cbad78..5e6b1c77e 100644 --- a/fendermint/vm/actor_interface/src/lib.rs +++ b/fendermint/vm/actor_interface/src/lib.rs @@ -43,6 +43,7 @@ macro_rules! define_singleton { } pub mod account; +pub mod activity; pub mod burntfunds; pub mod chainmetadata; pub mod cron; diff --git a/fendermint/vm/genesis/Cargo.toml b/fendermint/vm/genesis/Cargo.toml index c44bfbe96..27214281c 100644 --- a/fendermint/vm/genesis/Cargo.toml +++ b/fendermint/vm/genesis/Cargo.toml @@ -34,6 +34,7 @@ quickcheck = { workspace = true } quickcheck_macros = { workspace = true } hex = { workspace = true } serde_json = { workspace = true } +ipc-types = {workspace = true} # Enable arb on self for tests. fendermint_vm_genesis = { path = ".", features = ["arb"] } diff --git a/fendermint/vm/interpreter/Cargo.toml b/fendermint/vm/interpreter/Cargo.toml index 2aa8cc39a..a82fae6e1 100644 --- a/fendermint/vm/interpreter/Cargo.toml +++ b/fendermint/vm/interpreter/Cargo.toml @@ -24,6 +24,7 @@ fendermint_rpc = { path = "../../rpc" } fendermint_tracing = { path = "../../tracing" } fendermint_actors = { path = "../../actors" } fendermint_actor_chainmetadata = { path = "../../actors/chainmetadata" } +fendermint_actor_activity_tracker = { path = "../../actors/activity-tracker" } fendermint_actor_gas_market_eip1559 = { path = "../../actors/gas_market/eip1559" } fendermint_actor_eam = { workspace = true } fendermint_testing = { path = "../../testing", optional = true } @@ -69,6 +70,8 @@ arbitrary = { workspace = true, optional = true } quickcheck = { workspace = true, optional = true } rand = { workspace = true, optional = true } +merkle-tree-rs = { path = "../../../ext/merkle-tree-rs" } + [dev-dependencies] quickcheck = { workspace = true } quickcheck_macros = { workspace = true } @@ -80,6 +83,7 @@ fendermint_testing = { path = "../../testing", features = ["golden"] } fvm = { workspace = true, features = ["arb", "testing"] } fendermint_vm_genesis = { path = "../genesis", features = ["arb"] } multihash = { workspace = true } +hex = { workspace = true } [features] default = [] diff --git a/fendermint/vm/interpreter/src/chain.rs b/fendermint/vm/interpreter/src/chain.rs index 034090a30..d3fe2489e 100644 --- a/fendermint/vm/interpreter/src/chain.rs +++ b/fendermint/vm/interpreter/src/chain.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0, MIT use crate::fvm::state::ipc::GatewayCaller; use crate::fvm::store::ReadOnlyBlockstore; -use crate::fvm::{topdown, BlockGasLimit, FvmApplyRet, PowerUpdates}; +use crate::fvm::{topdown, EndBlockOutput, FvmApplyRet}; use crate::selector::{GasLimitSelector, MessageSelector}; use crate::{ fvm::state::FvmExecState, @@ -246,7 +246,7 @@ where Message = VerifiableMessage, DeliverOutput = SignedMessageApplyRes, State = FvmExecState, - EndOutput = (PowerUpdates, BlockGasLimit), + EndOutput = EndBlockOutput, >, { // The state consists of the resolver pool, which this interpreter needs, and the rest of the @@ -434,10 +434,10 @@ where let (state, out) = self.inner.end(state).await?; // Update any component that needs to know about changes in the power table. - if !out.0 .0.is_empty() { + if !out.power_updates.0.is_empty() { let power_updates = out + .power_updates .0 - .0 .iter() .map(|v| { let vk = ValidatorKey::from(v.public_key.0); diff --git a/fendermint/vm/interpreter/src/fvm/activity/actor.rs b/fendermint/vm/interpreter/src/fvm/activity/actor.rs new file mode 100644 index 000000000..289cd534c --- /dev/null +++ b/fendermint/vm/interpreter/src/fvm/activity/actor.rs @@ -0,0 +1,63 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use crate::fvm::activity::{FullActivity, ValidatorActivityTracker}; +use crate::fvm::state::FvmExecState; +use crate::fvm::FvmMessage; +use anyhow::Context; +use fendermint_actor_activity_tracker::types::FullActivityRollup; +use fendermint_crypto::PublicKey; +use fendermint_vm_actor_interface::activity::ACTIVITY_TRACKER_ACTOR_ADDR; +use fendermint_vm_actor_interface::eam::EthAddress; +use fendermint_vm_actor_interface::system; +use fvm_ipld_blockstore::Blockstore; +use fvm_shared::address::Address; + +pub struct ActorActivityTracker<'a, DB: Blockstore + Clone + 'static> { + pub(crate) executor: &'a mut FvmExecState, +} + +impl<'a, DB: Blockstore + Clone + 'static> ValidatorActivityTracker + for ActorActivityTracker<'a, DB> +{ + fn record_block_committed(&mut self, validator: PublicKey) -> anyhow::Result<()> { + let address: Address = EthAddress::from(validator).into(); + + let msg = FvmMessage { + from: system::SYSTEM_ACTOR_ADDR, + to: ACTIVITY_TRACKER_ACTOR_ADDR, + sequence: 0, // irrelevant + gas_limit: i64::MAX as u64, // exclude this from gas restriction + method_num: fendermint_actor_activity_tracker::Method::RecordBlockCommitted as u64, + params: fvm_ipld_encoding::RawBytes::serialize(address)?, + value: Default::default(), + version: Default::default(), + gas_fee_cap: Default::default(), + gas_premium: Default::default(), + }; + + self.executor.execute_implicit_ok(msg)?; + Ok(()) + } + + fn commit_activity(&mut self) -> anyhow::Result { + let msg = FvmMessage { + from: system::SYSTEM_ACTOR_ADDR, + to: ACTIVITY_TRACKER_ACTOR_ADDR, + sequence: 0, // irrelevant + gas_limit: i64::MAX as u64, // exclude this from gas restriction + method_num: fendermint_actor_activity_tracker::Method::CommitActivity as u64, + params: fvm_ipld_encoding::RawBytes::default(), + value: Default::default(), + version: Default::default(), + gas_fee_cap: Default::default(), + gas_premium: Default::default(), + }; + + let (apply_ret, _) = self.executor.execute_implicit_ok(msg)?; + let r = + fvm_ipld_encoding::from_slice::(&apply_ret.msg_receipt.return_data) + .context("failed to parse validator activities")?; + r.try_into() + } +} diff --git a/fendermint/vm/interpreter/src/fvm/activity/mod.rs b/fendermint/vm/interpreter/src/fvm/activity/mod.rs new file mode 100644 index 000000000..0aa4113c8 --- /dev/null +++ b/fendermint/vm/interpreter/src/fvm/activity/mod.rs @@ -0,0 +1,159 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +//! Tracks the current blockchain block mining activities and propagates to the parent subnet if +//! needed. + +pub mod actor; + +use fendermint_crypto::PublicKey; +use ipc_actors_abis::checkpointing_facet::{ + AggregatedStats, CompressedActivityRollup, CompressedSummary, FullActivityRollup, FullSummary, + ValidatorData, +}; +use ipc_api::checkpoint::VALIDATOR_REWARD_FIELDS; +use ipc_api::evm::payload_to_evm_address; +use ipc_api::merkle::MerkleGen; + +/// Wrapper for FullActivityRollup with some utility functions +pub struct FullActivity(FullActivityRollup); + +/// Tracks the validator activities in the current blockchain +pub trait ValidatorActivityTracker { + /// Mark the validator has mined the target block. + fn record_block_committed(&mut self, validator: PublicKey) -> anyhow::Result<()>; + + /// Get the validators activities summary since the checkpoint height + fn commit_activity(&mut self) -> anyhow::Result; +} + +impl TryFrom for FullActivity { + type Error = anyhow::Error; + + fn try_from( + value: fendermint_actor_activity_tracker::types::FullActivityRollup, + ) -> Result { + let stats = AggregatedStats { + total_active_validators: value.consensus.stats.total_active_validators, + total_num_blocks_committed: value.consensus.stats.total_num_blocks_committed, + }; + let data = value + .consensus + .data + .into_iter() + .map(|(addr, data)| { + let data = ValidatorData { + validator: payload_to_evm_address(addr.payload())?, + blocks_committed: data.blocks_committed, + }; + Ok(data) + }) + .collect::>>()?; + let consensus = FullSummary { stats, data }; + let f = FullActivityRollup { consensus }; + Ok(Self::new(f)) + } +} + +impl FullActivity { + pub fn new(mut full: FullActivityRollup) -> Self { + full.consensus.data.sort_by(|a, b| { + let cmp = a.validator.cmp(&b.validator); + if cmp.is_eq() { + // Address will be unique, do this just in case equal + a.blocks_committed.cmp(&b.blocks_committed) + } else { + cmp + } + }); + Self(full) + } + + pub fn compressed(&self) -> anyhow::Result { + let gen = MerkleGen::new( + |v| vec![format!("{:?}", v.validator), v.blocks_committed.to_string()], + self.0.consensus.data.as_slice(), + &VALIDATOR_REWARD_FIELDS, + )?; + Ok(CompressedActivityRollup { + consensus: CompressedSummary { + stats: self.0.consensus.stats.clone(), + data_root_commitment: gen.root().to_fixed_bytes(), + }, + }) + } + + pub fn into_inner(self) -> FullActivityRollup { + self.0 + } +} + +#[cfg(test)] +mod tests { + use crate::fvm::activity::FullActivity; + use ipc_actors_abis::checkpointing_facet::{ + AggregatedStats, FullActivityRollup, FullSummary, ValidatorData, + }; + use rand::prelude::SliceRandom; + use rand::thread_rng; + use std::str::FromStr; + + #[test] + fn test_commitment() { + let mut v = vec![ + ValidatorData { + validator: ethers::types::Address::from_str( + "0xB29C00299756135ec5d6A140CA54Ec77790a99d6", + ) + .unwrap(), + blocks_committed: 1, + }, + ValidatorData { + validator: ethers::types::Address::from_str( + "0x28345a43c2fBae4412f0AbadFa06Bd8BA3f58867", + ) + .unwrap(), + blocks_committed: 2, + }, + ValidatorData { + validator: ethers::types::Address::from_str( + "0x1A79385eAd0e873FE0C441C034636D3Edf7014cC", + ) + .unwrap(), + blocks_committed: 10, + }, + ValidatorData { + validator: ethers::types::Address::from_str( + "0x76B9d5a35C46B1fFEb37aadf929f1CA63a26A829", + ) + .unwrap(), + blocks_committed: 4, + }, + ValidatorData { + validator: ethers::types::Address::from_str( + "0x3c5cc76b07cb02a372e647887bD6780513659527", + ) + .unwrap(), + blocks_committed: 3, + }, + ]; + + for _ in 0..10 { + v.shuffle(&mut thread_rng()); + let full = FullActivityRollup { + consensus: FullSummary { + stats: AggregatedStats { + total_active_validators: 1, + total_num_blocks_committed: 2, + }, + data: v.clone(), + }, + }; + let details = FullActivity::new(full); + assert_eq!( + hex::encode(details.compressed().unwrap().consensus.data_root_commitment), + "5519955f33109df3338490473cb14458640efdccd4df05998c4c439738280ab0" + ); + } + } +} diff --git a/fendermint/vm/interpreter/src/fvm/checkpoint.rs b/fendermint/vm/interpreter/src/fvm/checkpoint.rs index b9ebd433a..6f077de65 100644 --- a/fendermint/vm/interpreter/src/fvm/checkpoint.rs +++ b/fendermint/vm/interpreter/src/fvm/checkpoint.rs @@ -1,38 +1,35 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use std::collections::HashMap; -use std::time::Duration; - +use super::observe::{ + CheckpointCreated, CheckpointFinalized, CheckpointSigned, CheckpointSignedRole, +}; +use super::state::ipc::tokens_to_burn; +use super::{ + broadcast::Broadcaster, + state::{ipc::GatewayCaller, FvmExecState}, + ValidatorContext, +}; +use crate::fvm::activity::ValidatorActivityTracker; +use crate::fvm::exec::BlockEndEvents; use anyhow::{anyhow, Context}; use ethers::abi::Tokenizable; -use tendermint::block::Height; -use tendermint_rpc::endpoint::commit; -use tendermint_rpc::{endpoint::validators, Client, Paging}; - -use fvm_ipld_blockstore::Blockstore; -use fvm_shared::{address::Address, chainid::ChainID}; - use fendermint_crypto::PublicKey; use fendermint_crypto::SecretKey; use fendermint_vm_actor_interface::eam::EthAddress; use fendermint_vm_actor_interface::ipc::BottomUpCheckpoint; use fendermint_vm_genesis::{Power, Validator, ValidatorKey}; - +use fvm_ipld_blockstore::Blockstore; +use fvm_shared::{address::Address, chainid::ChainID}; use ipc_actors_abis::checkpointing_facet as checkpoint; use ipc_actors_abis::gateway_getter_facet as getter; use ipc_api::staking::ConfigurationNumber; use ipc_observability::{emit, serde::HexEncodableBlockHash}; - -use super::observe::{ - CheckpointCreated, CheckpointFinalized, CheckpointSigned, CheckpointSignedRole, -}; -use super::state::ipc::tokens_to_burn; -use super::{ - broadcast::Broadcaster, - state::{ipc::GatewayCaller, FvmExecState}, - ValidatorContext, -}; +use std::collections::HashMap; +use std::time::Duration; +use tendermint::block::Height; +use tendermint_rpc::endpoint::commit; +use tendermint_rpc::{endpoint::validators, Client, Paging}; /// Validator voting power snapshot. #[derive(Debug, Clone, PartialEq, Eq)] @@ -50,6 +47,7 @@ pub struct PowerUpdates(pub Vec>); pub fn maybe_create_checkpoint( gateway: &GatewayCaller, state: &mut FvmExecState, + event_tracker: &mut BlockEndEvents, ) -> anyhow::Result> where DB: Blockstore + Sync + Send + Clone + 'static, @@ -96,6 +94,8 @@ where let num_msgs = msgs.len(); + let full_activity_rollup = state.activity_tracker().commit_activity()?; + // Construct checkpoint. let checkpoint = BottomUpCheckpoint { subnet_id, @@ -103,13 +103,20 @@ where block_hash, next_configuration_number, msgs, + activity: full_activity_rollup.compressed()?, }; // Save the checkpoint in the ledger. // Pass in the current power table, because these are the validators who can sign this checkpoint. - gateway - .create_bottom_up_checkpoint(state, checkpoint.clone(), &curr_power_table.0) + let ret = gateway + .create_bottom_up_checkpoint( + state, + checkpoint.clone(), + &curr_power_table.0, + full_activity_rollup.into_inner(), + ) .context("failed to store checkpoint")?; + event_tracker.push((ret.apply_ret.events, ret.emitters)); // Figure out the power updates if there was some change in the configuration. let power_updates = if next_configuration_number == 0 { @@ -242,6 +249,23 @@ where block_hash: cp.block_hash, next_configuration_number: cp.next_configuration_number, msgs: convert_tokenizables(cp.msgs)?, + activity: checkpoint::CompressedActivityRollup { + consensus: checkpoint::CompressedSummary { + stats: checkpoint::AggregatedStats { + total_active_validators: cp + .activity + .consensus + .stats + .total_active_validators, + total_num_blocks_committed: cp + .activity + .consensus + .stats + .total_num_blocks_committed, + }, + data_root_commitment: cp.activity.consensus.data_root_commitment, + }, + }, }; // We mustn't do these in parallel because of how nonces are fetched. diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index 0d84e888d..803d54bc4 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -1,25 +1,27 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT +use super::{ + checkpoint::{self, PowerUpdates}, + observe::{CheckpointFinalized, MsgExec, MsgExecPurpose}, + state::FvmExecState, + BlockGasLimit, FvmMessage, FvmMessageInterpreter, +}; +use crate::fvm::activity::ValidatorActivityTracker; +use crate::ExecInterpreter; use anyhow::Context; use async_trait::async_trait; -use std::collections::HashMap; - use fendermint_vm_actor_interface::{chainmetadata, cron, system}; use fvm::executor::ApplyRet; use fvm_ipld_blockstore::Blockstore; +use fvm_shared::event::StampedEvent; use fvm_shared::{address::Address, ActorID, MethodNum, BLOCK_GAS_LIMIT}; use ipc_observability::{emit, measure_time, observe::TracingError, Traceable}; +use std::collections::HashMap; use tendermint_rpc::Client; -use crate::ExecInterpreter; - -use super::{ - checkpoint::{self, PowerUpdates}, - observe::{CheckpointFinalized, MsgExec, MsgExecPurpose}, - state::FvmExecState, - BlockGasLimit, FvmMessage, FvmMessageInterpreter, -}; +pub type Event = (Vec, HashMap); +pub type BlockEndEvents = Vec; /// The return value extended with some things from the message that /// might not be available to the caller, because of the message lookups @@ -35,6 +37,13 @@ pub struct FvmApplyRet { pub emitters: HashMap, } +pub struct EndBlockOutput { + pub power_updates: PowerUpdates, + pub block_gas_limit: BlockGasLimit, + /// The end block events to be recorded + pub events: BlockEndEvents, +} + #[async_trait] impl ExecInterpreter for FvmMessageInterpreter where @@ -48,7 +57,7 @@ where /// Return validator power updates and the next base fee. /// Currently ignoring events as there aren't any emitted by the smart contract, /// but keep in mind that if there were, those would have to be propagated. - type EndOutput = (PowerUpdates, BlockGasLimit); + type EndOutput = EndBlockOutput; async fn begin( &self, @@ -192,6 +201,12 @@ where } async fn end(&self, mut state: Self::State) -> anyhow::Result<(Self::State, Self::EndOutput)> { + let mut block_end_events = BlockEndEvents::default(); + + if let Some(pubkey) = state.block_producer() { + state.activity_tracker().record_block_committed(pubkey)?; + } + let next_gas_market = state.finalize_gas_market()?; // TODO: Consider doing this async, since it's purely informational and not consensus-critical. @@ -204,7 +219,7 @@ where }); let updates = if let Some((checkpoint, updates)) = - checkpoint::maybe_create_checkpoint(&self.gateway, &mut state) + checkpoint::maybe_create_checkpoint(&self.gateway, &mut state, &mut block_end_events) .context("failed to create checkpoint")? { // Asynchronously broadcast signature, if validating. @@ -252,7 +267,11 @@ where PowerUpdates::default() }; - let ret = (updates, next_gas_market.block_gas_limit); + let ret = EndBlockOutput { + power_updates: updates, + block_gas_limit: next_gas_market.block_gas_limit, + events: block_end_events, + }; Ok((state, ret)) } } diff --git a/fendermint/vm/interpreter/src/fvm/mod.rs b/fendermint/vm/interpreter/src/fvm/mod.rs index e0fa04a26..85962ab4b 100644 --- a/fendermint/vm/interpreter/src/fvm/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/mod.rs @@ -15,12 +15,13 @@ pub mod upgrades; #[cfg(any(test, feature = "bundle"))] pub mod bundle; +pub mod activity; pub(crate) mod gas; pub(crate) mod topdown; pub use check::FvmCheckRet; pub use checkpoint::PowerUpdates; -pub use exec::FvmApplyRet; +pub use exec::{EndBlockOutput, FvmApplyRet}; use fendermint_crypto::{PublicKey, SecretKey}; pub use fendermint_vm_message::query::FvmQuery; use fvm_ipld_blockstore::Blockstore; diff --git a/fendermint/vm/interpreter/src/fvm/state/exec.rs b/fendermint/vm/interpreter/src/fvm/state/exec.rs index 8e70abcbd..d849328d7 100644 --- a/fendermint/vm/interpreter/src/fvm/state/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/state/exec.rs @@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet}; +use crate::fvm::activity::actor::ActorActivityTracker; use crate::fvm::externs::FendermintExterns; use crate::fvm::gas::BlockGasTracker; use anyhow::Ok; @@ -146,8 +147,8 @@ where let engine = multi_engine.get(&nc)?; let externs = FendermintExterns::new(blockstore.clone(), params.state_root); - let machine = DefaultMachine::new(&mc, blockstore, externs)?; - let mut executor = DefaultExecutor::new(engine, machine)?; + let machine = DefaultMachine::new(&mc, blockstore.clone(), externs)?; + let mut executor = DefaultExecutor::new(engine.clone(), machine)?; let block_gas_tracker = BlockGasTracker::create(&mut executor)?; @@ -195,6 +196,16 @@ where self.execute_message(msg, ApplyKind::Implicit) } + /// Execute message implicitly but ensures the execution is successful and returns only the ApplyRet. + pub fn execute_implicit_ok(&mut self, msg: Message) -> ExecResult { + let r = self.execute_implicit(msg)?; + if let Some(err) = &r.0.failure_info { + anyhow::bail!("failed to apply message: {}", err) + } else { + Ok(r) + } + } + /// Execute message explicitly. pub fn execute_explicit(&mut self, msg: Message) -> ExecResult { self.execute_message(msg, ApplyKind::Explicit) @@ -291,6 +302,10 @@ where self.executor.context().network.chain_id } + pub fn activity_tracker(&mut self) -> ActorActivityTracker { + ActorActivityTracker { executor: self } + } + /// Collect all the event emitters' delegated addresses, for those who have any. fn emitter_delegated_addresses(&self, apply_ret: &ApplyRet) -> anyhow::Result { let emitter_ids = apply_ret diff --git a/fendermint/vm/interpreter/src/fvm/state/genesis.rs b/fendermint/vm/interpreter/src/fvm/state/genesis.rs index b53fe5d36..7959bf47e 100644 --- a/fendermint/vm/interpreter/src/fvm/state/genesis.rs +++ b/fendermint/vm/interpreter/src/fvm/state/genesis.rs @@ -279,6 +279,23 @@ where self.create_actor_internal(code_cid, id, state, balance, delegated_address) } + pub fn construct_custom_actor( + &mut self, + name: &str, + id: ActorID, + state: &impl Serialize, + balance: TokenAmount, + delegated_address: Option
, + ) -> anyhow::Result<()> { + // Retrieve the CID of the actor code by the numeric ID. + let code_cid = *self + .custom_actor_manifest + .code_by_name(name) + .ok_or_else(|| anyhow!("can't find actor: {name} in the custom actor manifest"))?; + + self.create_actor_internal(code_cid, id, state, balance, delegated_address) + } + /// Creates an actor using code specified in the manifest. fn create_actor_internal( &mut self, diff --git a/fendermint/vm/interpreter/src/fvm/state/ipc.rs b/fendermint/vm/interpreter/src/fvm/state/ipc.rs index 12caa26c6..8fd153832 100644 --- a/fendermint/vm/interpreter/src/fvm/state/ipc.rs +++ b/fendermint/vm/interpreter/src/fvm/state/ipc.rs @@ -123,7 +123,8 @@ impl GatewayCaller { state: &mut FvmExecState, checkpoint: checkpointing_facet::BottomUpCheckpoint, power_table: &[Validator], - ) -> anyhow::Result<()> { + activity: checkpointing_facet::FullActivityRollup, + ) -> anyhow::Result { // Construct a Merkle tree from the power table, which we can use to validate validator set membership // when the signatures are submitted in transactions for accumulation. let tree = @@ -133,9 +134,12 @@ impl GatewayCaller { p.saturating_add(et::U256::from(v.power.0)) }); - self.checkpointing.call(state, |c| { - c.create_bottom_up_checkpoint(checkpoint, tree.root_hash().0, total_power) - }) + Ok(self + .checkpointing + .call_with_return(state, |c| { + c.create_bottom_up_checkpoint(checkpoint, tree.root_hash().0, total_power, activity) + })? + .into_return()) } /// Retrieve checkpoints which have not reached a quorum. diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 48b39ef33..ce610e0bf 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -19,7 +19,8 @@ use fendermint_vm_actor_interface::diamond::{EthContract, EthContractMap}; use fendermint_vm_actor_interface::eam::EthAddress; use fendermint_vm_actor_interface::ipc::IPC_CONTRACTS; use fendermint_vm_actor_interface::{ - account, burntfunds, chainmetadata, cron, eam, gas_market, init, ipc, reward, system, EMPTY_ARR, + account, activity, burntfunds, chainmetadata, cron, eam, gas_market, init, ipc, reward, system, + EMPTY_ARR, }; use fendermint_vm_core::{chainid, Timestamp}; use fendermint_vm_genesis::{ActorMeta, Collateral, Genesis, Power, PowerScale, Validator}; @@ -452,6 +453,17 @@ impl GenesisBuilder { ) .context("failed to create default eip1559 gas market actor")?; + let tracker_state = fendermint_actor_activity_tracker::State::new(state.store())?; + state + .create_custom_actor( + fendermint_actor_activity_tracker::IPC_ACTIVITY_TRACKER_ACTOR_NAME, + activity::ACTIVITY_TRACKER_ACTOR_ID, + &tracker_state, + TokenAmount::zero(), + None, + ) + .context("failed to create activity tracker actor")?; + // STAGE 2: Create non-builtin accounts which do not have a fixed ID. // The next ID is going to be _after_ the accounts, which have already been assigned an ID by the `Init` actor. @@ -582,6 +594,7 @@ fn deploy_contracts( let diamond_loupe_facet = facets.remove(0); let diamond_cut_facet = facets.remove(0); let ownership_facet = facets.remove(0); + let activity_facet = facets.remove(0); debug_assert_eq!(facets.len(), 2, "SubnetRegistry has 2 facets of its own"); @@ -595,6 +608,7 @@ fn deploy_contracts( diamond_cut_facet: diamond_cut_facet.facet_address, diamond_loupe_facet: diamond_loupe_facet.facet_address, ownership_facet: ownership_facet.facet_address, + activity_facet: activity_facet.facet_address, subnet_getter_selectors: getter_facet.function_selectors, subnet_manager_selectors: manager_facet.function_selectors, subnet_rewarder_selectors: rewarder_facet.function_selectors, @@ -603,6 +617,7 @@ fn deploy_contracts( subnet_actor_diamond_cut_selectors: diamond_cut_facet.function_selectors, subnet_actor_diamond_loupe_selectors: diamond_loupe_facet.function_selectors, subnet_actor_ownership_selectors: ownership_facet.function_selectors, + subnet_actor_activity_selectors: activity_facet.function_selectors, creation_privileges: 0, }; diff --git a/fendermint/vm/message/Cargo.toml b/fendermint/vm/message/Cargo.toml index 3816b9170..77d264fb2 100644 --- a/fendermint/vm/message/Cargo.toml +++ b/fendermint/vm/message/Cargo.toml @@ -22,7 +22,7 @@ quickcheck = { workspace = true, optional = true } rand = { workspace = true, optional = true } cid = { workspace = true } -fvm_shared = { workspace = true } +fvm_shared = { workspace = true, features = ["crypto"] } fvm_ipld_encoding = { workspace = true } ipc-api = { workspace = true } diff --git a/ipc/api/Cargo.toml b/ipc/api/Cargo.toml index 9cbcdceb1..df2c6bacb 100644 --- a/ipc/api/Cargo.toml +++ b/ipc/api/Cargo.toml @@ -31,6 +31,7 @@ serde_with = { workspace = true, features = ["hex"] } ipc_actors_abis = { workspace = true } ipc-types = { workspace = true } +merkle-tree-rs = { path = "../../ext/merkle-tree-rs" } [dev-dependencies] serde_json = { workspace = true } diff --git a/ipc/api/src/checkpoint.rs b/ipc/api/src/checkpoint.rs index 88f41f891..145e90a86 100644 --- a/ipc/api/src/checkpoint.rs +++ b/ipc/api/src/checkpoint.rs @@ -25,6 +25,8 @@ lazy_static! { // for storing the cid of an inaccessible HAMT. pub static ref CHECKPOINT_GENESIS_CID: Cid = Cid::new_v1(DAG_CBOR, Code::Blake2b256.digest("genesis".as_bytes())); + /// ABI types of the Merkle tree which contains validator addresses and their voting power. + pub static ref VALIDATOR_REWARD_FIELDS: Vec = vec!["address".to_owned(), "uint64".to_owned()]; } pub type Signature = Vec; @@ -76,6 +78,54 @@ pub struct BottomUpMsgBatch { pub msgs: Vec, } +/// Compressed representation of the activity summary that can be embedded in checkpoints to propagate up the hierarchy. +#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] +pub struct CompressedActivityRollup { + pub consensus: consensus::CompressedSummary, +} + +/// Namespace for consensus-level activity summaries. +/// XYZ(raulk) move to activity module +pub mod consensus { + use fvm_shared::address::Address; + use serde::{Deserialize, Serialize}; + + /// Aggregated stats for consensus-level activity. + #[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] + pub struct AggregatedStats { + /// The total number of unique validators that have mined within this period. + pub total_active_validators: u64, + /// The total number of blocks committed by all validators during this period. + pub total_num_blocks_committed: u64, + } + + /// The commitments for the child subnet activities that should be submitted to the parent subnet + /// together with a bottom up checkpoint + #[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] + pub struct ValidatorData { + /// The validator address + pub validator: Address, + /// The number of blocks mined + pub blocks_committed: u64, + } + + // The full activity summary for consensus-level activity. + #[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] + pub struct FullSummary { + pub stats: AggregatedStats, + /// The breakdown of activity per validator. + pub data: Vec, + } + + /// The compresed representation of the activity summary for consensus-level activity suitable for embedding in a checkpoint. + #[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] + pub struct CompressedSummary { + pub stats: AggregatedStats, + /// The commitment for the validator details, so that we don't have to transmit them in full. + pub data_root_commitment: Vec, + } +} + #[serde_as] #[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] pub struct BottomUpCheckpoint { @@ -94,6 +144,8 @@ pub struct BottomUpCheckpoint { pub next_configuration_number: u64, /// The list of messages for execution pub msgs: Vec, + /// The activity commitment from child subnet to parent subnet + pub activity_rollup: CompressedActivityRollup, } pub fn serialize_vec_bytes_to_vec_hex, S>( diff --git a/ipc/api/src/evm.rs b/ipc/api/src/evm.rs index 4c842fea6..d19a853a3 100644 --- a/ipc/api/src/evm.rs +++ b/ipc/api/src/evm.rs @@ -4,8 +4,8 @@ //! Type conversion for IPC Agent struct with solidity contract struct use crate::address::IPCAddress; -use crate::checkpoint::BottomUpCheckpoint; use crate::checkpoint::BottomUpMsgBatch; +use crate::checkpoint::{consensus, BottomUpCheckpoint, CompressedActivityRollup}; use crate::cross::{IpcEnvelope, IpcMsgKind}; use crate::staking::StakingChange; use crate::staking::StakingChangeRequest; @@ -18,9 +18,10 @@ use fvm_shared::address::{Address, Payload}; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use ipc_actors_abis::{ - gateway_getter_facet, gateway_manager_facet, gateway_messenger_facet, lib_gateway, - register_subnet_facet, subnet_actor_checkpointing_facet, subnet_actor_diamond, - subnet_actor_getter_facet, top_down_finality_facet, xnet_messaging_facet, + checkpointing_facet, gateway_getter_facet, gateway_manager_facet, gateway_messenger_facet, + lib_gateway, register_subnet_facet, subnet_actor_activity_facet, + subnet_actor_checkpointing_facet, subnet_actor_diamond, subnet_actor_getter_facet, + top_down_finality_facet, xnet_messaging_facet, }; /// The type conversion for IPC structs to evm solidity contracts. We need this convenient macro because @@ -121,6 +122,61 @@ macro_rules! cross_msg_types { /// The type conversion between different bottom up checkpoint definition in ethers and sdk macro_rules! bottom_up_checkpoint_conversion { ($module:ident) => { + impl TryFrom for $module::AggregatedStats { + type Error = anyhow::Error; + + fn try_from(c: consensus::AggregatedStats) -> Result { + Ok($module::AggregatedStats { + total_active_validators: c.total_active_validators, + total_num_blocks_committed: c.total_num_blocks_committed, + }) + } + } + + impl TryFrom for $module::CompressedActivityRollup { + type Error = anyhow::Error; + + fn try_from(c: CompressedActivityRollup) -> Result { + Ok($module::CompressedActivityRollup { + consensus: c.consensus.try_into()?, + }) + } + } + + impl From<$module::CompressedActivityRollup> for CompressedActivityRollup { + fn from(value: $module::CompressedActivityRollup) -> Self { + CompressedActivityRollup { + consensus: consensus::CompressedSummary { + stats: consensus::AggregatedStats { + total_active_validators: value.consensus.stats.total_active_validators, + total_num_blocks_committed: value + .consensus + .stats + .total_num_blocks_committed, + }, + data_root_commitment: value.consensus.data_root_commitment.to_vec(), + }, + } + } + } + + impl TryFrom for $module::CompressedSummary { + type Error = anyhow::Error; + + fn try_from(c: consensus::CompressedSummary) -> Result { + Ok($module::CompressedSummary { + stats: c + .stats + .try_into() + .map_err(|_| anyhow!("cannot convert aggregated stats"))?, + data_root_commitment: c + .data_root_commitment + .try_into() + .map_err(|_| anyhow!("cannot convert bytes32"))?, + }) + } + } + impl TryFrom for $module::BottomUpCheckpoint { type Error = anyhow::Error; @@ -135,6 +191,7 @@ macro_rules! bottom_up_checkpoint_conversion { .into_iter() .map($module::IpcEnvelope::try_from) .collect::, _>>()?, + activity: checkpoint.activity_rollup.try_into()?, }) } } @@ -153,6 +210,7 @@ macro_rules! bottom_up_checkpoint_conversion { .into_iter() .map(IpcEnvelope::try_from) .collect::, _>>()?, + activity_rollup: value.activity.into(), }) } } @@ -226,13 +284,17 @@ base_type_conversion!(subnet_actor_checkpointing_facet); base_type_conversion!(gateway_getter_facet); base_type_conversion!(gateway_messenger_facet); base_type_conversion!(lib_gateway); +base_type_conversion!(subnet_actor_activity_facet); +base_type_conversion!(checkpointing_facet); cross_msg_types!(gateway_getter_facet); cross_msg_types!(xnet_messaging_facet); cross_msg_types!(gateway_messenger_facet); cross_msg_types!(lib_gateway); cross_msg_types!(subnet_actor_checkpointing_facet); +cross_msg_types!(checkpointing_facet); +bottom_up_checkpoint_conversion!(checkpointing_facet); bottom_up_checkpoint_conversion!(gateway_getter_facet); bottom_up_checkpoint_conversion!(subnet_actor_checkpointing_facet); bottom_up_msg_batch_conversion!(gateway_getter_facet); diff --git a/ipc/api/src/lib.rs b/ipc/api/src/lib.rs index dd9336935..9dd45bef3 100644 --- a/ipc/api/src/lib.rs +++ b/ipc/api/src/lib.rs @@ -19,6 +19,7 @@ pub mod subnet_id; pub mod validator; pub mod evm; +pub mod merkle; pub mod staking; /// Converts an ethers::U256 TokenAmount into a FIL amount. diff --git a/ipc/api/src/merkle.rs b/ipc/api/src/merkle.rs new file mode 100644 index 000000000..c631db02a --- /dev/null +++ b/ipc/api/src/merkle.rs @@ -0,0 +1,41 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: MIT + +//! This is a util handles the merkle tree root and proof generation. + +use anyhow::Context; +use merkle_tree_rs::format::Raw; +use merkle_tree_rs::standard::{LeafType, StandardMerkleTree}; +use std::marker::PhantomData; + +pub type Hash = ethers::types::H256; + +pub struct MerkleGen { + /// The function that converts the payload `E` to vec string that feeds into the inner merkle tree + f_gen: F, + tree: StandardMerkleTree, + _p: PhantomData, +} + +impl Vec, E> MerkleGen { + pub fn root(&self) -> Hash { + self.tree.root() + } + + pub fn new(f: F, values: &[E], fields: &[S]) -> anyhow::Result { + let values = values.iter().map(&f).collect::>(); + + let tree = + StandardMerkleTree::of(&values, fields).context("failed to construct Merkle tree")?; + Ok(Self { + f_gen: f, + tree, + _p: Default::default(), + }) + } + + pub fn get_proof(&self, data: &E) -> anyhow::Result> { + let leaf = (self.f_gen)(data); + self.tree.get_proof(LeafType::LeafBytes(leaf)) + } +} diff --git a/ipc/api/src/subnet.rs b/ipc/api/src/subnet.rs index 386df79a3..3213d3502 100644 --- a/ipc/api/src/subnet.rs +++ b/ipc/api/src/subnet.rs @@ -88,6 +88,7 @@ pub struct ConstructParams { pub supply_source: Asset, pub collateral_source: Asset, pub validator_gater: Address, + pub validator_rewarder: Address, } /// Consensus types supported by hierarchical consensus diff --git a/ipc/cli/src/commands/mod.rs b/ipc/cli/src/commands/mod.rs index 782c497fd..54c803fa2 100644 --- a/ipc/cli/src/commands/mod.rs +++ b/ipc/cli/src/commands/mod.rs @@ -8,6 +8,7 @@ mod crossmsg; // mod daemon; mod subnet; mod util; +mod validator; mod wallet; use crate::commands::checkpoint::CheckpointCommandsArgs; @@ -30,6 +31,7 @@ use std::path::Path; use std::str::FromStr; use crate::commands::config::ConfigCommandsArgs; +use crate::commands::validator::ValidatorCommandsArgs; use crate::commands::wallet::WalletCommandsArgs; use subnet::SubnetCommandsArgs; @@ -47,6 +49,7 @@ enum Commands { CrossMsg(CrossMsgsCommandsArgs), Checkpoint(CheckpointCommandsArgs), Util(UtilCommandsArgs), + Validator(ValidatorCommandsArgs), } #[derive(Debug, Parser)] @@ -133,6 +136,7 @@ pub async fn cli() -> anyhow::Result<()> { Commands::Wallet(args) => args.handle(global).await, Commands::Checkpoint(args) => args.handle(global).await, Commands::Util(args) => args.handle(global).await, + Commands::Validator(args) => args.handle(global).await, }; r.with_context(|| format!("error processing command {:?}", args.command)) diff --git a/ipc/cli/src/commands/subnet/create.rs b/ipc/cli/src/commands/subnet/create.rs index f170827f0..fb1769d46 100644 --- a/ipc/cli/src/commands/subnet/create.rs +++ b/ipc/cli/src/commands/subnet/create.rs @@ -42,6 +42,12 @@ impl CreateSubnet { .clone() .unwrap_or(ZERO_ADDRESS.to_string()); let validator_gater = require_fil_addr_from_str(&raw_addr)?; + + let raw_addr = arguments + .validator_rewarder + .clone() + .unwrap_or(ZERO_ADDRESS.to_string()); + let validator_rewarder = require_fil_addr_from_str(&raw_addr)?; let addr = provider .create_subnet( from, @@ -57,6 +63,7 @@ impl CreateSubnet { supply_source, collateral_source, validator_gater, + validator_rewarder, ) .await?; @@ -165,6 +172,8 @@ pub struct CreateSubnetArgs { help = "The address of validator gating contract. None if validator gating is disabled" )] pub validator_gater: Option, + #[arg(long, help = "The address of validator rewarder contract.")] + pub validator_rewarder: Option, #[arg( long, help = "The kind of collateral source of a subnet on its parent subnet: native or erc20", diff --git a/ipc/cli/src/commands/validator/batch_claim.rs b/ipc/cli/src/commands/validator/batch_claim.rs new file mode 100644 index 000000000..efc0522fa --- /dev/null +++ b/ipc/cli/src/commands/validator/batch_claim.rs @@ -0,0 +1,56 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: MIT + +use crate::commands::get_ipc_provider; +use crate::{CommandLineHandler, GlobalArguments}; +use async_trait::async_trait; +use clap::Args; +use fvm_shared::{address::Address, clock::ChainEpoch}; +use ipc_api::subnet_id::SubnetID; +use std::str::FromStr; + +#[derive(Debug, Args)] +#[command(about = "validator batch claim rewards for a target subnet")] +pub(crate) struct BatchClaimArgs { + #[arg(long, help = "The JSON RPC server url for ipc agent")] + pub validator: String, + #[arg(long, help = "The checkpoint height to claim from")] + pub from: ChainEpoch, + #[arg(long, help = "The checkpoint height to claim to")] + pub to: ChainEpoch, + #[arg(long, help = "The source subnet that generated the reward")] + pub reward_source_subnet: String, + #[arg(long, help = "The subnet to claim reward from")] + pub reward_claim_subnet: String, +} + +pub(crate) struct BatchClaim; + +#[async_trait] +impl CommandLineHandler for BatchClaim { + type Arguments = BatchClaimArgs; + + async fn handle(global: &GlobalArguments, arguments: &Self::Arguments) -> anyhow::Result<()> { + log::debug!("batch claim operation with args: {:?}", arguments); + + let provider = get_ipc_provider(global)?; + + let reward_source_subnet = SubnetID::from_str(&arguments.reward_source_subnet)?; + let reward_claim_subnet = SubnetID::from_str(&arguments.reward_claim_subnet)?; + let validator = Address::from_str(&arguments.validator)?; + + provider + .batch_subnet_claim( + &reward_claim_subnet, + &reward_source_subnet, + arguments.from, + arguments.to, + &validator, + ) + .await?; + + println!("rewards claimed"); + + Ok(()) + } +} diff --git a/ipc/cli/src/commands/validator/list.rs b/ipc/cli/src/commands/validator/list.rs new file mode 100644 index 000000000..f3b666e76 --- /dev/null +++ b/ipc/cli/src/commands/validator/list.rs @@ -0,0 +1,51 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: MIT + +use crate::commands::get_ipc_provider; +use crate::{CommandLineHandler, GlobalArguments}; +use async_trait::async_trait; +use clap::Args; +use fvm_shared::{address::Address, clock::ChainEpoch}; +use ipc_api::subnet_id::SubnetID; +use std::str::FromStr; + +#[derive(Debug, Args)] +#[command(about = "validator list activities in a subnet")] +pub(crate) struct ListActivitiesArgs { + #[arg(long, help = "The JSON RPC server url for ipc agent")] + pub validator: String, + #[arg(long, help = "The checkpoint height to claim from")] + pub from: ChainEpoch, + #[arg(long, help = "The checkpoint height to claim to")] + pub to: ChainEpoch, + #[arg(long, help = "The subnet to list activities from")] + pub subnet: String, +} + +pub(crate) struct ListActivities; + +#[async_trait] +impl CommandLineHandler for ListActivities { + type Arguments = ListActivitiesArgs; + + async fn handle(global: &GlobalArguments, arguments: &Self::Arguments) -> anyhow::Result<()> { + log::debug!("list validator activities with args: {:?}", arguments); + + let provider = get_ipc_provider(global)?; + let subnet = SubnetID::from_str(&arguments.subnet)?; + let validator = Address::from_str(&arguments.validator)?; + + let r = provider + .list_validator_activities(&subnet, &validator, arguments.from, arguments.to) + .await?; + + println!("found total {} entries", r.len()); + for (checkpoint_height, v) in r { + println!(" checkpoint height: {}", checkpoint_height); + println!(" addr: {}", v.validator); + println!(" locks_committed: {}", v.blocks_committed); + } + + Ok(()) + } +} diff --git a/ipc/cli/src/commands/validator/mod.rs b/ipc/cli/src/commands/validator/mod.rs new file mode 100644 index 000000000..2845904c9 --- /dev/null +++ b/ipc/cli/src/commands/validator/mod.rs @@ -0,0 +1,33 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: MIT + +mod batch_claim; +mod list; + +use crate::commands::validator::batch_claim::{BatchClaim, BatchClaimArgs}; +use crate::commands::validator::list::{ListActivities, ListActivitiesArgs}; +use crate::{CommandLineHandler, GlobalArguments}; +use clap::{Args, Subcommand}; + +#[derive(Debug, Args)] +#[command(name = "validator", about = "validator reward related commands")] +#[command(args_conflicts_with_subcommands = true)] +pub(crate) struct ValidatorCommandsArgs { + #[command(subcommand)] + command: Commands, +} + +impl ValidatorCommandsArgs { + pub async fn handle(&self, global: &GlobalArguments) -> anyhow::Result<()> { + match &self.command { + Commands::BatchClaim(args) => BatchClaim::handle(global, args).await, + Commands::ListValidatorActivities(args) => ListActivities::handle(global, args).await, + } + } +} + +#[derive(Debug, Subcommand)] +pub(crate) enum Commands { + BatchClaim(BatchClaimArgs), + ListValidatorActivities(ListActivitiesArgs), +} diff --git a/ipc/provider/Cargo.toml b/ipc/provider/Cargo.toml index 100e413ae..4a9c7bbe9 100644 --- a/ipc/provider/Cargo.toml +++ b/ipc/provider/Cargo.toml @@ -42,7 +42,9 @@ zeroize = { workspace = true } fil_actors_runtime = { workspace = true } fvm_ipld_encoding = { workspace = true } -fvm_shared = { workspace = true } +fvm_shared = { workspace = true, features = ["crypto"] } + +merkle-tree-rs = { path = "../../ext/merkle-tree-rs" } ipc-types = { workspace = true } ipc-wallet = { workspace = true, features = ["with-ethers"] } diff --git a/ipc/provider/src/lib.rs b/ipc/provider/src/lib.rs index 5fa469f30..aad51e6b4 100644 --- a/ipc/provider/src/lib.rs +++ b/ipc/provider/src/lib.rs @@ -9,6 +9,7 @@ use config::Config; use fvm_shared::{ address::Address, clock::ChainEpoch, crypto::signature::SignatureType, econ::TokenAmount, }; +use ipc_api::checkpoint::consensus::ValidatorData; use ipc_api::checkpoint::{BottomUpCheckpointBundle, QuorumReachedEvent}; use ipc_api::evm::payload_to_evm_address; use ipc_api::staking::{StakingChangeRequest, ValidatorInfo}; @@ -256,6 +257,7 @@ impl IpcProvider { supply_source: Asset, collateral_source: Asset, validator_gater: Address, + validator_rewarder: Address, ) -> anyhow::Result
{ let conn = self.get_connection(&parent)?; @@ -275,6 +277,7 @@ impl IpcProvider { supply_source, collateral_source, validator_gater, + validator_rewarder, }; conn.manager() @@ -743,6 +746,43 @@ impl IpcProvider { .set_federated_power(from, subnet, validators, public_keys, federated_power) .await } + + pub async fn list_validator_activities( + &self, + subnet: &SubnetID, + validator: &Address, + from: ChainEpoch, + to: ChainEpoch, + ) -> anyhow::Result> { + let conn = self.get_connection(subnet)?; + conn.manager() + .query_validator_rewards(validator, from, to) + .await + } + + pub async fn batch_subnet_claim( + &self, + reward_claim_subnet: &SubnetID, + reward_source_subnet: &SubnetID, // TODO(review): eventually support multiple source subnets + from: ChainEpoch, + to: ChainEpoch, + validator: &Address, + ) -> anyhow::Result<()> { + let conn = self.get_connection(reward_source_subnet)?; + + let claims = conn + .manager() + .query_reward_claims(validator, from, to) + .await?; + + let parent = reward_claim_subnet + .parent() + .ok_or_else(|| anyhow!("no parent found"))?; + let conn = self.get_connection(&parent)?; + conn.manager() + .batch_subnet_claim(validator, reward_claim_subnet, reward_source_subnet, claims) + .await + } } /// Lotus JSON keytype format diff --git a/ipc/provider/src/manager/evm/manager.rs b/ipc/provider/src/manager/evm/manager.rs index fe393242f..6aff4d5aa 100644 --- a/ipc/provider/src/manager/evm/manager.rs +++ b/ipc/provider/src/manager/evm/manager.rs @@ -10,8 +10,8 @@ use ethers_contract::{ContractError, EthLogDecode, LogMeta}; use ipc_actors_abis::{ checkpointing_facet, gateway_getter_facet, gateway_manager_facet, gateway_messenger_facet, lib_gateway, lib_quorum, lib_staking_change_log, register_subnet_facet, - subnet_actor_checkpointing_facet, subnet_actor_getter_facet, subnet_actor_manager_facet, - subnet_actor_reward_facet, + subnet_actor_activity_facet, subnet_actor_checkpointing_facet, subnet_actor_getter_facet, + subnet_actor_manager_facet, subnet_actor_reward_facet, }; use ipc_api::evm::{fil_to_eth_amount, payload_to_evm_address, subnet_id_to_evm_addresses}; use ipc_api::validator::from_contract_validators; @@ -27,7 +27,7 @@ use crate::config::Subnet; use crate::lotus::message::ipc::SubnetInfo; use crate::manager::subnet::{ BottomUpCheckpointRelayer, GetBlockHashResult, SubnetGenesisInfo, TopDownFinalityQuery, - TopDownQueryPayload, + TopDownQueryPayload, ValidatorRewarder, }; use crate::manager::{EthManager, SubnetManager}; @@ -37,20 +37,25 @@ use ethers::abi::Tokenizable; use ethers::contract::abigen; use ethers::prelude::k256::ecdsa::SigningKey; use ethers::prelude::{Signer, SignerMiddleware}; -use ethers::providers::{Authorization, Http, Middleware, Provider}; +use ethers::providers::{Authorization, Http, Provider}; use ethers::signers::{LocalWallet, Wallet}; -use ethers::types::{Eip1559TransactionRequest, ValueOrArray, U256}; +use ethers::types::{Eip1559TransactionRequest, ValueOrArray, H256, U256}; use super::gas_estimator_middleware::Eip1559GasEstimatorMiddleware; +use ethers::middleware::Middleware; use fvm_shared::clock::ChainEpoch; use fvm_shared::{address::Address, econ::TokenAmount}; +use ipc_actors_abis::subnet_actor_activity_facet::ValidatorClaim; use ipc_api::checkpoint::{ - BottomUpCheckpoint, BottomUpCheckpointBundle, QuorumReachedEvent, Signature, + consensus::ValidatorData, BottomUpCheckpoint, BottomUpCheckpointBundle, QuorumReachedEvent, + Signature, VALIDATOR_REWARD_FIELDS, }; use ipc_api::cross::IpcEnvelope; +use ipc_api::merkle::MerkleGen; use ipc_api::staking::{StakingChangeRequest, ValidatorInfo, ValidatorStakingInfo}; use ipc_api::subnet::ConstructParams; use ipc_api::subnet_id::SubnetID; +use ipc_observability::lazy_static; use ipc_wallet::{EthKeyAddress, EvmKeyStore, PersistentKeyStore}; use num_traits::ToPrimitive; use std::result; @@ -281,6 +286,7 @@ impl SubnetManager for EthSubnetManager { supply_source: register_subnet_facet::Asset::try_from(params.supply_source)?, collateral_source: register_subnet_facet::Asset::try_from(params.collateral_source)?, validator_gater: payload_to_evm_address(params.validator_gater.payload())?, + validator_rewarder: payload_to_evm_address(params.validator_rewarder.payload())?, }; tracing::info!("creating subnet on evm with params: {params:?}"); @@ -1277,6 +1283,197 @@ impl BottomUpCheckpointRelayer for EthSubnetManager { Ok(epoch as ChainEpoch) } } + +lazy_static!( + /// ABI types of the Merkle tree which contains validator addresses and their committed block count. + pub static ref VALIDATOR_SUMMARY_FIELDS: Vec = vec!["address".to_owned(), "uint64".to_owned()]; +); + +#[async_trait] +impl ValidatorRewarder for EthSubnetManager { + /// Query validator claims, indexed by checkpoint height, to batch claim rewards. + async fn query_reward_claims( + &self, + validator_addr: &Address, + from_checkpoint: ChainEpoch, + to_checkpoint: ChainEpoch, + ) -> Result> { + let contract = checkpointing_facet::CheckpointingFacet::new( + self.ipc_contract_info.gateway_addr, + Arc::new(self.ipc_contract_info.provider.clone()), + ); + + let ev = contract + .event::() + .from_block(from_checkpoint as u64) + .to_block(to_checkpoint as u64) + .address(ValueOrArray::Value(contract.address())); + + let validator_eth_addr = payload_to_evm_address(validator_addr.payload())?; + + let mut claims = vec![]; + for (event, meta) in query_with_meta(ev, contract.client()).await? { + tracing::debug!( + "found activity bundle published at height: {}", + meta.block_number + ); + + // Check if we have claims for this validator in this block. + let our_data = event + .rollup + .consensus + .data + .iter() + .find(|v| v.validator == validator_eth_addr); + + // If we don't, skip this block. + let Some(data) = our_data else { + tracing::info!( + "target validator address has no reward claims in epoch {}", + meta.block_number + ); + continue; + }; + + let proof = gen_merkle_proof(&event.rollup.consensus.data, data)?; + + // Construct the claim and add it to the list. + let claim = ValidatorClaim { + // Even though it's the same struct but still need to do a mapping due to + // different crate from ethers-rs + data: subnet_actor_activity_facet::ValidatorData { + validator: data.validator, + blocks_committed: data.blocks_committed, + }, + proof: proof.into_iter().map(|v| v.into()).collect(), + }; + claims.push((event.checkpoint_height, claim)); + } + + Ok(claims) + } + + /// Query validator rewards in the current subnet, without obtaining proofs. + async fn query_validator_rewards( + &self, + validator_addr: &Address, + from_checkpoint: ChainEpoch, + to_checkpoint: ChainEpoch, + ) -> Result> { + let contract = checkpointing_facet::CheckpointingFacet::new( + self.ipc_contract_info.gateway_addr, + Arc::new(self.ipc_contract_info.provider.clone()), + ); + + let ev = contract + .event::() + .from_block(from_checkpoint as u64) + .to_block(to_checkpoint as u64) + .address(ValueOrArray::Value(contract.address())); + + let mut rewards = vec![]; + let validator_eth_addr = payload_to_evm_address(validator_addr.payload())?; + + for (event, meta) in query_with_meta(ev, contract.client()).await? { + tracing::debug!( + "found activity bundle published at height: {}", + meta.block_number + ); + + // Check if we have rewards for this validator in this block. + if let Some(data) = event + .rollup + .consensus + .data + .iter() + .find(|v| v.validator == validator_eth_addr) + { + // TODO type conversion. + let data = ValidatorData { + validator: *validator_addr, + blocks_committed: data.blocks_committed, + }; + rewards.push((meta.block_number.as_u64(), data)); + } + } + + Ok(rewards) + } + + /// Claim validator rewards in a batch for the specified subnet. + async fn batch_subnet_claim( + &self, + submitter: &Address, + reward_claim_subnet: &SubnetID, + reward_origin_subnet: &SubnetID, + claims: Vec<(u64, ValidatorClaim)>, + ) -> Result<()> { + let signer = Arc::new(self.get_signer_with_fee_estimator(submitter)?); + let contract = subnet_actor_activity_facet::SubnetActorActivityFacet::new( + contract_address_from_subnet(reward_claim_subnet)?, + signer.clone(), + ); + + // separate the Vec of tuples claims into two Vecs of Height and Claim + let (heights, claims): (Vec, Vec) = claims.into_iter().unzip(); + + let call = { + let call = + contract.batch_subnet_claim(reward_origin_subnet.try_into()?, heights, claims); + extend_call_with_pending_block(call).await? + }; + + call.send().await?; + + Ok(()) + } +} + +fn gen_merkle_proof( + validator_data: &[checkpointing_facet::ValidatorData], + validator: &checkpointing_facet::ValidatorData, +) -> anyhow::Result> { + // Utilty function to pack validator data into a vector of strings for proof generation. + let pack_validator_data = |v: &checkpointing_facet::ValidatorData| { + vec![format!("{:?}", v.validator), v.blocks_committed.to_string()] + }; + + let leaves = order_validator_data(validator_data)?; + let tree = MerkleGen::new(pack_validator_data, &leaves, &VALIDATOR_REWARD_FIELDS)?; + + tree.get_proof(validator) +} + +fn order_validator_data( + validator_data: &[checkpointing_facet::ValidatorData], +) -> anyhow::Result> { + let mut mapped = validator_data + .iter() + .map(|a| ethers_address_to_fil_address(&a.validator).map(|v| (v, a.blocks_committed))) + .collect::, _>>()?; + + mapped.sort_by(|a, b| { + let cmp = a.0.cmp(&b.0); + if cmp.is_eq() { + // Address will be unique, do this just in case equal + a.1.cmp(&b.1) + } else { + cmp + } + }); + + let back_to_eth = |(fvm_addr, blocks): (Address, u64)| { + payload_to_evm_address(fvm_addr.payload()).map(|v| checkpointing_facet::ValidatorData { + validator: v, + blocks_committed: blocks, + }) + }; + mapped + .into_iter() + .map(back_to_eth) + .collect::, _>>() +} + /// Takes a `FunctionCall` input and returns a new instance with an estimated optimal `gas_premium`. /// The function also uses the pending block number to help retrieve the latest nonce /// via `get_transaction_count` with the `pending` parameter. @@ -1396,7 +1593,12 @@ impl TryFrom for SubnetInfo { #[cfg(test)] mod tests { use crate::manager::evm::manager::contract_address_from_subnet; + use ethers::core::rand::prelude::SliceRandom; + use ethers::core::rand::{random, thread_rng}; use fvm_shared::address::Address; + use ipc_actors_abis::checkpointing_facet::{checkpointing_facet, ValidatorData}; + use ipc_api::checkpoint::VALIDATOR_REWARD_FIELDS; + use ipc_api::merkle::MerkleGen; use ipc_api::subnet_id::SubnetID; use std::str::FromStr; @@ -1411,4 +1613,107 @@ mod tests { "0x2e714a3c385ea88a09998ed74db265dae9853667" ); } + + /// test case that makes sure the commitment created for various addresses and blocks committed + /// are consistent + #[test] + fn test_validator_rewarder_claim_commitment() { + let pack_validator_data = |v: &checkpointing_facet::ValidatorData| { + vec![format!("{:?}", v.validator), v.blocks_committed.to_string()] + }; + + let mut random_validator_data = vec![ + ValidatorData { + validator: ethers::types::Address::from_str( + "0xB29C00299756135ec5d6A140CA54Ec77790a99d6", + ) + .unwrap(), + blocks_committed: 1, + }, + ValidatorData { + validator: ethers::types::Address::from_str( + "0x1A79385eAd0e873FE0C441C034636D3Edf7014cC", + ) + .unwrap(), + blocks_committed: 10, + }, + ValidatorData { + validator: ethers::types::Address::from_str( + "0x28345a43c2fBae4412f0AbadFa06Bd8BA3f58867", + ) + .unwrap(), + blocks_committed: 2, + }, + ValidatorData { + validator: ethers::types::Address::from_str( + "0x3c5cc76b07cb02a372e647887bD6780513659527", + ) + .unwrap(), + blocks_committed: 3, + }, + ValidatorData { + validator: ethers::types::Address::from_str( + "0x76B9d5a35C46B1fFEb37aadf929f1CA63a26A829", + ) + .unwrap(), + blocks_committed: 4, + }, + ]; + random_validator_data.shuffle(&mut thread_rng()); + + let root = MerkleGen::new( + pack_validator_data, + &random_validator_data, + &VALIDATOR_REWARD_FIELDS, + ) + .unwrap() + .root(); + assert_eq!( + hex::encode(root.0), + "5519955f33109df3338490473cb14458640efdccd4df05998c4c439738280ab0" + ); + } + + #[test] + fn test_validator_rewarder_claim_commitment_ii() { + let pack_validator_data = |v: &checkpointing_facet::ValidatorData| { + vec![format!("{:?}", v.validator), v.blocks_committed.to_string()] + }; + + let mut random_validator_data = (0..100) + .map(|_| ValidatorData { + validator: ethers::types::Address::random(), + blocks_committed: random::(), + }) + .collect::>(); + + random_validator_data.shuffle(&mut thread_rng()); + let root = MerkleGen::new( + pack_validator_data, + &random_validator_data, + &VALIDATOR_REWARD_FIELDS, + ) + .unwrap() + .root(); + + random_validator_data.shuffle(&mut thread_rng()); + let new_root = MerkleGen::new( + pack_validator_data, + &random_validator_data, + &VALIDATOR_REWARD_FIELDS, + ) + .unwrap() + .root(); + assert_eq!(new_root, root); + + random_validator_data.shuffle(&mut thread_rng()); + let new_root = MerkleGen::new( + pack_validator_data, + &random_validator_data, + &VALIDATOR_REWARD_FIELDS, + ) + .unwrap() + .root(); + assert_eq!(new_root, root); + } } diff --git a/ipc/provider/src/manager/subnet.rs b/ipc/provider/src/manager/subnet.rs index cc47ab093..6e31b4552 100644 --- a/ipc/provider/src/manager/subnet.rs +++ b/ipc/provider/src/manager/subnet.rs @@ -7,8 +7,10 @@ use anyhow::Result; use async_trait::async_trait; use fvm_shared::clock::ChainEpoch; use fvm_shared::{address::Address, econ::TokenAmount}; +use ipc_actors_abis::subnet_actor_activity_facet::ValidatorClaim; use ipc_api::checkpoint::{ - BottomUpCheckpoint, BottomUpCheckpointBundle, QuorumReachedEvent, Signature, + consensus::ValidatorData, BottomUpCheckpoint, BottomUpCheckpointBundle, QuorumReachedEvent, + Signature, }; use ipc_api::cross::IpcEnvelope; use ipc_api::staking::{StakingChangeRequest, ValidatorInfo}; @@ -20,7 +22,9 @@ use crate::lotus::message::ipc::SubnetInfo; /// Trait to interact with a subnet and handle its lifecycle. #[async_trait] -pub trait SubnetManager: Send + Sync + TopDownFinalityQuery + BottomUpCheckpointRelayer { +pub trait SubnetManager: + Send + Sync + TopDownFinalityQuery + BottomUpCheckpointRelayer + ValidatorRewarder +{ /// Deploys a new subnet actor on the `parent` subnet and with the /// configuration passed in `ConstructParams`. /// The result of the function is the ID address for the subnet actor from which the final @@ -279,3 +283,33 @@ pub trait BottomUpCheckpointRelayer: Send + Sync { /// Get the current epoch in the current subnet async fn current_epoch(&self) -> Result; } + +/// The validator reward related functions, such as check reward and claim reward for mining blocks +/// in the child subnet +#[async_trait] +pub trait ValidatorRewarder: Send + Sync { + /// Query validator claims, indexed by checkpoint height, to batch claim rewards. + async fn query_reward_claims( + &self, + validator_addr: &Address, + from_checkpoint: ChainEpoch, + to_checkpoint: ChainEpoch, + ) -> Result>; + + /// Query validator rewards in the current subnet, without obtaining proofs. + async fn query_validator_rewards( + &self, + validator: &Address, + from_checkpoint: ChainEpoch, + to_checkpoint: ChainEpoch, + ) -> Result>; + + /// Claim validator rewards in a batch for the specified subnet. + async fn batch_subnet_claim( + &self, + submitter: &Address, + reward_claim_subnet: &SubnetID, + reward_origin_subnet: &SubnetID, + claims: Vec<(u64, ValidatorClaim)>, + ) -> Result<()>; +} diff --git a/ipc/wallet/Cargo.toml b/ipc/wallet/Cargo.toml index 11cd5d946..984684d7c 100644 --- a/ipc/wallet/Cargo.toml +++ b/ipc/wallet/Cargo.toml @@ -15,7 +15,7 @@ base64 = { workspace = true } blake2b_simd = { workspace = true } bls-signatures = { version = "0.13.0", default-features = false, features = ["blst"] } ethers = { workspace = true, optional = true } -fvm_shared = { workspace = true } +fvm_shared = { workspace = true, features = ["crypto"] } hex = { workspace = true } libc = "0.2" libsecp256k1 = { workspace = true }