Skip to content

Commit

Permalink
[FAB-3470] update composite key format
Browse files Browse the repository at this point in the history
FAB-2183 changed the composite key string format to
include an additional delimiter as the first character

add test for ChaincodeStubImpl with mockito dependency in build.gradle
add CompositeKey.validateSimpleKeys(String... keys)
fix bug when CompositeKey.generateCompositeKeyString with empty attribute set
update .gitignore for Intellij IDEA

Change-Id: If9dd3ab076fab4f2b13bcd6fdfa107531d70734f
Signed-off-by: Luis Sanchez <[email protected]>
Signed-off-by: Jingxiao Gu <[email protected]>
  • Loading branch information
Luis Sanchez authored and Jingxiao Gu committed Oct 16, 2017
1 parent 61b8fc2 commit 9f2f4f9
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 158 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/.gradle/
/.settings/
/.project
/.idea
*.iml
*.log
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,8 @@

package org.hyperledger.fabric.shim.impl;

import static java.util.stream.Collectors.toList;

import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.hyperledger.fabric.protos.ledger.queryresult.KvQueryResult;
import org.hyperledger.fabric.protos.ledger.queryresult.KvQueryResult.KV;
import org.hyperledger.fabric.protos.peer.ChaincodeEventPackage.ChaincodeEvent;
Expand All @@ -35,11 +30,16 @@
import org.hyperledger.fabric.shim.ledger.KeyValue;
import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;

import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

class ChaincodeStubImpl implements ChaincodeStub {

private static final String UNSPECIFIED_KEY = new String(Character.toChars(0x000001));
private final String txId;
private final Handler handler;
private final List<ByteString> args;
Expand Down Expand Up @@ -105,6 +105,8 @@ public byte[] getState(String key) {

@Override
public void putState(String key, byte[] value) {
if(key == null) throw new NullPointerException("key cannot be null");
if(key.length() == 0) throw new IllegalArgumentException("key cannot not be an empty string");
handler.putState(txId, key, ByteString.copyFrom(value));
}

Expand All @@ -115,6 +117,10 @@ public void delState(String key) {

@Override
public QueryResultsIterator<KeyValue> getStateByRange(String startKey, String endKey) {
if (startKey == null || startKey.isEmpty()) startKey = UNSPECIFIED_KEY;
if (endKey == null || endKey.isEmpty()) endKey = UNSPECIFIED_KEY;
CompositeKey.validateSimpleKeys(startKey, endKey);

return new QueryResultsIteratorImpl<KeyValue>(this.handler, getTxId(),
handler.getStateByRange(getTxId(), startKey, endKey),
queryResultBytesToKv.andThen(KeyValueImpl::new)
Expand All @@ -133,6 +139,9 @@ public KV apply(QueryResultBytes queryResultBytes) {

@Override
public QueryResultsIterator<KeyValue> getStateByPartialCompositeKey(String compositeKey) {
if (compositeKey == null || compositeKey.isEmpty()) {
compositeKey = UNSPECIFIED_KEY;
}
return getStateByRange(compositeKey, compositeKey + "\udbff\udfff");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@
*/
package org.hyperledger.fabric.shim.ledger;

import static java.util.stream.Collectors.joining;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.stream.Collectors.joining;

public class CompositeKey {

private static final String DELIMITER = new String(Character.toChars(Character.MIN_CODE_POINT));
static final String NAMESPACE = DELIMITER;
private static final String INVALID_SEGMENT_CHAR = new String(Character.toChars(Character.MAX_CODE_POINT));
private static final String INVALID_SEGMENT_PATTERN = String.format("(?:%s|%s)", INVALID_SEGMENT_CHAR, DELIMITER);

Expand Down Expand Up @@ -59,20 +60,41 @@ public String toString() {

public static CompositeKey parseCompositeKey(String compositeKey) {
if (compositeKey == null) return null;
if (!compositeKey.startsWith(NAMESPACE)) throw CompositeKeyFormatException.forInputString(compositeKey, compositeKey, 0);
// relying on the fact that NAMESPACE == DELIMETER
final String[] segments = compositeKey.split(DELIMITER, 0);
return new CompositeKey(segments[0], Arrays.stream(segments).skip(1).toArray(String[]::new));
return new CompositeKey(segments[1], Arrays.stream(segments).skip(2).toArray(String[]::new));
}

/**
* To ensure that simple keys do not go into composite key namespace,
* we validate simple key to check whether the key starts with 0x00 (which
* is the namespace for compositeKey). This helps in avoding simple/composite
* key collisions.
*
* @throws CompositeKeyFormatException if First character of the key
*/
public static void validateSimpleKeys(String... keys) {
for (String key : keys) {
if(!key.isEmpty() && key.startsWith(NAMESPACE)) {
throw CompositeKeyFormatException.forSimpleKey(key);
}
}
}

private String generateCompositeKeyString(String objectType, List<String> attributes) {

// object type must be a valid composite key segment
validateCompositeKeySegment(objectType);

if (attributes == null || attributes.isEmpty()) {
return NAMESPACE + objectType + DELIMITER;
}
// the attributes must be valid composite key segments
attributes.stream().forEach(x -> validateCompositeKeySegment(x));
attributes.forEach(this::validateCompositeKeySegment);

// return objectType + DELIMITER + (attribute + DELIMITER)*
return attributes.stream().collect(joining(DELIMITER, objectType + DELIMITER, DELIMITER));
// return NAMESPACE + objectType + DELIMITER + (attribute + DELIMITER)*
return attributes.stream().collect(joining(DELIMITER, NAMESPACE + objectType + DELIMITER, DELIMITER));

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,15 @@
class CompositeKeyFormatException extends IllegalArgumentException {
private static final long serialVersionUID = 1L;

private CompositeKeyFormatException() {
super();
}

private CompositeKeyFormatException(String message, Throwable cause) {
super(message, cause);
}

private CompositeKeyFormatException(String s) {
super(s);
}

private CompositeKeyFormatException(Throwable cause) {
super(cause);
}

static CompositeKeyFormatException forInputString(String s, String group, int index) {
return new CompositeKeyFormatException(String.format("For input string '%s', found 'U+%06X' at index %d.", s, group.codePointAt(0), index));
}

static CompositeKeyFormatException forSimpleKey(String key) {
return new CompositeKeyFormatException(String.format("First character of the key [%s] contains a 'U+%06X' which is not allowed", key, CompositeKey.NAMESPACE.codePointAt(0)));
}
}
Loading

0 comments on commit 9f2f4f9

Please sign in to comment.