diff --git a/CHANGELOG.md b/CHANGELOG.md index 62a1220f112c..0111c6f50462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,9 @@ ### Added +- [#4473](https://github.com/ChainSafe/forest/pull/4473) Add support for NV23 + _Waffle_ network upgrade (FIP-0085, FIP-0091, v14 actors). + - [#4352](https://github.com/ChainSafe/forest/pull/4352) Add support for the `Filecoin.StateGetClaim` RPC method. diff --git a/Cargo.lock b/Cargo.lock index 00a086c58670..c07eef088db0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2159,7 +2159,16 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" dependencies = [ - "derive_builder_macro", + "derive_builder_macro 0.12.0", +] + +[[package]] +name = "derive_builder" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7" +dependencies = [ + "derive_builder_macro 0.20.0", ] [[package]] @@ -2174,16 +2183,38 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_builder_core" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" +dependencies = [ + "darling 0.20.9", + "proc-macro2", + "quote", + "syn 2.0.68", +] + [[package]] name = "derive_builder_macro" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" dependencies = [ - "derive_builder_core", + "derive_builder_core 0.12.0", "syn 1.0.109", ] +[[package]] +name = "derive_builder_macro" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" +dependencies = [ + "derive_builder_core 0.20.0", + "syn 2.0.68", +] + [[package]] name = "derive_more" version = "0.99.18" @@ -2599,9 +2630,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "fil_actor_account_state" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df862fbc729aad096ca2d86b2e601f0f1271651da0ec628b2a8e87e571e9c6b0" +checksum = "f0d3939c38bc261fa7ef6e8a97934f458e0fbfbcb236fe3ab7e3c6f18946c88e" dependencies = [ "frc42_macros", "fvm_ipld_encoding", @@ -2615,9 +2646,9 @@ dependencies = [ [[package]] name = "fil_actor_cron_state" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7975f05b356bb9427b9b9b29e2832e1e21a0b3fc939a8407f157d1bb29e13439" +checksum = "3aa7eb877f9ed1ec52f1ae700925d99711c4da9337d635ad73e7e324e94ec8a9" dependencies = [ "fvm_ipld_encoding", "fvm_shared 2.7.0", @@ -2630,9 +2661,9 @@ dependencies = [ [[package]] name = "fil_actor_datacap_state" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55262d78bccdf947713e1b37c93b97742101a31ff3e3cb3e5bcc1321e7dddfea" +checksum = "c41074d7da023c6999883dd382bd4b2178b03795e29bd162613a5a433c55c580" dependencies = [ "fil_actors_shared", "frc42_macros", @@ -2650,9 +2681,9 @@ dependencies = [ [[package]] name = "fil_actor_evm_state" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af36150c1951d2e646c22d1ba408f171879a59dd60a5bc47a81d0d5667730051" +checksum = "1952ed4016cb377710ca02fee055d018b7559fd288836c21843108312f184f3f" dependencies = [ "cid", "fil_actors_shared", @@ -2670,9 +2701,9 @@ dependencies = [ [[package]] name = "fil_actor_init_state" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1519d3cc042978ac658969809de47c8b5ce1d0698ee980638a82dbc3b7a50f3c" +checksum = "f8270be7a047b398a66319d21e23e792ff8a9d3c264325a97447c0e2c125a8b5" dependencies = [ "anyhow", "cid", @@ -2691,9 +2722,9 @@ dependencies = [ [[package]] name = "fil_actor_interface" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25318d52938f29faf9480d76bc49ddefd52c2ac17c62219da9ef0f655cbabf19" +checksum = "50a1bb143d49c8b3ad6fa29fdfc041cc5258e04a631757e136599c16b5ae856a" dependencies = [ "anyhow", "cid", @@ -2729,14 +2760,15 @@ dependencies = [ [[package]] name = "fil_actor_market_state" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "204dc5dfb78d8eccbc46020d242e66f28b899b569dafca5843ebfb293954d9cd" +checksum = "1b2f36c93351202dbf5c1c5b9132848ec537bfb28ca8a6d64db71de57fe4c770" dependencies = [ "anyhow", "cid", "fil_actor_verifreg_state", "fil_actors_shared", + "frc42_dispatch", "frc42_macros", "fvm_ipld_bitfield", "fvm_ipld_blockstore", @@ -2745,6 +2777,7 @@ dependencies = [ "fvm_shared 2.7.0", "fvm_shared 3.10.0", "fvm_shared 4.3.1", + "lazy_static", "libipld-core", "num-bigint", "num-derive", @@ -2754,15 +2787,16 @@ dependencies = [ [[package]] name = "fil_actor_miner_state" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "013ed63c8deb1169812129fa85e3399daa3e08ae29f226643ebdf3edd8bb4275" +checksum = "6f6e3ee635b3222173a9f0675683abd7a28ebbec729f1714a201a8d8fd0a8850" dependencies = [ "anyhow", "bitflags 2.6.0", "cid", "fil_actor_verifreg_state", "fil_actors_shared", + "frc42_dispatch", "frc42_macros", "fvm_ipld_amt", "fvm_ipld_bitfield", @@ -2784,13 +2818,14 @@ dependencies = [ [[package]] name = "fil_actor_multisig_state" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e31254757c5be501fd5be51f677977aff94cde23924756aa555f6e88eb03a9d" +checksum = "d9259179420d2cf112bf5b874d17fe038925037ff5951e3537cc67ac34b5bc23" dependencies = [ "anyhow", "cid", "fil_actors_shared", + "frc42_dispatch", "frc42_macros", "fvm_ipld_blockstore", "fvm_ipld_encoding", @@ -2807,13 +2842,14 @@ dependencies = [ [[package]] name = "fil_actor_power_state" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9113734f65fd6d2f634123fbf881198fc75b8f2fc5a72ceb1a238dd1188d94" +checksum = "274db1a8390af523b5ca18038bf1f443ed22cc344e584e95cf6b3dfeec6397e3" dependencies = [ "anyhow", "cid", "fil_actors_shared", + "frc42_dispatch", "frc42_macros", "fvm_ipld_blockstore", "fvm_ipld_encoding", @@ -2830,11 +2866,12 @@ dependencies = [ [[package]] name = "fil_actor_reward_state" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cce3b35f5f57133dc9c5706ff55875c9dc74eae3b8150c2a6f8506bb58e3345" +checksum = "3867426b007e1899abe99c2b42d904afaa1197f411761bee9cb6e7b938eb4590" dependencies = [ "fil_actor_miner_state", + "fil_actors_shared", "fvm_ipld_encoding", "fvm_shared 2.7.0", "fvm_shared 3.10.0", @@ -2847,9 +2884,9 @@ dependencies = [ [[package]] name = "fil_actor_system_state" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2737a9e1621080981e59e3b87e5db1a64024fab194fa475567d78c515ba098de" +checksum = "57ccabf8ba7ed4786189e80c2505003389cfd138b002866da3781a5091c1cc32" dependencies = [ "cid", "fil_actors_shared", @@ -2864,19 +2901,21 @@ dependencies = [ [[package]] name = "fil_actor_verifreg_state" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519b35d0caf12f8cb8aafbad9e989d52fa805a27174ffe8970ff067d561d123a" +checksum = "dcd1eab82dc350b7f2501d4b986349211f735f6a8e754cb1c8e2f5896f8875dd" dependencies = [ "anyhow", "cid", "fil_actors_shared", + "frc42_dispatch", "frc42_macros", "fvm_ipld_blockstore", "fvm_ipld_encoding", "fvm_shared 2.7.0", "fvm_shared 3.10.0", "fvm_shared 4.3.1", + "log", "num-derive", "num-traits", "serde", @@ -2884,9 +2923,9 @@ dependencies = [ [[package]] name = "fil_actors_shared" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f343720f910d5f295b69f79c7bb29a1ee043737c9b924a9e877d170025923d4" +checksum = "280b83694ee213140d8bb72a66176880df23a0d754a5519b5fc1813f7d4bf9eb" dependencies = [ "anyhow", "cid", @@ -2902,6 +2941,7 @@ dependencies = [ "fvm_shared 4.3.1", "integer-encoding", "itertools 0.13.0", + "lazy_static", "multihash 0.18.1", "num", "num-bigint", @@ -3102,6 +3142,7 @@ dependencies = [ "data-encoding", "data-encoding-macro", "derive-quickcheck-arbitrary", + "derive_builder 0.20.0", "derive_more", "dialoguer", "digest 0.10.7", @@ -3290,6 +3331,7 @@ dependencies = [ "frc42_hasher", "frc42_macros", "fvm_ipld_encoding", + "fvm_sdk", "fvm_shared 4.3.1", "thiserror", ] @@ -3528,7 +3570,7 @@ dependencies = [ "byteorder", "cid", "derive-getters", - "derive_builder", + "derive_builder 0.12.0", "derive_more", "filecoin-proofs-api", "fvm-wasm-instrument 0.2.0", diff --git a/Cargo.toml b/Cargo.toml index fb19d554aea6..ff5dd8950a08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,35 +40,36 @@ crypto_secretbox = "0.1" daemonize-me = "2" data-encoding = "2" data-encoding-macro = "0.1" +derive_builder = "0.20.0" derive_more = "0.99" dialoguer = "0.11" digest = "0.10" directories = "5" displaydoc = "0.2" ethereum-types = "0.14" -fil_actor_account_state = { version = "14" } -fil_actor_cron_state = { version = "14" } -fil_actor_datacap_state = { version = "14" } -fil_actor_init_state = { version = "14" } -fil_actor_interface = { version = "14" } -fil_actor_market_state = { version = "14" } -fil_actor_miner_state = { version = "14" } -fil_actor_power_state = { version = "14" } -fil_actor_reward_state = { version = "14" } -fil_actor_system_state = { version = "14" } -fil_actor_verifreg_state = { version = "14" } -fil_actors_shared = { version = "14", features = ["json"] } +fil_actor_account_state = { version = "15" } +fil_actor_cron_state = { version = "15" } +fil_actor_datacap_state = { version = "15" } +fil_actor_init_state = { version = "15" } +fil_actor_interface = { version = "15" } +fil_actor_market_state = { version = "15" } +fil_actor_miner_state = { version = "15" } +fil_actor_power_state = { version = "15" } +fil_actor_reward_state = { version = "15" } +fil_actor_system_state = { version = "15" } +fil_actor_verifreg_state = { version = "15" } +fil_actors_shared = { version = "15", features = ["json"] } flume = "0.11" fs_extra = "1" futures = "0.3" fvm2 = { package = "fvm", version = "~2.8", default-features = false } fvm3 = { package = "fvm", default-features = false, version = "~3.10", features = ["arb"] } -fvm4 = { package = "fvm", default-features = false, version = "~4.3", features = ["arb", "verify-signature"] } +fvm4 = { package = "fvm", default-features = false, version = "~4.3.1", features = ["arb", "verify-signature"] } fvm_ipld_blockstore = "0.2" fvm_ipld_encoding = "0.4" fvm_shared2 = { package = "fvm_shared", version = "~2.7" } fvm_shared3 = { package = "fvm_shared", version = "~3.10", features = ["arb", "testing", "proofs"] } -fvm_shared4 = { package = "fvm_shared", version = "~4.3", features = ["arb", "testing", "proofs"] } +fvm_shared4 = { package = "fvm_shared", version = "~4.3.1", features = ["arb", "testing", "proofs"] } gethostname = "0.4" git-version = "0.3" group = "0.13" diff --git a/build/bootstrap/butterflynet b/build/bootstrap/butterflynet index 524c3e9c5831..3b72f1ea6f10 100644 --- a/build/bootstrap/butterflynet +++ b/build/bootstrap/butterflynet @@ -1,2 +1,2 @@ -/dns4/bootstrap-0.butterfly.fildev.network/tcp/1347/p2p/12D3KooWSDtxY5PnoJMWQE2atCRzFon4zGhX3s5PyuRnbAmfJq3t -/dns4/bootstrap-1.butterfly.fildev.network/tcp/1347/p2p/12D3KooWPGoyrzLsTBuhHKQHrDvVsXxitFqBnrNLFssbyYv7Gazk +/dns4/bootstrap-0.butterfly.fildev.network/tcp/1347/p2p/12D3KooWE9GZUna2UpMMtcCcpmSwiEB7jcKse8gixzwVomgbQysC +/dns4/bootstrap-1.butterfly.fildev.network/tcp/1347/p2p/12D3KooWJGtqGX2XBssujaMn3FsybJu1xJhFZzhVjNnWvC6a26iG diff --git a/build/manifest.json b/build/manifest.json index 1612249283fc..d6ee3420b010 100644 --- a/build/manifest.json +++ b/build/manifest.json @@ -717,71 +717,71 @@ }, { "network": { - "type": "butterflynet" + "type": "calibnet" }, - "version": "v12.0.0", - "bundle_cid": "bafy2bzacectxvbk77ntedhztd6sszp2btrtvsmy7lp2ypnrk6yl74zb34t2cq", + "version": "v14.0.0-rc.1", + "bundle_cid": "bafy2bzacebq3hncszqpojglh2dkwekybq4zn6qpc4gceqbx36wndps5qehtau", "manifest": { "actors": [ [ "system", 1, - "bafk2bzacec3vwj2chzaram3iqupkbfiein5h2l5qiltlrngbju2vg5umelclm" + "bafk2bzacederl6tlpieldsn6mkndqwd4wj5orfoqgab6p2klswfn3cjagxwla" ], [ "init", 2, - "bafk2bzaceagyf3pwsthod7klfi25ow2zf2i5isfrrgr5ua3lvkgfojalrdbhw" + "bafk2bzacedfmsdlewihdcrkdepnfata26nj7akbvexzs3chicujhjf2uxsazc" ], [ "cron", 3, - "bafk2bzacecu2y3awtemmglpkroiglulc2fj3gpdn6eazdqr6avcautiaighrg" + "bafk2bzacedpbtttpyvtjncqoyobr63mhqqtlrygbnudhxyp2vha56f626dkfs" ], [ "account", 4, - "bafk2bzacebp7anjdtg2sohyt6lromx4xs7nujtwdfcsffnptphaayabx7ysxs" + "bafk2bzaced5ecfm56dvtw26q56j4d32yoccyd7ggxn3qdki2enxpqqav45ths" ], [ "storagepower", 5, - "bafk2bzacedxvlj5xmhytdjrjqyonz37duvxb2ioyzk75c27yypkqalxuh3xh6" + "bafk2bzacedgeolvjtnw7fkji5kqmx322abv6uls2v34fuml6nw36dvfcw4mtu" ], [ "storageminer", 6, - "bafk2bzaceb62clldtod2jimnri5k2koxttf6vqtlsvkjhnwduzs7sgsoakglw" + "bafk2bzacecr7ozkdz7l2pq3ig5qxae2ysivbnojhsn4gw3o57ov4mhksma7me" ], [ "storagemarket", 7, - "bafk2bzaceb2tdeqtt2eqpzeb3gezuchb7g7uzbd52bgvcdt6bg3ckq7oisb74" + "bafk2bzaceatwbyrec2nnwggxc2alpqve7rl52fmbhqflebuxmmnvg3qckjb7c" ], [ "paymentchannel", 8, - "bafk2bzacebm37tgu52cgzmiln6iip6etfmq73fd3qqz2j5gxlhtvachs7kw4c" + "bafk2bzacedbit7oo6lryhbo64uikvtjtfcth6oxwy3eebxerenu2h7rj44n24" ], [ "multisig", 9, - "bafk2bzacedgfo5mw2zqjwi37lah27sfxj4cw2abylgtxf3ucep4dyhgnppmqe" + "bafk2bzacedwx4svscsp6wqqu2vlcunjihvvm4u2jnsqjkwutjhir7dwtl7z6m" ], [ "reward", 10, - "bafk2bzacedebvitdsztwebi44t5es4ls3p3hor252igzawr3s6uznmbvzh2ou" + "bafk2bzaced5rlycj7fzpscfc7p3wwxarngwqylqshj7te3uffey5tevunz4we" ], [ "verifiedregistry", 11, - "bafk2bzacedv2irkql7nil3w5v3ohqq3e54w62pxeoppjmaktzokolaaoh5ksu" + "bafk2bzaceczw2kp6gjjdcjbso7mewp7guik7gr525pal6dotdja2lrct6ok3c" ], [ "datacap", 12, - "bafk2bzacebbh5aynu3v3fluqqrcdsphleodoig42xkid2ccwdnff3avhbdop4" + "bafk2bzacecded3lcvo7ndsk66samyecw2trnhrgzi7jxsary3sqgopxlk6rku" ], [ "placeholder", @@ -791,20 +791,20 @@ [ "evm", 14, - "bafk2bzacebygt6zh6p52rkg2ugehm4k5yuu6f56i2pu6ywrmjez4n4zsje4p4" + "bafk2bzacea4xnekruhfmdnzvzeo6cbf7jsfgco6x5wje2ckwc2ui2ojzcrlgu" ], [ "eam", 15, - "bafk2bzacebzwt4v4hqoltiblhliwrnttxpr2dggbu3wsrvq4pwzisp7idu5w4" + "bafk2bzacecsda4uw7dcu76a27gnrrdcm73tgms7wrte6jbou63vloktkqc5ne" ], [ "ethaccount", 16, - "bafk2bzaceb5f6vgjkl7ic6ry5sjspqm2iij6qlcdovwi3haodb7wn37pgebii" + "bafk2bzacebu2lcxfmohomjj3umslnylwugf5gssywdq3575tjarta7o227dls" ] ], - "actor_list_cid": "bafy2bzaceadu74qo3d3eon7xio4glro4q72ztpzuqrf5mttptrj2lg2rudrl4" + "actor_list_cid": "bafy2bzacea6rmcuwuqjlaehaar3vahdehhnudwempixctgoyrfg7vginjp3o6" } }, { @@ -899,6 +899,98 @@ "actor_list_cid": "bafy2bzaceaggwasbyfnshb7d6bph64jxqa5wireuhtiryuyl7glfhcmocooaa" } }, + { + "network": { + "type": "butterflynet" + }, + "version": "v14.0.0-rc.1", + "bundle_cid": "bafy2bzacecmkqezl3a5klkzz7z4ou4jwqk4zzd3nvz727l4qh44ngsxtxdblu", + "manifest": { + "actors": [ + [ + "system", + 1, + "bafk2bzacebbrs3dzgxwj43ztup7twz25xkbhhtmcbjjbscjvpsrpbwux3b32g" + ], + [ + "init", + 2, + "bafk2bzaceblybzwnn55uiivbsjae6l7haz5iocexnynfcz2yjg5spciimxdme" + ], + [ + "cron", + 3, + "bafk2bzacecsiz2nzjieposnkz2kqvjjnqyu5zwk6ccm4dbptx26v3qirm6zni" + ], + [ + "account", + 4, + "bafk2bzaceazutruyfvvqxgp5qoneq36uv6yethps2bonil5psy2vivl5j2hks" + ], + [ + "storagepower", + 5, + "bafk2bzacebcxydq2iampltz5zoo3oojka45hjkd62vz46xtpl6qilhkkjdeaq" + ], + [ + "storageminer", + 6, + "bafk2bzacebkiqu5pclx5zze4ugcsn3lvumihyathpcrjfq36b3hgmd7jqe2bk" + ], + [ + "storagemarket", + 7, + "bafk2bzaceceaqmhkxuerleq2dpju35mcsdiklpkisglzlj5xkf32hbyqn7sam" + ], + [ + "paymentchannel", + 8, + "bafk2bzacebyyn42ie7jekdytacqpqfll7xctsfpza3tb2sonzsjdeltxqgmdo" + ], + [ + "multisig", + 9, + "bafk2bzaceb54rbdcfdcdtzwbohshn64opgsqf5vhqh3xqb37iignsm3plrtpa" + ], + [ + "reward", + 10, + "bafk2bzaceczaoglexx6w3m744s4emfmjkeizpl4ofdkh4xzhevjtd6zift5iu" + ], + [ + "verifiedregistry", + 11, + "bafk2bzacebj3znhdpxqjgvztrv3petqwdkvrefg4j6lrp3n7wfrkdoan4os42" + ], + [ + "datacap", + 12, + "bafk2bzaceaavii766hmiawhw2fjvtoy5kvbukou3zejf6gtwu7xi4jxt4uidk" + ], + [ + "placeholder", + 13, + "bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro" + ], + [ + "evm", + 14, + "bafk2bzacebta2jkyxknvwnr6ldcimmwpzenhtdwqbuifzk6g2wktzqf3vj33a" + ], + [ + "eam", + 15, + "bafk2bzacebkzhnamn5ohtsvn76opprsi3ao3ujgytjr3c6kdcvhmhg4ze5xxm" + ], + [ + "ethaccount", + 16, + "bafk2bzacebvvri25rmgt6yy5qtdrikcsestk6z52aebynwd53s2rm2l3ukn7g" + ] + ], + "actor_list_cid": "bafy2bzacedcxw53okwtz7h2rp3ainkqw7fcj3vfcjg4ssj6elhujwjf3qbysw" + } + }, { "network": { "type": "devnet", @@ -1344,6 +1436,99 @@ "actor_list_cid": "bafy2bzacecvt4trddlt5eyuhp42itwb2a4ebje46juty2d7kogpdfnqtnyv5w" } }, + { + "network": { + "type": "devnet", + "name": "devnet" + }, + "version": "v14.0.0-rc.1", + "bundle_cid": "bafy2bzacebwn7ymtozv5yz3x5hnxl4bds2grlgsk5kncyxjak3hqyhslb534m", + "manifest": { + "actors": [ + [ + "system", + 1, + "bafk2bzacea4rh5i36ucj23zb4mid4tw5ym2wqlfap4ejjaynvobengeuby4ja" + ], + [ + "init", + 2, + "bafk2bzacebaywpmwlfhhog6vey3dkz25hjrlwnhacsjryq3ujymyttolglats" + ], + [ + "cron", + 3, + "bafk2bzacebmeovn3fehscsm2ejcd2vejgqmuqidzx3ytlvp4osa6fes3w73yy" + ], + [ + "account", + 4, + "bafk2bzacecqyi3xhyrze7hlo73zzyzz5jw5e6eqqyesmnbef4xr7br5amqsm2" + ], + [ + "storagepower", + 5, + "bafk2bzacebszcfmepyvssrg2tbbqgpqm2cnrl5ub4n6cfy7eie2wwseyloxvs" + ], + [ + "storageminer", + 6, + "bafk2bzacedoimzimltyfzbabwuam2bqw32nqwo2twjq73q7mklwtvqi2evsw2" + ], + [ + "storagemarket", + 7, + "bafk2bzacecegzksfqu35xlcc6ymxae3vqpkntbajx4jtahugszvb77fnr37ii" + ], + [ + "paymentchannel", + 8, + "bafk2bzacecsvfy77loubouoyqwl2ke574zpg3x5f2qon6ougjzfjna6eadwxg" + ], + [ + "multisig", + 9, + "bafk2bzacedm52r4h7upic7ynukzwjkadefbjeq7w7ozdonsbdumgoabk7xass" + ], + [ + "reward", + 10, + "bafk2bzacecvc7v5d2krwxqeyklfg2xb5qc3kalu66265smcbmwepjmj643uqu" + ], + [ + "verifiedregistry", + 11, + "bafk2bzaceapbsihfuk3munfpcoevtxlwuenxeiiv7dp7v3t2yjs26hcpypexi" + ], + [ + "datacap", + 12, + "bafk2bzaceasqdluegec5qllzjhu66jsyvb74dix6wjztpiaxvha74in7h4eek" + ], + [ + "placeholder", + 13, + "bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro" + ], + [ + "evm", + 14, + "bafk2bzaceafzevk77d6zhjbrw7grm5p3es2pzuklpvcthedjv6ejh7alvxqoc" + ], + [ + "eam", + 15, + "bafk2bzaced4shnjesuxk44ufllcywjbaixerx6hkcyj5rqqopjuic725fymx2" + ], + [ + "ethaccount", + 16, + "bafk2bzacebbw6hg766y4ouycqlr3llur7sxkgj7hnu7jq4xlwtycp3ovpqjee" + ] + ], + "actor_list_cid": "bafy2bzacecgrhoeusm6rvt7jrhzhvz574rd6potzwx5gpgktcfrcyfw6nydby" + } + }, { "network": { "type": "mainnet" diff --git a/scripts/devnet/.env b/scripts/devnet/.env index ceafbac0afbd..9495b3ed1e7b 100644 --- a/scripts/devnet/.env +++ b/scripts/devnet/.env @@ -1,4 +1,4 @@ -LOTUS_IMAGE=ghcr.io/chainsafe/lotus-devnet:2024-04-07-d6d22ca +LOTUS_IMAGE=ghcr.io/chainsafe/lotus-devnet:2024-07-03-1f030c2 FOREST_DATA_DIR=/forest_data LOTUS_DATA_DIR=/lotus_data FIL_PROOFS_PARAMETER_CACHE=/var/tmp/filecoin-proof-parameters @@ -13,5 +13,6 @@ LIGHTNING_HEIGHT=3 THUNDER_HEIGHT=6 WATERMELON_HEIGHT=9 DRAGON_HEIGHT=12 +WAFFLE_HEIGHT=18 DRAND_QUICKNET_HEIGHT=15 -TARGET_HEIGHT=20 +TARGET_HEIGHT=24 diff --git a/scripts/devnet/docker-compose.yml b/scripts/devnet/docker-compose.yml index e4cd55f694e2..73ff866ea08e 100644 --- a/scripts/devnet/docker-compose.yml +++ b/scripts/devnet/docker-compose.yml @@ -22,6 +22,7 @@ services: - LOTUS_DRAGON_HEIGHT=${DRAGON_HEIGHT} - LOTUS_GENESIS_NETWORK_VERSION=${GENESIS_NETWORK_VERSION} - LOTUS_PHOENIX_HEIGHT=${DRAND_QUICKNET_HEIGHT} + - LOTUS_WAFFLE_HEIGHT=${WAFFLE_HEIGHT} env_file: - lotus.env entrypoint: ["/bin/bash", "-c" ] @@ -68,6 +69,7 @@ services: - LOTUS_DRAGON_HEIGHT=${DRAGON_HEIGHT} - LOTUS_GENESIS_NETWORK_VERSION=${GENESIS_NETWORK_VERSION} - LOTUS_PHOENIX_HEIGHT=${DRAND_QUICKNET_HEIGHT} + - LOTUS_WAFFLE_HEIGHT=${WAFFLE_HEIGHT} ports: - ${LOTUS_RPC_PORT}:${LOTUS_RPC_PORT} - ${LOTUS_P2P_PORT}:${LOTUS_P2P_PORT} @@ -108,6 +110,7 @@ services: - LOTUS_DRAGON_HEIGHT=${DRAGON_HEIGHT} - LOTUS_GENESIS_NETWORK_VERSION=${GENESIS_NETWORK_VERSION} - LOTUS_PHOENIX_HEIGHT=${DRAND_QUICKNET_HEIGHT} + - LOTUS_WAFFLE_HEIGHT=${WAFFLE_HEIGHT} ports: - ${MINER_RPC_PORT}:${MINER_RPC_PORT} env_file: @@ -143,6 +146,7 @@ services: - LOTUS_DRAGON_HEIGHT=${DRAGON_HEIGHT} - LOTUS_GENESIS_NETWORK_VERSION=${GENESIS_NETWORK_VERSION} - LOTUS_PHOENIX_HEIGHT=${DRAND_QUICKNET_HEIGHT} + - LOTUS_WAFFLE_HEIGHT=${WAFFLE_HEIGHT} entrypoint: ["/bin/bash", "-c" ] env_file: - lotus.env @@ -185,6 +189,7 @@ services: - FOREST_WATERMELON_HEIGHT=${WATERMELON_HEIGHT} - FOREST_DRAGON_HEIGHT=${DRAGON_HEIGHT} - FOREST_DRAND_QUICKNET_HEIGHT=${DRAND_QUICKNET_HEIGHT} + - FOREST_WAFFLE_HEIGHT=${WAFFLE_HEIGHT} networks: - devnet ports: diff --git a/scripts/devnet/lotus.dockerfile b/scripts/devnet/lotus.dockerfile index c2edf960e4eb..e13fc1b36aec 100644 --- a/scripts/devnet/lotus.dockerfile +++ b/scripts/devnet/lotus.dockerfile @@ -10,7 +10,7 @@ RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --profile mini ENV PATH="/root/.cargo/bin:${PATH}" -RUN git clone --depth 1 --branch v1.26.1 https://github.com/filecoin-project/lotus.git . +RUN git clone --depth 1 --branch v1.28.0-rc1 https://github.com/filecoin-project/lotus.git . # https://github.com/Filecoin-project/filecoin-ffi?tab=readme-ov-file#building-from-source RUN CGO_CFLAGS_ALLOW="-D__BLST_PORTABLE__" \ diff --git a/src/chain_sync/tipset_syncer.rs b/src/chain_sync/tipset_syncer.rs index 115469a86684..9cccb12978d1 100644 --- a/src/chain_sync/tipset_syncer.rs +++ b/src/chain_sync/tipset_syncer.rs @@ -10,8 +10,6 @@ use std::{ task::{Context, Poll}, }; -use crate::libp2p::chain_exchange::TipsetBundle; -use crate::message::{valid_for_block_inclusion, Message as MessageTrait}; use crate::networks::Height; use crate::shim::clock::ALLOWABLE_CLOCK_DRIFT; use crate::shim::{ @@ -28,6 +26,11 @@ use crate::{ chain::{persist_objects, ChainStore, Error as ChainStoreError}, metrics::HistogramTimerExt, }; +use crate::{ + eth::is_valid_eth_tx_for_sending, + message::{valid_for_block_inclusion, Message as MessageTrait}, +}; +use crate::{libp2p::chain_exchange::TipsetBundle, shim::crypto::SignatureType}; use ahash::{HashMap, HashMapExt, HashSet}; use cid::Cid; use futures::stream::TryStreamExt as _; @@ -1429,6 +1432,7 @@ async fn check_block_messages( let network_version = state_manager .chain_config() .network_version(block.header.epoch); + let eth_chain_id = state_manager.chain_config().eth_chain_id; if let Some(sig) = &block.header().bls_aggregate { // Do the initial loop here @@ -1536,6 +1540,13 @@ async fn check_block_messages( // Check validity for SECP messages for (i, msg) in block.secp_msgs().iter().enumerate() { + if msg.signature().signature_type() == SignatureType::Delegated + && !is_valid_eth_tx_for_sending(eth_chain_id, network_version, msg) + { + return Err(TipsetRangeSyncerError::Validation( + "Network version must be at least NV23 for legacy Ethereum transactions".to_owned(), + )); + } check_msg(msg.message(), &mut account_sequences, &tree).map_err(|e| { TipsetRangeSyncerError::Validation(format!( "block had an invalid secp message at index {i}: {e}" diff --git a/src/db/ttl/mod.rs b/src/db/ttl/mod.rs index 92108ae918b4..d021d862d8b0 100644 --- a/src/db/ttl/mod.rs +++ b/src/db/ttl/mod.rs @@ -1,6 +1,7 @@ // Copyright 2019-2024 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use crate::eth::EthChainId; use crate::message::ChainMessage; use crate::rpc::eth::{self, eth_tx_from_signed_eth_message}; use fvm_ipld_blockstore::Blockstore; @@ -11,14 +12,14 @@ use super::EthMappingsStore; pub struct EthMappingCollector { db: Arc, - eth_chain_id: u32, + eth_chain_id: EthChainId, ttl: std::time::Duration, } impl EthMappingCollector { /// Creates a `TTL` collector for the Ethereum mapping. /// - pub fn new(db: Arc, eth_chain_id: u32, ttl: Duration) -> Self { + pub fn new(db: Arc, eth_chain_id: EthChainId, ttl: Duration) -> Self { Self { db, eth_chain_id, @@ -97,8 +98,6 @@ mod test { #[tokio::test] async fn test_ttl() { - let eth_chain_id: u32 = ETH_CHAIN_ID.try_into().unwrap(); - let blockstore = Arc::new(MemoryDB::default()); let (bls0, secp0) = construct_eth_messages(0); @@ -123,7 +122,7 @@ mod test { let unix_timestamp: DateTime = Utc.timestamp_opt(0, 0).unwrap(); // Add key0 with unix epoch - let tx0 = eth_tx_from_signed_eth_message(&secp0, eth_chain_id).unwrap(); + let tx0 = eth_tx_from_signed_eth_message(&secp0, ETH_CHAIN_ID).unwrap(); let key0 = tx0.eth_hash().unwrap(); let timestamp = unix_timestamp.timestamp() as u64; @@ -134,7 +133,7 @@ mod test { assert!(blockstore.exists(&key0).unwrap()); // Add key1 with unix epoch + 2 * ttl - let tx1 = eth_tx_from_signed_eth_message(&secp1, eth_chain_id).unwrap(); + let tx1 = eth_tx_from_signed_eth_message(&secp1, ETH_CHAIN_ID).unwrap(); let key1 = tx1.eth_hash().unwrap(); blockstore @@ -149,7 +148,7 @@ mod test { assert!(blockstore.exists(&key1).unwrap()); - let collector = EthMappingCollector::new(blockstore.clone(), eth_chain_id, TTL_DURATION); + let collector = EthMappingCollector::new(blockstore.clone(), ETH_CHAIN_ID, TTL_DURATION); collector.ttl_workflow(ZERO_DURATION).unwrap(); diff --git a/src/eth/eip_1559_transaction.rs b/src/eth/eip_1559_transaction.rs new file mode 100644 index 000000000000..72b7bc320415 --- /dev/null +++ b/src/eth/eip_1559_transaction.rs @@ -0,0 +1,112 @@ +// Copyright 2019-2024 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +//! This module contains the logic for EIP-1559 transaction types. +//! Constants are taken from [FIP-0091](https://github.com/filecoin-project/FIPs/blob/020bcb412ee20a2879b4a710337959c51b938d3b/FIPS/fip-0091.md). + +use anyhow::ensure; +use derive_builder::Builder; +use num::BigInt; +use num_bigint::Sign; + +use crate::{rpc::eth::types::EthAddress, shim::crypto::Signature}; + +use super::EthChainId; +pub(super) const EIP_1559_SIG_LEN: usize = 65; + +#[derive(PartialEq, Debug, Clone, Default, Builder)] +#[builder(setter(into))] +pub struct EthEip1559TxArgs { + pub chain_id: EthChainId, + pub nonce: u64, + pub to: Option, + pub value: BigInt, + pub max_fee_per_gas: BigInt, + pub max_priority_fee_per_gas: BigInt, + pub gas_limit: u64, + pub input: Vec, + #[builder(setter(skip))] + pub v: BigInt, + #[builder(setter(skip))] + pub r: BigInt, + #[builder(setter(skip))] + pub s: BigInt, +} +impl EthEip1559TxArgs { + pub fn with_signature(mut self, signature: &Signature) -> anyhow::Result { + ensure!( + signature.signature_type() == crate::shim::crypto::SignatureType::Delegated, + "Signature is not delegated type, is {}", + signature.signature_type() + ); + + ensure!( + signature.bytes().len() == EIP_1559_SIG_LEN, + "Invalid signature length for EIP1559 transaction: {}", + signature.bytes().len() + ); + + self.r = BigInt::from_bytes_be(Sign::Plus, signature.bytes().get(..32).expect("infalible")); + self.s = BigInt::from_bytes_be( + Sign::Plus, + signature.bytes().get(32..64).expect("infalible"), + ); + self.v = BigInt::from_bytes_be( + Sign::Plus, + signature + .bytes() + .get(64..EIP_1559_SIG_LEN) + .expect("infalible"), + ); + + Ok(self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::shim::crypto::{Signature, SignatureType}; + use num_bigint::ToBigInt; + + fn create_eip1559_tx_args() -> EthEip1559TxArgs { + EthEip1559TxArgsBuilder::default() + .chain_id(42u64) + .nonce(1u64) + .to(Some(EthAddress::default())) + .value(100.to_bigint().unwrap()) + .max_fee_per_gas(10.to_bigint().unwrap()) + .max_priority_fee_per_gas(1.to_bigint().unwrap()) + .gas_limit(1000u64) + .input(vec![1, 2, 3]) + .build() + .unwrap() + } + + // TODO(forest): https://github.com/ChainSafe/forest/issues/4478 + // Grab better test vectors, e.g., from Lotus: + // + // This will require implementing more methods for parsing different Ethereum transaction + // types. + + #[test] + fn test_valid_eip1559_tx_args_with_signature() { + let args = create_eip1559_tx_args(); + let signature = Signature::new(SignatureType::Delegated, vec![0u8; EIP_1559_SIG_LEN]); + args.with_signature(&signature).unwrap(); + } + + #[test] + fn test_invalid_eip1559_tx_args_not_delegated() { + let args = create_eip1559_tx_args(); + let signature = Signature::new(SignatureType::Secp256k1, vec![0u8; EIP_1559_SIG_LEN]); + assert!(args.with_signature(&signature).is_err()); + } + + #[test] + fn test_invalid_eip1559_tx_args_invalid_signature_len() { + let args = create_eip1559_tx_args(); + let signature = Signature::new(SignatureType::Delegated, vec![0u8; EIP_1559_SIG_LEN - 1]); + assert!(args.with_signature(&signature).is_err()); + } +} diff --git a/src/eth/eip_155_transaction.rs b/src/eth/eip_155_transaction.rs new file mode 100644 index 000000000000..03e10c9dff29 --- /dev/null +++ b/src/eth/eip_155_transaction.rs @@ -0,0 +1,208 @@ +// Copyright 2019-2024 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use crate::{ + rpc::eth::types::EthAddress, + shim::crypto::{Signature, SignatureType}, +}; +use anyhow::{ensure, Context}; +use derive_builder::Builder; +use num::{BigInt, BigUint}; +use num_bigint::Sign; +use num_traits::cast::ToPrimitive; + +use super::{homestead_transaction::HOMESTEAD_SIG_LEN, EthChainId}; + +pub const EIP_155_SIG_PREFIX: u8 = 0x02; + +/// Description from Lotus: +/// [`EthLegacyEip155TxArgs`] is a legacy Ethereum transaction that uses the EIP-155 chain replay protection mechanism +/// by incorporating the `ChainId` in the signature. +/// See how the `V` value in the signature is derived from the `ChainId` at +/// +/// For [`EthLegacyEip155TxArgs`], the digest that is used to create a signed transaction includes the `ChainID` but the serialized `RLP` transaction +/// does not include the `ChainID` as an explicit field. Instead, the `ChainID` is included in the V value of the signature as mentioned above. +#[derive(PartialEq, Debug, Clone, Default, Builder)] +#[builder(setter(into))] +pub struct EthLegacyEip155TxArgs { + pub nonce: u64, + pub gas_price: BigInt, + pub gas_limit: u64, + pub to: Option, + pub value: BigInt, + pub input: Vec, + #[builder(setter(skip))] + pub v: BigInt, + #[builder(setter(skip))] + pub r: BigInt, + #[builder(setter(skip))] + pub s: BigInt, +} + +impl EthLegacyEip155TxArgs { + pub(crate) fn with_signature( + mut self, + signature: &Signature, + eth_chain_id: EthChainId, + ) -> anyhow::Result { + ensure!( + signature.signature_type() == SignatureType::Delegated, + "Signature is not delegated type" + ); + + let valid_sig_len = calc_valid_eip155_sig_len(eth_chain_id); + ensure!( + signature.bytes().len() == valid_sig_len.0 as usize + || signature.bytes().len() == valid_sig_len.1 as usize, + "Invalid signature length for EIP155 transaction: {}. Must be either {} or {} bytes", + signature.bytes().len(), + valid_sig_len.0, + valid_sig_len.1 + ); + + // later on, we do slicing on the signature bytes, so we need to ensure that the signature is at least 1 + 65 bytes long + ensure!( + signature.bytes().len() >= 66, + "Invalid signature length for EIP155 transaction: {} < 66 bytes", + signature.bytes().len() + ); + + ensure!( + signature.bytes().first().expect("infallible") == &EIP_155_SIG_PREFIX, + "Invalid signature prefix for EIP155 transaction" + ); + + // ignore the first byte of the signature as it's only used for legacy transaction identification + let r = BigInt::from_bytes_be( + Sign::Plus, + signature.bytes().get(1..33).expect("infallible"), + ); + let s = BigInt::from_bytes_be( + Sign::Plus, + signature.bytes().get(33..65).expect("infallible"), + ); + let v = BigInt::from_bytes_be(Sign::Plus, signature.bytes().get(65..).expect("infallible")); + + validate_eip155_chain_id(eth_chain_id, &v)?; + + self.r = r; + self.s = s; + self.v = v; + + Ok(self) + } +} + +fn validate_eip155_chain_id(eth_chain_id: EthChainId, v: &BigInt) -> anyhow::Result<()> { + let derived_chain_id = derive_eip_155_chain_id(v)?; + ensure!( + derived_chain_id + .to_u64() + .context("unable to convert derived chain to `u64`")? + == eth_chain_id, + "EIP155 transaction chain ID mismatch: expected {eth_chain_id}, got {derived_chain_id}", + ); + + Ok(()) +} + +fn derive_eip_155_chain_id(v: &BigInt) -> anyhow::Result { + ensure!(v >= &35.into(), "Invalid V value for EIP155 transaction"); + + if v.bits() <= 64 { + let v = v.to_u64().context("Failed to convert v to u64")?; + if v == 27 || v == 28 { + return Ok(0.into()); + } + return Ok(((v - 35) / 2).into()); + } + + Ok((v - 35u32) / 2u32) +} + +pub(super) fn calc_eip155_sig_len(eth_chain_id: EthChainId, v: u64) -> u64 { + let chain_id = BigUint::from(eth_chain_id); + let v: BigUint = chain_id * 2u64 + v; + let v_len = v.to_bytes_be().len() as u64; + + // EthLegacyHomesteadTxSignatureLen includes the 1 byte legacy tx marker prefix and also 1 byte for the V value. + // So we subtract 1 to not double count the length of the v value + HOMESTEAD_SIG_LEN as u64 + v_len - 1u64 +} + +/// Returns the valid signature lengths for EIP-155 transactions. +/// The length is based on the chain ID and the V value in the signature. +pub(super) fn calc_valid_eip155_sig_len(eth_chain_id: EthChainId) -> (u64, u64) { + let sig_len1 = calc_eip155_sig_len(eth_chain_id, 35); + let sig_len2 = calc_eip155_sig_len(eth_chain_id, 36); + (sig_len1, sig_len2) +} + +#[cfg(test)] +mod tests { + use num_bigint::ToBigInt; + use quickcheck_macros::quickcheck; + + use super::*; + + #[quickcheck] + fn test_derive_eip_155_chain_id(eth_chain_id: EthChainId) { + let eth_chain_id = eth_chain_id.to_bigint().unwrap(); + let v = (eth_chain_id.clone() * 2.to_bigint().unwrap() + 35.to_bigint().unwrap()) + .to_bytes_be() + .1; + assert_eq!( + derive_eip_155_chain_id(&BigInt::from_bytes_be(Sign::Plus, &v)).unwrap(), + eth_chain_id + ); + } + + #[quickcheck] + fn test_validate_eip155_chain_id(eth_chain_id: EthChainId) { + let eth_chain_id = eth_chain_id.to_bigint().unwrap(); + let v = (eth_chain_id.clone() * 2.to_bigint().unwrap() + 35.to_bigint().unwrap()) + .to_bytes_be() + .1; + validate_eip155_chain_id( + eth_chain_id.clone().to_u64().unwrap(), + &BigInt::from_bytes_be(Sign::Plus, &v), + ) + .unwrap(); + } + + #[test] + fn test_calc_eip_155_sig_len() { + let cases = [ + ( + "ChainId fits in 1 byte", + 0x01, + HOMESTEAD_SIG_LEN as u64 + 1 - 1, + ), + ( + "ChainId fits in 2 bytes", + 0x0100, + HOMESTEAD_SIG_LEN as u64 + 2 - 1, + ), + ( + "ChainId fits in 3 bytes", + 0x10000, + HOMESTEAD_SIG_LEN as u64 + 3 - 1, + ), + ( + "ChainId fits in 4 bytes", + 0x01000000, + HOMESTEAD_SIG_LEN as u64 + 4 - 1, + ), + ( + "ChainId fits in 6 bytes", + 0x010000000000, + HOMESTEAD_SIG_LEN as u64 + 6 - 1, + ), + ]; + + for (name, chain_id, expected) in cases { + let actual = calc_eip155_sig_len(chain_id, 1); + assert_eq!(actual, expected, "{name}"); + } + } +} diff --git a/src/eth/homestead_transaction.rs b/src/eth/homestead_transaction.rs new file mode 100644 index 000000000000..ad2b86c1537e --- /dev/null +++ b/src/eth/homestead_transaction.rs @@ -0,0 +1,73 @@ +// Copyright 2019-2024 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use crate::{ + rpc::eth::types::EthAddress, + shim::crypto::{Signature, SignatureType}, +}; +use anyhow::{ensure, Context}; +use derive_builder::Builder; +use num::BigInt; +use num_bigint::Sign; +use num_traits::cast::ToPrimitive; + +pub const HOMESTEAD_SIG_LEN: usize = 66; +pub const HOMESTEAD_SIG_PREFIX: u8 = 0x01; + +#[derive(PartialEq, Debug, Clone, Default, Builder)] +#[builder(setter(into))] +pub struct EthLegacyHomesteadTxArgs { + pub nonce: u64, + pub gas_price: BigInt, + pub gas_limit: u64, + pub to: Option, + pub value: BigInt, + pub input: Vec, + #[builder(setter(skip))] + pub v: BigInt, + #[builder(setter(skip))] + pub r: BigInt, + #[builder(setter(skip))] + pub s: BigInt, +} +impl EthLegacyHomesteadTxArgs { + pub(crate) fn with_signature(mut self, signature: &Signature) -> anyhow::Result { + ensure!( + signature.signature_type() == SignatureType::Delegated, + "Signature is not delegated type" + ); + + ensure!( + signature.bytes().len() == HOMESTEAD_SIG_LEN, + "Invalid signature length for Homestead transaction" + ); + + ensure!( + signature.bytes().first().expect("infallible") == &HOMESTEAD_SIG_PREFIX, + "Invalid signature prefix for Homestead transaction" + ); + + // ignore the first byte of the signature as it's only used for legacy transaction identification + let r = BigInt::from_bytes_be( + Sign::Plus, + signature.bytes().get(1..33).expect("infallible"), + ); + let s = BigInt::from_bytes_be( + Sign::Plus, + signature.bytes().get(33..65).expect("infallible"), + ); + let v = BigInt::from_bytes_be(Sign::Plus, signature.bytes().get(65..).expect("infallible")); + + let v_int = v.to_i32().context("Failed to convert v to i32")?; + ensure!( + v_int == 27 || v_int == 28, + "Homestead transaction v value is invalid" + ); + + self.r = r; + self.s = s; + self.v = v; + + Ok(self) + } +} diff --git a/src/eth/mod.rs b/src/eth/mod.rs new file mode 100644 index 000000000000..fc6ca0019eda --- /dev/null +++ b/src/eth/mod.rs @@ -0,0 +1,10 @@ +// Copyright 2019-2024 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +mod eip_1559_transaction; +mod eip_155_transaction; +mod homestead_transaction; +mod transaction; + +pub use transaction::is_valid_eth_tx_for_sending; +pub type EthChainId = u64; diff --git a/src/eth/transaction.rs b/src/eth/transaction.rs new file mode 100644 index 000000000000..cc9246a88acb --- /dev/null +++ b/src/eth/transaction.rs @@ -0,0 +1,426 @@ +// Copyright 2019-2024 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use anyhow::{bail, ensure}; +use cbor4ii::core::{dec::Decode as _, utils::SliceReader, Value}; + +use crate::{ + message::{Message as _, SignedMessage}, + rpc::eth::types::EthAddress, + shim::{address::Address, crypto::SignatureType, message::Message, version::NetworkVersion}, +}; + +use super::{ + eip_1559_transaction::{EthEip1559TxArgs, EthEip1559TxArgsBuilder, EIP_1559_SIG_LEN}, + eip_155_transaction::{ + calc_valid_eip155_sig_len, EthLegacyEip155TxArgs, EthLegacyEip155TxArgsBuilder, + EIP_155_SIG_PREFIX, + }, + homestead_transaction::{ + EthLegacyHomesteadTxArgs, EthLegacyHomesteadTxArgsBuilder, HOMESTEAD_SIG_LEN, + HOMESTEAD_SIG_PREFIX, + }, + EthChainId, +}; +// As per `ref-fvm`, which hardcodes it as well. +#[repr(u64)] +enum EAMMethod { + CreateExternal = 4, +} + +#[repr(u64)] +enum EVMMethod { + // As per `ref-fvm`: + // it is very unfortunate but the hasher creates a circular dependency, so we use the raw + // number. + // InvokeContract = frc42_dispatch::method_hash!("InvokeEVM"), + InvokeContract = 3844450837, +} + +/// Ethereum transaction which can be of different types. +/// The currently supported types are defined in [FIP-0091](https://github.com/filecoin-project/FIPs/blob/020bcb412ee20a2879b4a710337959c51b938d3b/FIPS/fip-0091.md). +#[allow(dead_code)] +pub enum EthTx { + Homestead(Box), + Eip1559(Box), + Eip155(Box), +} + +impl EthTx { + /// Creates an Ethereum transaction from a signed Filecoin message. + /// The transaction type is determined based on the signature, as defined in FIP-0091. + pub fn from_signed_message( + eth_chain_id: EthChainId, + msg: &SignedMessage, + ) -> anyhow::Result { + Self::ensure_signed_message_valid(msg)?; + let (params, to) = get_eth_params_and_recipient(msg.message())?; + + // now we need to determine the transaction type based on the signature length + let sig_len = msg.signature().bytes().len(); + + // valid signature lengths are based on the chain ID, so we need to calculate it. This + // shouldn't be a resource-intensive operation, but if it becomes one, we can do some + // memoization. + let valid_eip_155_signature_lengths = calc_valid_eip155_sig_len(eth_chain_id); + + let tx: Self = if sig_len == EIP_1559_SIG_LEN { + let args = EthEip1559TxArgsBuilder::default() + .chain_id(eth_chain_id) + .nonce(msg.message().sequence()) + .to(to) + .value(msg.value()) + .max_fee_per_gas(msg.message().gas_fee_cap()) + .max_priority_fee_per_gas(msg.message().gas_premium()) + .gas_limit(msg.message().gas_limit()) + .input(params) + .build()? + .with_signature(msg.signature())?; + EthTx::Eip1559(Box::new(args)) + } else if sig_len == HOMESTEAD_SIG_LEN + || sig_len == valid_eip_155_signature_lengths.0 as usize + || sig_len == valid_eip_155_signature_lengths.1 as usize + { + // process based on the first byte of the signature + match *msg.signature().bytes().first().expect("infallible") { + HOMESTEAD_SIG_PREFIX => { + let args = EthLegacyHomesteadTxArgsBuilder::default() + .nonce(msg.message().sequence()) + .to(to) + .value(msg.value()) + .input(params) + .gas_price(msg.message().gas_fee_cap()) + .gas_limit(msg.message().gas_limit()) + .build()? + .with_signature(msg.signature())?; + EthTx::Homestead(Box::new(args)) + } + EIP_155_SIG_PREFIX => { + let args = EthLegacyEip155TxArgsBuilder::default() + .nonce(msg.message().sequence()) + .to(to) + .value(msg.value()) + .input(params) + .gas_price(msg.message().gas_fee_cap()) + .gas_limit(msg.message().gas_limit()) + .build()? + .with_signature(msg.signature(), eth_chain_id)?; + EthTx::Eip155(Box::new(args)) + } + _ => bail!("unsupported signature prefix"), + } + } else { + bail!("unsupported signature length: {sig_len}"); + }; + + Ok(tx) + } + + pub(crate) fn is_eip1559(&self) -> bool { + matches!(self, EthTx::Eip1559(_)) + } + + /// Validates that the signed Filecoin message is a valid Ethereum transaction. + /// Note: only basic checks are done. The signature and payload are not verified. + fn ensure_signed_message_valid(msg: &SignedMessage) -> anyhow::Result<()> { + ensure!( + msg.signature().signature_type() == SignatureType::Delegated, + "Signature is not delegated type" + ); + + ensure!( + msg.message().version == 0, + "unsupported msg version: {}", + msg.message().version + ); + + EthAddress::from_filecoin_address(&msg.from())?; + + Ok(()) + } +} + +/// Checks if a signed Filecoin message is valid for sending to Ethereum. +pub fn is_valid_eth_tx_for_sending( + eth_chain_id: EthChainId, + network_version: NetworkVersion, + message: &SignedMessage, +) -> bool { + let eth_tx = EthTx::from_signed_message(eth_chain_id, message); + + if let Ok(eth_tx) = eth_tx { + // EIP-1559 transactions are valid for all network versions. + // Legacy transactions are only valid for network versions >= V23. + network_version >= NetworkVersion::V23 || eth_tx.is_eip1559() + } else { + false + } +} + +/// Extracts the Ethereum transaction parameters and recipient from a Filecoin message. +fn get_eth_params_and_recipient(msg: &Message) -> anyhow::Result<(Vec, Option)> { + let mut to = None; + let mut params = vec![]; + + ensure!(msg.version == 0, "unsupported msg version: {}", msg.version); + + if !msg.params().bytes().is_empty() { + let mut reader = SliceReader::new(msg.params().bytes()); + match Value::decode(&mut reader) { + Ok(Value::Bytes(bytes)) => params = bytes, + _ => bail!("failed to read params byte array"), + } + } + + if msg.to == Address::ETHEREUM_ACCOUNT_MANAGER_ACTOR { + if msg.method_num() != EAMMethod::CreateExternal as u64 { + bail!("unsupported EAM method"); + } + } else if msg.method_num() == EVMMethod::InvokeContract as u64 { + let addr = EthAddress::from_filecoin_address(&msg.to)?; + to = Some(addr); + } else { + bail!( + "invalid methodnum {}: only allowed method is InvokeContract({})", + msg.method_num(), + EVMMethod::InvokeContract as u64 + ); + } + + Ok((params, to)) +} + +#[cfg(test)] +pub(crate) mod tests { + use std::str::FromStr as _; + + use num_bigint::ToBigUint as _; + + use crate::{ + networks::mainnet, + shim::{crypto::Signature, econ::TokenAmount}, + }; + + use super::*; + const ETH_ADDR_LEN: usize = 20; + + pub fn create_message() -> Message { + let from = EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa") + .unwrap() + .to_filecoin_address() + .unwrap(); + + let to = Address::new_id(1); + Message { + version: 0, + to, + from, + value: TokenAmount::from_atto(10), + gas_fee_cap: TokenAmount::from_atto(11), + gas_premium: TokenAmount::from_atto(12), + gas_limit: 13, + sequence: 14, + method_num: EVMMethod::InvokeContract as u64, + params: Default::default(), + } + } + + pub fn create_eip_1559_signed_message() -> SignedMessage { + let mut eip_1559_sig = vec![0u8; EIP_1559_SIG_LEN]; + eip_1559_sig[0] = EIP_155_SIG_PREFIX; + + SignedMessage { + message: create_message(), + signature: Signature::new(SignatureType::Delegated, eip_1559_sig), + } + } + + pub fn create_homestead_signed_message() -> SignedMessage { + let mut homestead_sig = vec![0u8; HOMESTEAD_SIG_LEN]; + homestead_sig[0] = HOMESTEAD_SIG_PREFIX; + homestead_sig[HOMESTEAD_SIG_LEN - 1] = 27; + + SignedMessage { + message: create_message(), + signature: Signature::new(SignatureType::Delegated, homestead_sig), + } + } + + #[test] + fn test_ensure_signed_message_valid() { + let create_empty_delegated_message = || SignedMessage { + message: create_message(), + signature: Signature::new(SignatureType::Delegated, vec![]), + }; + // ok + let msg = create_empty_delegated_message(); + EthTx::ensure_signed_message_valid(&msg).unwrap(); + + // wrong signature type + let mut msg = create_empty_delegated_message(); + msg.signature = Signature::new(SignatureType::Bls, vec![]); + assert!(EthTx::ensure_signed_message_valid(&msg).is_err()); + + // unsupported version + let mut msg = create_empty_delegated_message(); + msg.message.version = 1; + assert!(EthTx::ensure_signed_message_valid(&msg).is_err()); + + // invalid delegated address namespace + let mut msg = create_empty_delegated_message(); + msg.message.from = Address::new_delegated(0x42, &[0xff; ETH_ADDR_LEN]).unwrap(); + assert!(EthTx::ensure_signed_message_valid(&msg).is_err()); + } + + #[test] + fn test_eth_transaction_from_signed_filecoin_message_valid_eip1559() { + let msg = create_eip_1559_signed_message(); + + let tx = EthTx::from_signed_message(mainnet::ETH_CHAIN_ID, &msg).unwrap(); + if let EthTx::Eip1559(tx) = tx { + assert_eq!(tx.chain_id, mainnet::ETH_CHAIN_ID); + assert_eq!(tx.value, msg.message.value.into()); + assert_eq!(tx.max_fee_per_gas, msg.message.gas_fee_cap.into()); + assert_eq!(tx.max_priority_fee_per_gas, msg.message.gas_premium.into()); + assert_eq!(tx.gas_limit, msg.message.gas_limit); + assert_eq!(tx.nonce, msg.message.sequence); + assert_eq!( + tx.to.unwrap(), + EthAddress::from_filecoin_address(&msg.message.to).unwrap() + ); + assert!(tx.input.is_empty()); + } else { + panic!("invalid transaction type"); + } + } + + #[test] + fn test_eth_transaction_from_signed_filecoin_message_valid_homestead() { + let msg = create_homestead_signed_message(); + let tx = EthTx::from_signed_message(mainnet::ETH_CHAIN_ID, &msg).unwrap(); + if let EthTx::Homestead(tx) = tx { + assert_eq!(tx.value, msg.message.value.into()); + assert_eq!(tx.gas_limit, msg.message.gas_limit); + assert_eq!(tx.nonce, msg.message.sequence); + assert_eq!( + tx.to.unwrap(), + EthAddress::from_filecoin_address(&msg.message.to).unwrap() + ); + assert_eq!(tx.gas_price, msg.message.gas_fee_cap.into()); + assert!(tx.input.is_empty()); + } else { + panic!("invalid transaction type"); + } + } + + #[test] + fn test_eth_transaction_from_signed_filecoin_message_valid_eip_155() { + let eth_chain_id = mainnet::ETH_CHAIN_ID; + + // we need some reverse math here to get the correct V value + // from which the chain ID is derived. + let v = (2 * eth_chain_id + 35).to_biguint().unwrap().to_bytes_be(); + let eip_155_sig_len = calc_valid_eip155_sig_len(eth_chain_id).0 as usize; + let mut eip_155_sig = vec![0u8; eip_155_sig_len as usize - v.len()]; + eip_155_sig[0] = EIP_155_SIG_PREFIX; + eip_155_sig.extend(v); + + let msg = SignedMessage { + message: create_message(), + signature: Signature::new(SignatureType::Delegated, eip_155_sig), + }; + + let tx = EthTx::from_signed_message(mainnet::ETH_CHAIN_ID, &msg).unwrap(); + if let EthTx::Eip155(tx) = tx { + assert_eq!(tx.value, msg.message.value.into()); + assert_eq!(tx.gas_limit, msg.message.gas_limit); + assert_eq!(tx.nonce, msg.message.sequence); + assert_eq!( + tx.to.unwrap(), + EthAddress::from_filecoin_address(&msg.message.to).unwrap() + ); + assert_eq!(tx.gas_price, msg.message.gas_fee_cap.into()); + assert!(tx.input.is_empty()); + } else { + panic!("invalid transaction type"); + } + } + + #[test] + fn test_eth_transaction_from_signed_filecoin_message_empty_signature() { + let msg = SignedMessage { + message: create_message(), + signature: Signature::new(SignatureType::Delegated, vec![]), + }; + + assert!(EthTx::from_signed_message(mainnet::ETH_CHAIN_ID, &msg).is_err()); + } + + #[test] + fn test_eth_transaction_from_signed_filecoin_message_invalid_signature() { + let msg = SignedMessage { + message: create_message(), + signature: Signature::new( + SignatureType::Delegated, + b"Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn".to_vec(), + ), + }; + + assert!(EthTx::from_signed_message(mainnet::ETH_CHAIN_ID, &msg).is_err()); + } + + #[test] + fn test_is_valid_eth_tx_for_sending_eip1559_always_valid() { + let msg = create_eip_1559_signed_message(); + assert!(is_valid_eth_tx_for_sending( + mainnet::ETH_CHAIN_ID, + NetworkVersion::V22, + &msg + )); + + assert!(is_valid_eth_tx_for_sending( + mainnet::ETH_CHAIN_ID, + NetworkVersion::V23, + &msg + )); + } + + #[test] + fn test_is_valid_eth_tx_for_sending_legacy_valid_post_nv23() { + let msg = create_homestead_signed_message(); + assert!(is_valid_eth_tx_for_sending( + mainnet::ETH_CHAIN_ID, + NetworkVersion::V23, + &msg + )); + } + + #[test] + fn test_is_valid_eth_tx_for_sending_legacy_invalid_pre_nv23() { + let msg = create_homestead_signed_message(); + assert!(!is_valid_eth_tx_for_sending( + mainnet::ETH_CHAIN_ID, + NetworkVersion::V22, + &msg + )); + } + + #[test] + fn test_is_valid_eth_tx_for_sending_invalid_non_delegated() { + let msg = create_message(); + let msg = SignedMessage { + message: msg, + signature: Signature::new_secp256k1(vec![]), + }; + assert!(!is_valid_eth_tx_for_sending( + mainnet::ETH_CHAIN_ID, + NetworkVersion::V22, + &msg + )); + assert!(!is_valid_eth_tx_for_sending( + mainnet::ETH_CHAIN_ID, + NetworkVersion::V23, + &msg + )); + } +} diff --git a/src/interpreter/vm.rs b/src/interpreter/vm.rs index c674073f41d7..a9f4bde22640 100644 --- a/src/interpreter/vm.rs +++ b/src/interpreter/vm.rs @@ -188,7 +188,7 @@ where if network_version >= NetworkVersion::V21 { let mut config = NetworkConfig_v4::new(network_version.into()); // ChainId defines the chain ID used in the Ethereum JSON-RPC endpoint. - config.chain_id((chain_config.eth_chain_id as u64).into()); + config.chain_id((chain_config.eth_chain_id).into()); if let NetworkChain::Devnet(_) = chain_config.network { config.enable_actor_debugging(); } @@ -216,7 +216,7 @@ where } else if network_version >= NetworkVersion::V18 { let mut config = NetworkConfig_v3::new(network_version.into()); // ChainId defines the chain ID used in the Ethereum JSON-RPC endpoint. - config.chain_id((chain_config.eth_chain_id as u64).into()); + config.chain_id((chain_config.eth_chain_id).into()); if let NetworkChain::Devnet(_) = chain_config.network { config.enable_actor_debugging(); } diff --git a/src/lib.rs b/src/lib.rs index 87611d300abc..161f509a8979 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,7 @@ mod cli_shared; mod daemon; mod db; mod documentation; +mod eth; mod fil_cns; mod genesis; mod health; diff --git a/src/message_pool/msgpool/msg_pool.rs b/src/message_pool/msgpool/msg_pool.rs index 079644fde8e4..3fd085f2f1f7 100644 --- a/src/message_pool/msgpool/msg_pool.rs +++ b/src/message_pool/msgpool/msg_pool.rs @@ -12,6 +12,7 @@ use crate::blocks::{CachingBlockHeader, Tipset}; use crate::chain::{HeadChange, MINIMUM_BASE_FEE}; #[cfg(test)] use crate::db::SettingsStore; +use crate::eth::is_valid_eth_tx_for_sending; use crate::libp2p::{NetworkMessage, Topic, PUBSUB_MSG_STR}; use crate::message::{valid_for_block_inclusion, ChainMessage, Message, SignedMessage}; use crate::networks::{ChainConfig, NEWEST_NETWORK_VERSION}; @@ -282,6 +283,14 @@ where // This message can only be included in the next epoch and beyond, hence the +1. let nv = self.chain_config.network_version(cur_ts.epoch() + 1); + let eth_chain_id = self.chain_config.eth_chain_id; + if msg.signature().signature_type() == SignatureType::Delegated + && !is_valid_eth_tx_for_sending(eth_chain_id, nv, &msg) + { + return Err(Error::Other( + "Invalid Ethereum message for the current network version".to_owned(), + )); + } if !is_valid_for_sending(nv, &sender_actor) { return Err(Error::Other( "Sender actor is not a valid top-level sender".to_owned(), diff --git a/src/networks/actors_bundle.rs b/src/networks/actors_bundle.rs index 1bff9f2e4d71..17a6b46ad3ff 100644 --- a/src/networks/actors_bundle.rs +++ b/src/networks/actors_bundle.rs @@ -79,13 +79,15 @@ pub static ACTOR_BUNDLES: Lazy> = Lazy::new(|| { "bafy2bzacednzb3pkrfnbfhmoqtb3bc6dgvxszpqklf3qcc7qzcage4ewzxsca" @ "v12.0.0" for "calibrationnet", "bafy2bzacea4firkyvt2zzdwqjrws5pyeluaesh6uaid246tommayr4337xpmi" @ "v13.0.0-rc.3" for "calibrationnet", "bafy2bzacect4ktyujrwp6mjlsitnpvuw2pbuppz6w52sfljyo4agjevzm75qs" @ "v13.0.0" for "calibrationnet", - "bafy2bzacectxvbk77ntedhztd6sszp2btrtvsmy7lp2ypnrk6yl74zb34t2cq" @ "v12.0.0" for "butterflynet", + "bafy2bzacebq3hncszqpojglh2dkwekybq4zn6qpc4gceqbx36wndps5qehtau" @ "v14.0.0-rc.1" for "calibrationnet", "bafy2bzacec75zk7ufzwx6tg5avls5fxdjx5asaqmd2bfqdvkqrkzoxgyflosu" @ "v13.0.0" for "butterflynet", + "bafy2bzacecmkqezl3a5klkzz7z4ou4jwqk4zzd3nvz727l4qh44ngsxtxdblu" @ "v14.0.0-rc.1" for "butterflynet", "bafy2bzacedozk3jh2j4nobqotkbofodq4chbrabioxbfrygpldgoxs3zwgggk" @ "v9.0.3" for "devnet", "bafy2bzacebzz376j5kizfck56366kdz5aut6ktqrvqbi3efa2d4l2o2m653ts" @ "v10.0.0" for "devnet", "bafy2bzaceay35go4xbjb45km6o46e5bib3bi46panhovcbedrynzwmm3drr4i" @ "v11.0.0" for "devnet", "bafy2bzaceasjdukhhyjbegpli247vbf5h64f7uvxhhebdihuqsj2mwisdwa6o" @ "v12.0.0" for "devnet", "bafy2bzacecn7uxgehrqbcs462ktl2h23u23cmduy2etqj6xrd6tkkja56fna4" @ "v13.0.0" for "devnet", + "bafy2bzacebwn7ymtozv5yz3x5hnxl4bds2grlgsk5kncyxjak3hqyhslb534m" @ "v14.0.0-rc.1" for "devnet", "bafy2bzaceb6j6666h36xnhksu3ww4kxb6e25niayfgkdnifaqi6m6ooc66i6i" @ "v9.0.3" for "mainnet", "bafy2bzacecsuyf7mmvrhkx2evng5gnz5canlnz2fdlzu2lvcgptiq2pzuovos" @ "v10.0.0" for "mainnet", "bafy2bzacecnhaiwcrpyjvzl4uv4q3jzoif26okl3m66q3cijp3dfwlcxwztwo" @ "v11.0.0" for "mainnet", diff --git a/src/networks/butterflynet/mod.rs b/src/networks/butterflynet/mod.rs index 192d165664d1..208b73f30850 100644 --- a/src/networks/butterflynet/mod.rs +++ b/src/networks/butterflynet/mod.rs @@ -8,7 +8,10 @@ use once_cell::sync::Lazy; use std::str::FromStr; use url::Url; -use crate::{db::SettingsStore, make_height, shim::version::NetworkVersion, utils::net::http_get}; +use crate::{ + db::SettingsStore, eth::EthChainId, make_height, shim::version::NetworkVersion, + utils::net::http_get, +}; use super::{ actors_bundle::ACTOR_BUNDLES_METADATA, @@ -17,7 +20,7 @@ use super::{ NetworkChain, }; -pub const GENESIS_NETWORK_VERSION: NetworkVersion = NetworkVersion::V21; +pub const GENESIS_NETWORK_VERSION: NetworkVersion = NetworkVersion::V22; /// Fetches the genesis CAR from the local database or downloads it if it does not exist. /// The result bytes may be compressed. @@ -39,7 +42,7 @@ pub async fn fetch_genesis(db: &DB) -> anyhow::Result /// Genesis CID pub static GENESIS_CID: Lazy = Lazy::new(|| { - Cid::from_str("bafy2bzaceddfs2mf6ufmvszvwho2n6c7zvrnywpwkyq5wudtsknxhfvhwrhhs").unwrap() + Cid::from_str("bafy2bzaceajpno2eryhvocvmol7s3urztu7aie2sk3fp2ecl72qhxj7a3vone").unwrap() }); /// Compressed genesis file. It is compressed with zstd and cuts the download size by 80% (from 10 MB to 2 MB). @@ -54,7 +57,7 @@ static GENESIS_URL: Lazy = Lazy::new(|| { /// The genesis file does not live on the `master` branch, currently on a draft PR. /// `` static GENESIS_URL_ALT: Lazy = Lazy::new(|| { - "https://github.com/filecoin-project/lotus/raw/c643e174798202e77b3e8f8a080d81e1ea32f7c5/build/genesis/butterflynet.car".parse().expect("hard-coded URL must parse") + "https://github.com/filecoin-project/lotus/raw/4dfe16f58e55b3bbb87c5ff95fbe80bb41d44b80/build/genesis/butterflynet.car".parse().expect("hard-coded URL must parse") }); pub(crate) const MINIMUM_CONSENSUS_POWER: i64 = 2 << 30; @@ -66,7 +69,7 @@ pub static DEFAULT_BOOTSTRAP: Lazy> = Lazy::new(|| parse_bootstrap_peers(include_str!("../../../build/bootstrap/butterflynet"))); // https://github.com/ethereum-lists/chains/blob/4731f6713c6fc2bf2ae727388642954a6545b3a9/_data/chains/eip155-314159.json -pub const ETH_CHAIN_ID: u64 = 3141592; +pub const ETH_CHAIN_ID: EthChainId = 3141592; pub const BREEZE_GAS_TAMPING_DURATION: i64 = 120; @@ -96,16 +99,10 @@ pub static HEIGHT_INFOS: Lazy> = Lazy::new(|| { make_height!(Hygge, -21), make_height!(Lightning, -22), make_height!(Thunder, -23), - make_height!(Watermelon, -1, get_bundle_cid("v12.0.0")), - make_height!(Dragon, 480, get_bundle_cid("v13.0.0")), - ( - Height::Phoenix, - HeightInfo { - epoch: 600, - bundle: None, - }, - ), - make_height!(Aussie, 9999999999), + make_height!(Watermelon, -24), + make_height!(Dragon, -25, get_bundle_cid("v13.0.0")), + make_height!(Phoenix, i64::MAX), + make_height!(Waffle, 100, get_bundle_cid("v14.0.0-rc.1")), ]) }); @@ -201,11 +198,13 @@ mod tests { let v11 = make_butterfly_policy!(v11); let v12 = make_butterfly_policy!(v12); let v13 = make_butterfly_policy!(v13); + let v14 = make_butterfly_policy!(v14); // basic sanity checks assert_eq!(v10.minimum_consensus_power, MINIMUM_CONSENSUS_POWER.into()); assert_eq!(v11.minimum_consensus_power, MINIMUM_CONSENSUS_POWER.into()); assert_eq!(v12.minimum_consensus_power, MINIMUM_CONSENSUS_POWER.into()); assert_eq!(v13.minimum_consensus_power, MINIMUM_CONSENSUS_POWER.into()); + assert_eq!(v14.minimum_consensus_power, MINIMUM_CONSENSUS_POWER.into()); } } diff --git a/src/networks/calibnet/mod.rs b/src/networks/calibnet/mod.rs index 6d99f43d1c19..44900f331b4b 100644 --- a/src/networks/calibnet/mod.rs +++ b/src/networks/calibnet/mod.rs @@ -7,7 +7,7 @@ use libp2p::Multiaddr; use once_cell::sync::Lazy; use std::str::FromStr; -use crate::{make_height, shim::version::NetworkVersion}; +use crate::{eth::EthChainId, make_height, shim::version::NetworkVersion}; use super::{ actors_bundle::ACTOR_BUNDLES_METADATA, @@ -36,7 +36,7 @@ const LIGHTNING_EPOCH: i64 = 489_094; const LIGHTNING_ROLLOVER_PERIOD: i64 = 3120; // https://github.com/ethereum-lists/chains/blob/4731f6713c6fc2bf2ae727388642954a6545b3a9/_data/chains/eip155-314159.json -pub const ETH_CHAIN_ID: u64 = 314159; +pub const ETH_CHAIN_ID: EthChainId = 314159; pub const BREEZE_GAS_TAMPING_DURATION: i64 = 120; @@ -72,7 +72,8 @@ pub static HEIGHT_INFOS: Lazy> = Lazy::new(|| { make_height!(Dragon, 1_427_974, get_bundle_cid("v13.0.0-rc.3")), make_height!(DragonFix, 1_493_854, get_bundle_cid("v13.0.0")), make_height!(Phoenix, 1_428_094), - make_height!(Aussie, 999999999999999), + // 2024-07-11 12:00:00Z + make_height!(Waffle, 1_779_094, get_bundle_cid("v14.0.0-rc.1")), ]) }); diff --git a/src/networks/devnet/mod.rs b/src/networks/devnet/mod.rs index 78fde36ab043..4fecfa63470f 100644 --- a/src/networks/devnet/mod.rs +++ b/src/networks/devnet/mod.rs @@ -5,7 +5,7 @@ use ahash::HashMap; use cid::Cid; use once_cell::sync::Lazy; -use crate::{make_height, shim::version::NetworkVersion}; +use crate::{eth::EthChainId, make_height, shim::version::NetworkVersion}; use super::{ actors_bundle::ACTOR_BUNDLES_METADATA, @@ -14,7 +14,7 @@ use super::{ }; // https://github.com/ethereum-lists/chains/blob/6b1e3ccad1cfcaae5aa1ab917960258f0ef1a6b6/_data/chains/eip155-31415926.json -pub const ETH_CHAIN_ID: u64 = 31415926; +pub const ETH_CHAIN_ID: EthChainId = 31415926; pub const BREEZE_GAS_TAMPING_DURATION: i64 = 0; @@ -145,8 +145,9 @@ pub static HEIGHT_INFOS: Lazy> = Lazy::new(|| { get_upgrade_height_from_env("FOREST_DRAND_QUICKNET_HEIGHT").unwrap_or(i64::MAX) ), make_height!( - Aussie, - get_upgrade_height_from_env("FOREST_AUSSIE_HEIGHT").unwrap_or(9999999999) + Waffle, + get_upgrade_height_from_env("FOREST_WAFFLE_HEIGHT").unwrap_or(9999999999), + get_bundle_cid("v14.0.0-rc.1") ), ]) }); diff --git a/src/networks/mainnet/mod.rs b/src/networks/mainnet/mod.rs index bb8d725a3fbd..736ac2587622 100644 --- a/src/networks/mainnet/mod.rs +++ b/src/networks/mainnet/mod.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0, MIT use crate::{ + eth::EthChainId, make_height, shim::{clock::ChainEpoch, version::NetworkVersion}, }; @@ -36,7 +37,7 @@ pub static DEFAULT_BOOTSTRAP: Lazy> = const LIGHTNING_ROLLOVER_PERIOD: i64 = 2880 * 21; // https://github.com/ethereum-lists/chains/blob/4731f6713c6fc2bf2ae727388642954a6545b3a9/_data/chains/eip155-314.json -pub const ETH_CHAIN_ID: u64 = 314; +pub const ETH_CHAIN_ID: EthChainId = 314; pub const BREEZE_GAS_TAMPING_DURATION: i64 = 120; @@ -70,7 +71,7 @@ pub static HEIGHT_INFOS: Lazy> = Lazy::new(|| { // Thu Apr 24 02:00:00 PM UTC 2024 make_height!(Dragon, 3_855_360, get_bundle_cid("v13.0.0")), make_height!(Phoenix, 3_855_480), - make_height!(Aussie, 9999999999), + make_height!(Waffle, 9999999999), ]) }); diff --git a/src/networks/mod.rs b/src/networks/mod.rs index 44f51883d4a9..ea3b481116ca 100644 --- a/src/networks/mod.rs +++ b/src/networks/mod.rs @@ -15,6 +15,7 @@ use tracing::warn; use crate::beacon::{BeaconPoint, BeaconSchedule, DrandBeacon, DrandConfig}; use crate::db::SettingsStore; +use crate::eth::EthChainId; use crate::shim::clock::{ChainEpoch, EPOCH_DURATION_SECONDS}; use crate::shim::sector::{RegisteredPoStProofV3, RegisteredSealProofV3}; use crate::shim::version::NetworkVersion; @@ -131,7 +132,7 @@ pub enum Height { Dragon, DragonFix, Phoenix, - Aussie, + Waffle, } impl Default for Height { @@ -172,7 +173,7 @@ impl From for NetworkVersion { Height::Dragon => NetworkVersion::V22, Height::DragonFix => NetworkVersion::V22, Height::Phoenix => NetworkVersion::V22, - Height::Aussie => NetworkVersion::V23, + Height::Waffle => NetworkVersion::V23, } } } @@ -211,7 +212,7 @@ pub struct ChainConfig { pub height_infos: HashMap, #[cfg_attr(test, arbitrary(gen(|_g| Policy::default())))] pub policy: Policy, - pub eth_chain_id: u32, + pub eth_chain_id: EthChainId, pub breeze_gas_tamping_duration: i64, } @@ -227,7 +228,7 @@ impl ChainConfig { genesis_network: GENESIS_NETWORK_VERSION, height_infos: HEIGHT_INFOS.clone(), policy: make_mainnet_policy!(v13), - eth_chain_id: ETH_CHAIN_ID as u32, + eth_chain_id: ETH_CHAIN_ID, breeze_gas_tamping_duration: BREEZE_GAS_TAMPING_DURATION, } } @@ -243,7 +244,7 @@ impl ChainConfig { genesis_network: GENESIS_NETWORK_VERSION, height_infos: HEIGHT_INFOS.clone(), policy: make_calibnet_policy!(v13), - eth_chain_id: ETH_CHAIN_ID as u32, + eth_chain_id: ETH_CHAIN_ID, breeze_gas_tamping_duration: BREEZE_GAS_TAMPING_DURATION, } } @@ -259,7 +260,7 @@ impl ChainConfig { genesis_network: *GENESIS_NETWORK_VERSION, height_infos: HEIGHT_INFOS.clone(), policy: make_devnet_policy!(v13), - eth_chain_id: ETH_CHAIN_ID as u32, + eth_chain_id: ETH_CHAIN_ID, breeze_gas_tamping_duration: BREEZE_GAS_TAMPING_DURATION, } } @@ -276,7 +277,7 @@ impl ChainConfig { genesis_network: GENESIS_NETWORK_VERSION, height_infos: HEIGHT_INFOS.clone(), policy: make_butterfly_policy!(v13), - eth_chain_id: ETH_CHAIN_ID as u32, + eth_chain_id: ETH_CHAIN_ID, breeze_gas_tamping_duration: BREEZE_GAS_TAMPING_DURATION, } } @@ -476,7 +477,7 @@ mod tests { Height::Watermelon, Height::Dragon, Height::Phoenix, - Height::Aussie, + Height::Waffle, ]; for height in &REQUIRED_HEIGHTS { diff --git a/src/rpc/methods/eth.rs b/src/rpc/methods/eth.rs index 64ad16e6751e..e4791b98c680 100644 --- a/src/rpc/methods/eth.rs +++ b/src/rpc/methods/eth.rs @@ -9,6 +9,7 @@ use crate::blocks::Tipset; use crate::chain::{index::ResolveNullTipset, ChainStore}; use crate::chain_sync::SyncStage; use crate::cid_collections::CidHashSet; +use crate::eth::EthChainId as EthChainIdType; use crate::lotus_json::{lotus_json_with_self, HasLotusJson}; use crate::message::{ChainMessage, Message as _, SignedMessage}; use crate::rpc::error::ServerError; @@ -368,6 +369,9 @@ pub struct Tx { pub gas: Uint64, pub max_fee_per_gas: EthBigInt, pub max_priority_fee_per_gas: EthBigInt, + // TODO(forest): https://github.com/ChainSafe/forest/issues/4477 + // RPC methods will need to be updated to support different Ethereum transaction types. + // pub gas_price: EthBigInt, #[schemars(with = "Option>")] #[serde(with = "crate::lotus_json")] pub access_list: Vec, @@ -880,7 +884,10 @@ fn recover_sig(sig: &Signature) -> Result<(EthBigInt, EthBigInt, EthBigInt)> { /// - `block_hash` /// - `block_number` /// - `transaction_index` -pub fn eth_tx_from_signed_eth_message(smsg: &SignedMessage, chain_id: u32) -> Result { +pub fn eth_tx_from_signed_eth_message( + smsg: &SignedMessage, + chain_id: EthChainIdType, +) -> Result { // The from address is always an f410f address, never an ID or other address. let from = smsg.message().from; if !is_eth_address(&from) { @@ -903,7 +910,7 @@ pub fn eth_tx_from_signed_eth_message(smsg: &SignedMessage, chain_id: u32) -> Re Ok(Tx { nonce: Uint64(tx_args.nonce), - chain_id: Uint64(chain_id as u64), + chain_id: Uint64(chain_id), to: tx_args.to, from, value: tx_args.value, @@ -911,6 +918,9 @@ pub fn eth_tx_from_signed_eth_message(smsg: &SignedMessage, chain_id: u32) -> Re gas: Uint64(tx_args.gas_limit), max_fee_per_gas: tx_args.max_fee_per_gas, max_priority_fee_per_gas: tx_args.max_priority_fee_per_gas, + // TODO(forest): https://github.com/ChainSafe/forest/issues/4477 + // RPC methods will need to be updated to support different Ethereum transaction types. + // gas_price: EthBigInt::default(), access_list: vec![], v, r, @@ -1047,7 +1057,7 @@ fn decode_payload(payload: &fvm_ipld_encoding::RawBytes, codec: u64) -> Result( msg: &Message, state: &StateTree, - chain_id: u32, + chain_id: EthChainIdType, ) -> Result { // Lookup the from address. This must succeed. let from = match lookup_eth_address(&msg.from(), state) { @@ -1099,12 +1109,15 @@ fn eth_tx_from_native_message( from, input, nonce: Uint64(msg.sequence), - chain_id: Uint64(chain_id as u64), + chain_id: Uint64(chain_id), value: msg.value.clone().into(), r#type: Uint64(EIP_1559_TX_TYPE), gas: Uint64(msg.gas_limit), max_fee_per_gas: msg.gas_fee_cap.clone().into(), max_priority_fee_per_gas: msg.gas_premium.clone().into(), + // TODO(forest): https://github.com/ChainSafe/forest/issues/4477 + // RPC methods will need to be updated to support different Ethereum transaction types. + // gas_price: EthBigInt::default(), access_list: vec![], ..Tx::default() }) @@ -1113,7 +1126,7 @@ fn eth_tx_from_native_message( pub fn new_eth_tx_from_signed_message( smsg: &SignedMessage, state: &StateTree, - chain_id: u32, + chain_id: EthChainIdType, ) -> Result { let (tx, hash) = if smsg.is_delegated() { // This is an eth tx diff --git a/src/rpc/methods/state.rs b/src/rpc/methods/state.rs index 9907ce2b6c32..6b37606e5b34 100644 --- a/src/rpc/methods/state.rs +++ b/src/rpc/methods/state.rs @@ -11,6 +11,7 @@ pub use types::*; use crate::blocks::Tipset; use crate::cid_collections::CidHashSet; +use crate::eth::EthChainId; use crate::libp2p::NetworkMessage; use crate::lotus_json::lotus_json_with_self; use crate::networks::{ChainConfig, NetworkChain}; @@ -660,6 +661,10 @@ impl RpcMethod<2> for StateMinerAvailableBalance { let state = miner::State::load(ctx.store(), actor.code, actor.state)?; let actor_balance: TokenAmount = actor.balance.clone().into(); let (vested, available): (TokenAmount, TokenAmount) = match &state { + miner::State::V14(s) => ( + s.check_vested_funds(ctx.store(), ts.epoch())?.into(), + s.get_available_balance(&actor_balance.into())?.into(), + ), miner::State::V13(s) => ( s.check_vested_funds(ctx.store(), ts.epoch())?.into(), s.get_available_balance(&actor_balance.into())?.into(), @@ -1659,60 +1664,86 @@ impl StateSectorPreCommitInfo { _, fil_actor_miner_state::v8::SectorPreCommitOnChainInfo, >(&s.pre_committed_sectors, store)?; - precommitted.for_each(|_k, v| { - sectors.push(v.info.sector_number); - Ok(()) - }) + precommitted + .for_each(|_k, v| { + sectors.push(v.info.sector_number); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") } miner::State::V9(s) => { let precommitted = fil_actors_shared::v9::make_map_with_root::< _, fil_actor_miner_state::v9::SectorPreCommitOnChainInfo, >(&s.pre_committed_sectors, store)?; - precommitted.for_each(|_k, v| { - sectors.push(v.info.sector_number); - Ok(()) - }) + precommitted + .for_each(|_k, v| { + sectors.push(v.info.sector_number); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") } miner::State::V10(s) => { let precommitted = fil_actors_shared::v10::make_map_with_root::< _, fil_actor_miner_state::v10::SectorPreCommitOnChainInfo, >(&s.pre_committed_sectors, store)?; - precommitted.for_each(|_k, v| { - sectors.push(v.info.sector_number); - Ok(()) - }) + precommitted + .for_each(|_k, v| { + sectors.push(v.info.sector_number); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") } miner::State::V11(s) => { let precommitted = fil_actors_shared::v11::make_map_with_root::< _, fil_actor_miner_state::v11::SectorPreCommitOnChainInfo, >(&s.pre_committed_sectors, store)?; - precommitted.for_each(|_k, v| { - sectors.push(v.info.sector_number); - Ok(()) - }) + precommitted + .for_each(|_k, v| { + sectors.push(v.info.sector_number); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") } miner::State::V12(s) => { let precommitted = fil_actors_shared::v12::make_map_with_root::< _, fil_actor_miner_state::v12::SectorPreCommitOnChainInfo, >(&s.pre_committed_sectors, store)?; - precommitted.for_each(|_k, v| { - sectors.push(v.info.sector_number); - Ok(()) - }) + precommitted + .for_each(|_k, v| { + sectors.push(v.info.sector_number); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") } miner::State::V13(s) => { let precommitted = fil_actors_shared::v13::make_map_with_root::< _, fil_actor_miner_state::v13::SectorPreCommitOnChainInfo, >(&s.pre_committed_sectors, store)?; - precommitted.for_each(|_k, v| { - sectors.push(v.info.sector_number); - Ok(()) - }) + precommitted + .for_each(|_k, v| { + sectors.push(v.info.sector_number); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") + } + miner::State::V14(s) => { + let precommitted = fil_actor_miner_state::v14::PreCommitMap::load( + store, + &s.pre_committed_sectors, + fil_actor_miner_state::v14::PRECOMMIT_CONFIG, + "precommits", + )?; + precommitted + .for_each(|_k, v| { + sectors.push(v.info.sector_number); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") } }?; @@ -1734,60 +1765,86 @@ impl StateSectorPreCommitInfo { _, fil_actor_miner_state::v8::SectorPreCommitOnChainInfo, >(&s.pre_committed_sectors, store)?; - precommitted.for_each(|_k, v| { - infos.push(v.info.clone().into()); - Ok(()) - }) + precommitted + .for_each(|_k, v| { + infos.push(v.info.clone().into()); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") } miner::State::V9(s) => { let precommitted = fil_actors_shared::v9::make_map_with_root::< _, fil_actor_miner_state::v9::SectorPreCommitOnChainInfo, >(&s.pre_committed_sectors, store)?; - precommitted.for_each(|_k, v| { - infos.push(v.info.clone().into()); - Ok(()) - }) + precommitted + .for_each(|_k, v| { + infos.push(v.info.clone().into()); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") } miner::State::V10(s) => { let precommitted = fil_actors_shared::v10::make_map_with_root::< _, fil_actor_miner_state::v10::SectorPreCommitOnChainInfo, >(&s.pre_committed_sectors, store)?; - precommitted.for_each(|_k, v| { - infos.push(v.info.clone().into()); - Ok(()) - }) + precommitted + .for_each(|_k, v| { + infos.push(v.info.clone().into()); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") } miner::State::V11(s) => { let precommitted = fil_actors_shared::v11::make_map_with_root::< _, fil_actor_miner_state::v11::SectorPreCommitOnChainInfo, >(&s.pre_committed_sectors, store)?; - precommitted.for_each(|_k, v| { - infos.push(v.info.clone().into()); - Ok(()) - }) + precommitted + .for_each(|_k, v| { + infos.push(v.info.clone().into()); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") } miner::State::V12(s) => { let precommitted = fil_actors_shared::v12::make_map_with_root::< _, fil_actor_miner_state::v12::SectorPreCommitOnChainInfo, >(&s.pre_committed_sectors, store)?; - precommitted.for_each(|_k, v| { - infos.push(v.info.clone().into()); - Ok(()) - }) + precommitted + .for_each(|_k, v| { + infos.push(v.info.clone().into()); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") } miner::State::V13(s) => { let precommitted = fil_actors_shared::v13::make_map_with_root::< _, fil_actor_miner_state::v13::SectorPreCommitOnChainInfo, >(&s.pre_committed_sectors, store)?; - precommitted.for_each(|_k, v| { - infos.push(v.info.clone().into()); - Ok(()) - }) + precommitted + .for_each(|_k, v| { + infos.push(v.info.clone().into()); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") + } + miner::State::V14(s) => { + let precommitted = fil_actor_miner_state::v14::PreCommitMap::load( + store, + &s.pre_committed_sectors, + fil_actor_miner_state::v14::PRECOMMIT_CONFIG, + "precommits", + )?; + precommitted + .for_each(|_k, v| { + infos.push(v.info.clone().into()); + Ok(()) + }) + .context("failed to iterate over precommitted sectors") } }?; @@ -2201,6 +2258,18 @@ impl StateGetAllocations { Ok(()) })?; } + init::State::V14(s) => { + let map = fil_actor_init_state::v14::AddressMap::load( + store, + &s.address_map, + fil_actors_shared::v14::DEFAULT_HAMT_CONFIG, + "address_map", + )?; + map.for_each(|_k, v| { + addresses.insert(Address::new_id(*v)); + Ok(()) + })?; + } }; } @@ -2295,7 +2364,7 @@ pub struct NetworkParams { pre_commit_challenge_delay: ChainEpoch, fork_upgrade_params: ForkUpgradeParams, #[serde(rename = "Eip155ChainID")] - eip155_chain_id: u32, + eip155_chain_id: EthChainId, } lotus_json_with_self!(NetworkParams); @@ -2331,7 +2400,7 @@ pub struct ForkUpgradeParams { upgrade_dragon_height: ChainEpoch, upgrade_phoenix_height: ChainEpoch, // To be added in the next Lotus release - // upgrade_aussie_height: ChainEpoch, + // upgrade_waffle_height: ChainEpoch, } impl TryFrom<&ChainConfig> for ForkUpgradeParams { @@ -2375,7 +2444,7 @@ impl TryFrom<&ChainConfig> for ForkUpgradeParams { upgrade_watermelon_height: get_height(Watermelon)?, upgrade_dragon_height: get_height(Dragon)?, upgrade_phoenix_height: get_height(Phoenix)?, - // upgrade_aussie_height: get_height(Aussie)?, + // upgrade_waffle_height: get_height(Waffle)?, }) } } diff --git a/src/rpc/types/sector_impl.rs b/src/rpc/types/sector_impl.rs index 45d5e527433a..56b38c3b7f18 100644 --- a/src/rpc/types/sector_impl.rs +++ b/src/rpc/types/sector_impl.rs @@ -149,6 +149,28 @@ impl From for SectorOnChainInfo { } } +impl From for SectorOnChainInfo { + fn from(info: fil_actor_miner_state::v14::SectorOnChainInfo) -> Self { + Self { + sector_number: info.sector_number, + seal_proof: info.seal_proof.into(), + sealed_cid: info.sealed_cid, + deal_ids: info.deprecated_deal_ids, + activation: info.activation, + expiration: info.expiration, + flags: info.flags.bits(), + deal_weight: info.deal_weight, + verified_deal_weight: info.verified_deal_weight, + initial_pledge: info.initial_pledge.into(), + expected_day_reward: info.expected_day_reward.into(), + expected_storage_pledge: info.expected_storage_pledge.into(), + replaced_day_reward: info.replaced_day_reward.into(), + sector_key_cid: info.sector_key_cid, + power_base_epoch: info.power_base_epoch, + } + } +} + impl From for SectorPreCommitOnChainInfo { fn from(i: fil_actor_miner_state::v8::SectorPreCommitOnChainInfo) -> Self { Self { @@ -209,6 +231,16 @@ impl From for SectorPreC } } +impl From for SectorPreCommitOnChainInfo { + fn from(i: fil_actor_miner_state::v14::SectorPreCommitOnChainInfo) -> Self { + Self { + info: i.info.into(), + pre_commit_deposit: i.pre_commit_deposit.into(), + pre_commit_epoch: i.pre_commit_epoch, + } + } +} + impl From for SectorPreCommitInfo { fn from(i: fil_actor_miner_state::v8::SectorPreCommitInfo) -> Self { Self { @@ -292,3 +324,17 @@ impl From for SectorPreCommitIn } } } + +impl From for SectorPreCommitInfo { + fn from(i: fil_actor_miner_state::v14::SectorPreCommitInfo) -> Self { + Self { + seal_proof: i.seal_proof.into(), + sector_number: i.sector_number, + sealed_cid: i.sealed_cid, + seal_rand_epoch: i.seal_rand_epoch, + deal_ids: i.deal_ids, + expiration: i.expiration, + unsealed_cid: i.unsealed_cid.0, + } + } +} diff --git a/src/shim/actors/market/balance_table.rs b/src/shim/actors/market/balance_table.rs index 2d3619edd32e..3b22ed64ac7c 100644 --- a/src/shim/actors/market/balance_table.rs +++ b/src/shim/actors/market/balance_table.rs @@ -31,6 +31,10 @@ impl<'bs, BS: Blockstore> BalanceTableExt for BalanceTable<'bs, BS> { f(&address.into(), &escrow.into()) .map_err(|e| fil_actors_shared::v13::ActorError::unspecified(e.to_string())) })?, + Self::V14(t) => t.0.for_each(|address, escrow| { + f(&address.into(), &escrow.into()) + .map_err(|e| fil_actors_shared::v14::ActorError::unspecified(e.to_string())) + })?, }; Ok(()) } diff --git a/src/shim/actors/miner/partition.rs b/src/shim/actors/miner/partition.rs index be57c8c4af20..06a2b09965f3 100644 --- a/src/shim/actors/miner/partition.rs +++ b/src/shim/actors/miner/partition.rs @@ -13,6 +13,7 @@ impl<'a> PartitionExt for Partition<'a> { Partition::V11(dl) => &dl.terminated, Partition::V12(dl) => &dl.terminated, Partition::V13(dl) => &dl.terminated, + Partition::V14(dl) => &dl.terminated, } } @@ -24,6 +25,7 @@ impl<'a> PartitionExt for Partition<'a> { Partition::V11(dl) => dl.expirations_epochs, Partition::V12(dl) => dl.expirations_epochs, Partition::V13(dl) => dl.expirations_epochs, + Partition::V14(dl) => dl.expirations_epochs, } } } diff --git a/src/shim/actors/miner/state.rs b/src/shim/actors/miner/state.rs index 31a3062edbba..416826c0249a 100644 --- a/src/shim/actors/miner/state.rs +++ b/src/shim/actors/miner/state.rs @@ -1,6 +1,8 @@ // Copyright 2019-2024 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use anyhow::Context as _; + use super::*; impl MinerStateExt for State { @@ -112,6 +114,23 @@ impl MinerStateExt for State { Ok(infos) } } + State::V14(st) => { + if let Some(sectors) = sectors { + Ok(st + .load_sector_infos(&store, sectors)? + .into_iter() + .map(From::from) + .collect()) + } else { + let sectors = fil_actor_miner_state::v14::Sectors::load(&store, &st.sectors)?; + let mut infos = Vec::with_capacity(sectors.amt.count() as usize); + sectors.amt.for_each(|_, info| { + infos.push(info.clone().into()); + Ok(()) + })?; + Ok(infos) + } + } } } @@ -126,6 +145,7 @@ impl MinerStateExt for State { Self::V11(s) => s.allocated_sectors, Self::V12(s) => s.allocated_sectors, Self::V13(s) => s.allocated_sectors, + Self::V14(s) => s.allocated_sectors, }; store.get_cbor_required(&allocated_sectors) } @@ -154,6 +174,10 @@ impl MinerStateExt for State { Self::V13(s) => s .get_precommitted_sector(store, sector_number)? .map(SectorPreCommitOnChainInfo::from), + Self::V14(s) => s + .get_precommitted_sector(store, sector_number) + .context("precommit info does not exist")? + .map(SectorPreCommitOnChainInfo::from), }) } } diff --git a/src/shim/actors/multisig/state.rs b/src/shim/actors/multisig/state.rs index 92e2dbbb88e5..78c8e408ba54 100644 --- a/src/shim/actors/multisig/state.rs +++ b/src/shim/actors/multisig/state.rs @@ -36,6 +36,11 @@ impl MultisigExt for State { start_epoch: st.start_epoch, unlock_duration: st.unlock_duration, }), + State::V14(st) => Ok(MsigVesting { + initial_balance: st.initial_balance.atto().clone(), + start_epoch: st.start_epoch, + unlock_duration: st.unlock_duration, + }), } } } diff --git a/src/shim/actors/verifreg/state.rs b/src/shim/actors/verifreg/state.rs index d1424ba0a311..b43d9a71541e 100644 --- a/src/shim/actors/verifreg/state.rs +++ b/src/shim/actors/verifreg/state.rs @@ -54,6 +54,14 @@ impl VerifiedRegistryStateExt for State { Ok(()) })?; } + State::V14(state) => { + let mut map = state.load_allocs(store)?; + map.for_each_in(address_id, |k, v| { + let allocation_id = fil_actors_shared::v14::parse_uint_key(k)?; + result.insert(allocation_id, v.into()); + Ok(()) + })?; + } }; Ok(result) } @@ -109,6 +117,14 @@ impl VerifiedRegistryStateExt for State { Ok(()) })?; } + Self::V14(s) => { + let mut claims = s.load_claims(store)?; + claims.for_each_in(provider_id, |k, v| { + let claim_id = fil_actors_shared::v14::parse_uint_key(k)?; + result.insert(claim_id, v.into()); + Ok(()) + })?; + } }; Ok(result) } diff --git a/src/shim/econ.rs b/src/shim/econ.rs index 32eb017f21a1..70bba553dd7b 100644 --- a/src/shim/econ.rs +++ b/src/shim/econ.rs @@ -124,6 +124,12 @@ impl TokenAmount { } } +impl From for BigInt { + fn from(value: TokenAmount) -> Self { + value.atto().to_owned() + } +} + impl From for TokenAmount { fn from(other: TokenAmount_v2) -> Self { (&other).into() diff --git a/src/state_migration/mod.rs b/src/state_migration/mod.rs index ea3cee5aba6c..faec219c3e5e 100644 --- a/src/state_migration/mod.rs +++ b/src/state_migration/mod.rs @@ -23,6 +23,7 @@ mod nv21fix; mod nv21fix2; mod nv22; mod nv22fix; +mod nv23; mod type_migrations; type RunMigration = fn(&ChainConfig, &Arc, &Cid, ChainEpoch) -> anyhow::Result; @@ -57,10 +58,14 @@ where (Height::WatermelonFix2, nv21fix2::run_migration::), (Height::Dragon, nv22::run_migration::), (Height::DragonFix, nv22fix::run_migration::), + (Height::Waffle, nv23::run_migration::), ] } NetworkChain::Butterflynet => { - vec![(Height::Dragon, nv22::run_migration::)] + vec![ + (Height::Dragon, nv22::run_migration::), + (Height::Waffle, nv23::run_migration::), + ] } NetworkChain::Devnet(_) => { vec![ @@ -69,6 +74,7 @@ where (Height::Lightning, nv19::run_migration::), (Height::Watermelon, nv21::run_migration::), (Height::Dragon, nv22::run_migration::), + (Height::Waffle, nv23::run_migration::), ] } }; diff --git a/src/state_migration/nv23/migration.rs b/src/state_migration/nv23/migration.rs new file mode 100644 index 000000000000..e3e6f1ed1b5b --- /dev/null +++ b/src/state_migration/nv23/migration.rs @@ -0,0 +1,95 @@ +// Copyright 2019-2024 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT +// +//! This module contains the migration logic for the `NV23` upgrade. + +use std::sync::Arc; + +use crate::networks::{ChainConfig, Height}; +use crate::shim::{ + address::Address, + clock::ChainEpoch, + machine::{BuiltinActor, BuiltinActorManifest}, + state_tree::{StateTree, StateTreeVersion}, +}; +use crate::utils::db::CborStoreExt as _; +use anyhow::Context; +use cid::Cid; + +use fvm_ipld_blockstore::Blockstore; + +use super::mining_reserve::MiningReservePostMigrator; +use super::{system, verifier::Verifier, SystemStateOld}; +use crate::state_migration::common::{migrators::nil_migrator, StateMigration}; + +impl StateMigration { + pub fn add_nv23_migrations( + &mut self, + store: &Arc, + state: &Cid, + new_manifest: &BuiltinActorManifest, + _chain_config: &ChainConfig, + ) -> anyhow::Result<()> { + let state_tree = StateTree::new_from_root(store.clone(), state)?; + let system_actor = state_tree.get_required_actor(&Address::SYSTEM_ACTOR)?; + let system_actor_state = store.get_cbor_required::(&system_actor.state)?; + + let current_manifest_data = system_actor_state.builtin_actors; + + let current_manifest = + BuiltinActorManifest::load_v1_actor_list(store, ¤t_manifest_data)?; + + for (name, code) in current_manifest.builtin_actors() { + let new_code = new_manifest.get(name)?; + self.add_migrator(code, nil_migrator(new_code)) + } + + self.add_migrator( + current_manifest.get_system(), + system::system_migrator(new_manifest), + ); + + self.add_post_migrator(Arc::new(MiningReservePostMigrator { + new_account_code_cid: new_manifest.get(BuiltinActor::Account)?, + new_multisig_code_cid: new_manifest.get(BuiltinActor::Multisig)?, + })); + + Ok(()) + } +} + +/// Runs the migration for `NV23`. Returns the new state root. +pub fn run_migration( + chain_config: &ChainConfig, + blockstore: &Arc, + state: &Cid, + epoch: ChainEpoch, +) -> anyhow::Result +where + DB: Blockstore + Send + Sync, +{ + let new_manifest_cid = chain_config + .height_infos + .get(&Height::Waffle) + .context("no height info for network version NV23")? + .bundle + .as_ref() + .context("no bundle for network version NV23")?; + + blockstore.get(new_manifest_cid)?.context(format!( + "manifest for network version NV23 not found in blockstore: {new_manifest_cid}" + ))?; + + // Add migration specification verification + let verifier = Arc::new(Verifier::default()); + + let new_manifest = BuiltinActorManifest::load_manifest(blockstore, new_manifest_cid)?; + let mut migration = StateMigration::::new(Some(verifier)); + migration.add_nv23_migrations(blockstore, state, &new_manifest, chain_config)?; + + let actors_in = StateTree::new_from_root(blockstore.clone(), state)?; + let actors_out = StateTree::new(blockstore.clone(), StateTreeVersion::V5)?; + let new_state = migration.migrate_state_tree(blockstore, epoch, actors_in, actors_out)?; + + Ok(new_state) +} diff --git a/src/state_migration/nv23/mining_reserve.rs b/src/state_migration/nv23/mining_reserve.rs new file mode 100644 index 000000000000..9f35d7af875f --- /dev/null +++ b/src/state_migration/nv23/mining_reserve.rs @@ -0,0 +1,48 @@ +// Copyright 2019-2024 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +//! This module contains the logic for converting the mining reserve actor to a key-less account +//! actor. See the [FIP-0085](https://fips.filecoin.io/FIPS/fip-0085.html) for more details. + +use crate::shim::address::Address; +use crate::shim::state_tree::ActorState; +use crate::state_migration::common::PostMigrator; +use crate::utils::db::CborStoreExt as _; +use cid::Cid; +use fvm_ipld_blockstore::Blockstore; + +pub struct MiningReservePostMigrator { + pub new_account_code_cid: Cid, + pub new_multisig_code_cid: Cid, +} + +impl PostMigrator for MiningReservePostMigrator { + fn post_migrate_state( + &self, + store: &BS, + actors_out: &mut crate::shim::state_tree::StateTree, + ) -> anyhow::Result<()> { + let f090_old_actor = actors_out.get_required_actor(&Address::RESERVE_ACTOR)?; + // only migrate f090 if it is a `multisig` + if f090_old_actor.code != self.new_multisig_code_cid { + return Ok(()); + } + let f090_new_state = fil_actor_account_state::v14::State { + address: Address::RESERVE_ACTOR.into(), + }; + let f090_new_state = store.put_cbor_default(&f090_new_state)?; + + actors_out.set_actor( + &Address::RESERVE_ACTOR, + ActorState::new( + self.new_account_code_cid, + f090_new_state, + f090_old_actor.balance.clone().into(), + f090_old_actor.sequence, + None, + ), + )?; + + Ok(()) + } +} diff --git a/src/state_migration/nv23/mod.rs b/src/state_migration/nv23/mod.rs new file mode 100644 index 000000000000..5741d8d416ce --- /dev/null +++ b/src/state_migration/nv23/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2019-2024 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +//! This module contains the migration logic for the `NV23` upgrade. +//! The corresponding Go implementation can be found here: +//! + +mod migration; +mod mining_reserve; + +/// Run migration for `NV23`. This should be the only exported method in this +/// module. +pub use migration::run_migration; + +use crate::{define_system_states, impl_system, impl_verifier}; + +define_system_states!( + fil_actor_system_state::v13::State, + fil_actor_system_state::v14::State +); + +impl_system!(); +impl_verifier!(); diff --git a/src/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index 68e183718d78..a7bf644789f9 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -7,6 +7,7 @@ use crate::chain_sync::{SyncConfig, SyncStage}; use crate::cli_shared::snapshot::TrustedVendor; use crate::daemon::db_util::{download_to, populate_eth_mappings}; use crate::db::{car::ManyCar, MemoryDB}; +use crate::eth::EthChainId as EthChainIdType; use crate::genesis::{get_network_name_from_genesis, read_genesis_header}; use crate::key_management::{KeyStore, KeyStoreConfig}; use crate::lotus_json::HasLotusJson; @@ -65,7 +66,7 @@ use types::BlockNumberOrPredefined; const COLLECTION_SAMPLE_SIZE: usize = 5; -const CALIBNET_CHAIN_ID: u32 = crate::networks::calibnet::ETH_CHAIN_ID as u32; +const CALIBNET_CHAIN_ID: EthChainIdType = crate::networks::calibnet::ETH_CHAIN_ID; #[derive(Debug, Subcommand)] #[allow(clippy::large_enum_variant)] @@ -128,7 +129,7 @@ pub enum ApiCommands { worker_address: Option
, /// Ethereum chain ID. Default to the calibnet chain ID. #[arg(long, default_value_t = CALIBNET_CHAIN_ID)] - eth_chain_id: u32, + eth_chain_id: EthChainIdType, }, } @@ -143,7 +144,7 @@ struct ApiTestFlags { max_concurrent_requests: usize, miner_address: Option
, worker_address: Option
, - eth_chain_id: u32, + eth_chain_id: EthChainIdType, } impl ApiCommands { @@ -1242,7 +1243,7 @@ fn eth_tests_with_tipset(shared_tipset: &Tipset) -> Vec { fn eth_state_tests_with_tipset( store: &Arc, shared_tipset: &Tipset, - eth_chain_id: u32, + eth_chain_id: EthChainIdType, ) -> anyhow::Result> { let mut tests = vec![];