Skip to content

Commit

Permalink
FGJ-86: Allow client to specify transaction ID (#57)
Browse files Browse the repository at this point in the history
- Add Channel.newTransactionContext() to allow transaction IDs to be generated prior to sending proposals.
- Add TransactionRequest.setTransactionContext() to allow pre-generated transactions IDs to be used.
- Up-front validation of user context on TransactionRequest to avoid numerous checks further through the submit flow.

Signed-off-by: Mark S. Lewis <mark_lewis@uk.ibm.com>
  • Loading branch information
bestbeforetoday authored May 15, 2020
1 parent 9da9a07 commit 3b32367
Show file tree
Hide file tree
Showing 11 changed files with 208 additions and 114 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@
</reporting>

<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.3.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down
89 changes: 49 additions & 40 deletions src/main/java/org/hyperledger/fabric/sdk/Channel.java

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions src/main/java/org/hyperledger/fabric/sdk/HFClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -684,13 +684,15 @@ public QueryByChaincodeRequest newQueryProposalRequest() {
*
* @param userContext
* @return the old user context. Maybe null if never set!
* @throws InvalidArgumentException
* @throws IllegalStateException if no crypto suite has been set.
* @throws NullPointerException if the user context is null.
* @throws IllegalArgumentException if the user context is not valid.
*/

public User setUserContext(User userContext) throws InvalidArgumentException {
public User setUserContext(User userContext) {

if (null == cryptoSuite) {
throw new InvalidArgumentException("No cryptoSuite has been set.");
throw new IllegalStateException("No cryptoSuite has been set.");
}
userContextCheck(userContext);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ public class TransactionProposalRequest extends TransactionRequest {

public static TransactionProposalRequest newInstance(User userContext) {
return new TransactionProposalRequest(userContext);

}

public void setChaincodeLanguage(Type chaincodeLanguage) {
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/org/hyperledger/fabric/sdk/TransactionRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import org.hyperledger.fabric.protos.peer.Chaincode;
import org.hyperledger.fabric.sdk.helper.Config;
import org.hyperledger.fabric.sdk.transaction.TransactionContext;

/**
* A base transaction request common for InstallProposalRequest,trRequest, and QueryRequest.
Expand Down Expand Up @@ -63,6 +65,8 @@ public void setChaincodeName(String chaincodeName) {
protected Map<String, byte[]> transientMap;
protected ChaincodeCollectionConfiguration chaincodeCollectionConfiguration = null;

private TransactionContext transactionContext;

/**
* The user context to use on this request.
*
Expand All @@ -79,7 +83,26 @@ User getUserContext() {
* @param userContext The user context for this request used for signing.
*/
public void setUserContext(User userContext) {
User.userContextCheck(userContext);
this.userContext = userContext;
this.transactionContext = null;
}

/**
* Get the transaction context to be used when submitting this transaction request, if one has been set.
* @return A transaction context.
*/
public Optional<TransactionContext> getTransactionContext() {
return Optional.ofNullable(transactionContext);
}

/**
* Get the transaction context to be used when submitting this transaction request.
* @param transactionContext A transaction ID.
*/
public void setTransactionContext(final TransactionContext transactionContext) {
userContext = transactionContext.getUser();
this.transactionContext = transactionContext;
}

/**
Expand Down Expand Up @@ -345,6 +368,7 @@ public void setProposalWaitTime(long proposalWaitTime) {
}

protected TransactionRequest(User userContext) {
User.userContextCheck(userContext);
this.userContext = userContext;
}

Expand Down
19 changes: 8 additions & 11 deletions src/main/java/org/hyperledger/fabric/sdk/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import java.util.Set;

import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.helper.Utils;
import org.hyperledger.fabric.sdk.identity.X509Enrollment;

Expand Down Expand Up @@ -70,33 +69,31 @@ public interface User {
*/
String getMspId();

static void userContextCheck(User userContext) throws InvalidArgumentException {

static void userContextCheck(User userContext) {
if (userContext == null) {
throw new InvalidArgumentException("UserContext is null");
throw new NullPointerException("UserContext is null");
}

final String userName = userContext.getName();
if (Utils.isNullOrEmpty(userName)) {
throw new InvalidArgumentException("UserContext user's name missing.");
throw new IllegalArgumentException("UserContext user's name missing.");
}

Enrollment enrollment = userContext.getEnrollment();
if (enrollment == null) {
throw new InvalidArgumentException(format("UserContext for user %s has no enrollment set.", userName));
throw new IllegalArgumentException(format("UserContext for user %s has no enrollment set.", userName));
}
if (enrollment instanceof X509Enrollment) {
if (Utils.isNullOrEmpty(enrollment.getCert())) {
throw new InvalidArgumentException(format("UserContext for user %s enrollment missing user certificate.", userName));
throw new IllegalArgumentException(format("UserContext for user %s enrollment missing user certificate.", userName));
}
if (null == enrollment.getKey()) {
throw new InvalidArgumentException(format("UserContext for user %s has Enrollment missing signing key", userName));
throw new IllegalArgumentException(format("UserContext for user %s has Enrollment missing signing key", userName));
}
}

if (Utils.isNullOrEmpty(userContext.getMspId())) {
throw new InvalidArgumentException(format("UserContext for user %s has user's MSPID missing.", userName));
throw new IllegalArgumentException(format("UserContext for user %s has user's MSPID missing.", userName));
}

}

}
13 changes: 8 additions & 5 deletions src/test/java/org/hyperledger/fabric/sdk/ChannelTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;


import com.google.protobuf.ByteString;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
Expand Down Expand Up @@ -961,7 +960,8 @@ public void testProposalBuilderWithOutMetaInf() throws Exception {
installProposalBuilder.chaincodeVersion("1");

Channel channel = hfclient.newChannel("testProposalBuilderWithOutMetaInf");
TransactionContext transactionContext = new TransactionContext(channel, getMockUser("rick", "rickORG"), CryptoSuite.Factory.getCryptoSuite());
User user = getMockUser("rick", "rickORG");
TransactionContext transactionContext = new TransactionContext(channel, user, CryptoSuite.Factory.getCryptoSuite());

installProposalBuilder.context(transactionContext);

Expand Down Expand Up @@ -1001,7 +1001,8 @@ public void testProposalBuilderWithNoMetaInfDir() throws Exception {
installProposalBuilder.setChaincodeMetaInfLocation(new File("src/test/fixture/meta-infs/test1/META-INF")); // points into which is not what's expected.

Channel channel = hfclient.newChannel("testProposalBuilderWithNoMetaInfDir");
TransactionContext transactionContext = new TransactionContext(channel, getMockUser("rick", "rickORG"), CryptoSuite.Factory.getCryptoSuite());
User user = getMockUser("rick", "rickORG");
TransactionContext transactionContext = new TransactionContext(channel, user, CryptoSuite.Factory.getCryptoSuite());

installProposalBuilder.context(transactionContext);

Expand All @@ -1023,7 +1024,8 @@ public void testProposalBuilderWithMetaInfExistsNOT() throws Exception {
installProposalBuilder.setChaincodeMetaInfLocation(new File("/tmp/fdsjfksfj/fjksfjskd/fjskfjdsk/should never exist")); // points into which is not what's expected.

Channel channel = hfclient.newChannel("testProposalBuilderWithMetaInfExistsNOT");
TransactionContext transactionContext = new TransactionContext(channel, getMockUser("rick", "rickORG"), CryptoSuite.Factory.getCryptoSuite());
User user = getMockUser("rick", "rickORG");
TransactionContext transactionContext = new TransactionContext(channel, user, CryptoSuite.Factory.getCryptoSuite());

installProposalBuilder.context(transactionContext);

Expand Down Expand Up @@ -1102,7 +1104,8 @@ public void testProposalBuilderWithMetaInfEmpty() throws Exception {
installProposalBuilder.setChaincodeMetaInfLocation(new File("src/test/fixture/meta-infs/emptyMetaInf")); // points into which is not what's expected.

Channel channel = hfclient.newChannel("testProposalBuilderWithMetaInfEmpty");
TransactionContext transactionContext = new TransactionContext(channel, getMockUser("rick", "rickORG"), CryptoSuite.Factory.getCryptoSuite());
User user = getMockUser("rick", "rickORG");
TransactionContext transactionContext = new TransactionContext(channel, user, CryptoSuite.Factory.getCryptoSuite());

installProposalBuilder.context(transactionContext);

Expand Down
26 changes: 8 additions & 18 deletions src/test/java/org/hyperledger/fabric/sdk/ClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,60 +121,55 @@ public void testGoodMockUser() throws Exception {

}

@Test (expected = InvalidArgumentException.class)
@Test (expected = NullPointerException.class)
public void testBadUserContextNull() throws Exception {
HFClient client = HFClient.createNewInstance();
client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());

client.setUserContext(null);

}

@Test (expected = InvalidArgumentException.class)
@Test (expected = IllegalArgumentException.class)
public void testBadUserNameNull() throws Exception {
HFClient client = HFClient.createNewInstance();
client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());

MockUser mockUser = TestUtils.getMockUser(null, USER_MSP_ID);

client.setUserContext(mockUser);

}

@Test (expected = InvalidArgumentException.class)
@Test (expected = IllegalArgumentException.class)
public void testBadUserNameEmpty() throws Exception {
HFClient client = HFClient.createNewInstance();
client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());

MockUser mockUser = TestUtils.getMockUser("", USER_MSP_ID);

client.setUserContext(mockUser);

}

@Test (expected = InvalidArgumentException.class)
@Test (expected = IllegalArgumentException.class)
public void testBadUserMSPIDNull() throws Exception {
HFClient client = HFClient.createNewInstance();
client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());

MockUser mockUser = TestUtils.getMockUser(USER_NAME, null);

client.setUserContext(mockUser);

}

@Test (expected = InvalidArgumentException.class)
@Test (expected = IllegalArgumentException.class)
public void testBadUserMSPIDEmpty() throws Exception {
HFClient client = HFClient.createNewInstance();
client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());

MockUser mockUser = TestUtils.getMockUser(USER_NAME, "");

client.setUserContext(mockUser);

}

@Test (expected = InvalidArgumentException.class)
@Test (expected = IllegalArgumentException.class)
public void testBadEnrollmentNull() throws Exception {
HFClient client = HFClient.createNewInstance();
client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
Expand All @@ -183,35 +178,30 @@ public void testBadEnrollmentNull() throws Exception {
mockUser.setEnrollment(null);

client.setUserContext(mockUser);

}

@Test (expected = InvalidArgumentException.class)
@Test (expected = IllegalArgumentException.class)
public void testBadEnrollmentBadCert() throws Exception {
HFClient client = HFClient.createNewInstance();
client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());

MockUser mockUser = TestUtils.getMockUser(USER_NAME, USER_MSP_ID);

Enrollment mockEnrollment = TestUtils.getMockEnrollment(null);
mockUser.setEnrollment(mockEnrollment);

client.setUserContext(mockUser);

}

@Test (expected = InvalidArgumentException.class)
@Test (expected = IllegalArgumentException.class)
public void testBadEnrollmentBadKey() throws Exception {
HFClient client = HFClient.createNewInstance();
client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());

MockUser mockUser = TestUtils.getMockUser(USER_NAME, USER_MSP_ID);

Enrollment mockEnrollment = TestUtils.getMockEnrollment(null, "mockCert");
mockUser.setEnrollment(mockEnrollment);

client.setUserContext(mockUser);

}

@Test
Expand Down
4 changes: 4 additions & 0 deletions src/test/java/org/hyperledger/fabric/sdk/RequestTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import java.io.InputStream;

import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.security.CryptoSuite;
import org.hyperledger.fabric.sdk.testutils.TestUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
Expand All @@ -40,6 +42,8 @@ public class RequestTest {
@Before
public void setupClient() throws Exception {
hfclient = HFClient.createNewInstance();
hfclient.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
hfclient.setUserContext(TestUtils.getMockUser("user", "mspId"));
mockstream = new ByteArrayInputStream(new byte[0]);

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2020 IBM - All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.hyperledger.fabric.sdk;

import java.util.Optional;

import org.hyperledger.fabric.sdk.testutils.TestUtils;
import org.hyperledger.fabric.sdk.transaction.TransactionContext;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;

public class TransactionRequestTest {
@Test(expected = NullPointerException.class)
public void newWithNullUserThrows() {
TransactionRequest request = new TransactionRequest(null);
}

@Test(expected = NullPointerException.class)
public void testSetUserContextWithNullUserThrows() {
User user = TestUtils.getMockUser("user", "mspId");
TransactionRequest request = new TransactionRequest(user);

request.setUserContext(null);
}

@Test
public void testSetTransactionContext() {
User user = TestUtils.getMockUser("user", "mspId");
TransactionRequest request = new TransactionRequest(user);

TransactionContext context = Mockito.mock(TransactionContext.class);
Mockito.when(context.getUser()).thenReturn(user);

request.setTransactionContext(context);

Optional<TransactionContext> actual = request.getTransactionContext();
Assert.assertTrue("Transaction context is present", actual.isPresent());
Assert.assertEquals("Excepted context", context, actual.get());
}

@Test
public void testSetTransactionContextAlsoSetsUserContext() {
User oldUser = TestUtils.getMockUser("oldUser", "mspId");
TransactionRequest request = new TransactionRequest(oldUser);

TransactionContext context = Mockito.mock(TransactionContext.class);
User newUser = TestUtils.getMockUser("newUser", "mspId");
Mockito.when(context.getUser()).thenReturn(newUser);

request.setTransactionContext(context);

Assert.assertEquals(newUser, request.getUserContext());
}

@Test
public void testSetUserContextRemovesTransactionContext() {
User user = TestUtils.getMockUser("user", "mspId");
TransactionRequest request = new TransactionRequest(user);

TransactionContext context = Mockito.mock(TransactionContext.class);
Mockito.when(context.getUser()).thenReturn(user);
request.setTransactionContext(context);

request.setUserContext(user);

Assert.assertFalse(request.getTransactionContext().isPresent());
}
}
Loading

0 comments on commit 3b32367

Please sign in to comment.