1
1
//! Contains functions related to the migration script of Testnet78.
2
- use anyhow:: Context ;
3
- use cnidarium:: { Snapshot , StateDelta , Storage } ;
2
+ use cnidarium:: { Snapshot , StateDelta , StateWrite , Storage } ;
4
3
use futures:: TryStreamExt as _;
4
+ use futures:: { pin_mut, StreamExt } ;
5
5
use jmt:: RootHash ;
6
6
use penumbra_app:: app:: StateReadExt as _;
7
+ use penumbra_dex:: component:: PositionManager ;
8
+ use penumbra_dex:: lp:: position;
9
+ use penumbra_dex:: lp:: position:: Position ;
7
10
use penumbra_governance:: proposal_state:: State as ProposalState ;
8
11
use penumbra_governance:: Proposal ;
9
12
use penumbra_governance:: StateReadExt as _;
10
- use penumbra_governance:: StateWriteExt ;
13
+ use penumbra_governance:: StateWriteExt as _ ;
11
14
use penumbra_proto:: core:: component:: governance:: v1 as pb_governance;
12
- use penumbra_proto:: { StateReadProto as _ , StateWriteProto as _ } ;
15
+ use penumbra_proto:: { StateReadProto , StateWriteProto } ;
13
16
use penumbra_sct:: component:: clock:: EpochManager ;
14
- use penumbra_sct:: component:: clock:: EpochRead as _ ;
17
+ use penumbra_sct:: component:: clock:: EpochRead ;
15
18
use penumbra_stake:: validator:: Validator ;
16
19
use std:: path:: PathBuf ;
17
20
use tracing:: instrument;
@@ -41,32 +44,36 @@ use crate::testnet::generate::TestnetConfig;
41
44
/// - `client_id` (128 bytes)
42
45
/// * Governance Signaling Proposals:
43
46
/// - `commit hash` (255 bytes)
47
+ /// - Close and re-open all *open* positions so that they are re-indexed.
44
48
#[ instrument]
45
49
pub async fn migrate (
46
50
storage : Storage ,
47
51
pd_home : PathBuf ,
48
52
genesis_start : Option < tendermint:: time:: Time > ,
49
53
) -> anyhow:: Result < ( ) > {
50
- // Setup:
54
+ /* `Migration::prepare`: collect basic migration data, logging, initialize alt-storage if needed */
51
55
let initial_state = storage. latest_snapshot ( ) ;
56
+
52
57
let chain_id = initial_state. get_chain_id ( ) . await ?;
53
58
let root_hash = initial_state
54
59
. root_hash ( )
55
60
. await
56
61
. expect ( "chain state has a root hash" ) ;
57
- let pre_upgrade_root_hash : RootHash = root_hash . into ( ) ;
62
+
58
63
let pre_upgrade_height = initial_state
59
64
. get_block_height ( )
60
65
. await
61
66
. expect ( "chain state has a block height" ) ;
62
67
let post_upgrade_height = pre_upgrade_height. wrapping_add ( 1 ) ;
63
68
64
- // We initialize a `StateDelta` and start by reaching into the JMT for all entries matching the
65
- // swap execution prefix. Then, we write each entry to the nv-storage.
69
+ let pre_upgrade_root_hash: RootHash = root_hash. into ( ) ;
70
+
71
+ /* `Migration::migrate`: reach into the chain state and perform an offline state transition */
66
72
let mut delta = StateDelta :: new ( initial_state) ;
67
- tracing :: info! ( "beginning migration steps" ) ;
73
+
68
74
let ( migration_duration, post_upgrade_root_hash) = {
69
75
let start_time = std:: time:: SystemTime :: now ( ) ;
76
+
70
77
// Adjust the length of `Validator` fields.
71
78
truncate_validator_fields ( & mut delta) . await ?;
72
79
@@ -76,30 +83,26 @@ pub async fn migrate(
76
83
// Adjust the length of governance proposal outcome fields.
77
84
truncate_proposal_outcome_fields ( & mut delta) . await ?;
78
85
86
+ // Re-index all open positions.
87
+ reindex_dex_positions ( & mut delta) . await ?;
88
+
89
+ // Reset the application height and halt flag.
90
+ delta. ready_to_start ( ) ;
91
+ delta. put_block_height ( 0u64 ) ;
92
+
93
+ // Finally, commit the changes to the chain state.
79
94
let post_upgrade_root_hash = storage. commit_in_place ( delta) . await ?;
80
95
tracing:: info!( ?post_upgrade_root_hash, "post-migration root hash" ) ;
81
96
82
97
(
83
- start_time. elapsed ( ) . expect ( "start time not set" ) ,
98
+ start_time. elapsed ( ) . expect ( "start is set" ) ,
84
99
post_upgrade_root_hash,
85
100
)
86
101
} ;
87
- tracing:: info!( "completed migration steps" ) ;
88
-
89
- // Set halt bit to 0, so chain can start again.
90
- let migrated_state = storage. latest_snapshot ( ) ;
91
- let mut delta = StateDelta :: new ( migrated_state) ;
92
- delta. ready_to_start ( ) ;
93
- delta. put_block_height ( 0u64 ) ;
94
- let _ = storage
95
- . commit_in_place ( delta)
96
- . await
97
- . context ( "failed to reset halt bit" ) ?;
98
- storage. release ( ) . await ;
99
102
100
- // The migration is complete, now we need to generate a genesis file. To do this, we need
101
- // to lookup a validator view from the chain, and specify the post-upgrade app hash and
102
- // initial height.
103
+ tracing :: info! ( " migration completed, generating genesis and signing state..." ) ;
104
+
105
+ /* `Migration::complete`: the state transition has been performed, we prepare the checkpointed genesis and signing state */
103
106
let app_state = penumbra_app:: genesis:: Content {
104
107
chain_id,
105
108
..Default :: default ( )
@@ -110,22 +113,26 @@ pub async fn migrate(
110
113
. to_vec ( )
111
114
. try_into ( )
112
115
. expect ( "infaillible conversion" ) ;
116
+
113
117
genesis. initial_height = post_upgrade_height as i64 ;
114
118
genesis. genesis_time = genesis_start. unwrap_or_else ( || {
115
119
let now = tendermint:: time:: Time :: now ( ) ;
116
120
tracing:: info!( %now, "no genesis time provided, detecting a testing setup" ) ;
117
121
now
118
122
} ) ;
123
+
124
+ tracing:: info!( "generating checkpointed genesis" ) ;
119
125
let checkpoint = post_upgrade_root_hash. 0 . to_vec ( ) ;
120
126
let genesis = TestnetConfig :: make_checkpoint ( genesis, Some ( checkpoint) ) ;
121
127
128
+ tracing:: info!( "writing genesis to disk" ) ;
122
129
let genesis_json = serde_json:: to_string ( & genesis) . expect ( "can serialize genesis" ) ;
123
130
tracing:: info!( "genesis: {}" , genesis_json) ;
124
131
let genesis_path = pd_home. join ( "genesis.json" ) ;
125
132
std:: fs:: write ( genesis_path, genesis_json) . expect ( "can write genesis" ) ;
126
133
134
+ tracing:: info!( "updating signing state" ) ;
127
135
let validator_state_path = pd_home. join ( "priv_validator_state.json" ) ;
128
-
129
136
let fresh_validator_state = crate :: testnet:: generate:: TestnetValidator :: initial_state ( ) ;
130
137
std:: fs:: write ( validator_state_path, fresh_validator_state) . expect ( "can write validator state" ) ;
131
138
@@ -135,12 +142,38 @@ pub async fn migrate(
135
142
?pre_upgrade_root_hash,
136
143
?post_upgrade_root_hash,
137
144
duration = migration_duration. as_secs( ) ,
138
- "successful migration! "
145
+ "migration fully complete "
139
146
) ;
140
147
141
148
Ok ( ( ) )
142
149
}
143
150
151
+ async fn reindex_dex_positions ( delta : & mut StateDelta < Snapshot > ) -> anyhow:: Result < ( ) > {
152
+ tracing:: info!( "running dex re-indexing migration" ) ;
153
+ let prefix_key_lp = penumbra_dex:: state_key:: all_positions ( ) ;
154
+ let stream_all_lp = delta. prefix :: < Position > ( & prefix_key_lp) ;
155
+ let stream_open_lp = stream_all_lp. filter_map ( |entry| async {
156
+ match entry {
157
+ Ok ( ( _, lp) ) if lp. state == position:: State :: Opened => Some ( lp) ,
158
+ _ => None ,
159
+ }
160
+ } ) ;
161
+ pin_mut ! ( stream_open_lp) ;
162
+
163
+ while let Some ( lp) = stream_open_lp. next ( ) . await {
164
+ // Re-hash the position, since the key is a bech32 string.
165
+ let id = lp. id ( ) ;
166
+ // Close the position, adjusting all its index entries.
167
+ delta. close_position_by_id ( & id) . await ?;
168
+ // Erase the position from the state, so that we circumvent the `update_position` guard.
169
+ delta. delete ( penumbra_dex:: state_key:: position_by_id ( & id) ) ;
170
+ // Open a position with the adjusted indexing logic.
171
+ delta. open_position ( lp) . await ?;
172
+ }
173
+ tracing:: info!( "completed dex migration" ) ;
174
+ Ok ( ( ) )
175
+ }
176
+
144
177
/// * Validators:
145
178
/// - `name` (140 bytes)
146
179
/// - `website` (70 bytes)
0 commit comments