@@ -1,7 +1,7 @@ | |||
package com.jd.blockchain.contract; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
@Contract | |||
public class ReadContractImpl implements EventProcessingAware, ReadContract { | |||
@@ -24,7 +24,7 @@ public class ReadContractImpl implements EventProcessingAware, ReadContract { | |||
@Override | |||
@ContractEvent(name = "read-key") | |||
public String read(String address, String key) { | |||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); | |||
TypedKVEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); | |||
if (kvDataEntries != null && kvDataEntries.length == 1) { | |||
return kvDataEntries[0].getValue().toString(); | |||
@@ -35,7 +35,7 @@ public class ReadContractImpl implements EventProcessingAware, ReadContract { | |||
@Override | |||
@ContractEvent(name = "version-key") | |||
public Long readVersion(String address, String key) { | |||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); | |||
TypedKVEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); | |||
if (kvDataEntries != null && kvDataEntries.length == 1) { | |||
return kvDataEntries[0].getVersion(); | |||
@@ -2,7 +2,7 @@ package com.jd.blockchain.contract; | |||
import com.alibaba.fastjson.JSON; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.KVDataVO; | |||
import com.jd.blockchain.ledger.KVInfoVO; | |||
@@ -14,7 +14,7 @@ public class TransferContractImpl implements EventProcessingAware, TransferContr | |||
@Override | |||
public String create(String address, String account, long money) { | |||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); | |||
TypedKVEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); | |||
// 肯定有返回值,但若不存在则返回version=-1 | |||
if (kvDataEntries != null && kvDataEntries.length > 0) { | |||
long currVersion = kvDataEntries[0].getVersion(); | |||
@@ -32,13 +32,13 @@ public class TransferContractImpl implements EventProcessingAware, TransferContr | |||
@Override | |||
public String transfer(String address, String from, String to, long money) { | |||
// 首先查询余额 | |||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, from, to); | |||
TypedKVEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, from, to); | |||
if (kvDataEntries == null || kvDataEntries.length != 2) { | |||
throw new IllegalStateException(String.format("%s -> %s - %s may be not created !!!", address, from, to)); | |||
} else { | |||
// 判断from账号中钱数量是否足够 | |||
long fromMoney = 0L, toMoney = 0L, fromVersion = 0L, toVersion = 0L; | |||
for (KVDataEntry kvDataEntry : kvDataEntries) { | |||
for (TypedKVEntry kvDataEntry : kvDataEntries) { | |||
if (kvDataEntry.getKey().equals(from)) { | |||
fromMoney = (long) kvDataEntry.getValue(); | |||
fromVersion = kvDataEntry.getVersion(); | |||
@@ -62,7 +62,7 @@ public class TransferContractImpl implements EventProcessingAware, TransferContr | |||
@Override | |||
public long read(String address, String account) { | |||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); | |||
TypedKVEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); | |||
if (kvDataEntries == null || kvDataEntries.length == 0) { | |||
return -1; | |||
} | |||
@@ -71,7 +71,7 @@ public class TransferContractImpl implements EventProcessingAware, TransferContr | |||
@Override | |||
public String readAll(String address, String account) { | |||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); | |||
TypedKVEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); | |||
// 获取最新的版本号 | |||
if (kvDataEntries == null || kvDataEntries.length == 0) { | |||
return ""; | |||
@@ -91,7 +91,7 @@ public class TransferContractImpl implements EventProcessingAware, TransferContr | |||
KVInfoVO kvInfoVO = new KVInfoVO(kvDataVOS); | |||
KVDataEntry[] allEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, kvInfoVO); | |||
TypedKVEntry[] allEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, kvInfoVO); | |||
return JSON.toJSONString(allEntries); | |||
} | |||
@@ -25,7 +25,7 @@ import com.jd.blockchain.gateway.PeerService; | |||
import com.jd.blockchain.gateway.service.DataRetrievalService; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.ContractInfo; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.KVInfoVO; | |||
import com.jd.blockchain.ledger.LedgerAdminInfo; | |||
import com.jd.blockchain.ledger.LedgerBlock; | |||
@@ -261,7 +261,7 @@ public class BlockBrowserController implements BlockchainExtendQueryService { | |||
@RequestMapping(method = { RequestMethod.GET, | |||
RequestMethod.POST }, path = "ledgers/{ledgerHash}/accounts/{address}/entries") | |||
@Override | |||
public KVDataEntry[] getDataEntries(@PathVariable("ledgerHash") HashDigest ledgerHash, | |||
public TypedKVEntry[] getDataEntries(@PathVariable("ledgerHash") HashDigest ledgerHash, | |||
@PathVariable("address") String address, @RequestParam("keys") String... keys) { | |||
return peerService.getQueryService().getDataEntries(ledgerHash, address, keys); | |||
} | |||
@@ -269,7 +269,7 @@ public class BlockBrowserController implements BlockchainExtendQueryService { | |||
@RequestMapping(method = { RequestMethod.GET, | |||
RequestMethod.POST }, path = "ledgers/{ledgerHash}/accounts/{address}/entries-version") | |||
@Override | |||
public KVDataEntry[] getDataEntries(@PathVariable("ledgerHash") HashDigest ledgerHash, | |||
public TypedKVEntry[] getDataEntries(@PathVariable("ledgerHash") HashDigest ledgerHash, | |||
@PathVariable("address") String address, @RequestBody KVInfoVO kvInfoVO) { | |||
return peerService.getQueryService().getDataEntries(ledgerHash, address, kvInfoVO); | |||
} | |||
@@ -277,7 +277,7 @@ public class BlockBrowserController implements BlockchainExtendQueryService { | |||
@RequestMapping(method = { RequestMethod.GET, | |||
RequestMethod.POST }, path = "ledgers/{ledgerHash}/accounts/address/{address}/entries") | |||
@Override | |||
public KVDataEntry[] getDataEntries(@PathVariable("ledgerHash") HashDigest ledgerHash, | |||
public TypedKVEntry[] getDataEntries(@PathVariable("ledgerHash") HashDigest ledgerHash, | |||
@PathVariable("address") String address, | |||
@RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, | |||
@RequestParam(name = "count", required = false, defaultValue = "-1") int count) { | |||
@@ -2,8 +2,8 @@ package com.jd.blockchain.ledger.core; | |||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.KVDataObject; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.TypedKVData; | |||
import com.jd.blockchain.ledger.MerkleProof; | |||
import com.jd.blockchain.ledger.TypedValue; | |||
import com.jd.blockchain.utils.Bytes; | |||
@@ -8,8 +8,9 @@ import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.ContractInfo; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.KVDataObject; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.TypedValue; | |||
import com.jd.blockchain.ledger.TypedKVData; | |||
import com.jd.blockchain.ledger.KVDataVO; | |||
import com.jd.blockchain.ledger.KVInfoVO; | |||
import com.jd.blockchain.ledger.LedgerAdminInfo; | |||
@@ -22,12 +23,15 @@ import com.jd.blockchain.ledger.ParticipantNode; | |||
import com.jd.blockchain.ledger.TransactionState; | |||
import com.jd.blockchain.ledger.UserInfo; | |||
import com.jd.blockchain.transaction.BlockchainQueryService; | |||
import com.jd.blockchain.utils.ArrayUtils; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.DataEntry; | |||
import com.jd.blockchain.utils.DataIterator; | |||
import com.jd.blockchain.utils.QueryUtil; | |||
public class LedgerQueryService implements BlockchainQueryService { | |||
private static final KVDataEntry[] EMPTY_ENTRIES = new KVDataEntry[0]; | |||
private static final TypedKVEntry[] EMPTY_ENTRIES = new TypedKVEntry[0]; | |||
private HashDigest[] ledgerHashs; | |||
@@ -278,7 +282,7 @@ public class LedgerQueryService implements BlockchainQueryService { | |||
} | |||
@Override | |||
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { | |||
public TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { | |||
if (keys == null || keys.length == 0) { | |||
return EMPTY_ENTRIES; | |||
} | |||
@@ -287,7 +291,8 @@ public class LedgerQueryService implements BlockchainQueryService { | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
DataAccount dataAccount = dataAccountSet.getAccount(Bytes.fromBase58(address)); | |||
KVDataEntry[] entries = new KVDataEntry[keys.length]; | |||
TypedKVEntry[] entries = new TypedKVEntry[keys.length]; | |||
long ver; | |||
for (int i = 0; i < entries.length; i++) { | |||
final String currKey = keys[i]; | |||
@@ -295,17 +300,17 @@ public class LedgerQueryService implements BlockchainQueryService { | |||
ver = dataAccount == null ? -1 : dataAccount.getDataset().getVersion(currKey); | |||
if (ver < 0) { | |||
entries[i] = new KVDataObject(currKey, -1, null); | |||
entries[i] = new TypedKVData(currKey, -1, null); | |||
} else { | |||
BytesValue value = dataAccount.getDataset().getValue(currKey, ver); | |||
entries[i] = new KVDataObject(currKey, ver, value); | |||
entries[i] = new TypedKVData(currKey, ver, value); | |||
} | |||
} | |||
return entries; | |||
} | |||
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO) { | |||
public TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO) { | |||
// parse kvInfoVO; | |||
List<String> keyList = new ArrayList<>(); | |||
List<Long> versionList = new ArrayList<>(); | |||
@@ -335,22 +340,22 @@ public class LedgerQueryService implements BlockchainQueryService { | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
DataAccount dataAccount = dataAccountSet.getAccount(Bytes.fromBase58(address)); | |||
KVDataEntry[] entries = new KVDataEntry[keys.length]; | |||
TypedKVEntry[] entries = new TypedKVEntry[keys.length]; | |||
long ver = -1; | |||
for (int i = 0; i < entries.length; i++) { | |||
// ver = dataAccount.getDataVersion(Bytes.fromString(keys[i])); | |||
// dataAccount.getBytes(Bytes.fromString(keys[i]),1); | |||
ver = versions[i]; | |||
if (ver < 0) { | |||
entries[i] = new KVDataObject(keys[i], -1, null); | |||
entries[i] = new TypedKVData(keys[i], -1, null); | |||
} else { | |||
if (dataAccount.getDataset().getDataCount() == 0 | |||
|| dataAccount.getDataset().getValue(keys[i], ver) == null) { | |||
// is the address is not exist; the result is null; | |||
entries[i] = new KVDataObject(keys[i], -1, null); | |||
entries[i] = new TypedKVData(keys[i], -1, null); | |||
} else { | |||
BytesValue value = dataAccount.getDataset().getValue(keys[i], ver); | |||
entries[i] = new KVDataObject(keys[i], ver, value); | |||
entries[i] = new TypedKVData(keys[i], ver, value); | |||
} | |||
} | |||
} | |||
@@ -359,14 +364,22 @@ public class LedgerQueryService implements BlockchainQueryService { | |||
} | |||
@Override | |||
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { | |||
public TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
DataAccount dataAccount = dataAccountSet.getAccount(Bytes.fromBase58(address)); | |||
int pages[] = QueryUtil.calFromIndexAndCount(fromIndex, count, (int) dataAccount.getDataset().getDataCount()); | |||
return dataAccount.getDataset()..getDataEntries(pages[0], pages[1]); | |||
// int pages[] = QueryUtil.calFromIndexAndCount(fromIndex, count, (int) dataAccount.getDataset().getDataCount()); | |||
// return dataAccount.getDataset().getDataEntry(key, version).getDataEntries(pages[0], pages[1]); | |||
DataIterator<String, TypedValue> iterator = dataAccount.getDataset().iterator(); | |||
iterator.skip(fromIndex); | |||
DataEntry<String, TypedValue>[] dataEntries = iterator.next(count); | |||
TypedKVEntry[] typedKVEntries = ArrayUtils.castTo(dataEntries, TypedKVEntry.class, | |||
e -> e == null ? null : new TypedKVData(e.getKey(), e.getVersion(), e.getValue())); | |||
return typedKVEntries; | |||
} | |||
@Override | |||
@@ -136,7 +136,12 @@ public class MerkleAccountSet implements Transactional, MerkleProvable, AccountQ | |||
* @return | |||
*/ | |||
public boolean contains(Bytes address) { | |||
long latestVersion = getVersion(address); | |||
InnerMerkleAccount acc = latestAccountsCache.get(address); | |||
if (acc != null) { | |||
// 无论是新注册未提交的,还是缓存已提交的账户实例,都认为是存在; | |||
return true; | |||
} | |||
long latestVersion = merkleDataset.getVersion(address); | |||
return latestVersion > -1; | |||
} | |||
@@ -10,10 +10,12 @@ import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; | |||
import com.jd.blockchain.storage.service.VersioningKVStorage; | |||
import com.jd.blockchain.storage.service.utils.BufferedKVStorage; | |||
import com.jd.blockchain.storage.service.utils.VersioningKVData; | |||
import com.jd.blockchain.utils.ArrayUtils; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.Transactional; | |||
import com.jd.blockchain.utils.DataEntry; | |||
import com.jd.blockchain.utils.DataIterator; | |||
import com.jd.blockchain.utils.Dataset; | |||
import com.jd.blockchain.utils.Transactional; | |||
import com.jd.blockchain.utils.io.BytesUtils; | |||
/** | |||
@@ -39,6 +41,9 @@ public class MerkleDataSet implements Transactional, MerkleProvable, Dataset<Byt | |||
private final Bytes dataKeyPrefix; | |||
private final Bytes merkleKeyPrefix; | |||
@SuppressWarnings("unchecked") | |||
private static final DataEntry<Bytes, byte[]>[] EMPTY_ENTRIES = new DataEntry[0]; | |||
private BufferedKVStorage bufferedStorage; | |||
private VersioningKVStorage valueStorage; | |||
@@ -162,11 +167,16 @@ public class MerkleDataSet implements Transactional, MerkleProvable, Dataset<Byt | |||
return merkleTree.getDataCount(); | |||
} | |||
/** | |||
* 返回理论上允许的最大数据索引; | |||
* | |||
* @return | |||
*/ | |||
public long getMaxIndex() { | |||
return merkleTree.getMaxSn(); | |||
} | |||
public byte[][] getLatestValues(int fromIndex, int count) { | |||
public byte[][] getLatestValues(long fromIndex, int count) { | |||
if (count > LedgerConsts.MAX_LIST_COUNT) { | |||
throw new IllegalArgumentException("Count exceed the upper limit[" + LedgerConsts.MAX_LIST_COUNT + "]!"); | |||
} | |||
@@ -182,13 +192,16 @@ public class MerkleDataSet implements Transactional, MerkleProvable, Dataset<Byt | |||
return values; | |||
} | |||
public DataEntry<Bytes, byte[]>[] getLatestDataEntries(int fromIndex, int count) { | |||
public DataEntry<Bytes, byte[]>[] getLatestDataEntries(long fromIndex, int count) { | |||
if (count > LedgerConsts.MAX_LIST_COUNT) { | |||
throw new IllegalArgumentException("Count exceed the upper limit[" + LedgerConsts.MAX_LIST_COUNT + "]!"); | |||
} | |||
if (fromIndex < 0 || (fromIndex + count) > merkleTree.getDataCount()) { | |||
throw new IllegalArgumentException("Index out of bound!"); | |||
} | |||
if (count == 0) { | |||
return EMPTY_ENTRIES; | |||
} | |||
@SuppressWarnings("unchecked") | |||
DataEntry<Bytes, byte[]>[] values = new DataEntry[count]; | |||
byte[] bytesValue; | |||
@@ -201,6 +214,19 @@ public class MerkleDataSet implements Transactional, MerkleProvable, Dataset<Byt | |||
return values; | |||
} | |||
public DataEntry<Bytes, byte[]> getLatestDataEntry(long index) { | |||
if (index < 0 || index + 1 > merkleTree.getDataCount()) { | |||
throw new IllegalArgumentException("Index out of bound!"); | |||
} | |||
byte[] bytesValue; | |||
MerkleDataNode dataNode = merkleTree.getData(index); | |||
Bytes dataKey = encodeDataKey(dataNode.getKey()); | |||
bytesValue = valueStorage.get(dataKey, dataNode.getVersion()); | |||
DataEntry<Bytes, byte[]> entry = new VersioningKVData<Bytes, byte[]>(dataNode.getKey(), dataNode.getVersion(), | |||
bytesValue); | |||
return entry; | |||
} | |||
/** | |||
* get the data at the specific index; | |||
* | |||
@@ -505,6 +531,16 @@ public class MerkleDataSet implements Transactional, MerkleProvable, Dataset<Byt | |||
return new VersioningKVData<Bytes, byte[]>(key, version, value); | |||
} | |||
@Override | |||
public DataIterator<Bytes, byte[]> iterator() { | |||
return new AscDataInterator(getDataCount()); | |||
} | |||
@Override | |||
public DataIterator<Bytes, byte[]> iteratorDesc() { | |||
return new DescDataInterator(getDataCount()); | |||
} | |||
public MerkleDataEntry getMerkleEntry(Bytes key, long version) { | |||
DataEntry<Bytes, byte[]> dataEntry = getDataEntry(key, version); | |||
if (dataEntry == null) { | |||
@@ -586,4 +622,119 @@ public class MerkleDataSet implements Transactional, MerkleProvable, Dataset<Byt | |||
merkleTree.cancel(); | |||
snGenerator = new MerkleSequenceSNGenerator(merkleTree); | |||
} | |||
// ---------------------------------------------------------- | |||
private class AscDataInterator implements DataIterator<Bytes, byte[]> { | |||
private final long total; | |||
private long cursor = 0; | |||
public AscDataInterator(long total) { | |||
this.total = total; | |||
} | |||
@Override | |||
public void skip(long count) { | |||
cursor = nextCursor(count); | |||
} | |||
private long nextCursor(long skippingCount) { | |||
long c = cursor + skippingCount; | |||
return c > total ? total : c; | |||
} | |||
@Override | |||
public DataEntry<Bytes, byte[]> next() { | |||
if (hasNext()) { | |||
DataEntry<Bytes, byte[]> entry = getLatestDataEntry(cursor); | |||
cursor = nextCursor(1); | |||
return entry; | |||
} | |||
return null; | |||
} | |||
@Override | |||
public DataEntry<Bytes, byte[]>[] next(int count) { | |||
if (hasNext()) { | |||
long from = cursor; | |||
long nextCursor = nextCursor(count); | |||
long c = nextCursor - cursor; | |||
if (c > LedgerConsts.MAX_LIST_COUNT) { | |||
throw new IllegalArgumentException( | |||
"Count exceed the upper limit[" + LedgerConsts.MAX_LIST_COUNT + "]!"); | |||
} | |||
DataEntry<Bytes, byte[]>[] entries = getLatestDataEntries(from, (int) c); | |||
cursor = nextCursor; | |||
return entries; | |||
} | |||
return EMPTY_ENTRIES; | |||
} | |||
@Override | |||
public boolean hasNext() { | |||
return cursor < total; | |||
} | |||
} | |||
private class DescDataInterator implements DataIterator<Bytes, byte[]> { | |||
private final long total; | |||
private long cursor; | |||
public DescDataInterator(long total) { | |||
this.total = total; | |||
this.cursor = total - 1; | |||
} | |||
@Override | |||
public void skip(long count) { | |||
cursor = nextCursor(count); | |||
} | |||
private long nextCursor(long skippingCount) { | |||
long c = cursor - skippingCount; | |||
return c < 0 ? -1 : c; | |||
} | |||
@Override | |||
public DataEntry<Bytes, byte[]> next() { | |||
if (hasNext()) { | |||
DataEntry<Bytes, byte[]> entry = getLatestDataEntry(cursor); | |||
cursor = nextCursor(1); | |||
return entry; | |||
} | |||
return null; | |||
} | |||
@Override | |||
public DataEntry<Bytes, byte[]>[] next(int count) { | |||
if (hasNext()) { | |||
long nextCursor = nextCursor(count); | |||
long from = nextCursor + 1; | |||
long c = cursor - nextCursor; | |||
if (c > LedgerConsts.MAX_LIST_COUNT) { | |||
throw new IllegalArgumentException( | |||
"Count exceed the upper limit[" + LedgerConsts.MAX_LIST_COUNT + "]!"); | |||
} | |||
DataEntry<Bytes, byte[]>[] entries = getLatestDataEntries(from, (int) c); | |||
// reverse; | |||
ArrayUtils.reverse(entries); | |||
cursor = nextCursor; | |||
return entries; | |||
} | |||
return EMPTY_ENTRIES; | |||
} | |||
@Override | |||
public boolean hasNext() { | |||
return cursor < total; | |||
} | |||
} | |||
} |
@@ -10,7 +10,7 @@ import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.ContractInfo; | |||
import com.jd.blockchain.ledger.DataAccountKVSetOperation; | |||
import com.jd.blockchain.ledger.DataAccountRegisterOperation; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.KVInfoVO; | |||
import com.jd.blockchain.ledger.LedgerAdminInfo; | |||
import com.jd.blockchain.ledger.LedgerBlock; | |||
@@ -172,17 +172,17 @@ public class ContractLedgerContext implements LedgerContext { | |||
} | |||
@Override | |||
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { | |||
public TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { | |||
return innerQueryService.getDataEntries(ledgerHash, address, keys); | |||
} | |||
@Override | |||
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO) { | |||
public TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO) { | |||
return innerQueryService.getDataEntries(ledgerHash, address, kvInfoVO); | |||
} | |||
@Override | |||
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { | |||
public TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { | |||
return innerQueryService.getDataEntries(ledgerHash, address, fromIndex, count); | |||
} | |||
@@ -3,7 +3,7 @@ package test.com.jd.blockchain.ledger; | |||
import com.jd.blockchain.contract.ContractEventContext; | |||
import com.jd.blockchain.contract.ContractLifecycleAware; | |||
import com.jd.blockchain.contract.EventProcessingAware; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.utils.Bytes; | |||
public class TxTestContractImpl implements TxTestContract, ContractLifecycleAware, EventProcessingAware { | |||
@@ -16,7 +16,7 @@ public class TxTestContractImpl implements TxTestContract, ContractLifecycleAwar | |||
@Override | |||
public boolean testReadable() { | |||
KVDataEntry v1 = eventContext.getLedger().getDataEntries(eventContext.getCurrentLedgerHash(), | |||
TypedKVEntry v1 = eventContext.getLedger().getDataEntries(eventContext.getCurrentLedgerHash(), | |||
dataAddress.toBase58(), KEY)[0]; | |||
String text1 = (String) v1.getValue(); | |||
System.out.printf("k1=%s, version=%s \r\n", text1, v1.getVersion()); | |||
@@ -26,7 +26,7 @@ public class TxTestContractImpl implements TxTestContract, ContractLifecycleAwar | |||
System.out.printf("new value = %s\r\n", newValue); | |||
eventContext.getLedger().dataAccount(dataAddress).setText(KEY, newValue, v1.getVersion()); | |||
KVDataEntry v2 = eventContext.getLedger().getDataEntries(eventContext.getCurrentLedgerHash(), | |||
TypedKVEntry v2 = eventContext.getLedger().getDataEntries(eventContext.getCurrentLedgerHash(), | |||
dataAddress.toBase58(), KEY)[0]; | |||
System.out.printf("---- read new value ----\r\nk1=%s, version=%s \r\n", v2.getValue(), v2.getVersion()); | |||
@@ -48,7 +48,8 @@ public class AccountSetTest { | |||
BlockchainKeypair userKey = BlockchainKeyGenerator.getInstance().generate(); | |||
accset.register(userKey.getAddress(), userKey.getPubKey()); | |||
//尚未提交之前,可以检索到账户的存在,但版本仍然标记为 -1; | |||
MerkleAccount userAcc = accset.getAccount(userKey.getAddress()); | |||
assertNotNull(userAcc); | |||
assertTrue(accset.contains(userKey.getAddress())); | |||
@@ -6,8 +6,9 @@ import com.jd.blockchain.binaryproto.PrimitiveType; | |||
import com.jd.blockchain.consts.DataCodes; | |||
@DataContract(code= DataCodes.CONTRACT_ACCOUNT_HEADER) | |||
public interface ContractInfo extends BlockchainIdentity { | |||
public interface ContractInfo extends BlockchainIdentity, MerkleSnapshot { | |||
@DataField(order=4, primitiveType= PrimitiveType.BYTES) | |||
byte[] getChainCode(); | |||
} |
@@ -1,616 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
import java.math.BigInteger; | |||
import java.util.Date; | |||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.io.ByteArray; | |||
import com.jd.blockchain.utils.io.BytesUtils; | |||
/** | |||
* KV数据项; | |||
* | |||
* <p> | |||
* | |||
* {@link KVDataObject} 被设计为只读对象; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class KVDataObject implements KVDataEntry { | |||
private String key; | |||
private long version; | |||
private BytesValue bytesValue; | |||
public KVDataObject(String key, long version, BytesValue bytesValue) { | |||
this.key = key; | |||
this.version = version < 0 ? -1 : version; | |||
this.bytesValue = bytesValue; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.jd.blockchain.ledger.KVDataEntry#getKey() | |||
*/ | |||
@Override | |||
public String getKey() { | |||
return key; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.jd.blockchain.ledger.KVDataEntry#getVersion() | |||
*/ | |||
@Override | |||
public long getVersion() { | |||
return version; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.jd.blockchain.ledger.KVDataEntry#getType() | |||
*/ | |||
@Override | |||
public DataType getType() { | |||
return bytesValue == null ? DataType.NIL : bytesValue.getType(); | |||
} | |||
@Override | |||
public Object getValue() { | |||
if (bytesValue == null) { | |||
return null; | |||
} | |||
switch (getType()) { | |||
case NIL: | |||
return null; | |||
case TEXT: | |||
return bytesValue.getBytes().toUTF8String(); | |||
case BYTES: | |||
return ByteArray.toHex(bytesValue.getBytes().toBytes()); | |||
case INT64: | |||
return BytesUtils.toLong(bytesValue.getBytes().toBytes()); | |||
case JSON: | |||
return bytesValue.getBytes().toUTF8String(); | |||
case XML: | |||
return bytesValue.getBytes().toUTF8String(); | |||
default: | |||
throw new IllegalStateException("Unsupported value type[" + getType() + "] to resolve!"); | |||
} | |||
} | |||
/** | |||
* 是否为空值; | |||
* <p> | |||
* | |||
* 仅当数据类型 {@link #getType()} 为 {@link PrimitiveType#NIL} 时返回 true,其它情况返回 false; | |||
* <p> | |||
* | |||
* @return | |||
*/ | |||
public boolean isNil() { | |||
return bytesValue == null || DataType.NIL == bytesValue.getType(); | |||
} | |||
/** | |||
* 字节数组形式的原始内容; | |||
* | |||
* @return | |||
*/ | |||
Bytes bytesArray() { | |||
return bytesValue.getBytes(); | |||
} | |||
/** | |||
* 返回 8 位整数值; | |||
* <p> | |||
* | |||
* 仅当数据类型 {@link #getType()} 为 {@link PrimitiveType#INT8} 有效; | |||
* <p> | |||
* | |||
* 无效类型将引发 {@link IllegalStateException} 异常; | |||
* | |||
* @return | |||
*/ | |||
public byte tinyValue() { | |||
if (DataType.INT8 == getType()) { | |||
return bytesValue.getBytes().toBytes()[0]; | |||
} | |||
throw new IllegalStateException(String.format("Expected type [%s], but [%s]", DataType.INT8, getType())); | |||
} | |||
/** | |||
* 返回 16 位整数值; | |||
* <p> | |||
* | |||
* 仅当数据类型 {@link #getType()} 为 {@link PrimitiveType#INT16} 有效; | |||
* <p> | |||
* | |||
* 无效类型将引发 {@link IllegalStateException} 异常; | |||
* | |||
* @return | |||
*/ | |||
public short shortValue() { | |||
if (DataType.INT16 == getType()) { | |||
return BytesUtils.toShort(bytesValue.getBytes().toBytes(), 0); | |||
} | |||
throw new IllegalStateException(String.format("Expected type [%s], but [%s]", DataType.INT16, getType())); | |||
} | |||
/** | |||
* 返回 32 位整数值; | |||
* <p> | |||
* | |||
* 仅当数据类型 {@link #getType()} 为 {@link PrimitiveType#INT32} 有效; | |||
* <p> | |||
* | |||
* 无效类型将引发 {@link IllegalStateException} 异常; | |||
* | |||
* @return | |||
*/ | |||
public int intValue() { | |||
if (DataType.INT32 == getType()) { | |||
return BytesUtils.toInt(bytesValue.getBytes().toBytes(), 0); | |||
} | |||
throw new IllegalStateException(String.format("Expected type [%s], but [%s]", DataType.INT32, getType())); | |||
} | |||
/** | |||
* 返回 64 位整数值; | |||
* <p> | |||
* | |||
* 仅当数据类型 {@link #getType()} 为 {@link PrimitiveType#INT64} 有效; | |||
* <p> | |||
* | |||
* 无效类型将引发 {@link IllegalStateException} 异常; | |||
* | |||
* @return | |||
*/ | |||
public long longValue() { | |||
if (DataType.INT64 == getType()) { | |||
return BytesUtils.toLong(bytesValue.getBytes().toBytes(), 0); | |||
} | |||
throw new IllegalStateException(String.format("Expected type [%s], but [%s]", DataType.INT64, getType())); | |||
} | |||
/** | |||
* 返回大整数值; | |||
* <p> | |||
* | |||
* 仅当数据类型 {@link #getType()} 为 {@link PrimitiveType#BIG_INT} 有效; | |||
* <p> | |||
* | |||
* 无效类型将引发 {@link IllegalStateException} 异常; | |||
* | |||
* @return | |||
*/ | |||
public BigInteger bigIntValue() { | |||
if (DataType.BIG_INT == getType()) { | |||
return new BigInteger(bytesValue.getBytes().toBytes()); | |||
} | |||
throw new IllegalStateException( | |||
String.format("Expected type [%s], but [%s]", DataType.BIG_INT, getType())); | |||
} | |||
/** | |||
* 返回布尔值; | |||
* <p> | |||
* | |||
* 仅当数据类型 {@link #getType()} 为 {@link PrimitiveType#BIG_INT} 有效; | |||
* <p> | |||
* | |||
* 无效类型将引发 {@link IllegalStateException} 异常; | |||
* | |||
* @return | |||
*/ | |||
public boolean boolValue() { | |||
if (DataType.BOOLEAN == getType()) { | |||
return BytesUtils.toBoolean(bytesValue.getBytes().toBytes()[0]); | |||
} | |||
throw new IllegalStateException( | |||
String.format("Expected type [%s], but [%s]", DataType.BOOLEAN, getType())); | |||
} | |||
/** | |||
* 返回日期时间值; | |||
* <p> | |||
* | |||
* 仅当数据类型 {@link #getType()} 为 {@link PrimitiveType#TIMESTAMP} 有效; | |||
* <p> | |||
* | |||
* 无效类型将引发 {@link IllegalStateException} 异常; | |||
* | |||
* @return | |||
*/ | |||
public Date datetimeValue() { | |||
if (DataType.TIMESTAMP == getType()) { | |||
long ts = BytesUtils.toLong(bytesValue.getBytes().toBytes()); | |||
return new Date(ts); | |||
} | |||
throw new IllegalStateException( | |||
String.format("Expected type [%s], but [%s]", DataType.TIMESTAMP, getType())); | |||
} | |||
/** | |||
* 返回大整数值; | |||
* <p> | |||
* | |||
* 仅当数据类型 {@link #getType()} 为 {@link PrimitiveType#TEXT} / | |||
* {@link PrimitiveType#JSON} / {@link PrimitiveType#XML} 有效; | |||
* <p> | |||
* | |||
* 无效类型将引发 {@link IllegalStateException} 异常; | |||
* | |||
* @return | |||
*/ | |||
public String stringValue() { | |||
DataType type = getType(); | |||
if (DataType.TEXT == type || DataType.JSON == type || DataType.XML == type) { | |||
return bytesValue.getBytes().toUTF8String(); | |||
} | |||
throw new IllegalStateException(String.format("Expected type [%s] or [%s] or [%s] , but [%s]", | |||
PrimitiveType.TEXT, DataType.JSON, DataType.XML, type)); | |||
} | |||
// // ---------------- | |||
// public KVDataEntry nextVersionNil() { | |||
// return nilState(key, version + 1); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionBoolean(boolean value) { | |||
// return booleanState(key, version + 1, value); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionTiny(byte value) { | |||
// return tinyState(key, version + 1, value); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionShort(short value) { | |||
// return shortState(key, version + 1, value); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionInt(int value) { | |||
// return intState(key, version + 1, value); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionLong(long value) { | |||
// return longState(key, version + 1, value); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionDatetime(Date value) { | |||
// return datetimeState(key, version + 1, value); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionJson(String value) { | |||
// return jsonState(key, version + 1, value); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionXml(String value) { | |||
// return xmlState(key, version + 1, value); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionBigInt(BigInteger value) { | |||
// return bigIntState(key, version + 1, value); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionText(boolean encrypted, String value) { | |||
// return textState(key, version + 1, encrypted, value); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionBytes(boolean encrypted, byte[] value) { | |||
// return bytesState(key, version + 1, encrypted, value); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionImage(boolean encrypted, byte[] value) { | |||
// return imageState(key, version + 1, encrypted, value); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionVideo(boolean encrypted, byte[] value) { | |||
// return videoState(key, version + 1, encrypted, value); | |||
// } | |||
// | |||
// public KVDataEntry nextVersionLocation(boolean encrypted, byte[] value) { | |||
// return locationState(key, version + 1, encrypted, value); | |||
// } | |||
// // ---------------- | |||
// | |||
// public KVDataEntry newNil() { | |||
// return nilState(key, version); | |||
// } | |||
// | |||
// public KVDataEntry newBoolean(boolean value) { | |||
// return booleanState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newTiny(byte value) { | |||
// return tinyState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newShort(short value) { | |||
// return shortState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newInt(int value) { | |||
// return intState(key, version, value); | |||
// } | |||
// | |||
// public KVDataObject newLong(long value) { | |||
// return longState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newDatetime(Date value) { | |||
// return datetimeState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newJson(String value) { | |||
// return jsonState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newXml(String value) { | |||
// return xmlState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newBigInt(BigInteger value) { | |||
// return bigIntState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newText(boolean encrypted, String value) { | |||
// return textState(key, version, encrypted, value); | |||
// } | |||
// | |||
// public KVDataEntry newBytes(boolean encrypted, byte[] value) { | |||
// return bytesState(key, version, encrypted, value); | |||
// } | |||
// | |||
// public KVDataEntry newImage(boolean encrypted, byte[] value) { | |||
// return imageState(key, version, encrypted, value); | |||
// } | |||
// | |||
// public KVDataEntry newVideo(boolean encrypted, byte[] value) { | |||
// return videoState(key, version, encrypted, value); | |||
// } | |||
// | |||
// public KVDataEntry newLocation(boolean encrypted, byte[] value) { | |||
// return locationState(key, version, encrypted, value); | |||
// } | |||
// | |||
// // ---------------- | |||
// | |||
// public KVDataEntry newNil(long version) { | |||
// return nilState(key, version); | |||
// } | |||
// | |||
// public KVDataEntry newBoolean(long version, boolean value) { | |||
// return booleanState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newTiny(long version, byte value) { | |||
// return tinyState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newShort(long version, short value) { | |||
// return shortState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newInt(long version, int value) { | |||
// return intState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newLong(long version, long value) { | |||
// return longState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newDatetime(long version, Date value) { | |||
// return datetimeState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newJson(long version, String value) { | |||
// return jsonState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newXml(long version, String value) { | |||
// return xmlState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newBigInt(long version, BigInteger value) { | |||
// return bigIntState(key, version, value); | |||
// } | |||
// | |||
// public KVDataEntry newText(long version, boolean encrypted, String value) { | |||
// return textState(key, version, encrypted, value); | |||
// } | |||
// | |||
// public KVDataEntry newBytes(long version, boolean encrypted, byte[] value) { | |||
// return bytesState(key, version, encrypted, value); | |||
// } | |||
// | |||
// public KVDataEntry newImage(long version, boolean encrypted, byte[] value) { | |||
// return imageState(key, version, encrypted, value); | |||
// } | |||
// | |||
// public KVDataEntry newVideo(long version, boolean encrypted, byte[] value) { | |||
// return videoState(key, version, encrypted, value); | |||
// } | |||
// | |||
// public KVDataEntry newLocation(long version, boolean encrypted, byte[] value) { | |||
// return locationState(key, version, encrypted, value); | |||
// } | |||
// | |||
// // ---------------- | |||
// | |||
// public static KVDataEntry booleanState(String key, boolean value) { | |||
// return booleanState(key, -1, value); | |||
// } | |||
// | |||
// public static KVDataEntry tinyState(String key, byte value) { | |||
// return tinyState(key, -1, value); | |||
// } | |||
// | |||
// public static KVDataEntry shortState(String key, short value) { | |||
// return shortState(key, -1, value); | |||
// } | |||
// | |||
// public static KVDataEntry intState(String key, int value) { | |||
// return intState(key, -1, value); | |||
// } | |||
// | |||
// public static KVDataEntry longState(String key, long value) { | |||
// return longState(key, -1, value); | |||
// } | |||
// | |||
// public static KVDataEntry datetimeState(String key, Date value) { | |||
// return datetimeState(key, -1, value); | |||
// } | |||
// | |||
// public static KVDataEntry jsonState(String key, String value) { | |||
// return jsonState(key, -1, value); | |||
// } | |||
// | |||
// public static KVDataEntry xmlState(String key, String value) { | |||
// return xmlState(key, -1, value); | |||
// } | |||
// | |||
// public static KVDataEntry bigIntState(String key, BigInteger value) { | |||
// return bigIntState(key, -1, value); | |||
// } | |||
// | |||
// public static KVDataObject textState(String key, String value) { | |||
// return textState(key, -1, false, value); | |||
// } | |||
// | |||
// public static KVDataEntry bytesState(String key, byte[] value) { | |||
// return bytesState(key, -1, false, value); | |||
// } | |||
// | |||
// public static KVDataEntry imageState(String key, byte[] value) { | |||
// return imageState(key, -1, false, value); | |||
// } | |||
// | |||
// public static KVDataEntry videoState(String key, byte[] value) { | |||
// return videoState(key, -1, false, value); | |||
// } | |||
// | |||
// public static KVDataEntry locationState(String key, byte[] value) { | |||
// return locationState(key, -1, false, value); | |||
// } | |||
// | |||
// // ---------------- | |||
// | |||
// public static KVDataEntry textState(String key, boolean encrypted, String value) { | |||
// return textState(key, -1, encrypted, value); | |||
// } | |||
// | |||
// public static KVDataEntry bytesState(String key, boolean encrypted, byte[] value) { | |||
// return bytesState(key, -1, encrypted, value); | |||
// } | |||
// | |||
// public static KVDataEntry imageState(String key, boolean encrypted, byte[] value) { | |||
// return imageState(key, -1, encrypted, value); | |||
// } | |||
// | |||
// public static KVDataEntry videoState(String key, boolean encrypted, byte[] value) { | |||
// return videoState(key, -1, encrypted, value); | |||
// } | |||
// | |||
// public static KVDataEntry locationState(String key, boolean encrypted, byte[] value) { | |||
// return locationState(key, -1, encrypted, value); | |||
// } | |||
// | |||
// // ---------------------- | |||
// | |||
// public static KVDataEntry nilState(String key) { | |||
// return new KVDataObject(key, ValueType.NIL, -1, false, BytesUtils.EMPTY_BYTES); | |||
// } | |||
// | |||
// public static KVDataEntry nilState(String key, long version) { | |||
// return new KVDataObject(key, ValueType.NIL, version, false, BytesUtils.EMPTY_BYTES); | |||
// } | |||
// | |||
// public static KVDataEntry booleanState(String key, long version, boolean value) { | |||
// byte[] v = { value ? (byte) 1 : (byte) 0 }; | |||
// return new KVDataObject(key, ValueType.BOOLEAN, version, false, v); | |||
// } | |||
// | |||
// public static KVDataEntry tinyState(String key, long version, byte value) { | |||
// byte[] v = { value }; | |||
// return new KVDataObject(key, ValueType.INT8, version, false, v); | |||
// } | |||
// | |||
// public static KVDataEntry shortState(String key, long version, short value) { | |||
// byte[] v = BytesUtils.toBytes(value); | |||
// return new KVDataObject(key, ValueType.INT16, version, false, v); | |||
// } | |||
// | |||
// public static KVDataEntry intState(String key, long version, int value) { | |||
// byte[] v = BytesUtils.toBytes(value); | |||
// return new KVDataObject(key, ValueType.INT32, version, false, v); | |||
// } | |||
// | |||
// public static KVDataObject longState(String key, long version, long value) { | |||
// byte[] v = BytesUtils.toBytes(value); | |||
// return new KVDataObject(key, ValueType.INT64, version, false, v); | |||
// } | |||
// | |||
// public static KVDataEntry datetimeState(String key, long version, Date value) { | |||
// byte[] v = BytesUtils.toBytes(value.getTime()); | |||
// return new KVDataObject(key, ValueType.DATETIME, version, false, v); | |||
// } | |||
// | |||
// public static KVDataObject textState(String key, long version, boolean encrypted, String value) { | |||
// try { | |||
// byte[] v = value.getBytes("UTF-8"); | |||
// return new KVDataObject(key, ValueType.TEXT, version, encrypted, v); | |||
// } catch (UnsupportedEncodingException e) { | |||
// throw new IllegalStateException(e.getMessage(), e); | |||
// } | |||
// } | |||
// | |||
// public static KVDataEntry jsonState(String key, long version, String value) { | |||
// try { | |||
// byte[] v = value.getBytes("UTF-8"); | |||
// return new KVDataObject(key, ValueType.JSON, version, false, v); | |||
// } catch (UnsupportedEncodingException e) { | |||
// throw new IllegalStateException(e.getMessage(), e); | |||
// } | |||
// } | |||
// | |||
// public static KVDataEntry xmlState(String key, long version, String value) { | |||
// try { | |||
// byte[] v = value.getBytes("UTF-8"); | |||
// return new KVDataObject(key, ValueType.XML, version, false, v); | |||
// } catch (UnsupportedEncodingException e) { | |||
// throw new IllegalStateException(e.getMessage(), e); | |||
// } | |||
// } | |||
// | |||
// public static KVDataEntry bigIntState(String key, long version, BigInteger value) { | |||
// byte[] v = value.toByteArray(); | |||
// return new KVDataObject(key, ValueType.BIG_INT, version, false, v); | |||
// } | |||
// | |||
// public static KVDataEntry bytesState(String key, long version, boolean encrypted, byte[] value) { | |||
// return new KVDataObject(key, ValueType.BYTES, version, encrypted, value); | |||
// } | |||
// | |||
// public static KVDataEntry imageState(String key, long version, boolean encrypted, byte[] value) { | |||
// return new KVDataObject(key, ValueType.IMG, version, encrypted, value); | |||
// } | |||
// | |||
// public static KVDataEntry videoState(String key, long version, boolean encrypted, byte[] value) { | |||
// return new KVDataObject(key, ValueType.VIDEO, version, encrypted, value); | |||
// } | |||
// | |||
// public static KVDataEntry locationState(String key, long version, boolean encrypted, byte[] value) { | |||
// return new KVDataObject(key, ValueType.LOCATION, version, encrypted, value); | |||
// } | |||
} |
@@ -0,0 +1,78 @@ | |||
package com.jd.blockchain.ledger; | |||
/** | |||
* 强类型的“键-值”数据对象; | |||
* | |||
* <p> | |||
* | |||
* {@link TypedKVData} 被设计为只读对象; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class TypedKVData implements TypedKVEntry { | |||
private String key; | |||
private long version; | |||
private DataType type; | |||
private Object value; | |||
public TypedKVData(String key, long version, DataType type, Object value) { | |||
this.key = key; | |||
this.version = version; | |||
this.type = type; | |||
this.value = value; | |||
} | |||
public TypedKVData(String key, long version, BytesValue bytesValue) { | |||
this.key = key; | |||
this.version = version; | |||
TypedValue typedValue; | |||
if (bytesValue != null && bytesValue instanceof TypedValue) { | |||
typedValue = (TypedValue) bytesValue; | |||
} else { | |||
typedValue = TypedValue.wrap(bytesValue); | |||
} | |||
this.type = typedValue.getType(); | |||
this.value = typedValue.getValue(); | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.jd.blockchain.ledger.KVDataEntry#getKey() | |||
*/ | |||
@Override | |||
public String getKey() { | |||
return key; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.jd.blockchain.ledger.KVDataEntry#getVersion() | |||
*/ | |||
@Override | |||
public long getVersion() { | |||
return version; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.jd.blockchain.ledger.KVDataEntry#getType() | |||
*/ | |||
@Override | |||
public DataType getType() { | |||
return type; | |||
} | |||
@Override | |||
public Object getValue() { | |||
return value; | |||
} | |||
} |
@@ -1,6 +1,6 @@ | |||
package com.jd.blockchain.ledger; | |||
public interface KVDataEntry { | |||
public interface TypedKVEntry { | |||
/** | |||
* 键名; | |||
@@ -33,4 +33,12 @@ public interface KVDataEntry { | |||
*/ | |||
Object getValue(); | |||
default long longValue() { | |||
if (getType() == DataType.INT64) { | |||
Object value = getValue(); | |||
return value == null ? 0 : (long) value; | |||
} | |||
throw new IllegalStateException(String.format("Expected type [%s], but [%s]", DataType.INT64, getType())); | |||
} | |||
} |
@@ -16,9 +16,9 @@ import com.jd.blockchain.utils.io.BytesUtils; | |||
* | |||
*/ | |||
public class TypedValue implements BytesValue { | |||
public static final BytesValue NIL = new TypedValue(); | |||
private DataType type; | |||
private Bytes value; | |||
@@ -31,16 +31,19 @@ public class TypedValue implements BytesValue { | |||
this.type = type; | |||
this.value = bytes; | |||
} | |||
private TypedValue(BytesValue bytesValue) { | |||
this.type = bytesValue.getType(); | |||
this.value = bytesValue.getBytes(); | |||
if (bytesValue == null) { | |||
this.type = DataType.NIL; | |||
} else { | |||
this.type = bytesValue.getType(); | |||
this.value = bytesValue.getBytes(); | |||
} | |||
} | |||
private TypedValue() { | |||
this.type = DataType.NIL; | |||
} | |||
@Override | |||
public DataType getType() { | |||
@@ -229,8 +232,7 @@ public class TypedValue implements BytesValue { | |||
if (DataType.BIG_INT == type) { | |||
return toBigInteger(); | |||
} | |||
throw new IllegalStateException( | |||
String.format("Type [%s] cannot be convert to BigInteger!", type)); | |||
throw new IllegalStateException(String.format("Type [%s] cannot be convert to BigInteger!", type)); | |||
} | |||
private BigInteger toBigInteger() { | |||
@@ -280,8 +282,7 @@ public class TypedValue implements BytesValue { | |||
if (DataType.TIMESTAMP == type) { | |||
return toDatetime(); | |||
} | |||
throw new IllegalStateException( | |||
String.format("Type [%s] cannot be convert to datetime!", type)); | |||
throw new IllegalStateException(String.format("Type [%s] cannot be convert to datetime!", type)); | |||
} | |||
private Date toDatetime() { | |||
@@ -346,8 +347,7 @@ public class TypedValue implements BytesValue { | |||
if (DataType.HASH_DIGEST == type) { | |||
return toHashDegist(); | |||
} | |||
throw new IllegalStateException( | |||
String.format("Type [%s] cannot be convert to hash digest!", type)); | |||
throw new IllegalStateException(String.format("Type [%s] cannot be convert to hash digest!", type)); | |||
} | |||
private HashDigest toHashDegist() { | |||
@@ -375,14 +375,13 @@ public class TypedValue implements BytesValue { | |||
if (DataType.SIGNATURE_DIGEST == type) { | |||
return toSignatureDigest(); | |||
} | |||
throw new IllegalStateException( | |||
String.format("Type [%s] cannot be convert to signature digest!", type)); | |||
throw new IllegalStateException(String.format("Type [%s] cannot be convert to signature digest!", type)); | |||
} | |||
private SignatureDigest toSignatureDigest() { | |||
return new SignatureDigest(toBytesArray()); | |||
} | |||
public static TypedValue wrap(BytesValue value) { | |||
return new TypedValue(value); | |||
} | |||
@@ -5,7 +5,7 @@ import org.springframework.cglib.core.Block; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.ContractInfo; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.KVInfoVO; | |||
import com.jd.blockchain.ledger.LedgerAdminInfo; | |||
import com.jd.blockchain.ledger.LedgerBlock; | |||
@@ -264,9 +264,9 @@ public interface BlockchainQueryService { | |||
* @param keys | |||
* @return | |||
*/ | |||
KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys); | |||
TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys); | |||
KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO); | |||
TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO); | |||
/** | |||
* 返回指定数据账户中KV数据的总数; <br> | |||
@@ -287,7 +287,7 @@ public interface BlockchainQueryService { | |||
* 如果参数值为 -1,则返回全部的记录;<br> | |||
* @return | |||
*/ | |||
KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count); | |||
TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count); | |||
/** | |||
* 返回合约账户信息; | |||
@@ -3,7 +3,6 @@ package com.jd.blockchain.peer.web; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import com.jd.blockchain.ledger.*; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.web.bind.annotation.PathVariable; | |||
import org.springframework.web.bind.annotation.RequestBody; | |||
@@ -14,6 +13,22 @@ import org.springframework.web.bind.annotation.RestController; | |||
import com.jd.blockchain.contract.ContractException; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.ContractInfo; | |||
import com.jd.blockchain.ledger.KVDataVO; | |||
import com.jd.blockchain.ledger.KVInfoVO; | |||
import com.jd.blockchain.ledger.LedgerAdminInfo; | |||
import com.jd.blockchain.ledger.LedgerBlock; | |||
import com.jd.blockchain.ledger.LedgerInfo; | |||
import com.jd.blockchain.ledger.LedgerMetadata; | |||
import com.jd.blockchain.ledger.LedgerTransaction; | |||
import com.jd.blockchain.ledger.ParticipantNode; | |||
import com.jd.blockchain.ledger.TransactionState; | |||
import com.jd.blockchain.ledger.TypedKVData; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.TypedValue; | |||
import com.jd.blockchain.ledger.UserInfo; | |||
import com.jd.blockchain.ledger.core.ContractAccountQuery; | |||
import com.jd.blockchain.ledger.core.DataAccount; | |||
import com.jd.blockchain.ledger.core.DataAccountQuery; | |||
@@ -23,7 +38,10 @@ import com.jd.blockchain.ledger.core.ParticipantCertData; | |||
import com.jd.blockchain.ledger.core.TransactionQuery; | |||
import com.jd.blockchain.ledger.core.UserAccountQuery; | |||
import com.jd.blockchain.transaction.BlockchainQueryService; | |||
import com.jd.blockchain.utils.ArrayUtils; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.DataEntry; | |||
import com.jd.blockchain.utils.DataIterator; | |||
import com.jd.blockchain.utils.QueryUtil; | |||
@RestController | |||
@@ -72,7 +90,7 @@ public class LedgerQueryController implements BlockchainQueryService { | |||
} | |||
return null; | |||
} | |||
@RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/admininfo") | |||
@Override | |||
public LedgerAdminInfo getLedgerAdminInfo(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { | |||
@@ -337,7 +355,7 @@ public class LedgerQueryController implements BlockchainQueryService { | |||
@RequestMapping(method = { RequestMethod.GET, | |||
RequestMethod.POST }, path = "ledgers/{ledgerHash}/accounts/{address}/entries") | |||
@Override | |||
public KVDataEntry[] getDataEntries(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, | |||
public TypedKVEntry[] getDataEntries(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, | |||
@PathVariable(name = "address") String address, @RequestParam("keys") String... keys) { | |||
if (keys == null || keys.length == 0) { | |||
return null; | |||
@@ -347,15 +365,15 @@ public class LedgerQueryController implements BlockchainQueryService { | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
DataAccount dataAccount = dataAccountSet.getAccount(Bytes.fromBase58(address)); | |||
KVDataEntry[] entries = new KVDataEntry[keys.length]; | |||
TypedKVEntry[] entries = new TypedKVEntry[keys.length]; | |||
long ver; | |||
for (int i = 0; i < entries.length; i++) { | |||
ver = dataAccount.getDataset().getVersion(keys[i]); | |||
if (ver < 0) { | |||
entries[i] = new KVDataObject(keys[i], -1, null); | |||
entries[i] = new TypedKVData(keys[i], -1, null); | |||
} else { | |||
BytesValue value = dataAccount.getDataset().getValue(keys[i], ver); | |||
entries[i] = new KVDataObject(keys[i], ver, value); | |||
entries[i] = new TypedKVData(keys[i], ver, value); | |||
} | |||
} | |||
@@ -365,7 +383,7 @@ public class LedgerQueryController implements BlockchainQueryService { | |||
@RequestMapping(method = { RequestMethod.GET, | |||
RequestMethod.POST }, path = "ledgers/{ledgerHash}/accounts/{address}/entries-version") | |||
@Override | |||
public KVDataEntry[] getDataEntries(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, | |||
public TypedKVEntry[] getDataEntries(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, | |||
@PathVariable(name = "address") String address, @RequestBody KVInfoVO kvInfoVO) { | |||
// parse kvInfoVO; | |||
List<String> keyList = new ArrayList<>(); | |||
@@ -396,21 +414,21 @@ public class LedgerQueryController implements BlockchainQueryService { | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
DataAccount dataAccount = dataAccountSet.getAccount(Bytes.fromBase58(address)); | |||
KVDataEntry[] entries = new KVDataEntry[keys.length]; | |||
TypedKVEntry[] entries = new TypedKVEntry[keys.length]; | |||
long ver = -1; | |||
for (int i = 0; i < entries.length; i++) { | |||
// ver = dataAccount.getDataVersion(Bytes.fromString(keys[i])); | |||
ver = versions[i]; | |||
if (ver < 0) { | |||
entries[i] = new KVDataObject(keys[i], -1, null); | |||
entries[i] = new TypedKVData(keys[i], -1, null); | |||
} else { | |||
if (dataAccount.getDataset().getDataCount() == 0 | |||
|| dataAccount.getDataset().getValue(keys[i], ver) == null) { | |||
// is the address is not exist; the result is null; | |||
entries[i] = new KVDataObject(keys[i], -1, null); | |||
entries[i] = new TypedKVData(keys[i], -1, null); | |||
} else { | |||
BytesValue value = dataAccount.getDataset().getValue(keys[i], ver); | |||
entries[i] = new KVDataObject(keys[i], ver, value); | |||
entries[i] = new TypedKVData(keys[i], ver, value); | |||
} | |||
} | |||
} | |||
@@ -421,7 +439,7 @@ public class LedgerQueryController implements BlockchainQueryService { | |||
@RequestMapping(method = { RequestMethod.GET, | |||
RequestMethod.POST }, path = "ledgers/{ledgerHash}/accounts/address/{address}/entries") | |||
@Override | |||
public KVDataEntry[] getDataEntries(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, | |||
public TypedKVEntry[] getDataEntries(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, | |||
@PathVariable(name = "address") String address, | |||
@RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, | |||
@RequestParam(name = "count", required = false, defaultValue = "-1") int count) { | |||
@@ -431,15 +449,21 @@ public class LedgerQueryController implements BlockchainQueryService { | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
DataAccount dataAccount = dataAccountSet.getAccount(Bytes.fromBase58(address)); | |||
int pages[] = QueryUtil.calFromIndexAndCount(fromIndex, count, (int) dataAccount.getDataset().getDataCount()); | |||
return dataAccount.getDataEntries(pages[0], pages[1]); | |||
// int pages[] = QueryUtil.calFromIndexAndCount(fromIndex, count, (int) dataAccount.getDataset().getDataCount()); | |||
// return dataAccount.getDataEntries(pages[0], pages[1]); | |||
DataIterator<String, TypedValue> iterator = dataAccount.getDataset().iterator(); | |||
iterator.skip(fromIndex); | |||
DataEntry<String, TypedValue>[] dataEntries = iterator.next(count); | |||
TypedKVEntry[] typedKVEntries = ArrayUtils.castTo(dataEntries, TypedKVEntry.class, | |||
e -> e == null ? null : new TypedKVData(e.getKey(), e.getVersion(), e.getValue())); | |||
return typedKVEntries; | |||
} | |||
@RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts/address/{address}/entries/count") | |||
@Override | |||
public long getDataEntriesTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, | |||
@PathVariable(name = "address") String address) { | |||
LedgerQuery ledger = ledgerService.getLedger(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
@@ -451,7 +475,7 @@ public class LedgerQueryController implements BlockchainQueryService { | |||
@RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts/address/{address}") | |||
@Override | |||
public ContractInfo getContract(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, | |||
@PathVariable(name = "address") String address) { | |||
@PathVariable(name = "address") String address) { | |||
LedgerQuery ledger = ledgerService.getLedger(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
ContractAccountQuery contractAccountSet = ledger.getContractAccountSet(block); | |||
@@ -39,14 +39,14 @@ import com.jd.blockchain.utils.io.BytesUtils; | |||
public class ClientResolveUtil { | |||
public static KVDataEntry[] read(KVDataEntry[] kvDataEntries) { | |||
public static TypedKVEntry[] read(TypedKVEntry[] kvDataEntries) { | |||
if (kvDataEntries == null || kvDataEntries.length == 0) { | |||
return kvDataEntries; | |||
} | |||
KVDataEntry[] resolveKvDataEntries = new KVDataEntry[kvDataEntries.length]; | |||
TypedKVEntry[] resolveKvDataEntries = new TypedKVEntry[kvDataEntries.length]; | |||
// kvDataEntries是代理对象,需要处理 | |||
for (int i = 0; i < kvDataEntries.length; i++) { | |||
KVDataEntry kvDataEntry = kvDataEntries[i]; | |||
TypedKVEntry kvDataEntry = kvDataEntries[i]; | |||
String key = kvDataEntry.getKey(); | |||
long version = kvDataEntry.getVersion(); | |||
DataType dataType = kvDataEntry.getType(); | |||
@@ -330,7 +330,7 @@ public class ClientResolveUtil { | |||
} | |||
public static class KvData implements KVDataEntry { | |||
public static class KvData implements TypedKVEntry { | |||
private String key; | |||
@@ -3,7 +3,7 @@ package com.jd.blockchain.sdk.proxy; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.ContractInfo; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.KVInfoVO; | |||
import com.jd.blockchain.ledger.LedgerAdminInfo; | |||
import com.jd.blockchain.ledger.LedgerBlock; | |||
@@ -161,20 +161,20 @@ public abstract class BlockchainServiceProxy implements BlockchainService { | |||
} | |||
@Override | |||
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { | |||
KVDataEntry[] kvDataEntries = getQueryService(ledgerHash).getDataEntries(ledgerHash, address, keys); | |||
public TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { | |||
TypedKVEntry[] kvDataEntries = getQueryService(ledgerHash).getDataEntries(ledgerHash, address, keys); | |||
return ClientResolveUtil.read(kvDataEntries); | |||
} | |||
@Override | |||
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO) { | |||
KVDataEntry[] kvDataEntries = getQueryService(ledgerHash).getDataEntries(ledgerHash, address, kvInfoVO); | |||
public TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO) { | |||
TypedKVEntry[] kvDataEntries = getQueryService(ledgerHash).getDataEntries(ledgerHash, address, kvInfoVO); | |||
return ClientResolveUtil.read(kvDataEntries); | |||
} | |||
@Override | |||
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { | |||
KVDataEntry[] kvDataEntries = getQueryService(ledgerHash).getDataEntries(ledgerHash, address, fromIndex, count); | |||
public TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { | |||
TypedKVEntry[] kvDataEntries = getQueryService(ledgerHash).getDataEntries(ledgerHash, address, fromIndex, count); | |||
return ClientResolveUtil.read(kvDataEntries); | |||
} | |||
@@ -504,13 +504,13 @@ public interface HttpBlockchainQueryService extends BlockchainExtendQueryService | |||
*/ | |||
@HttpAction(method=HttpMethod.POST, path="ledgers/{ledgerHash}/accounts/{address}/entries") | |||
@Override | |||
KVDataEntry[] getDataEntries(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, | |||
TypedKVEntry[] getDataEntries(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, | |||
@PathParam(name="address") String address, | |||
@RequestParam(name="keys", array = true) String... keys); | |||
@HttpAction(method=HttpMethod.POST, path="ledgers/{ledgerHash}/accounts/{address}/entries-version") | |||
@Override | |||
KVDataEntry[] getDataEntries(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, | |||
TypedKVEntry[] getDataEntries(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, | |||
@PathParam(name="address") String address, | |||
@RequestBody KVInfoVO kvInfoVO); | |||
@@ -531,7 +531,7 @@ public interface HttpBlockchainQueryService extends BlockchainExtendQueryService | |||
*/ | |||
@HttpAction(method = HttpMethod.POST, path = "ledgers/{ledgerHash}/accounts/address/{address}/entries") | |||
@Override | |||
KVDataEntry[] getDataEntries(@PathParam(name = "ledgerHash") HashDigest ledgerHash, | |||
TypedKVEntry[] getDataEntries(@PathParam(name = "ledgerHash") HashDigest ledgerHash, | |||
@PathParam(name = "address") String address, | |||
@RequestParam(name = "fromIndex", required = false) int fromIndex, | |||
@RequestParam(name = "count", required = false) int count); | |||
@@ -9,8 +9,8 @@ import com.jd.blockchain.contract.ContractException; | |||
import com.jd.blockchain.contract.EventProcessingAware; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.KVDataObject; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.TypedKVData; | |||
import com.jd.blockchain.utils.Bytes; | |||
/** | |||
@@ -47,16 +47,16 @@ public class AssetContractImpl implements EventProcessingAware, AssetContract { | |||
} | |||
// 查询当前值; | |||
KVDataEntry[] kvEntries = eventContext.getLedger().getDataEntries(currentLedgerHash(), ASSET_ADDRESS, KEY_TOTAL, | |||
TypedKVEntry[] kvEntries = eventContext.getLedger().getDataEntries(currentLedgerHash(), ASSET_ADDRESS, KEY_TOTAL, | |||
assetHolderAddress); | |||
// 计算资产的发行总数; | |||
KVDataObject currTotal = (KVDataObject) kvEntries[0]; | |||
TypedKVData currTotal = (TypedKVData) kvEntries[0]; | |||
long newTotal = currTotal.longValue() + amount; | |||
eventContext.getLedger().dataAccount(ASSET_ADDRESS).setInt64(KEY_TOTAL, newTotal, currTotal.getVersion()); | |||
// 分配到持有者账户; | |||
KVDataObject holderAmount = (KVDataObject) kvEntries[1]; | |||
TypedKVData holderAmount = (TypedKVData) kvEntries[1]; | |||
long newHodlerAmount = holderAmount.longValue() + amount; | |||
eventContext.getLedger().dataAccount(ASSET_ADDRESS) | |||
.setInt64(assetHolderAddress, newHodlerAmount, holderAmount.getVersion()).setText("K2", "info2", -1) | |||
@@ -77,10 +77,10 @@ public class AssetContractImpl implements EventProcessingAware, AssetContract { | |||
checkSignerPermission(fromAddress); | |||
// 查询现有的余额; | |||
KVDataEntry[] origBalances = eventContext.getLedger().getDataEntries(currentLedgerHash(), ASSET_ADDRESS, | |||
TypedKVEntry[] origBalances = eventContext.getLedger().getDataEntries(currentLedgerHash(), ASSET_ADDRESS, | |||
fromAddress, toAddress); | |||
KVDataEntry fromBalanceKV = origBalances[0]; | |||
KVDataEntry toBalanceKV = origBalances[1]; | |||
TypedKVEntry fromBalanceKV = origBalances[0]; | |||
TypedKVEntry toBalanceKV = origBalances[1]; | |||
long fromBalance = fromBalanceKV.getVersion() == -1 ? 0 : (long) fromBalanceKV.getValue(); | |||
long toBalance = toBalanceKV.getVersion() == -1 ? 0 : (long) toBalanceKV.getValue(); | |||
@@ -4,7 +4,7 @@ import com.jd.blockchain.crypto.Crypto; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.LedgerBlock; | |||
import com.jd.blockchain.ledger.LedgerTransaction; | |||
import com.jd.blockchain.ledger.Transaction; | |||
@@ -67,7 +67,7 @@ public class SDKDemo_Query { | |||
// 获取数据; | |||
String commerceAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||
String[] objKeys = new String[] { "x001", "x002" }; | |||
KVDataEntry[] kvData = service.getDataEntries(LEDGER_HASH, commerceAccount, objKeys); | |||
TypedKVEntry[] kvData = service.getDataEntries(LEDGER_HASH, commerceAccount, objKeys); | |||
long payloadVersion = kvData[0].getVersion(); | |||
@@ -6,7 +6,7 @@ import static com.jd.blockchain.transaction.ContractReturnValue.decode; | |||
import com.jd.blockchain.contract.TransferContract; | |||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.PreparedTransaction; | |||
import com.jd.blockchain.ledger.TransactionResponse; | |||
import com.jd.blockchain.ledger.TransactionTemplate; | |||
@@ -106,11 +106,11 @@ public class SDK_Contract_Demo extends SDK_Base_Demo { | |||
} | |||
private long readByKvOperation(String address, String account) { | |||
KVDataEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, address, account); | |||
TypedKVEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, address, account); | |||
if (kvDataEntries == null || kvDataEntries.length == 0) { | |||
throw new IllegalStateException(String.format("Ledger %s Service inner Error !!!", ledgerHash.toBase58())); | |||
} | |||
KVDataEntry kvDataEntry = kvDataEntries[0]; | |||
TypedKVEntry kvDataEntry = kvDataEntries[0]; | |||
if (kvDataEntry.getVersion() == -1) { | |||
return 0L; | |||
} | |||
@@ -79,11 +79,11 @@ public class SDK_Contract_Random_Demo extends SDK_Base_Demo { | |||
} | |||
private long readByKvOperation(String address, String account) { | |||
KVDataEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, address, account); | |||
TypedKVEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, address, account); | |||
if (kvDataEntries == null || kvDataEntries.length == 0) { | |||
throw new IllegalStateException(String.format("Ledger %s Service inner Error !!!", ledgerHash.toBase58())); | |||
} | |||
KVDataEntry kvDataEntry = kvDataEntries[0]; | |||
TypedKVEntry kvDataEntry = kvDataEntries[0]; | |||
if (kvDataEntry.getVersion() == -1) { | |||
return 0L; | |||
} | |||
@@ -107,9 +107,9 @@ public class SDK_InsertData_Demo extends SDK_Base_Demo { | |||
// KVDataEntry[] kvData = blockchainService.getDataEntries(ledgerHash, commerceAccount, objKeys); | |||
// 获取数据账户下所有的KV列表 | |||
KVDataEntry[] kvData = blockchainService.getDataEntries(ledgerHash, commerceAccount, 0, 100); | |||
TypedKVEntry[] kvData = blockchainService.getDataEntries(ledgerHash, commerceAccount, 0, 100); | |||
if (kvData != null && kvData.length > 0) { | |||
for (KVDataEntry kvDatum : kvData) { | |||
for (TypedKVEntry kvDatum : kvData) { | |||
System.out.println("kvData.key=" + kvDatum.getKey()); | |||
System.out.println("kvData.version=" + kvDatum.getVersion()); | |||
System.out.println("kvData.type=" + kvDatum.getType()); | |||
@@ -24,7 +24,7 @@ import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||
import com.jd.blockchain.ledger.DigitalSignature; | |||
import com.jd.blockchain.ledger.EndpointRequest; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.LedgerBlock; | |||
import com.jd.blockchain.ledger.LedgerInfo; | |||
import com.jd.blockchain.ledger.LedgerTransaction; | |||
@@ -149,8 +149,8 @@ public class SDK_GateWay_Query_Test_ { | |||
String commerceAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||
String[] objKeys = new String[] { "x001", "x002" }; | |||
KVDataEntry[] kvData = service.getDataEntries(ledgerHash, commerceAccount, objKeys); | |||
for (KVDataEntry kvDatum : kvData) { | |||
TypedKVEntry[] kvData = service.getDataEntries(ledgerHash, commerceAccount, objKeys); | |||
for (TypedKVEntry kvDatum : kvData) { | |||
System.out.println("kvData.key=" + kvDatum.getKey()); | |||
System.out.println("kvData.version=" + kvDatum.getVersion()); | |||
System.out.println("kvData.value=" + kvDatum.getValue()); | |||
@@ -26,7 +26,7 @@ import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.DataAccountKVSetOperation; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.LedgerBlock; | |||
import com.jd.blockchain.ledger.LedgerInfo; | |||
import com.jd.blockchain.ledger.LedgerInitProperties; | |||
@@ -216,8 +216,8 @@ public class IntegrationTest { | |||
ledgerOfNode0.retrieveLatestBlock(); // 更新内存 | |||
// 先验证应答 | |||
KVDataEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, dataAccountAddress, dataKey); | |||
for (KVDataEntry kvDataEntry : kvDataEntries) { | |||
TypedKVEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, dataAccountAddress, dataKey); | |||
for (TypedKVEntry kvDataEntry : kvDataEntries) { | |||
String valHexText = (String) kvDataEntry.getValue(); | |||
byte[] valBytes = HexUtils.decode(valHexText); | |||
String valText = new String(valBytes); | |||
@@ -286,8 +286,8 @@ public class IntegrationBase { | |||
assertEquals(txResp.getContentHash(), transactionHash); | |||
assertEquals(txResp.getBlockHash(), ledgerRepository.getLatestBlockHash()); | |||
KVDataEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, daAddress, dataKey); | |||
for (KVDataEntry kvDataEntry : kvDataEntries) { | |||
TypedKVEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, daAddress, dataKey); | |||
for (TypedKVEntry kvDataEntry : kvDataEntries) { | |||
assertEquals(dataKey, kvDataEntry.getKey()); | |||
String valHexText = (String) kvDataEntry.getValue(); | |||
assertEquals(dataVal, valHexText); | |||
@@ -26,7 +26,7 @@ import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.DataAccountKVSetOperation; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.LedgerBlock; | |||
import com.jd.blockchain.ledger.LedgerInfo; | |||
import com.jd.blockchain.ledger.LedgerInitProperties; | |||
@@ -302,9 +302,9 @@ public class IntegrationTestAll4Redis { | |||
assertEquals(txResp.getContentHash(), prepTx.getHash()); | |||
assertEquals(txResp.getBlockHash(), ledgerRepository.getLatestBlockHash()); | |||
KVDataEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, dataAccountAddress.toString(), | |||
TypedKVEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, dataAccountAddress.toString(), | |||
dataKey); | |||
for (KVDataEntry kvDataEntry : kvDataEntries) { | |||
for (TypedKVEntry kvDataEntry : kvDataEntries) { | |||
assertEquals(dataKey, kvDataEntry.getKey()); | |||
String valHexText = (String) kvDataEntry.getValue(); | |||
byte[] valBytes = HexUtils.decode(valHexText); | |||
@@ -23,7 +23,7 @@ import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; | |||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||
import com.jd.blockchain.ledger.DataAccountKVSetOperation; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.LedgerBlock; | |||
import com.jd.blockchain.ledger.LedgerInitProperties; | |||
import com.jd.blockchain.ledger.PreparedTransaction; | |||
@@ -214,7 +214,7 @@ public class IntegrationTestDataAccount { | |||
e.printStackTrace(); | |||
} | |||
KVDataEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHashs[0], dataAddr.toBase58(), "A", "B", | |||
TypedKVEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHashs[0], dataAddr.toBase58(), "A", "B", | |||
"C", "D"); | |||
for (int i = 0; i < kvDataEntries.length; i++) { | |||
Object result = kvDataEntries[i].getValue(); | |||
@@ -9,17 +9,29 @@ import java.util.*; | |||
*/ | |||
public abstract class ArrayUtils { | |||
private ArrayUtils() { | |||
} | |||
public static <T, R> R[] castTo(T[] objs, Class<R> clazz, CastFunction<T, R> cf) { | |||
if (objs == null) { | |||
return null; | |||
} | |||
@SuppressWarnings("unchecked") | |||
R[] array = (R[]) Array.newInstance(clazz, objs.length); | |||
for (int i = 0; i < objs.length; i++) { | |||
array[i] = cf.cast(objs[i]); | |||
} | |||
return array; | |||
} | |||
public static <T> T[] singleton(T obj, Class<T> clazz) { | |||
@SuppressWarnings("unchecked") | |||
T[] array = (T[]) Array.newInstance(clazz, 1); | |||
array[0] = obj; | |||
return array; | |||
} | |||
public static <T> T[] toArray(Iterator<T> itr, Class<T> clazz){ | |||
public static <T> T[] toArray(Iterator<T> itr, Class<T> clazz) { | |||
List<T> lst = new LinkedList<T>(); | |||
while (itr.hasNext()) { | |||
T t = (T) itr.next(); | |||
@@ -30,19 +42,19 @@ public abstract class ArrayUtils { | |||
lst.toArray(array); | |||
return array; | |||
} | |||
public static <T> T[] toArray(Collection<T> collection, Class<T> clazz){ | |||
public static <T> T[] toArray(Collection<T> collection, Class<T> clazz) { | |||
@SuppressWarnings("unchecked") | |||
T[] array = (T[]) Array.newInstance(clazz, collection.size()); | |||
collection.toArray(array); | |||
return array; | |||
} | |||
public static <T> List<T> asList(T[] array){ | |||
public static <T> List<T> asList(T[] array) { | |||
return asList(array, 0, array.length); | |||
} | |||
public static <T> Set<T> asSet(T[] array){ | |||
public static <T> Set<T> asSet(T[] array) { | |||
if (array == null || array.length == 0) { | |||
return Collections.emptySet(); | |||
} | |||
@@ -52,8 +64,8 @@ public abstract class ArrayUtils { | |||
} | |||
return set; | |||
} | |||
public static <T> SortedSet<T> asSortedSet(T[] array){ | |||
public static <T> SortedSet<T> asSortedSet(T[] array) { | |||
if (array == null || array.length == 0) { | |||
return Collections.emptySortedSet(); | |||
} | |||
@@ -63,12 +75,12 @@ public abstract class ArrayUtils { | |||
} | |||
return set; | |||
} | |||
public static <T> List<T> asList(T[] array, int fromIndex){ | |||
public static <T> List<T> asList(T[] array, int fromIndex) { | |||
return asList(array, fromIndex, array.length); | |||
} | |||
public static <T> List<T> asList(T[] array, int fromIndex, int toIndex){ | |||
public static <T> List<T> asList(T[] array, int fromIndex, int toIndex) { | |||
if (toIndex < fromIndex) { | |||
throw new IllegalArgumentException("The toIndex less than fromIndex!"); | |||
} | |||
@@ -78,10 +90,33 @@ public abstract class ArrayUtils { | |||
if (toIndex > array.length) { | |||
throw new IllegalArgumentException("The toIndex great than the length of array!"); | |||
} | |||
if (fromIndex == toIndex) { | |||
return Collections.emptyList(); | |||
} | |||
return new ReadonlyArrayListWrapper<T>(array, fromIndex, toIndex); | |||
} | |||
public static interface CastFunction<T, R> { | |||
public R cast(T data); | |||
} | |||
/** | |||
* Reverse all elements of the specified array; <br> | |||
* | |||
* @param <T> | |||
* @param array | |||
*/ | |||
public static <T> void reverse(T[] array) { | |||
if (array == null || array.length < 2) { | |||
return; | |||
} | |||
T t; | |||
for (int i = 0, j = array.length - 1; i < j; i++, j--) { | |||
t = array[i]; | |||
array[i] = array[j]; | |||
array[j] = t; | |||
} | |||
} | |||
} |
@@ -1,7 +1,5 @@ | |||
package com.jd.blockchain.utils; | |||
import java.io.File; | |||
/** | |||
* | |||
* @author zhaogw | |||
@@ -1,7 +1,7 @@ | |||
package com.jd.blockchain.utils; | |||
/** | |||
* 版本化的键值数据项; | |||
* Versioning Key-Value data entry; | |||
* | |||
* @author huanghaiquan | |||
* | |||
@@ -0,0 +1,21 @@ | |||
package com.jd.blockchain.utils; | |||
/** | |||
* 数据迭代器; | |||
* | |||
* @author huanghaiquan | |||
* | |||
* @param <K> | |||
* @param <V> | |||
*/ | |||
public interface DataIterator<K, V> { | |||
void skip(long count); | |||
DataEntry<K, V> next(); | |||
DataEntry<K, V>[] next(int count); | |||
boolean hasNext(); | |||
} |
@@ -1,34 +0,0 @@ | |||
package com.jd.blockchain.utils; | |||
import java.lang.reflect.Type; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.springframework.util.TypeUtils; | |||
public abstract class DataTypeUtils { | |||
private static Map<Class<?>, Class<?>> wrapperTypes = new HashMap<Class<?>, Class<?>>(); | |||
static{ | |||
wrapperTypes.put(long.class, Long.class); | |||
wrapperTypes.put(int.class, Integer.class); | |||
wrapperTypes.put(char.class, Character.class); | |||
wrapperTypes.put(byte.class, Byte.class); | |||
wrapperTypes.put(boolean.class, Boolean.class); | |||
} | |||
public static boolean isAssignable(Type lhsType, Type rhsType) { | |||
boolean assignable = TypeUtils.isAssignable(lhsType, rhsType); | |||
if (assignable) { | |||
return true; | |||
} | |||
if (lhsType instanceof Class) { | |||
Class<?> lhsClass = (Class<?>) lhsType; | |||
} | |||
return false; | |||
} | |||
} |
@@ -1,7 +1,20 @@ | |||
package com.jd.blockchain.utils; | |||
/** | |||
* Key-Value data set; | |||
* | |||
* @author huanghaiquan | |||
* | |||
* @param <K> | |||
* @param <V> | |||
*/ | |||
public interface Dataset<K, V> { | |||
/** | |||
* Total count of data entries; | |||
* | |||
* @return | |||
*/ | |||
long getDataCount(); | |||
/** | |||
@@ -26,17 +39,6 @@ public interface Dataset<K, V> { | |||
*/ | |||
long setValue(K key, V value, long version); | |||
// /** | |||
// * Return the specified version's value;<br> | |||
// * | |||
// * If the key with the specified version doesn't exist, then return null;<br> | |||
// * If the version is specified to -1, then return the latest version's value; | |||
// * | |||
// * @param key | |||
// * @param version | |||
// */ | |||
// byte[] getValue(String key, long version); | |||
/** | |||
* Return the specified version's value;<br> | |||
* | |||
@@ -48,47 +50,51 @@ public interface Dataset<K, V> { | |||
*/ | |||
V getValue(K key, long version); | |||
// /** | |||
// * Return the latest version's value; | |||
// * | |||
// * @param key | |||
// * @return return null if not exist; | |||
// */ | |||
// byte[] getValue(String key); | |||
/** | |||
* Return the latest version's value; | |||
* Return the value of the latest version; | |||
* | |||
* @param key | |||
* @return return null if not exist; | |||
*/ | |||
V getValue(K key); | |||
// /** | |||
// * Return the latest version entry associated the specified key; If the key | |||
// * doesn't exist, then return -1; | |||
// * | |||
// * @param key | |||
// * @return | |||
// */ | |||
// long getVersion(String key); | |||
/** | |||
* Return the latest version entry associated the specified key; If the key | |||
* doesn't exist, then return -1; | |||
* Return the latest version number of the specified key; | |||
* | |||
* @param key | |||
* @return | |||
* @return The version number of the specified key; If the key doesn't exist, | |||
* then return -1; | |||
*/ | |||
long getVersion(K key); | |||
/** | |||
* Return data entry | |||
* | |||
* @param key | |||
* @return Null if the key doesn't exist! | |||
*/ | |||
DataEntry<K, V> getDataEntry(K key); | |||
/** | |||
* | |||
* @param key | |||
* @param version | |||
* @return | |||
*/ | |||
DataEntry<K, V> getDataEntry(K key, long version); | |||
/** | |||
* Ascending iterator; | |||
* | |||
* @return | |||
*/ | |||
DataIterator<K, V> iterator(); | |||
/** | |||
* Descending iterator; | |||
* | |||
* @return | |||
*/ | |||
DataIterator<K, V> iteratorDesc(); | |||
} |
@@ -1,8 +1,12 @@ | |||
package com.jd.blockchain.utils; | |||
/** | |||
* Helper for {@link Dataset}; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class DatasetHelper { | |||
public static final TypeMapper<Bytes, String> UTF8_STRING_BYTES_MAPPER = new TypeMapper<Bytes, String>() { | |||
@@ -29,7 +33,7 @@ public class DatasetHelper { | |||
return Bytes.fromString(t2); | |||
} | |||
}; | |||
/** | |||
* 适配两个不同类型参数的数据集; | |||
* | |||
@@ -121,8 +125,8 @@ public class DatasetHelper { | |||
T2 decode(T1 t1); | |||
} | |||
private static class EmptyMapper<T> implements TypeMapper<T, T>{ | |||
private static class EmptyMapper<T> implements TypeMapper<T, T> { | |||
@Override | |||
public T encode(T t) { | |||
@@ -133,7 +137,7 @@ public class DatasetHelper { | |||
public T decode(T t) { | |||
return t; | |||
} | |||
} | |||
private static class DatasetUpdatingMonitor<K, V> implements Dataset<K, V> { | |||
@@ -186,6 +190,16 @@ public class DatasetHelper { | |||
return dataset.getDataEntry(key, version); | |||
} | |||
@Override | |||
public DataIterator<K, V> iterator() { | |||
return dataset.iterator(); | |||
} | |||
@Override | |||
public DataIterator<K, V> iteratorDesc() { | |||
return dataset.iteratorDesc(); | |||
} | |||
} | |||
/** | |||
@@ -269,6 +283,73 @@ public class DatasetHelper { | |||
return new KeyValueEntry<K2, V2>(key, v, entry.getVersion()); | |||
} | |||
@Override | |||
public DataIterator<K2, V2> iterator() { | |||
DataIterator<K1, V1> it = dataset.iterator(); | |||
return new DataIteratorAdapter<K1, K2, V1, V2>(it, keyMapper, valueMapper); | |||
} | |||
@Override | |||
public DataIterator<K2, V2> iteratorDesc() { | |||
DataIterator<K1, V1> it = dataset.iteratorDesc(); | |||
return new DataIteratorAdapter<K1, K2, V1, V2>(it, keyMapper, valueMapper); | |||
} | |||
} | |||
private static class DataIteratorAdapter<K1, K2, V1, V2> implements DataIterator<K2, V2> { | |||
private DataIterator<K1, V1> iterator; | |||
private TypeMapper<K1, K2> keyMapper; | |||
private TypeMapper<V1, V2> valueMapper; | |||
public DataIteratorAdapter(DataIterator<K1, V1> iterator, TypeMapper<K1, K2> keyMapper, | |||
TypeMapper<V1, V2> valueMapper) { | |||
this.iterator = iterator; | |||
this.keyMapper = keyMapper; | |||
this.valueMapper = valueMapper; | |||
} | |||
@Override | |||
public void skip(long count) { | |||
iterator.skip(count); | |||
} | |||
@Override | |||
public DataEntry<K2, V2> next() { | |||
DataEntry<K1, V1> entry = iterator.next(); | |||
return cast(entry); | |||
} | |||
private DataEntry<K2, V2> cast(DataEntry<K1, V1> entry) { | |||
if (entry == null) { | |||
return null; | |||
} | |||
K2 k = keyMapper.decode(entry.getKey()); | |||
V2 v = valueMapper.decode(entry.getValue()); | |||
return new KeyValueEntry<K2, V2>(k, v, entry.getVersion()); | |||
} | |||
@SuppressWarnings("unchecked") | |||
@Override | |||
public DataEntry<K2, V2>[] next(int count) { | |||
DataEntry<K1, V1>[] entries = iterator.next(count); | |||
if (entries == null) { | |||
return null; | |||
} | |||
if (entries.length == 0) { | |||
return (DataEntry<K2, V2>[]) entries; | |||
} | |||
return ArrayUtils.castTo(entries, DataEntry.class, e -> cast(e)); | |||
} | |||
@Override | |||
public boolean hasNext() { | |||
return iterator.hasNext(); | |||
} | |||
} | |||
private static class KeyValueEntry<K, V> implements DataEntry<K, V> { | |||