@@ -28,6 +28,8 @@ public class StorageConfig {
2828 private CheckpointConfig checkpoint = new CheckpointConfig ();
2929 private SnapshotConfig snapshot = new SnapshotConfig ();
3030 private TxCacheConfig txCache = new TxCacheConfig ();
31+ // ConfigBeanFactory requires all bean fields present per item, so we parse manually.
32+ @ Setter (lombok .AccessLevel .NONE )
3133 private List <PropertyConfig > properties = new ArrayList <>();
3234
3335 // merkleRoot is a nested object (e.g. { reward-vi = "hash..." }) not a string.
@@ -54,6 +56,7 @@ public class StorageConfig {
5456 @ Getter
5557 @ Setter
5658 public static class DbConfig {
59+
5760 private String engine = "LEVELDB" ;
5861 private boolean sync = false ;
5962 private String directory = "database" ;
@@ -62,6 +65,7 @@ public static class DbConfig {
6265 @ Getter
6366 @ Setter
6467 public static class TransHistoryConfig {
68+
6569 // "switch" is a reserved Java keyword; ConfigBeanFactory calls setSwitch() which works fine
6670 @ Getter (lombok .AccessLevel .NONE )
6771 @ Setter (lombok .AccessLevel .NONE )
@@ -79,6 +83,7 @@ public void setSwitch(String v) {
7983 @ Getter
8084 @ Setter
8185 public static class DbSettingsConfig {
86+
8287 private int levelNumber = 7 ;
8388 private int compactThreads = 0 ; // 0 = auto: max(availableProcessors, 1)
8489 private int blocksize = 16 ;
@@ -100,25 +105,29 @@ void postProcess() {
100105 @ Getter
101106 @ Setter
102107 public static class BalanceConfig {
108+
103109 private HistoryConfig history = new HistoryConfig ();
104110
105111 @ Getter
106112 @ Setter
107113 public static class HistoryConfig {
114+
108115 private boolean lookup = false ;
109116 }
110117 }
111118
112119 @ Getter
113120 @ Setter
114121 public static class CheckpointConfig {
122+
115123 private int version = 1 ;
116124 private boolean sync = true ;
117125 }
118126
119127 @ Getter
120128 @ Setter
121129 public static class SnapshotConfig {
130+
122131 private int maxFlushCount = 1 ;
123132
124133 // Reject out-of-range values. Mirrors develop Storage.getSnapshotMaxFlushCountFromConfig.
@@ -135,6 +144,7 @@ void postProcess() {
135144 @ Getter
136145 @ Setter
137146 public static class TxCacheConfig {
147+
138148 private int estimatedTransactions = 1000 ;
139149 private boolean initOptimization = false ;
140150
@@ -148,19 +158,14 @@ void postProcess() {
148158 }
149159 }
150160
161+ // A named database entry: name/path plus the optional LevelDB option overrides
162+ // inherited from DbOptionOverride (boxed types, null = "inherit per-tier defaults").
151163 @ Getter
152164 @ Setter
153- public static class PropertyConfig {
165+ public static class PropertyConfig extends DbOptionOverride {
166+
154167 private String name = "" ;
155168 private String path = "" ;
156- private boolean createIfMissing = true ;
157- private boolean paranoidChecks = true ;
158- private boolean verifyChecksums = true ;
159- private int compressionType = 1 ;
160- private int blockSize = 4096 ;
161- private int writeBufferSize = 10485760 ;
162- private long cacheSize = 10485760 ;
163- private int maxOpenFiles = 100 ;
164169 }
165170
166171 // Defaults come from reference.conf (loaded globally via Configuration.java)
@@ -170,6 +175,7 @@ public static StorageConfig fromConfig(Config config) {
170175
171176 StorageConfig sc = ConfigBeanFactory .create (section , StorageConfig .class );
172177 sc .rawStorageConfig = section ;
178+ sc .properties = readProperties (section );
173179
174180 // Read optional LevelDB option overrides (default, defaultM, defaultL).
175181 sc .defaultDbOption = readDbOption (section , "default" );
@@ -187,45 +193,17 @@ public static StorageConfig fromConfig(Config config) {
187193 @ Getter
188194 @ Setter
189195 public static class DbOptionOverride {
190- private Boolean createIfMissing ;
191- private Boolean paranoidChecks ;
192- private Boolean verifyChecksums ;
193- private Integer compressionType ;
196+
194197 private Integer blockSize ;
195198 private Integer writeBufferSize ;
196199 private Long cacheSize ;
197200 private Integer maxOpenFiles ;
198201 }
199202
200- // Read optional LevelDB option override (default/defaultM/defaultL).
201- // Not bean-bound: users may only set a subset of keys (e.g. just maxOpenFiles),
202- // ConfigBeanFactory requires all fields present so partial overrides would fail.
203- private static DbOptionOverride readDbOption (Config section , String key ) {
204- if (!section .hasPath (key )) {
205- return null ;
206- }
207- ConfigObject conf = section .getObject (key );
208- DbOptionOverride o = new DbOptionOverride ();
209- if (conf .containsKey ("createIfMissing" )) {
210- o .setCreateIfMissing (
211- Boolean .parseBoolean (conf .get ("createIfMissing" ).unwrapped ().toString ()));
212- }
213- if (conf .containsKey ("paranoidChecks" )) {
214- o .setParanoidChecks (
215- Boolean .parseBoolean (conf .get ("paranoidChecks" ).unwrapped ().toString ()));
216- }
217- if (conf .containsKey ("verifyChecksums" )) {
218- o .setVerifyChecksums (
219- Boolean .parseBoolean (conf .get ("verifyChecksums" ).unwrapped ().toString ()));
220- }
221- if (conf .containsKey ("compressionType" )) {
222- String param = conf .get ("compressionType" ).unwrapped ().toString ();
223- try {
224- o .setCompressionType (Integer .parseInt (param ));
225- } catch (NumberFormatException e ) {
226- throwIllegalArgumentException ("compressionType" , Integer .class , param );
227- }
228- }
203+ // Shared LevelDB option parser used by both readDbOption and readProperties.
204+ // Fills the given target (boxed fields, null means "not specified by user") so the
205+ // same parser can populate a plain DbOptionOverride or a PropertyConfig (which extends it).
206+ private static void readLevelDbOptions (ConfigObject conf , DbOptionOverride o ) {
229207 if (conf .containsKey ("blockSize" )) {
230208 String param = conf .get ("blockSize" ).unwrapped ().toString ();
231209 try {
@@ -258,9 +236,43 @@ private static DbOptionOverride readDbOption(Config section, String key) {
258236 throwIllegalArgumentException ("maxOpenFiles" , Integer .class , param );
259237 }
260238 }
239+ }
240+
241+ // Read optional LevelDB option override for default/defaultM/defaultL keys.
242+ private static DbOptionOverride readDbOption (Config section , String key ) {
243+ if (!section .hasPath (key )) {
244+ return null ;
245+ }
246+ DbOptionOverride o = new DbOptionOverride ();
247+ readLevelDbOptions (section .getObject (key ), o );
261248 return o ;
262249 }
263250
251+ // Parse storage.properties list manually: ConfigBeanFactory requires every bean field to be
252+ // present in each list item, but name+path-only entries (all LevelDB opts commented out) are
253+ // valid — missing fields fall back to PropertyConfig Java defaults.
254+ private static List <PropertyConfig > readProperties (Config section ) {
255+ if (!section .hasPath ("properties" )) {
256+ return new ArrayList <>();
257+ }
258+ List <? extends ConfigObject > items = section .getObjectList ("properties" );
259+ List <PropertyConfig > result = new ArrayList <>(items .size ());
260+ for (ConfigObject obj : items ) {
261+ PropertyConfig p = new PropertyConfig ();
262+ if (obj .containsKey ("name" )) {
263+ p .setName (obj .get ("name" ).unwrapped ().toString ());
264+ }
265+ if (obj .containsKey ("path" )) {
266+ p .setPath (obj .get ("path" ).unwrapped ().toString ());
267+ }
268+ // Boxed nullable fields: unset options stay null so they inherit the per-tier
269+ // defaults applied by newDefaultDbOptions instead of resetting them.
270+ readLevelDbOptions (obj , p );
271+ result .add (p );
272+ }
273+ return result ;
274+ }
275+
264276 private static void throwIllegalArgumentException (String param , Class <?> type , String actual ) {
265277 throw new IllegalArgumentException (
266278 String .format ("[storage.properties] %s must be %s type, actual: %s." ,
0 commit comments