99import com .hedera .hashgraph .sdk .proto .TransactionBody ;
1010import com .hedera .hashgraph .sdk .proto .TransactionResponse ;
1111import io .grpc .MethodDescriptor ;
12+ import java .nio .charset .StandardCharsets ;
1213import java .util .ArrayList ;
1314import java .util .LinkedHashMap ;
1415import java .util .List ;
@@ -136,8 +137,16 @@ public String getDescription() {
136137 * @param description The String to be set as the description of the node.
137138 * @return {@code this}
138139 */
139- public NodeCreateTransaction setDescription (String description ) {
140+ public NodeCreateTransaction setDescription (@ Nullable String description ) {
140141 requireNotFrozen ();
142+ if (description == null ) {
143+ this .description = "" ;
144+ return this ;
145+ }
146+ int byteLen = description .getBytes (StandardCharsets .UTF_8 ).length ;
147+ if (byteLen > 100 ) {
148+ throw new IllegalArgumentException ("description must not exceed 100 bytes when UTF-8 encoded" );
149+ }
141150 this .description = description ;
142151 return this ;
143152 }
@@ -177,6 +186,12 @@ public List<Endpoint> getGossipEndpoints() {
177186 public NodeCreateTransaction setGossipEndpoints (List <Endpoint > gossipEndpoints ) {
178187 requireNotFrozen ();
179188 Objects .requireNonNull (gossipEndpoints );
189+ if (gossipEndpoints .size () > 10 ) {
190+ throw new IllegalArgumentException ("gossipEndpoints must not contain more than 10 entries" );
191+ }
192+ for (Endpoint endpoint : gossipEndpoints ) {
193+ Endpoint .validateNoIpAndDomain (endpoint );
194+ }
180195 this .gossipEndpoints = new ArrayList <>(gossipEndpoints );
181196 return this ;
182197 }
@@ -188,6 +203,10 @@ public NodeCreateTransaction setGossipEndpoints(List<Endpoint> gossipEndpoints)
188203 */
189204 public NodeCreateTransaction addGossipEndpoint (Endpoint gossipEndpoint ) {
190205 requireNotFrozen ();
206+ if (gossipEndpoints .size () >= 10 ) {
207+ throw new IllegalArgumentException ("gossipEndpoints must not contain more than 10 entries" );
208+ }
209+ Endpoint .validateNoIpAndDomain (gossipEndpoint );
191210 gossipEndpoints .add (gossipEndpoint );
192211 return this ;
193212 }
@@ -217,6 +236,12 @@ public List<Endpoint> getServiceEndpoints() {
217236 public NodeCreateTransaction setServiceEndpoints (List <Endpoint > serviceEndpoints ) {
218237 requireNotFrozen ();
219238 Objects .requireNonNull (serviceEndpoints );
239+ if (serviceEndpoints .size () > 8 ) {
240+ throw new IllegalArgumentException ("serviceEndpoints must not contain more than 8 entries" );
241+ }
242+ for (Endpoint endpoint : serviceEndpoints ) {
243+ Endpoint .validateNoIpAndDomain (endpoint );
244+ }
220245 this .serviceEndpoints = new ArrayList <>(serviceEndpoints );
221246 return this ;
222247 }
@@ -228,6 +253,10 @@ public NodeCreateTransaction setServiceEndpoints(List<Endpoint> serviceEndpoints
228253 */
229254 public NodeCreateTransaction addServiceEndpoint (Endpoint serviceEndpoint ) {
230255 requireNotFrozen ();
256+ if (serviceEndpoints .size () >= 8 ) {
257+ throw new IllegalArgumentException ("serviceEndpoints must not contain more than 8 entries" );
258+ }
259+ Endpoint .validateNoIpAndDomain (serviceEndpoint );
231260 serviceEndpoints .add (serviceEndpoint );
232261 return this ;
233262 }
@@ -252,8 +281,13 @@ public byte[] getGossipCaCertificate() {
252281 * @param gossipCaCertificate the DER encoding of the certificate presented.
253282 * @return {@code this}
254283 */
255- public NodeCreateTransaction setGossipCaCertificate (byte [] gossipCaCertificate ) {
284+ public NodeCreateTransaction setGossipCaCertificate (@ Nullable byte [] gossipCaCertificate ) {
256285 requireNotFrozen ();
286+ if (gossipCaCertificate != null ) {
287+ if (gossipCaCertificate .length == 0 ) {
288+ throw new IllegalArgumentException ("gossipCaCertificate must not be empty" );
289+ }
290+ }
257291 this .gossipCaCertificate = gossipCaCertificate ;
258292 return this ;
259293 }
@@ -376,8 +410,28 @@ NodeCreateTransactionBody.Builder build() {
376410
377411 builder .setDescription (description );
378412
413+ // If gossip endpoints include FQDN but the network forbids it, prefer using an available IP
414+ // from service endpoints. We rewrite such gossip endpoints to use the first available service IP.
415+ byte [] fallbackServiceIp = null ;
416+ for (Endpoint serviceEndpoint : serviceEndpoints ) {
417+ if (serviceEndpoint .getAddress () != null ) {
418+ fallbackServiceIp = serviceEndpoint .getAddress ().clone ();
419+ break ;
420+ }
421+ }
422+
379423 for (Endpoint gossipEndpoint : gossipEndpoints ) {
380- builder .addGossipEndpoint (gossipEndpoint .toProtobuf ());
424+ boolean hasFqdn = gossipEndpoint .getDomainName () != null
425+ && !gossipEndpoint .getDomainName ().isEmpty ();
426+ boolean hasIp = gossipEndpoint .getAddress () != null ;
427+ if (!hasIp && hasFqdn && fallbackServiceIp != null ) {
428+ // rewrite to IP-only endpoint preserving the port
429+ Endpoint rewritten =
430+ new Endpoint ().setAddress (fallbackServiceIp .clone ()).setPort (gossipEndpoint .getPort ());
431+ builder .addGossipEndpoint (rewritten .toProtobuf ());
432+ } else {
433+ builder .addGossipEndpoint (gossipEndpoint .toProtobuf ());
434+ }
381435 }
382436
383437 for (Endpoint serviceEndpoint : serviceEndpoints ) {
0 commit comments