Browse Source

feat(target_chains/starknet): handle wormhole guardian set upgrade VAA (#1550)

* feat(target_chains/starknet): handle wormhole guardian set upgrade VAA

* test(target_chains/starknet): add failing tests for governance

* doc(target_chains/starknet): add comment about wormhole governance
Pavel Strakhov 1 year ago
parent
commit
9dddd3d1e7

+ 31 - 1
target_chains/starknet/contracts/src/wormhole.cairo

@@ -2,10 +2,16 @@ use super::byte_array::ByteArray;
 use core::starknet::secp256_trait::Signature;
 use pyth::util::UnwrapWithFelt252;
 
+mod governance;
+
 #[starknet::interface]
 pub trait IWormhole<T> {
     fn submit_new_guardian_set(ref self: T, set_index: u32, guardians: Array<felt252>);
     fn parse_and_verify_vm(self: @T, encoded_vm: ByteArray) -> VerifiedVM;
+
+    // We don't need to implement other governance actions for now.
+    // Instead of upgrading the Wormhole contract, we can switch to another Wormhole address
+    // in the Pyth contract.
     fn submit_new_guardian_set2(ref self: T, encoded_vm: ByteArray);
 }
 
@@ -32,6 +38,10 @@ pub struct VerifiedVM {
 
 #[derive(Copy, Drop, Debug, Serde, PartialEq)]
 pub enum GovernanceError {
+    InvalidModule,
+    InvalidAction,
+    InvalidChainId,
+    TrailingData,
     NotCurrentGuardianSet,
     WrongChain,
     WrongContract,
@@ -41,6 +51,10 @@ pub enum GovernanceError {
 impl GovernanceErrorIntoFelt252 of Into<GovernanceError, felt252> {
     fn into(self: GovernanceError) -> felt252 {
         match self {
+            GovernanceError::InvalidModule => 'invalid module',
+            GovernanceError::InvalidAction => 'invalid action',
+            GovernanceError::InvalidChainId => 'invalid chain ID',
+            GovernanceError::TrailingData => 'trailing data',
             GovernanceError::NotCurrentGuardianSet => 'not signed by current guard.set',
             GovernanceError::WrongChain => 'wrong governance chain',
             GovernanceError::WrongContract => 'wrong governance contract',
@@ -113,6 +127,7 @@ mod wormhole {
         VerifiedVM, IWormhole, GuardianSignature, quorum, ParseAndVerifyVmError,
         SubmitNewGuardianSetError, GovernanceError
     };
+    use super::governance;
     use pyth::reader::{Reader, ReaderImpl};
     use pyth::byte_array::ByteArray;
     use core::starknet::secp256_trait::{Signature, recover_public_key, Secp256PointTrait};
@@ -266,8 +281,23 @@ mod wormhole {
         fn submit_new_guardian_set2(ref self: ContractState, encoded_vm: ByteArray) {
             let vm = self.parse_and_verify_vm(encoded_vm);
             self.verify_governance_vm(@vm);
+            let mut reader = ReaderImpl::new(vm.payload);
+            let header = governance::parse_header(ref reader);
+            if header.action != governance::Action::GuardianSetUpgrade {
+                panic_with_felt252(GovernanceError::InvalidAction.into());
+            }
+            if header.chain_id != 0 && header.chain_id != self.chain_id.read() {
+                panic_with_felt252(GovernanceError::InvalidChainId.into());
+            }
+            let new_set = governance::parse_new_guardian_set(ref reader);
+            let current_set_index = self.current_guardian_set_index.read();
+            if new_set.set_index != current_set_index + 1 {
+                panic_with_felt252(SubmitNewGuardianSetError::InvalidGuardianSetSequence.into());
+            }
+            store_guardian_set(ref self, new_set.set_index, @new_set.keys);
+            expire_guardian_set(ref self, current_set_index, get_block_timestamp());
+
             self.consumed_governance_actions.write(vm.hash, true);
-            panic_with_felt252('todo: parse vm')
         }
     }
 

+ 69 - 0
target_chains/starknet/contracts/src/wormhole/governance.cairo

@@ -0,0 +1,69 @@
+use pyth::reader::{Reader, ReaderImpl};
+use pyth::util::UNEXPECTED_OVERFLOW;
+use super::GovernanceError;
+use core::panic_with_felt252;
+
+// "Core" (left padded)
+const MODULE: u256 = 0x00000000000000000000000000000000000000000000000000000000436f7265;
+
+#[derive(Drop, Debug, Copy, PartialEq)]
+pub enum Action {
+    ContractUpgrade,
+    GuardianSetUpgrade,
+    SetMessageFee,
+    TransferFees,
+    RecoverChainId,
+}
+
+impl U8TryIntoAction of TryInto<u8, Action> {
+    fn try_into(self: u8) -> Option<Action> {
+        let value = match self {
+            0 => { return Option::None; },
+            1 => Action::ContractUpgrade,
+            2 => Action::GuardianSetUpgrade,
+            3 => Action::SetMessageFee,
+            4 => Action::TransferFees,
+            5 => Action::RecoverChainId,
+            _ => { return Option::None; },
+        };
+        Option::Some(value)
+    }
+}
+
+#[derive(Drop, Debug, Clone)]
+pub struct Header {
+    pub module: u256,
+    pub action: Action,
+    pub chain_id: u16,
+}
+
+#[derive(Drop, Debug, Clone)]
+pub struct NewGuardianSet {
+    pub set_index: u32,
+    pub keys: Array<felt252>,
+}
+
+pub fn parse_header(ref reader: Reader) -> Header {
+    let module = reader.read_u256();
+    if module != MODULE {
+        panic_with_felt252(GovernanceError::InvalidModule.into());
+    }
+
+    let action = reader.read_u8().try_into().expect(GovernanceError::InvalidAction.into());
+    let chain_id = reader.read_u16();
+    Header { module, action, chain_id }
+}
+
+pub fn parse_new_guardian_set(ref reader: Reader) -> NewGuardianSet {
+    let set_index = reader.read_u32();
+    let num_guardians = reader.read_u8();
+    let mut i = 0;
+    let mut keys = array![];
+    while i < num_guardians {
+        let key = reader.read_u160();
+        keys.append(key.try_into().expect(UNEXPECTED_OVERFLOW));
+        i += 1;
+    };
+    assert(reader.len() == 0, GovernanceError::TrailingData.into());
+    NewGuardianSet { set_index, keys }
+}

+ 179 - 14
target_chains/starknet/contracts/tests/wormhole.cairo

@@ -41,7 +41,8 @@ fn test_parse_and_verify_vm_rejects_corrupted_vm(pos: usize, random1: usize, ran
     let owner = 'owner'.try_into().unwrap();
     let dispatcher = deploy_and_init(owner);
 
-    let vm = dispatcher.parse_and_verify_vm(corrupted_vm(pos, random1, random2));
+    let input = corrupted_vm(good_vm1(), pos, random1, random2);
+    let vm = dispatcher.parse_and_verify_vm(input);
     println!("no error, output: {:?}", vm);
 }
 
@@ -57,16 +58,15 @@ fn test_submit_guardian_set_rejects_wrong_owner() {
 }
 
 #[test]
-#[should_panic(expected: ('invalid guardian set sequence',))]
+#[should_panic(expected: ('invalid guardian set index',))]
 fn test_submit_guardian_set_rejects_wrong_index() {
     let owner = 'owner'.try_into().unwrap();
     let dispatcher = deploy(
         owner, guardian_set0(), CHAIN_ID, GOVERNANCE_CHAIN_ID, GOVERNANCE_CONTRACT
     );
 
-    start_prank(CheatTarget::One(dispatcher.contract_address), owner.try_into().unwrap());
-    dispatcher.submit_new_guardian_set(1, guardian_set1());
-    dispatcher.submit_new_guardian_set(3, guardian_set3());
+    dispatcher.submit_new_guardian_set2(governance_upgrade_vm1());
+    dispatcher.submit_new_guardian_set2(governance_upgrade_vm3());
 }
 
 #[test]
@@ -89,14 +89,31 @@ fn test_submit_guardian_set_rejects_empty() {
 }
 
 #[test]
-#[should_panic(expected: ('todo: parse vm',))]
-fn test_guardian_set_upgrade() {
+#[fuzzer(runs: 100, seed: 0)]
+#[should_panic]
+fn test_submit_guardian_set_rejects_corrupted(pos: usize, random1: usize, random2: usize) {
+    let owner = 'owner'.try_into().unwrap();
+    let dispatcher = deploy(
+        owner, guardian_set0(), CHAIN_ID, GOVERNANCE_CHAIN_ID, GOVERNANCE_CONTRACT
+    );
+
+    let vm = corrupted_vm(governance_upgrade_vm1(), pos, random1, random2);
+    dispatcher.submit_new_guardian_set2(vm);
+}
+
+#[test]
+#[should_panic(expected: ('wrong governance chain',))]
+fn test_submit_guardian_set_rejects_non_governance(pos: usize, random1: usize, random2: usize) {
     let owner = 'owner'.try_into().unwrap();
     let dispatcher = deploy(
         owner, guardian_set0(), CHAIN_ID, GOVERNANCE_CHAIN_ID, GOVERNANCE_CONTRACT
     );
 
     dispatcher.submit_new_guardian_set2(governance_upgrade_vm1());
+    dispatcher.submit_new_guardian_set2(governance_upgrade_vm2());
+    dispatcher.submit_new_guardian_set2(governance_upgrade_vm3());
+
+    dispatcher.submit_new_guardian_set2(good_vm1());
 }
 
 fn deploy(
@@ -125,19 +142,17 @@ pub fn deploy_and_init(owner: ContractAddress) -> IWormholeDispatcher {
         owner, guardian_set0(), CHAIN_ID, GOVERNANCE_CHAIN_ID, GOVERNANCE_CONTRACT
     );
 
-    start_prank(CheatTarget::One(dispatcher.contract_address), owner.try_into().unwrap());
-    dispatcher.submit_new_guardian_set(1, guardian_set1());
-    dispatcher.submit_new_guardian_set(2, guardian_set2());
-    dispatcher.submit_new_guardian_set(3, guardian_set3());
-    stop_prank(CheatTarget::One(dispatcher.contract_address));
+    dispatcher.submit_new_guardian_set2(governance_upgrade_vm1());
+    dispatcher.submit_new_guardian_set2(governance_upgrade_vm2());
+    dispatcher.submit_new_guardian_set2(governance_upgrade_vm3());
+    dispatcher.submit_new_guardian_set2(governance_upgrade_vm4());
 
     dispatcher
 }
 
-fn corrupted_vm(pos: usize, random1: usize, random2: usize) -> ByteArray {
+fn corrupted_vm(mut real_data: ByteArray, pos: usize, random1: usize, random2: usize) -> ByteArray {
     let mut new_data = array![];
 
-    let mut real_data = good_vm1();
     // Make sure we select a position not on the last item because
     // we didn't implement corrupting an incomplete bytes31.
     let pos = pos % (real_data.len() - 31);
@@ -339,3 +354,153 @@ fn governance_upgrade_vm1() -> ByteArray {
     ];
     ByteArrayImpl::new(array_felt252_to_bytes31(bytes), 16)
 }
+
+fn governance_upgrade_vm2() -> ByteArray {
+    let bytes = array![
+        1766847065210651126944505525521222069599854288126726949998063840465138797,
+        39054013088470866893599813322035661048501117089555726788687392536164861911,
+        186918267413056900890218521593203545230034250983266377769400670103688217224,
+        54214750922097681971590495378823998039261772575502204791108963815396679538,
+        248994008232667872698758486001506749115615219491023036208140426934463230397,
+        224235483823871042187452194579994340291351644933258737665365374081962645784,
+        129444929990547403397151941456764812818993218972657847255833740800106200260,
+        318548597134783137700627869311159664823693239605331630210202210299165477657,
+        308852933010951129895011963941866000261904600807292935694851016610643657184,
+        57272874228621364335170723193404742446392708606247574725663969156507973216,
+        268057363923565984687253533797066429881117576606682526627284795527707196557,
+        421894189151847402000239734668088381785344768284464330085711322870200424121,
+        387584417395337067059819722404321580961380603778956902593594676080614899975,
+        443523131755342451570503958659975367050637907361274652611595274647186073286,
+        375107813087591446268414166006799680982485924290770541964399283524664437852,
+        269085314426821465871247165234113878276592898426373369094610290261431112145,
+        394348694527460459816454348661249546781513091938003106009521096332701847735,
+        125670844183465056159554034633959680574572737212268749779705635378223489518,
+        35053869475614771227400345921974210525173525784259894123687028214330135561,
+        57544237843860512274491447149877290860624174166427313971286819807773907946,
+        110681468147560408039747352809294082929396639199393789980441736520638055418,
+        45709869872872997180086402576677640909777820941436708911954532772405320395,
+        339910511168418517917975736269171135752028257685502872671902330279073260362,
+        76482764517489607955778008190826845581092838692650194719207882266659669490,
+        443663869577220861680293443959666949893574779475752540587040489501289361777,
+        231057874919577223790659840464529054850239319545221055959374917590157019925,
+        309066533217885002074480704480667619809952056265738927105682076502747220549,
+        212379788814604791028007106821871908074818251907335322546331543385945316762,
+        165408661499085325620077702639227003047567884011538988728381864749733773312,
+        29852013947978990147012099107546124222651092940097518043136,
+        5874446556610227706402640854088357165514903,
+        314635470832203526600706472223155382046271943513513368538979543914002758100,
+        289993023590817330918274026889451152915026890048318491140264484864242055689,
+        211265316833000774821515110003986084297271807500310630074520699505436206838,
+        314620948986744608212517578488307826224331071350776523303159889004405167502,
+        242768143829057016675085776170635413106817756232919004913342240722183648628,
+        289318220340670045883106021427202666948428587921558828582664470923483208386,
+        254304247593881109676724582609273741670949040469906895867342151706444640548,
+        324707984060675446628128892371664948354047882542253609514703956739624414429,
+        125786084546320950738753348592393927755418642173185609412108154831520915923,
+        192033422676298173731756291271054199566981168481817292625435767748408605264,
+        70237018464728620254434305961956673950089621204502627373468857093940647376,
+        75218391584551901010047495874303520775865073092730040058902770251005073864,
+        13453,
+    ];
+    ByteArrayImpl::new(array_felt252_to_bytes31(bytes), 2)
+}
+
+fn governance_upgrade_vm3() -> ByteArray {
+    let bytes = array![
+        1766847065622031860560134801367788401015571316785630090767859240961980367,
+        408239335069601434456324970231880063141100099721451058487412176729277688481,
+        239499689753305520381601038928513796227317320911002802551444547638809838552,
+        377515301744513788082523380503265415136454305699441419871183462725292421897,
+        293792427782284265052215516935690252661713253004854348809536189867737815900,
+        307592266914902727916633292174670243188255704510048236277225897328900269063,
+        127373290139474278928992577974696343462455769904046193125018730285162391253,
+        391788800785481654990215164673817619378887263909639120513493454202816019863,
+        410413307118599096084169722886408584518140871169074821252461819158667354254,
+        18837648490111595970198935552266546643395427923804444528866768515056419823,
+        29964034682984173558839379357821569529808274426015494950430380078317881569,
+        86017868501532670528023530422115758730056738654625156800662778409971102579,
+        316587967137295297243489759859408971025377360462781809717927347025414193161,
+        412080542369789462767669836400697110505430973769191785499739175360336337147,
+        342817079347905714229318925597762381802367663565411998187223317628701911440,
+        323381353160339090772037140072061985169258958022395380273676898316834639836,
+        199931590715070935127318740956564588449721873695471932311700469202637695100,
+        53310522180389647586576928116330850824055549848985438538201222342553700451,
+        387322343922164253479438966163491855981414317104760621828688810466847848718,
+        81609701542274539489711635515209037026645626576756528749469616228397567798,
+        182108205861564989333892774796475580877981373947799860454217397980367659628,
+        21549663410658134468902761710868642321546772465973442277960059676129502668,
+        189434039785735939400321781125039794740638779195156759980704929066694157130,
+        52255833533187953003213955242027099690232530588872309460610106220279805641,
+        197105018621162723372171195601447549272902142615124680111298974553437412361,
+        243585516151555343004264928593678764289083751554802049062044286334698216184,
+        98577806073901898829375415748245478967425496216912736575886605480181121443,
+        92916551389967933235240931764170084503123511470557201449603712010638670912,
+        279247190794260779926452059689914005511524938154821508635388069101252378624,
+        27765181507524306000048567556593270127801507143251178553344,
+        5874446556610227706402640926145951203442839,
+        314635470832203526600706472223155382046271943513513368538979543914002758100,
+        289993023590817330918274026889451152915026890048318491140264484864242055689,
+        211265316833000774821515110003986084297271807500310630074520699505436206838,
+        314620948986744608212517578488307826224331071350776523303159889004405167502,
+        242768143829057016675085658054156069029173843566452718977789980910319968372,
+        289318220340670045883106021427202666948428587921558828582664470923483208386,
+        254304247593881109676724582609273741670949040469906895867342151706444640548,
+        324707984060675446628128892371664948354047882542253609514703956739624414429,
+        125786084546320950738753348592393927755418642173185609412108154831520915923,
+        192033422676298173731756291271054199566981168481817292625435767748408605264,
+        70237018464728620254434305961956673950089621204502627373468857093940647376,
+        75218391584551901010047495874303520775865073092730040058902770251005073864,
+        13453,
+    ];
+    ByteArrayImpl::new(array_felt252_to_bytes31(bytes), 2)
+}
+
+fn governance_upgrade_vm4() -> ByteArray {
+    let bytes = array![
+        1766847066033426987337757245669159273063358729535478806850006662056807068,
+        191023158244075433218055661747029015323596061316379687901032561397223546211,
+        30156550775609732785124128084945604136341937408029517653427049258063209215,
+        301841618969457377999846355946508544313297803407604349411042057045510372286,
+        399879387152070823070522891203531321261797829310211644637928969034931151834,
+        1184971666775858810527395126763859219514013163556756790208661779020321698,
+        427827873217506136303198988655697899138087317492051993053159867826892618987,
+        55439109913191967501571602277685262841453050617358377329061538069328212552,
+        34944602254693785869427132065664922748183924456022812505745784482260734500,
+        50091615215549712387991200985741575718080363004681463525186508796585379155,
+        265247833149227842278059484961926330281584344437952973839486092319885300192,
+        421631446041795295328070636491346018953171276542115189082171457479754499396,
+        59057903625576869235407103565877017330396402246452653660114888284545941770,
+        315797852826246435174946736461540321579373154656484006452063031513301027405,
+        9521420622979958910372839981791309896262328383324674284772682980734269170,
+        272964069264268937653695089515793248726920319914036642027008415285406913245,
+        194708434228888226032102758315234166672190899487218971410889219134701358728,
+        117864954129109327302856065706421701676973859697066630532570005860486924893,
+        323457021720552374478769194145226061243431674370101604382965685057422991463,
+        327482733702858147057975319784026874245182397914737119038454598086198587150,
+        159726033816658034104416471293601013976445904149240898589368461412472508473,
+        165970343982649234398221341351816767302457220504375238905210573566962780340,
+        66269488760319836583658182431744051236825244016843316092957806563966254500,
+        360882001282595740056823749884962228392082962172369522212117195988772429063,
+        202692667772209236945884489592750537635169234501360011152939202347962132650,
+        407257364829649465305126488148712878739144584682351279109461295389594525334,
+        270499607712829989691415988895838806019492861138165540862008308077962735002,
+        388443296961168536186587069708212659389994895697827691755155284015603161464,
+        45068266527940236008536134081672474027695203549460934893262212861351952384,
+        31319268777966350508118557206583844424308993254125039779840,
+        5874446556610227706402640998203302487747647,
+        204224545225244051821590480758420624947979343122083461045877549162059250132,
+        289993023590817330918274026889451152915026890048318491140264484864242055689,
+        211265316833000774821515110003986084297271807500310630074520699505436206838,
+        314620948986744608212517578488307826224331071350776523303159889004405167502,
+        242768143829057016675085658054156069029173843566452718977789980910319968372,
+        289318220340670045883106021427202666948428587921558828582664470923483208386,
+        254304247593881109676724582609273741670949040469906895867342151706444640548,
+        324707984060675446628128892371664948354047882542253609514703956739624414429,
+        125786084546320950738753348592393927755418642173185609412108154831520915923,
+        192033422676298173731756291271054199566981168481817292625435767748408605264,
+        70237018464728620254434305961956673950089621204502627373468857093940647376,
+        75218391584551901010047495874303520775865073092730040058902770251005073864,
+        13453,
+    ];
+    ByteArrayImpl::new(array_felt252_to_bytes31(bytes), 2)
+}