Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ public class HapiNodeUpdate extends HapiTxnOp<HapiNodeUpdate> {

private final String nodeName;
private Optional<String> newAccountId = Optional.empty();
private Optional<AccountID> fullNewAccountId = Optional.empty();

private Optional<AccountID> newAccountAlias = Optional.empty();
private Optional<String> newDescription = Optional.empty();
Expand Down Expand Up @@ -76,11 +75,6 @@ public HapiNodeUpdate accountId(@NonNull final String accountId) {
return this;
}

public HapiNodeUpdate fullAccountId(@NonNull final AccountID accountId) {
this.fullNewAccountId = Optional.of(accountId);
return this;
}

public HapiNodeUpdate aliasAccountId(@NonNull final String alias) {
this.newAccountAlias = Optional.of(
AccountID.newBuilder().setAlias(ByteString.copyFromUtf8(alias)).build());
Expand Down Expand Up @@ -185,11 +179,7 @@ protected Consumer<TransactionBody.Builder> opBodyDef(@NonNull final HapiSpec sp
.<NodeUpdateTransactionBody, NodeUpdateTransactionBody.Builder>body(
NodeUpdateTransactionBody.class, builder -> {
builder.setNodeId(asPosNodeId(nodeName, spec));
if (fullNewAccountId.isPresent()) {
builder.setAccountId(fullNewAccountId.get());
} else {
newAccountId.ifPresent(id -> setNewAccountId(id, spec, builder));
}
newAccountId.ifPresent(id -> setNewAccountId(id, spec, builder));
newAccountAlias.ifPresent(builder::setAccountId);
newDescription.ifPresent(s -> builder.setDescription(StringValue.of(s)));
newAdminKey.ifPresent(builder::setAdminKey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import static com.hedera.services.bdd.junit.TestTags.MATS;
import static com.hedera.services.bdd.junit.TestTags.ONLY_SUBPROCESS;
import static com.hedera.services.bdd.junit.hedera.utils.AddressBookUtils.CLASSIC_FIRST_NODE_ACCOUNT_NUM;
import static com.hedera.services.bdd.spec.HapiPropertySource.asAccountString;
import static com.hedera.services.bdd.spec.HapiSpec.hapiTest;
import static com.hedera.services.bdd.spec.keys.ControlForKey.forKey;
import static com.hedera.services.bdd.spec.keys.KeyShape.SIMPLE;
Expand All @@ -14,6 +15,7 @@
import static com.hedera.services.bdd.spec.keys.KeyShape.threshOf;
import static com.hedera.services.bdd.spec.keys.SigControl.OFF;
import static com.hedera.services.bdd.spec.keys.SigControl.ON;
import static com.hedera.services.bdd.spec.queries.QueryVerbs.getAccountInfo;
import static com.hedera.services.bdd.spec.queries.QueryVerbs.getTxnRecord;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.createDefaultContract;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoCreate;
Expand All @@ -27,6 +29,7 @@
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.newKeyNamed;
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.sourcing;
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.validateChargedUsd;
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.withOpContext;
import static com.hedera.services.bdd.suites.HapiSuite.DEFAULT_PAYER;
import static com.hedera.services.bdd.suites.HapiSuite.GENESIS;
import static com.hedera.services.bdd.suites.HapiSuite.ONE_HBAR;
Expand All @@ -42,7 +45,9 @@
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.NODE_ACCOUNT_HAS_ZERO_BALANCE;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.RECORD_NOT_FOUND;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.hedera.services.bdd.junit.EmbeddedHapiTest;
import com.hedera.services.bdd.junit.HapiTest;
Expand All @@ -52,6 +57,7 @@
import com.hedera.services.bdd.spec.keys.SigControl;
import com.hederahashgraph.api.proto.java.AccountID;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.nio.file.Paths;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.List;
Expand Down Expand Up @@ -99,18 +105,12 @@ final Stream<DynamicTest> updateAccountIdSuccessfullyHappyPath() throws Certific
"testNode",
node -> assertEquals(
newAccountId.get(),
node.accountId().accountNum(),
node.accountIdOrThrow().accountNum(),
"Node accountId should be updated")));
}

@EmbeddedHapiTest(NEEDS_STATE_ACCESS)
@Tag(MATS)
final Stream<DynamicTest> updateAccountIdRequiredSignatures() {
final AccountID sentinelValue = AccountID.newBuilder()
.setShardNum(0)
.setRealmNum(0)
.setAccountNum(0)
.build();
final AtomicReference<AccountID> initialNodeAccountId = new AtomicReference<>();
final AtomicReference<AccountID> newAccountId = new AtomicReference<>();
return hapiTest(
Expand All @@ -129,7 +129,7 @@ final Stream<DynamicTest> updateAccountIdRequiredSignatures() {
}),
// signed with correct sig fails if account is sentinel
nodeUpdate("testNode")
.fullAccountId(sentinelValue)
.accountId("0.0.0")
.payingWith(DEFAULT_PAYER)
.signedByPayerAnd("initialNodeAccount")
.hasPrecheck(INVALID_NODE_ACCOUNT_ID),
Expand Down Expand Up @@ -537,6 +537,31 @@ final Stream<DynamicTest> restrictNodeAccountDeletion() throws CertificateEncodi
// verify we can delete the second account
cryptoDelete(secondAccount));
}

@Tag(ONLY_SUBPROCESS)
@HapiTest
final Stream<DynamicTest> accountUpdateBuildsProperRecordPath() {
final AtomicReference<AccountID> newAccountId = new AtomicReference<>();
final AtomicReference<AccountID> oldNodeAccountId = new AtomicReference<>();
final String nodeToUpdate = "3";
final String baseDir = "build/hapi-test/node" + nodeToUpdate + "/data/recordStreams/";

return hapiTest(
cryptoCreate("newAccount").exposingCreatedIdTo(newAccountId::set),
// account 6 is the node account of node 3
getAccountInfo("6").exposingIdTo(oldNodeAccountId::set),
nodeUpdate(nodeToUpdate).accountId("newAccount").signedByPayerAnd("newAccount"),
// create a transaction after the update so record files are generated
cryptoCreate("foo"),
// assert record paths
withOpContext((spec, log) -> {
final var oldRecordPath =
Paths.get(baseDir + "record" + asAccountString(oldNodeAccountId.get()));
final var newRecordPath = Paths.get(baseDir + "record" + asAccountString(newAccountId.get()));
assertTrue(oldRecordPath.toFile().exists());
assertFalse(newRecordPath.toFile().exists());
}));
}
}

@Nested
Expand Down Expand Up @@ -986,8 +1011,28 @@ final Stream<DynamicTest> updateNodeAccountAdminKeyToEmptyKeyListFails() throws
"testNode",
node -> assertNotEquals(
newAccountId.get(),
node.accountId().accountNum(),
node.accountIdOrThrow().accountNum(),
"Node accountId should not be updated")));
}
}

@HapiTest
final Stream<DynamicTest> testUnlinkAndLinkAccounts() throws CertificateEncodingException {
return hapiTest(
newKeyNamed("adminKey"),
cryptoCreate("node1Account"),
cryptoCreate("node2Account"),
cryptoCreate("placeHolder"),
nodeCreate("testNode1", "node1Account")
.adminKey("adminKey")
.gossipCaCertificate(gossipCertificates.getFirst().getEncoded()),
nodeCreate("testNode2", "node2Account")
.adminKey("adminKey")
.gossipCaCertificate(gossipCertificates.getFirst().getEncoded()),
nodeUpdate("testNode1").accountId("placeHolder").signedByPayerAnd("adminKey", "placeHolder"),
nodeUpdate("testNode2").accountId("node1Account").signedByPayerAnd("adminKey", "node1Account"),
cryptoDelete("placeHolder").hasKnownStatus(ACCOUNT_IS_LINKED_TO_A_NODE),
cryptoDelete("node1Account").hasKnownStatus(ACCOUNT_IS_LINKED_TO_A_NODE),
cryptoDelete("node2Account"));
}
}
Loading