Skip to content

Commit

Permalink
Merge branch 'develop' into 14255-reusable-gradle-config
Browse files Browse the repository at this point in the history
Signed-off-by: Jendrik Johannes <jendrik.johannes@gmail.com>
  • Loading branch information
jjohannes committed Dec 12, 2024
2 parents a8f9b22 + e17e124 commit 40e196a
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 57 deletions.
5 changes: 2 additions & 3 deletions hapi/hedera-protobufs/services/node_create.proto
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ import "basic_types.proto";
* address book. The transaction, once complete, enables a new consensus node
* to join the network, and requires governing council authorization.
*
* - A `NodeCreateTransactionBody` MUST be signed by the governing council.
* - A `NodeCreateTransactionBody` MUST be signed by the `Key` assigned to the
* `admin_key` field.
* `admin_key` field and one of those keys: treasure account (2) key,
* systemAdmin(50) key, or addressBookAdmin(55) key.
* - The newly created node information SHALL be added to the network address
* book information in the network state.
* - The new entry SHALL be created in "state" but SHALL NOT participate in
Expand Down Expand Up @@ -132,7 +132,6 @@ message NodeCreateTransactionBody {
* An administrative key controlled by the node operator.
* <p>
* This key MUST sign this transaction.<br/>
* This key MUST sign each transaction to update this node.<br/>
* This field MUST contain a valid `Key` value.<br/>
* This field is REQUIRED and MUST NOT be set to an empty `KeyList`.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
import com.hedera.hapi.node.base.ResponseCodeEnum;
import com.hedera.hapi.node.base.ServiceEndpoint;
import com.hedera.hapi.node.base.TransactionID;
import com.hedera.hapi.node.state.token.Account;
import com.hedera.hapi.node.transaction.TransactionBody;
import com.hedera.node.app.service.addressbook.impl.WritableNodeStore;
import com.hedera.node.app.service.addressbook.impl.handlers.NodeCreateHandler;
Expand Down Expand Up @@ -111,23 +110,23 @@ void setUp() {
@Test
@DisplayName("pureChecks fail when accountId is null")
void accountIdCannotNull() {
txn = new NodeCreateBuilder().build();
txn = new NodeCreateBuilder().build(payerId);
final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn));
assertThat(msg.responseCode()).isEqualTo(INVALID_NODE_ACCOUNT_ID);
}

@Test
@DisplayName("pureChecks fail when accountId not set")
void accountIdNeedSet() {
txn = new NodeCreateBuilder().withAccountId(AccountID.DEFAULT).build();
txn = new NodeCreateBuilder().withAccountId(AccountID.DEFAULT).build(payerId);
final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn));
assertThat(msg.responseCode()).isEqualTo(INVALID_NODE_ACCOUNT_ID);
}

@Test
@DisplayName("pureChecks fail when accountId is alias")
void accountIdCannotAlias() {
txn = new NodeCreateBuilder().withAccountId(alias).build();
txn = new NodeCreateBuilder().withAccountId(alias).build(payerId);
final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn));
assertThat(msg.responseCode()).isEqualTo(INVALID_NODE_ACCOUNT_ID);
}
Expand All @@ -138,7 +137,7 @@ void gossipEndpointNeedSet() {
txn = new NodeCreateBuilder()
.withAccountId(accountId)
.withGossipEndpoint(List.of())
.build();
.build(payerId);
final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn));
assertThat(msg.responseCode()).isEqualTo(INVALID_GOSSIP_ENDPOINT);
}
Expand All @@ -150,7 +149,7 @@ void serviceEndpointNeedSet() {
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1))
.withServiceEndpoint(List.of())
.build();
.build(payerId);
final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn));
assertThat(msg.responseCode()).isEqualTo(INVALID_SERVICE_ENDPOINT);
}
Expand All @@ -162,7 +161,7 @@ void gossipCaCertificateNeedSet() {
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1))
.withServiceEndpoint(List.of(endpoint2))
.build();
.build(payerId);
final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn));
assertThat(msg.responseCode()).isEqualTo(INVALID_GOSSIP_CA_CERTIFICATE);
}
Expand All @@ -175,7 +174,7 @@ void adminKeyNeedSet() throws CertificateEncodingException {
.withGossipEndpoint(List.of(endpoint1))
.withServiceEndpoint(List.of(endpoint2))
.withGossipCaCertificate(Bytes.wrap(certList.get(1).getEncoded()))
.build();
.build(payerId);
final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn));
assertThat(msg.responseCode()).isEqualTo(KEY_REQUIRED);
}
Expand All @@ -189,7 +188,7 @@ void adminKeyEmpty() throws CertificateEncodingException {
.withServiceEndpoint(List.of(endpoint2))
.withGossipCaCertificate(Bytes.wrap(certList.get(0).getEncoded()))
.withAdminKey(IMMUTABILITY_SENTINEL_KEY)
.build();
.build(payerId);
final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn));
assertThat(msg.responseCode()).isEqualTo(KEY_REQUIRED);
}
Expand All @@ -203,7 +202,7 @@ void adminKeyNeedValid() throws CertificateEncodingException {
.withServiceEndpoint(List.of(endpoint2))
.withGossipCaCertificate(Bytes.wrap(certList.get(0).getEncoded()))
.withAdminKey(invalidKey)
.build();
.build(payerId);
final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn));
assertThat(msg.responseCode()).isEqualTo(INVALID_ADMIN_KEY);
}
Expand All @@ -217,13 +216,13 @@ void pureCheckPass() throws CertificateEncodingException {
.withServiceEndpoint(List.of(endpoint2))
.withGossipCaCertificate(Bytes.wrap(certList.get(2).getEncoded()))
.withAdminKey(key)
.build();
.build(payerId);
assertDoesNotThrow(() -> subject.pureChecks(txn));
}

@Test
void failsWhenMaxNodesExceeds() {
txn = new NodeCreateBuilder().withAccountId(accountId).build();
txn = new NodeCreateBuilder().withAccountId(accountId).build(payerId);
given(handleContext.body()).willReturn(txn);
refreshStoresWithCurrentNodeInWritable();
final var config = HederaTestConfigBuilder.create()
Expand All @@ -241,7 +240,7 @@ void failsWhenMaxNodesExceeds() {

@Test
void accountIdMustInState() {
txn = new NodeCreateBuilder().withAccountId(accountId).build();
txn = new NodeCreateBuilder().withAccountId(accountId).build(payerId);
given(accountStore.contains(accountId)).willReturn(false);
given(handleContext.body()).willReturn(txn);
refreshStoresWithCurrentNodeInWritable();
Expand All @@ -262,7 +261,7 @@ void failsWhenDescriptionTooLarge() {
txn = new NodeCreateBuilder()
.withAccountId(accountId)
.withDescription("Description")
.build();
.build(payerId);
setupHandle();

refreshStoresWithCurrentNodeInWritable();
Expand All @@ -280,7 +279,7 @@ void failsWhenDescriptionContainZeroByte() {
txn = new NodeCreateBuilder()
.withAccountId(accountId)
.withDescription("Des\0cription")
.build();
.build(payerId);
setupHandle();
final var config = HederaTestConfigBuilder.create()
.withValue("nodes.nodeMaxDescriptionUtf8Bytes", 12)
Expand All @@ -296,7 +295,7 @@ void failsWhenGossipEndpointTooLarge() {
txn = new NodeCreateBuilder()
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1, endpoint2, endpoint3))
.build();
.build(payerId);
setupHandle();

final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext));
Expand All @@ -308,7 +307,7 @@ void failsWhenGossipEndpointNull() {
txn = new NodeCreateBuilder()
.withAccountId(accountId)
.withGossipEndpoint(null)
.build();
.build(payerId);
setupHandle();

final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext));
Expand All @@ -320,7 +319,7 @@ void failsWhenGossipEndpointEmpty() {
txn = new NodeCreateBuilder()
.withAccountId(accountId)
.withGossipEndpoint(List.of())
.build();
.build(payerId);
setupHandle();

final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext));
Expand All @@ -332,7 +331,7 @@ void failsWhenGossipEndpointTooSmall() {
txn = new NodeCreateBuilder()
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1))
.build();
.build(payerId);
setupHandle();

final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext));
Expand All @@ -344,7 +343,7 @@ void failsWhenGossipEndpointHaveIPAndFQDN() {
txn = new NodeCreateBuilder()
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1, endpoint4))
.build();
.build(payerId);
setupHandle();

final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext));
Expand All @@ -356,7 +355,7 @@ void failsWhenEndpointHaveEmptyIPAndFQDN() {
txn = new NodeCreateBuilder()
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1, endpoint5))
.build();
.build(payerId);
setupHandle();

final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext));
Expand All @@ -368,7 +367,7 @@ void failsWhenEndpointHaveNullIp() {
txn = new NodeCreateBuilder()
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1, endpoint7))
.build();
.build(payerId);
setupHandle();

final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext));
Expand All @@ -380,7 +379,7 @@ void failsWhenEndpointHaveZeroIp() {
txn = new NodeCreateBuilder()
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1, endpoint6))
.build();
.build(payerId);
setupHandle();

final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext));
Expand All @@ -392,7 +391,7 @@ void failsWhenEndpointHaveInvalidIp() {
txn = new NodeCreateBuilder()
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1, endpoint8))
.build();
.build(payerId);
setupHandle();

final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext));
Expand All @@ -404,7 +403,7 @@ void failsWhenEndpointHaveInvalidIp2() {
txn = new NodeCreateBuilder()
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1, endpoint9))
.build();
.build(payerId);
setupHandle();

final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext));
Expand All @@ -417,7 +416,7 @@ void failsWhenEndpointHaveInvalidIp3() {
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1, endpoint2))
.withServiceEndpoint(List.of(endpoint10))
.build();
.build(payerId);
setupHandle();

final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext));
Expand All @@ -430,7 +429,7 @@ void failsWhenServiceEndpointTooLarge() {
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1, endpoint2))
.withServiceEndpoint(List.of(endpoint1, endpoint2, endpoint3))
.build();
.build(payerId);
setupHandle();
final var config = HederaTestConfigBuilder.create()
.withValue("nodes.maxGossipEndpoint", 2)
Expand All @@ -448,7 +447,7 @@ void failsWhenServiceEndpointNull() {
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1, endpoint2))
.withServiceEndpoint(null)
.build();
.build(payerId);
setupHandle();

final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext));
Expand All @@ -461,7 +460,7 @@ void failsWhenServiceEndpointEmpty() {
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1, endpoint2))
.withServiceEndpoint(List.of())
.build();
.build(payerId);
setupHandle();

final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext));
Expand All @@ -474,7 +473,7 @@ void failsWhenEndpointHaveIPAndFQDN() {
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1, endpoint2))
.withServiceEndpoint(List.of(endpoint1, endpoint4))
.build();
.build(payerId);
setupHandle();

final var config = HederaTestConfigBuilder.create()
Expand All @@ -493,7 +492,7 @@ void failsWhenEndpointFQDNTooLarge() {
.withAccountId(accountId)
.withGossipEndpoint(List.of(endpoint1, endpoint2))
.withServiceEndpoint(List.of(endpoint1, endpoint3))
.build();
.build(payerId);
setupHandle();

final var config = HederaTestConfigBuilder.create()
Expand All @@ -514,7 +513,7 @@ void handleFailsWhenInvalidAdminKey() {
.withGossipEndpoint(List.of(endpoint1, endpoint2))
.withServiceEndpoint(List.of(endpoint1, endpoint3))
.withAdminKey(invalidKey)
.build();
.build(payerId);
setupHandle();

final var config = HederaTestConfigBuilder.create()
Expand All @@ -540,7 +539,7 @@ void handleWorksAsExpected() throws CertificateEncodingException {
.withGossipCaCertificate(Bytes.wrap(certList.get(0).getEncoded()))
.withGrpcCertificateHash(Bytes.wrap("hash"))
.withAdminKey(key)
.build();
.build(payerId);
given(handleContext.body()).willReturn(txn);
refreshStoresWithMoreNodeInWritable();
final var config = HederaTestConfigBuilder.create()
Expand Down Expand Up @@ -578,8 +577,8 @@ void handleWorksAsExpected() throws CertificateEncodingException {

@Test
void preHandleWorksWhenAdminKeyValid() throws PreCheckException {
mockPayerLookup(anotherKey);
txn = new NodeCreateBuilder().withAdminKey(key).build();
mockPayerLookup(anotherKey, payerId, accountStore);
txn = new NodeCreateBuilder().withAdminKey(key).build(payerId);
final var context = new FakePreHandleContext(accountStore, txn);
subject.preHandle(context);
assertThat(txn).isEqualTo(context.body());
Expand All @@ -589,8 +588,8 @@ void preHandleWorksWhenAdminKeyValid() throws PreCheckException {

@Test
void preHandleFailedWhenAdminKeyInValid() throws PreCheckException {
mockPayerLookup(anotherKey);
txn = new NodeCreateBuilder().withAdminKey(invalidKey).build();
mockPayerLookup(anotherKey, payerId, accountStore);
txn = new NodeCreateBuilder().withAdminKey(invalidKey).build(payerId);
final var context = new FakePreHandleContext(accountStore, txn);
assertThrowsPreCheck(() -> subject.preHandle(context), INVALID_ADMIN_KEY);
assertThat(context.payerKey()).isEqualTo(anotherKey);
Expand Down Expand Up @@ -630,13 +629,6 @@ private void setupHandle() {
given(storeFactory.readableStore(ReadableAccountStore.class)).willReturn(accountStore);
}

private Key mockPayerLookup(Key key) {
final var account = mock(Account.class);
given(account.key()).willReturn(key);
given(accountStore.getAccountById(payerId)).willReturn(account);
return key;
}

private class NodeCreateBuilder {
private AccountID accountId = null;
private String description = null;
Expand All @@ -652,7 +644,7 @@ private class NodeCreateBuilder {

private NodeCreateBuilder() {}

public TransactionBody build() {
public TransactionBody build(AccountID payerId) {
final var txnId = TransactionID.newBuilder().accountID(payerId).transactionValidStart(consensusTimestamp);
final var txnBody = NodeCreateTransactionBody.newBuilder();
if (accountId != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public SystemPrivilege hasPrivileges(
txBody.fileDeleteOrThrow().fileIDOrThrow().fileNum());
case CRYPTO_DELETE -> checkEntityDelete(
txBody.cryptoDeleteOrThrow().deleteAccountIDOrThrow().accountNumOrThrow());
case NODE_CREATE -> checkNodeChange(payerId);
case NODE_CREATE -> checkNodeCreate(payerId);
default -> SystemPrivilege.UNNECESSARY;
};
}
Expand Down Expand Up @@ -137,7 +137,7 @@ private boolean hasFeeSchedulePrivilige(@NonNull final AccountID accountID) {
return accountID.accountNumOrThrow() == accountsConfig.feeSchedulesAdmin() || isSuperUser(accountID);
}

private boolean hasNodeChangePrivilige(@NonNull final AccountID accountID) {
private boolean hasNodeCreatePrivilige(@NonNull final AccountID accountID) {
return accountID.accountNumOrThrow() == accountsConfig.addressBookAdmin() || isSuperUser(accountID);
}

Expand Down Expand Up @@ -217,8 +217,8 @@ private SystemPrivilege checkCryptoUpdate(
}
}

private SystemPrivilege checkNodeChange(@NonNull final AccountID payerId) {
return hasNodeChangePrivilige(payerId) ? AUTHORIZED : UNAUTHORIZED;
private SystemPrivilege checkNodeCreate(@NonNull final AccountID payerId) {
return hasNodeCreatePrivilige(payerId) ? AUTHORIZED : UNAUTHORIZED;
}

private SystemPrivilege checkEntityDelete(final long entityNum) {
Expand Down
Loading

0 comments on commit 40e196a

Please sign in to comment.