@@ -0,0 +1,62 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> | |||||
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出--> | |||||
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数--> | |||||
<configuration status="WARN" monitorInterval="60"> | |||||
<!--先定义所有的appender--> | |||||
<appenders> | |||||
<!--这个输出控制台的配置--> | |||||
<console name="Console" target="SYSTEM_OUT"> | |||||
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> | |||||
<ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/> | |||||
<!--输出日志的格式--> | |||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | |||||
</console> | |||||
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用--> | |||||
<File name="log" fileName="../logs/test.log" append="false"> | |||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | |||||
</File> | |||||
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> | |||||
<RollingFile name="PeerRollingInfo" fileName="../logs/peer.out.info.log" | |||||
filePattern="../logs/$${date:yyyy-MM}/peer.out.info-%d{yyyy-MM-dd}-%i.log"> | |||||
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> | |||||
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> | |||||
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | |||||
<Policies> | |||||
<TimeBasedTriggeringPolicy/> | |||||
<SizeBasedTriggeringPolicy size="100 MB"/> | |||||
</Policies> | |||||
</RollingFile> | |||||
<RollingFile name="PeerRollingWarn" fileName="../logs/peer.out.warn.log" | |||||
filePattern="../logs/$${date:yyyy-MM}/peer.out.warn-%d{yyyy-MM-dd}-%i.log"> | |||||
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> | |||||
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | |||||
<Policies> | |||||
<TimeBasedTriggeringPolicy/> | |||||
<SizeBasedTriggeringPolicy size="100 MB"/> | |||||
</Policies> | |||||
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 --> | |||||
<DefaultRolloverStrategy max="20"/> | |||||
</RollingFile> | |||||
<RollingFile name="PeerRollingError" fileName="../logs/peer.out.error.log" | |||||
filePattern="../logs/$${date:yyyy-MM}/peer.out.error-%d{yyyy-MM-dd}-%i.log"> | |||||
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> | |||||
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | |||||
<Policies> | |||||
<TimeBasedTriggeringPolicy/> | |||||
<SizeBasedTriggeringPolicy size="100 MB"/> | |||||
</Policies> | |||||
</RollingFile> | |||||
</appenders> | |||||
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--> | |||||
<loggers> | |||||
<!--过滤掉spring的一些DEBUG信息--> | |||||
<logger name="org.springframework" level="INFO"></logger> | |||||
<root level="all"> | |||||
<appender-ref ref="Console"/> | |||||
<appender-ref ref="PeerRollingInfo"/> | |||||
<appender-ref ref="PeerRollingWarn"/> | |||||
<appender-ref ref="PeerRollingError"/> | |||||
</root> | |||||
</loggers> | |||||
</configuration> |
@@ -63,7 +63,7 @@ | |||||
<scope>test</scope> | <scope>test</scope> | ||||
</dependency> | </dependency> | ||||
</dependencies> | |||||
</dependencies> | |||||
<build> | <build> | ||||
<plugins> | <plugins> | ||||
@@ -78,42 +78,32 @@ | |||||
</plugins> | </plugins> | ||||
</build> | </build> | ||||
<!--<build> | |||||
<plugins> | |||||
<plugin> | |||||
<groupId>org.apache.maven.plugins</groupId> | |||||
<artifactId>maven-surefire-plugin</artifactId> | |||||
<version>2.5</version> | |||||
<configuration> | |||||
<excludes> | |||||
<exclude>**/TransactionBatchProcessorTest.java</exclude> | |||||
</excludes> | |||||
</configuration> | |||||
</plugin> | |||||
</plugins> | |||||
</build>--> | |||||
<!--<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> | |||||
<artifactId>maven-surefire-plugin</artifactId> <version>2.5</version> <configuration> | |||||
<excludes> <exclude>**/TransactionBatchProcessorTest.java</exclude> </excludes> | |||||
</configuration> </plugin> </plugins> </build> --> | |||||
<!--<build>--> | |||||
<!--<plugins>--> | |||||
<!--<plugin>--> | |||||
<!--<groupId>org.apache.maven.plugins</groupId>--> | |||||
<!--<artifactId>maven-compiler-plugin</artifactId>--> | |||||
<!--<version>3.1</version>--> | |||||
<!--<configuration>--> | |||||
<!--<source>1.8</source>--> | |||||
<!--<target>1.8</target>--> | |||||
<!--<encoding>UTF-8</encoding>--> | |||||
<!--<compilerArgs>--> | |||||
<!--<!–<arg>-verbose</arg>–>--> | |||||
<!--<!–<arg>-Xlint:unchecked</arg>–>--> | |||||
<!--<!–<arg>-Xlint:deprecation</arg>–>--> | |||||
<!--<!–<arg>-bootclasspath</arg>–>--> | |||||
<!--<!–<arg>${env.JAVA_HOME}/jre/lib/rt.jar</arg>–>--> | |||||
<!--<arg>-extdirs</arg>--> | |||||
<!--<arg>${project.basedir}/../contract/contract-libs;$JAVA_HOME/jre/lib/ext</arg>--> | |||||
<!--</compilerArgs>--> | |||||
<!--</configuration>--> | |||||
<!--</plugin>--> | |||||
<!--</plugins>--> | |||||
<!--</build>--> | |||||
<!--<build> --> | |||||
<!--<plugins> --> | |||||
<!--<plugin> --> | |||||
<!--<groupId>org.apache.maven.plugins</groupId> --> | |||||
<!--<artifactId>maven-compiler-plugin</artifactId> --> | |||||
<!--<version>3.1</version> --> | |||||
<!--<configuration> --> | |||||
<!--<source>1.8</source> --> | |||||
<!--<target>1.8</target> --> | |||||
<!--<encoding>UTF-8</encoding> --> | |||||
<!--<compilerArgs> --> | |||||
<!--<!–<arg>-verbose</arg>–> --> | |||||
<!--<!–<arg>-Xlint:unchecked</arg>–> --> | |||||
<!--<!–<arg>-Xlint:deprecation</arg>–> --> | |||||
<!--<!–<arg>-bootclasspath</arg>–> --> | |||||
<!--<!–<arg>${env.JAVA_HOME}/jre/lib/rt.jar</arg>–> --> | |||||
<!--<arg>-extdirs</arg> --> | |||||
<!--<arg>${project.basedir}/../contract/contract-libs;$JAVA_HOME/jre/lib/ext</arg> --> | |||||
<!--</compilerArgs> --> | |||||
<!--</configuration> --> | |||||
<!--</plugin> --> | |||||
<!--</plugins> --> | |||||
<!--</build> --> | |||||
</project> | </project> |
@@ -1,5 +1,6 @@ | |||||
package com.jd.blockchain.ledger.core; | package com.jd.blockchain.ledger.core; | ||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.ledger.LedgerBlock; | import com.jd.blockchain.ledger.LedgerBlock; | ||||
import com.jd.blockchain.ledger.LedgerException; | import com.jd.blockchain.ledger.LedgerException; | ||||
import com.jd.blockchain.ledger.LedgerTransaction; | import com.jd.blockchain.ledger.LedgerTransaction; | ||||
@@ -18,6 +19,20 @@ import com.jd.blockchain.ledger.TransactionRequest; | |||||
*/ | */ | ||||
public interface LedgerEditor { | public interface LedgerEditor { | ||||
/** | |||||
* 账本Hash; | |||||
* | |||||
* @return | |||||
*/ | |||||
HashDigest getLedgerHash(); | |||||
/** | |||||
* 新区块的高度; | |||||
* | |||||
* @return | |||||
*/ | |||||
long getBlockHeight(); | |||||
/** | /** | ||||
* 开始新事务;<br> | * 开始新事务;<br> | ||||
* | * | ||||
@@ -32,7 +47,8 @@ public interface LedgerEditor { | |||||
* 或者全部回滚(通过方法 {@link LedgerTransactionContext#rollback()}),以此实现原子性写入; | * 或者全部回滚(通过方法 {@link LedgerTransactionContext#rollback()}),以此实现原子性写入; | ||||
* <p> | * <p> | ||||
* | * | ||||
* 每一次事务性的账本写入操作在提交后,都会记录该事务相关的系统全局快照,以交易对象 {@link LedgerTransaction} 进行保存;<p> | |||||
* 每一次事务性的账本写入操作在提交后,都会记录该事务相关的系统全局快照,以交易对象 {@link LedgerTransaction} 进行保存; | |||||
* <p> | |||||
* | * | ||||
* 注:方法不解析、不执行交易中的操作; | * 注:方法不解析、不执行交易中的操作; | ||||
* | * | ||||
@@ -401,7 +401,7 @@ public class LedgerRepositoryImpl implements LedgerRepository { | |||||
"A new block is in process, cann't create another one until it finish by committing or canceling."); | "A new block is in process, cann't create another one until it finish by committing or canceling."); | ||||
} | } | ||||
LedgerBlock previousBlock = getLatestBlock(); | LedgerBlock previousBlock = getLatestBlock(); | ||||
LedgerTransactionalEditor editor = LedgerTransactionalEditor.createEditor( | |||||
LedgerTransactionalEditor editor = LedgerTransactionalEditor.createEditor(ledgerHash, | |||||
getAdminInfo().getMetadata().getSetting(), previousBlock, keyPrefix, exPolicyStorage, | getAdminInfo().getMetadata().getSetting(), previousBlock, keyPrefix, exPolicyStorage, | ||||
versioningStorage); | versioningStorage); | ||||
NewBlockCommittingMonitor committingMonitor = new NewBlockCommittingMonitor(editor, this); | NewBlockCommittingMonitor committingMonitor = new NewBlockCommittingMonitor(editor, this); | ||||
@@ -479,7 +479,7 @@ public class LedgerRepositoryImpl implements LedgerRepository { | |||||
} | } | ||||
static TransactionSet newTransactionSet(LedgerSetting ledgerSetting, String keyPrefix, | static TransactionSet newTransactionSet(LedgerSetting ledgerSetting, String keyPrefix, | ||||
ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { | |||||
ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { | |||||
// TransactionSet transactionSet = new | // TransactionSet transactionSet = new | ||||
// TransactionSet(ledgerSetting.getCryptoSetting(), | // TransactionSet(ledgerSetting.getCryptoSetting(), | ||||
// PrefixAppender.prefix(TRANSACTION_SET_PREFIX, ledgerExStorage), | // PrefixAppender.prefix(TRANSACTION_SET_PREFIX, ledgerExStorage), | ||||
@@ -576,6 +576,16 @@ public class LedgerRepositoryImpl implements LedgerRepository { | |||||
this.ledgerRepo = ledgerRepo; | this.ledgerRepo = ledgerRepo; | ||||
} | } | ||||
@Override | |||||
public HashDigest getLedgerHash() { | |||||
return editor.getLedgerHash(); | |||||
} | |||||
@Override | |||||
public long getBlockHeight() { | |||||
return editor.getBlockHeight(); | |||||
} | |||||
@Override | @Override | ||||
public LedgerTransactionContext newTransaction(TransactionRequest txRequest) { | public LedgerTransactionContext newTransaction(TransactionRequest txRequest) { | ||||
return editor.newTransaction(txRequest); | return editor.newTransaction(txRequest); | ||||
@@ -26,6 +26,11 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
System.out.println("------ [[ parallel-dbwrite=" + PARALLEL_DB_WRITE + " ]] ------"); | System.out.println("------ [[ parallel-dbwrite=" + PARALLEL_DB_WRITE + " ]] ------"); | ||||
} | } | ||||
/** | |||||
* 账本Hash,创世区块的编辑器则返回 null; | |||||
*/ | |||||
private HashDigest ledgerHash; | |||||
private final String ledgerKeyPrefix; | private final String ledgerKeyPrefix; | ||||
private CryptoSetting cryptoSetting; | private CryptoSetting cryptoSetting; | ||||
@@ -49,8 +54,9 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
private LedgerDataContext newTxCtx; | private LedgerDataContext newTxCtx; | ||||
private LedgerTransactionalEditor(CryptoSetting cryptoSetting, LedgerBlockData newlyBlock, | |||||
private LedgerTransactionalEditor(HashDigest ledgerHash, CryptoSetting cryptoSetting, LedgerBlockData newlyBlock, | |||||
StagedSnapshot startingPoint, String ledgerKeyPrefix, BufferedKVStorage bufferedStorage) { | StagedSnapshot startingPoint, String ledgerKeyPrefix, BufferedKVStorage bufferedStorage) { | ||||
this.ledgerHash = ledgerHash; | |||||
this.ledgerKeyPrefix = ledgerKeyPrefix; | this.ledgerKeyPrefix = ledgerKeyPrefix; | ||||
this.cryptoSetting = cryptoSetting; | this.cryptoSetting = cryptoSetting; | ||||
this.newlyBlock = newlyBlock; | this.newlyBlock = newlyBlock; | ||||
@@ -59,8 +65,9 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
this.stagedSnapshots.push(startingPoint); | this.stagedSnapshots.push(startingPoint); | ||||
} | } | ||||
public static LedgerTransactionalEditor createEditor(LedgerSetting ledgerSetting, LedgerBlock previousBlock, | |||||
String ledgerKeyPrefix, ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { | |||||
public static LedgerTransactionalEditor createEditor(HashDigest ledgerHash, LedgerSetting ledgerSetting, | |||||
LedgerBlock previousBlock, String ledgerKeyPrefix, ExPolicyKVStorage ledgerExStorage, | |||||
VersioningKVStorage ledgerVerStorage) { | |||||
// new block; | // new block; | ||||
LedgerBlockData currBlock = new LedgerBlockData(previousBlock.getHeight() + 1, previousBlock.getLedgerHash(), | LedgerBlockData currBlock = new LedgerBlockData(previousBlock.getHeight() + 1, previousBlock.getLedgerHash(), | ||||
previousBlock.getHash()); | previousBlock.getHash()); | ||||
@@ -71,7 +78,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
StagedSnapshot startingPoint = new TxSnapshot(previousBlock, previousBlock.getTransactionSetHash()); | StagedSnapshot startingPoint = new TxSnapshot(previousBlock, previousBlock.getTransactionSetHash()); | ||||
// instantiate editor; | // instantiate editor; | ||||
return new LedgerTransactionalEditor(ledgerSetting.getCryptoSetting(), currBlock, startingPoint, | |||||
return new LedgerTransactionalEditor(ledgerHash, ledgerSetting.getCryptoSetting(), currBlock, startingPoint, | |||||
ledgerKeyPrefix, txStagedStorage); | ledgerKeyPrefix, txStagedStorage); | ||||
} | } | ||||
@@ -81,7 +88,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
StagedSnapshot startingPoint = new GenesisSnapshot(initSetting); | StagedSnapshot startingPoint = new GenesisSnapshot(initSetting); | ||||
// init storage; | // init storage; | ||||
BufferedKVStorage txStagedStorage = new BufferedKVStorage(ledgerExStorage, ledgerVerStorage, false); | BufferedKVStorage txStagedStorage = new BufferedKVStorage(ledgerExStorage, ledgerVerStorage, false); | ||||
return new LedgerTransactionalEditor(initSetting.getCryptoSetting(), genesisBlock, startingPoint, | |||||
return new LedgerTransactionalEditor(null, initSetting.getCryptoSetting(), genesisBlock, startingPoint, | |||||
ledgerKeyPrefix, txStagedStorage); | ledgerKeyPrefix, txStagedStorage); | ||||
} | } | ||||
@@ -102,12 +109,39 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
// return lastTxCtx.getDataSet(); | // return lastTxCtx.getDataSet(); | ||||
// } | // } | ||||
public LedgerBlock getNewlyBlock() { | |||||
LedgerBlock getNewlyBlock() { | |||||
return newlyBlock; | return newlyBlock; | ||||
} | } | ||||
@Override | |||||
public long getBlockHeight() { | |||||
return newlyBlock.getHeight(); | |||||
} | |||||
@Override | |||||
public HashDigest getLedgerHash() { | |||||
return ledgerHash; | |||||
} | |||||
private boolean isRequestedLedger(TransactionRequest txRequest) { | |||||
HashDigest reqLedgerHash = txRequest.getTransactionContent().getLedgerHash(); | |||||
if (ledgerHash == reqLedgerHash) { | |||||
return true; | |||||
} | |||||
if (ledgerHash == null || reqLedgerHash == null) { | |||||
return false; | |||||
} | |||||
return ledgerHash.equals(reqLedgerHash); | |||||
} | |||||
@Override | @Override | ||||
public LedgerTransactionContext newTransaction(TransactionRequest txRequest) { | public LedgerTransactionContext newTransaction(TransactionRequest txRequest) { | ||||
// 验证账本是否; | |||||
if (!isRequestedLedger(txRequest)) { | |||||
throw new LedgerException("This ledger is not the target ledger of transaction request[" | |||||
+ txRequest.getTransactionContent().getHash() + "]!"); | |||||
} | |||||
checkState(); | checkState(); | ||||
// TODO:验证交易签名; | // TODO:验证交易签名; | ||||
@@ -9,11 +9,13 @@ import org.slf4j.LoggerFactory; | |||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.BytesValue; | import com.jd.blockchain.ledger.BytesValue; | ||||
import com.jd.blockchain.ledger.DigitalSignature; | |||||
import com.jd.blockchain.ledger.LedgerBlock; | import com.jd.blockchain.ledger.LedgerBlock; | ||||
import com.jd.blockchain.ledger.LedgerException; | import com.jd.blockchain.ledger.LedgerException; | ||||
import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
import com.jd.blockchain.ledger.OperationResult; | import com.jd.blockchain.ledger.OperationResult; | ||||
import com.jd.blockchain.ledger.OperationResultData; | import com.jd.blockchain.ledger.OperationResultData; | ||||
import com.jd.blockchain.ledger.TransactionContent; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
import com.jd.blockchain.ledger.TransactionResponse; | import com.jd.blockchain.ledger.TransactionResponse; | ||||
import com.jd.blockchain.ledger.TransactionState; | import com.jd.blockchain.ledger.TransactionState; | ||||
@@ -26,6 +28,9 @@ import com.jd.blockchain.ledger.core.TransactionRequestContext; | |||||
import com.jd.blockchain.service.TransactionBatchProcess; | import com.jd.blockchain.service.TransactionBatchProcess; | ||||
import com.jd.blockchain.service.TransactionBatchResult; | import com.jd.blockchain.service.TransactionBatchResult; | ||||
import com.jd.blockchain.service.TransactionBatchResultHandle; | import com.jd.blockchain.service.TransactionBatchResultHandle; | ||||
import com.jd.blockchain.transaction.TxBuilder; | |||||
import com.jd.blockchain.transaction.TxRequestBuilder; | |||||
import com.jd.blockchain.transaction.TxResponseMessage; | |||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
public class TransactionBatchProcessor implements TransactionBatchProcess { | public class TransactionBatchProcessor implements TransactionBatchProcess { | ||||
@@ -50,12 +55,9 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
private TransactionBatchResult batchResult; | private TransactionBatchResult batchResult; | ||||
/** | /** | ||||
* @param newBlockEditor | |||||
* 新区块的数据编辑器; | |||||
* @param previousBlockDataset | |||||
* 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | |||||
* @param opHandles | |||||
* 操作处理对象注册表; | |||||
* @param newBlockEditor 新区块的数据编辑器; | |||||
* @param previousBlockDataset 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | |||||
* @param opHandles 操作处理对象注册表; | |||||
*/ | */ | ||||
public TransactionBatchProcessor(LedgerEditor newBlockEditor, LedgerDataSet previousBlockDataset, | public TransactionBatchProcessor(LedgerEditor newBlockEditor, LedgerDataSet previousBlockDataset, | ||||
OperationHandleRegisteration opHandles, LedgerService ledgerService) { | OperationHandleRegisteration opHandles, LedgerService ledgerService) { | ||||
@@ -65,6 +67,18 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
this.ledgerService = ledgerService; | this.ledgerService = ledgerService; | ||||
} | } | ||||
private boolean isRequestedLedger(TransactionRequest txRequest) { | |||||
HashDigest currLedgerHash = newBlockEditor.getLedgerHash(); | |||||
HashDigest reqLedgerHash = txRequest.getTransactionContent().getLedgerHash(); | |||||
if (currLedgerHash == reqLedgerHash) { | |||||
return true; | |||||
} | |||||
if (currLedgerHash == null || reqLedgerHash == null) { | |||||
return false; | |||||
} | |||||
return currLedgerHash.equals(reqLedgerHash); | |||||
} | |||||
/* | /* | ||||
* (non-Javadoc) | * (non-Javadoc) | ||||
* | * | ||||
@@ -74,14 +88,61 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
*/ | */ | ||||
@Override | @Override | ||||
public TransactionResponse schedule(TransactionRequest request) { | public TransactionResponse schedule(TransactionRequest request) { | ||||
// 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | |||||
LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | |||||
TransactionState result; | |||||
TransactionResponse resp; | |||||
try { | |||||
if (!isRequestedLedger(request)) { | |||||
// 抛弃不属于当前账本的交易请求; | |||||
resp = discard(request, TransactionState.DISCARD_BY_WRONG_LEDGER); | |||||
responseList.add(resp); | |||||
return resp; | |||||
} | |||||
if (!verifyTxContent(request)) { | |||||
// 抛弃哈希和签名校验失败的交易请求; | |||||
resp = discard(request, TransactionState.DISCARD_BY_WRONG_CONTENT_SIGNATURE); | |||||
responseList.add(resp); | |||||
return resp; | |||||
} | |||||
List<OperationResult> operationResults = new ArrayList<>(); | |||||
LOGGER.debug("Start handling transaction... --[BlockHeight=%s][RequestHash=%s][TxHash=%s]", | |||||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash()); | |||||
// 创建交易上下文; | |||||
// 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | |||||
LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | |||||
// 处理交易; | |||||
resp = handleTx(request, txCtx); | |||||
LOGGER.debug("Complete handling transaction. --[BlockHeight=%s][RequestHash=%s][TxHash=%s]", | |||||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash()); | |||||
responseList.add(resp); | |||||
return resp; | |||||
} catch (Exception e) { | |||||
// 抛弃发生处理异常的交易请求; | |||||
resp = discard(request, TransactionState.SYSTEM_ERROR); | |||||
LOGGER.error(String.format( | |||||
"Discard transaction rollback caused by the system exception! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||||
e.getMessage()), e); | |||||
responseList.add(resp); | |||||
return resp; | |||||
} | |||||
} | |||||
/** | |||||
* 处理交易;<br> | |||||
* | |||||
* 此方法会处理所有的异常,以不同结果的 {@link TransactionResponse} 返回; | |||||
* | |||||
* @param request | |||||
* @param txCtx | |||||
* @return | |||||
*/ | |||||
private TransactionResponse handleTx(TransactionRequest request, LedgerTransactionContext txCtx) { | |||||
TransactionState result; | |||||
List<OperationResult> operationResults = new ArrayList<>(); | |||||
try { | try { | ||||
LedgerDataSet dataset = txCtx.getDataSet(); | LedgerDataSet dataset = txCtx.getDataSet(); | ||||
TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | ||||
// TODO: 验证签名者的有效性; | // TODO: 验证签名者的有效性; | ||||
@@ -101,7 +162,8 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
OperationHandleContext handleContext = new OperationHandleContext() { | OperationHandleContext handleContext = new OperationHandleContext() { | ||||
@Override | @Override | ||||
public void handle(Operation operation) { | public void handle(Operation operation) { | ||||
//assert; Instance of operation are one of User related operations or DataAccount related operations; | |||||
// assert; Instance of operation are one of User related operations or | |||||
// DataAccount related operations; | |||||
OperationHandle hdl = opHandles.getHandle(operation.getClass()); | OperationHandle hdl = opHandles.getHandle(operation.getClass()); | ||||
hdl.process(operation, dataset, reqCtx, previousBlockDataset, this, ledgerService); | hdl.process(operation, dataset, reqCtx, previousBlockDataset, this, ledgerService); | ||||
} | } | ||||
@@ -110,7 +172,8 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
int opIndex = 0; | int opIndex = 0; | ||||
for (Operation op : ops) { | for (Operation op : ops) { | ||||
opHandle = opHandles.getHandle(op.getClass()); | opHandle = opHandles.getHandle(op.getClass()); | ||||
BytesValue opResult = opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | |||||
BytesValue opResult = opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, | |||||
ledgerService); | |||||
if (opResult != null) { | if (opResult != null) { | ||||
operationResults.add(new OperationResultData(opIndex, opResult)); | operationResults.add(new OperationResultData(opIndex, opResult)); | ||||
} | } | ||||
@@ -119,19 +182,22 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
// 提交交易(事务); | // 提交交易(事务); | ||||
result = TransactionState.SUCCESS; | result = TransactionState.SUCCESS; | ||||
txCtx.commit(result, operationResults); | txCtx.commit(result, operationResults); | ||||
} catch (LedgerException e) { | } catch (LedgerException e) { | ||||
// TODO: 识别更详细的异常类型以及执行对应的处理; | // TODO: 识别更详细的异常类型以及执行对应的处理; | ||||
result = TransactionState.LEDGER_ERROR; | result = TransactionState.LEDGER_ERROR; | ||||
txCtx.discardAndCommit(TransactionState.LEDGER_ERROR, operationResults); | txCtx.discardAndCommit(TransactionState.LEDGER_ERROR, operationResults); | ||||
LOGGER.warn(String.format("Transaction rollback caused by the ledger exception! --[TxHash=%s] --%s", | |||||
request.getHash().toBase58(), e.getMessage()), e); | |||||
LOGGER.error(String.format( | |||||
"Transaction rollback caused by the ledger exception! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||||
e.getMessage()), e); | |||||
} catch (Exception e) { | } catch (Exception e) { | ||||
result = TransactionState.SYSTEM_ERROR; | result = TransactionState.SYSTEM_ERROR; | ||||
txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR, operationResults); | txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR, operationResults); | ||||
LOGGER.warn(String.format("Transaction rollback caused by the system exception! --[TxHash=%s] --%s", | |||||
request.getHash().toBase58(), e.getMessage()), e); | |||||
LOGGER.error(String.format( | |||||
"Transaction rollback caused by the system exception! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||||
e.getMessage()), e); | |||||
} | } | ||||
TxResponseHandle resp = new TxResponseHandle(request, result); | TxResponseHandle resp = new TxResponseHandle(request, result); | ||||
@@ -139,9 +205,50 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
OperationResult[] operationResultArray = new OperationResult[operationResults.size()]; | OperationResult[] operationResultArray = new OperationResult[operationResults.size()]; | ||||
resp.setOperationResults(operationResults.toArray(operationResultArray)); | resp.setOperationResults(operationResults.toArray(operationResultArray)); | ||||
} | } | ||||
return resp; | |||||
} | |||||
responseList.add(resp); | |||||
private boolean verifyTxContent(TransactionRequest request) { | |||||
TransactionContent txContent = request.getTransactionContent(); | |||||
if (!TxBuilder.verifyTxContentHash(txContent, txContent.getHash())) { | |||||
return false; | |||||
} | |||||
DigitalSignature[] endpointSignatures = request.getEndpointSignatures(); | |||||
if (endpointSignatures != null) { | |||||
for (DigitalSignature signature : endpointSignatures) { | |||||
if (!TxRequestBuilder.verifyHashSignature(txContent.getHash(), signature.getDigest(), | |||||
signature.getPubKey())) { | |||||
return false; | |||||
} | |||||
} | |||||
} | |||||
DigitalSignature[] nodeSignatures = request.getNodeSignatures(); | |||||
if (nodeSignatures != null) { | |||||
for (DigitalSignature signature : nodeSignatures) { | |||||
if (!TxRequestBuilder.verifyHashSignature(txContent.getHash(), signature.getDigest(), | |||||
signature.getPubKey())) { | |||||
return false; | |||||
} | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
/** | |||||
* 直接丢弃交易; | |||||
* | |||||
* @param request | |||||
* @param txState | |||||
* @return 丢弃交易的回复;只包含原始请求中的交易内容哈希和交易被丢弃的原因,而不包含区块信息; | |||||
*/ | |||||
private TransactionResponse discard(TransactionRequest request, TransactionState txState) { | |||||
// 丢弃交易的回复;只返回请求的交易内容哈希和交易被丢弃的原因, | |||||
TxResponseMessage resp = new TxResponseMessage(request.getTransactionContent().getHash()); | |||||
resp.setExecutionState(txState); | |||||
LOGGER.error("Discard transaction request! --[BlockHeight=%s][RequestHash=%s][TxHash=%s][ResponseState=%s]", | |||||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||||
resp.getExecutionState()); | |||||
return resp; | return resp; | ||||
} | } | ||||
@@ -184,7 +184,7 @@ public class ContractInvokingTest { | |||||
// 创建账本; | // 创建账本; | ||||
LedgerEditor ldgEdt = LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | LedgerEditor ldgEdt = LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | ||||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||||
TransactionRequest genesisTxReq = LedgerTestUtils.createLedgerInitTxRequest(partiKeys); | |||||
LedgerTransactionContext genisisTxCtx = ldgEdt.newTransaction(genesisTxReq); | LedgerTransactionContext genisisTxCtx = ldgEdt.newTransaction(genesisTxReq); | ||||
LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | ||||
@@ -16,6 +16,7 @@ import com.jd.blockchain.crypto.SignatureFunction; | |||||
import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; | import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; | ||||
import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; | import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; | ||||
import com.jd.blockchain.crypto.service.sm.SMCryptoService; | import com.jd.blockchain.crypto.service.sm.SMCryptoService; | ||||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||||
import com.jd.blockchain.ledger.BlockchainKeypair; | import com.jd.blockchain.ledger.BlockchainKeypair; | ||||
import com.jd.blockchain.ledger.BytesValue; | import com.jd.blockchain.ledger.BytesValue; | ||||
import com.jd.blockchain.ledger.DataType; | import com.jd.blockchain.ledger.DataType; | ||||
@@ -52,6 +53,13 @@ public class LedgerEditerTest { | |||||
private static final String LEDGER_KEY_PREFIX = "LDG://"; | private static final String LEDGER_KEY_PREFIX = "LDG://"; | ||||
private SignatureFunction signatureFunction; | private SignatureFunction signatureFunction; | ||||
private BlockchainKeypair parti0 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair parti1 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair parti3 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair[] participants = { parti0, parti1, parti2, parti3 }; | |||||
/** | /** | ||||
* 初始化一个; | * 初始化一个; | ||||
*/ | */ | ||||
@@ -74,8 +82,8 @@ public class LedgerEditerTest { | |||||
return LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | return LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | ||||
} | } | ||||
private LedgerTransactionContext createGenisisTx(LedgerEditor ldgEdt) { | |||||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||||
private LedgerTransactionContext createGenisisTx(LedgerEditor ldgEdt, BlockchainKeypair[] partis) { | |||||
TransactionRequest genesisTxReq = LedgerTestUtils.createLedgerInitTxRequest(partis); | |||||
LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | ||||
@@ -86,7 +94,7 @@ public class LedgerEditerTest { | |||||
@Test | @Test | ||||
public void testWriteDataAccoutKvOp() { | public void testWriteDataAccoutKvOp() { | ||||
LedgerEditor ldgEdt = createLedgerInitEditor(); | LedgerEditor ldgEdt = createLedgerInitEditor(); | ||||
LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt); | |||||
LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt, participants); | |||||
LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | ||||
AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | ||||
@@ -119,7 +127,7 @@ public class LedgerEditerTest { | |||||
@Test | @Test | ||||
public void testGennesisBlockCreation() { | public void testGennesisBlockCreation() { | ||||
LedgerEditor ldgEdt = createLedgerInitEditor(); | LedgerEditor ldgEdt = createLedgerInitEditor(); | ||||
LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt); | |||||
LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt, participants); | |||||
LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | ||||
AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | ||||
@@ -146,5 +154,5 @@ public class LedgerEditerTest { | |||||
ldgEdt.commit(); | ldgEdt.commit(); | ||||
} | } | ||||
} | } |
@@ -63,6 +63,13 @@ public class LedgerManagerTest { | |||||
public static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(), | public static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(), | ||||
SMCryptoService.class.getName() }; | SMCryptoService.class.getName() }; | ||||
private BlockchainKeypair parti0 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair parti1 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair parti3 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair[] participants = { parti0, parti1, parti2, parti3 }; | |||||
private SignatureFunction signatureFunction; | private SignatureFunction signatureFunction; | ||||
@Before | @Before | ||||
@@ -83,13 +90,13 @@ public class LedgerManagerTest { | |||||
LedgerEditor ldgEdt = ledgerManager.newLedger(initSetting, storage); | LedgerEditor ldgEdt = ledgerManager.newLedger(initSetting, storage); | ||||
// 创建一个模拟的创世交易; | // 创建一个模拟的创世交易; | ||||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||||
TransactionRequest genesisTxReq = LedgerTestUtils.createLedgerInitTxRequest(participants); | |||||
// 记录交易,注册用户; | // 记录交易,注册用户; | ||||
LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | ||||
LedgerDataSet ldgDS = txCtx.getDataSet(); | LedgerDataSet ldgDS = txCtx.getDataSet(); | ||||
BlockchainKeypair userKP = BlockchainKeyGenerator.getInstance().generate(); | BlockchainKeypair userKP = BlockchainKeyGenerator.getInstance().generate(); | ||||
UserAccount userAccount = ldgDS.getUserAccountSet().register(userKP.getAddress(), userKP.getPubKey()); | UserAccount userAccount = ldgDS.getUserAccountSet().register(userKP.getAddress(), userKP.getPubKey()); | ||||
userAccount.setProperty("Name", "孙悟空", -1); | userAccount.setProperty("Name", "孙悟空", -1); | ||||
userAccount.setProperty("Age", "10000", -1); | userAccount.setProperty("Age", "10000", -1); | ||||
@@ -38,10 +38,10 @@ public class LedgerTestUtils { | |||||
private static Random rand = new Random(); | private static Random rand = new Random(); | ||||
public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash) { | |||||
BlockchainKeypair key = BlockchainKeyGenerator.getInstance().generate(ED25519); | |||||
return createTxRequest_UserReg(ledgerHash, key); | |||||
} | |||||
// public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash) { | |||||
// BlockchainKeypair key = BlockchainKeyGenerator.getInstance().generate(ED25519); | |||||
// return createTxRequest_UserReg(ledgerHash, key); | |||||
// } | |||||
public static LedgerInitSetting createLedgerInitSetting() { | public static LedgerInitSetting createLedgerInitSetting() { | ||||
BlockchainKeypair[] partiKeys = new BlockchainKeypair[2]; | BlockchainKeypair[] partiKeys = new BlockchainKeypair[2]; | ||||
@@ -81,22 +81,61 @@ public class LedgerTestUtils { | |||||
return initSetting; | return initSetting; | ||||
} | } | ||||
public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash, BlockchainKeypair userKeypair) { | |||||
return createTxRequest_UserReg(ledgerHash, userKeypair, null); | |||||
// public static TransactionRequest createTxRequest_UserReg(BlockchainKeypair userKeypair, HashDigest ledgerHash, BlockchainKeypair... partiKeys) { | |||||
// return createTxRequest_UserReg(userKeypair, ledgerHash, null, null); | |||||
// } | |||||
public static TransactionRequest createLedgerInitTxRequest(BlockchainKeypair... participants) { | |||||
TxBuilder txBuilder = new TxBuilder(null); | |||||
for (BlockchainKeypair parti : participants) { | |||||
txBuilder.users().register(parti.getIdentity()); | |||||
} | |||||
TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||||
for (BlockchainKeypair parti : participants) { | |||||
txReqBuilder.signAsNode(parti); | |||||
} | |||||
return txReqBuilder.buildRequest(); | |||||
} | |||||
public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash, | |||||
BlockchainKeypair nodeKeypair, BlockchainKeypair... signers) { | |||||
return createTxRequest_UserReg(BlockchainKeyGenerator.getInstance().generate(), ledgerHash, nodeKeypair, | |||||
signers); | |||||
} | } | ||||
public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash, BlockchainKeypair userKeypair, | |||||
BlockchainKeypair gatewayKeypair) { | |||||
public static TransactionRequest createTxRequest_UserReg(BlockchainKeypair userKeypair, HashDigest ledgerHash, | |||||
BlockchainKeypair nodeKeypair, BlockchainKeypair... signers) { | |||||
TxBuilder txBuilder = new TxBuilder(ledgerHash); | TxBuilder txBuilder = new TxBuilder(ledgerHash); | ||||
txBuilder.users().register(userKeypair.getIdentity()); | txBuilder.users().register(userKeypair.getIdentity()); | ||||
TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | ||||
txReqBuilder.signAsEndpoint(userKeypair); | |||||
if (gatewayKeypair != null) { | |||||
txReqBuilder.signAsNode(gatewayKeypair); | |||||
txReqBuilder.signAsEndpoint(nodeKeypair); | |||||
if (nodeKeypair != null) { | |||||
txReqBuilder.signAsNode(nodeKeypair); | |||||
} | } | ||||
return txReqBuilder.buildRequest(); | |||||
} | |||||
public static TransactionRequest createTxRequest_MultiOPs_WithError(HashDigest ledgerHash, | |||||
BlockchainKeypair userKeypair, BlockchainKeypair nodeKeypair) { | |||||
TxBuilder txBuilder = new TxBuilder(ledgerHash); | |||||
txBuilder.users().register(userKeypair.getIdentity()); | |||||
BlockchainKeypair testKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
txBuilder.dataAccount(testKey.getAddress()).setBytes("AA", "Value".getBytes(), 1); | |||||
TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||||
txReqBuilder.signAsEndpoint(nodeKeypair); | |||||
if (nodeKeypair != null) { | |||||
txReqBuilder.signAsNode(nodeKeypair); | |||||
} | |||||
return txReqBuilder.buildRequest(); | return txReqBuilder.buildRequest(); | ||||
} | } | ||||
@@ -1,6 +1,7 @@ | |||||
package test.com.jd.blockchain.ledger; | package test.com.jd.blockchain.ledger; | ||||
import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||
import static org.junit.Assert.assertFalse; | |||||
import static org.junit.Assert.assertNotNull; | import static org.junit.Assert.assertNotNull; | ||||
import static org.junit.Assert.assertNull; | import static org.junit.Assert.assertNull; | ||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
@@ -54,19 +55,22 @@ public class TransactionBatchProcessorTest { | |||||
private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); | private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); | ||||
private BlockchainKeypair parti3 = BlockchainKeyGenerator.getInstance().generate(); | private BlockchainKeypair parti3 = BlockchainKeyGenerator.getInstance().generate(); | ||||
private BlockchainKeypair[] participants = { parti0, parti1, parti2, parti3 }; | |||||
private TransactionRequest transactionRequest; | private TransactionRequest transactionRequest; | ||||
// 采用基于内存的 Storage; | |||||
private MemoryKVStorage storage = new MemoryKVStorage(); | |||||
// TODO: 验证无效签名会被拒绝; | |||||
@Test | @Test | ||||
public void testTxReqProcess() { | |||||
public void testSingleTxProcess() { | |||||
final MemoryKVStorage STORAGE = new MemoryKVStorage(); | |||||
// 初始化账本到指定的存储库; | // 初始化账本到指定的存储库; | ||||
ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3); | |||||
ledgerHash = initLedger(STORAGE, parti0, parti1, parti2, parti3); | |||||
// 加载账本; | // 加载账本; | ||||
LedgerManager ledgerManager = new LedgerManager(); | LedgerManager ledgerManager = new LedgerManager(); | ||||
LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, storage); | |||||
LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
// 验证参与方账户的存在; | // 验证参与方账户的存在; | ||||
LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | ||||
@@ -85,18 +89,137 @@ public class TransactionBatchProcessorTest { | |||||
// 注册新用户; | // 注册新用户; | ||||
BlockchainKeypair userKeypair = BlockchainKeyGenerator.getInstance().generate(); | BlockchainKeypair userKeypair = BlockchainKeyGenerator.getInstance().generate(); | ||||
transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair, parti0); | transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair, parti0); | ||||
txbatchProcessor.schedule(transactionRequest); | |||||
TransactionResponse txResp = txbatchProcessor.schedule(transactionRequest); | |||||
LedgerBlock newBlock = newBlockEditor.prepare(); | |||||
newBlockEditor.commit(); | |||||
// 验证正确性; | |||||
ledgerManager = new LedgerManager(); | |||||
ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); | |||||
assertEquals(newBlock.getHash(), latestBlock.getHash()); | |||||
assertEquals(1, newBlock.getHeight()); | |||||
assertEquals(TransactionState.SUCCESS, txResp.getExecutionState()); | |||||
} | |||||
@Test | |||||
public void testMultiTxsProcess() { | |||||
final MemoryKVStorage STORAGE = new MemoryKVStorage(); | |||||
// 初始化账本到指定的存储库; | |||||
ledgerHash = initLedger(STORAGE, parti0, parti1, parti2, parti3); | |||||
// 加载账本; | |||||
LedgerManager ledgerManager = new LedgerManager(); | |||||
LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
// 验证参与方账户的存在; | |||||
LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | |||||
UserAccount user0 = previousBlockDataset.getUserAccountSet().getUser(parti0.getAddress()); | |||||
assertNotNull(user0); | |||||
boolean partiRegistered = previousBlockDataset.getUserAccountSet().contains(parti0.getAddress()); | |||||
assertTrue(partiRegistered); | |||||
// 生成新区块; | |||||
LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); | |||||
OperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration(); | |||||
TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, | |||||
opReg, ledgerManager); | |||||
// 注册新用户; | |||||
BlockchainKeypair userKeypair1 = BlockchainKeyGenerator.getInstance().generate(); | |||||
transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair1, parti0); | |||||
TransactionResponse txResp1 = txbatchProcessor.schedule(transactionRequest); | |||||
// BlockchainKeypair userKeypair2 = BlockchainKeyGenerator.getInstance().generate(); | |||||
// transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair2, parti0); | |||||
// TransactionResponse txResp2 = txbatchProcessor.schedule(transactionRequest); | |||||
LedgerBlock newBlock = newBlockEditor.prepare(); | |||||
newBlockEditor.commit(); | |||||
assertEquals(TransactionState.SUCCESS, txResp1.getExecutionState()); | |||||
// assertEquals(TransactionState.SUCCESS, txResp2.getExecutionState()); | |||||
// 验证正确性; | |||||
ledgerManager = new LedgerManager(); | |||||
ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); | |||||
assertEquals(newBlock.getHash(), latestBlock.getHash()); | |||||
assertEquals(1, newBlock.getHeight()); | |||||
LedgerDataSet ledgerDS = ledgerRepo.getDataSet(latestBlock); | |||||
boolean existUser1 = ledgerDS.getUserAccountSet().contains(userKeypair1.getAddress()); | |||||
// boolean existUser2 = ledgerDS.getUserAccountSet().contains(userKeypair2.getAddress()); | |||||
assertTrue(existUser1); | |||||
// assertTrue(existUser2); | |||||
} | |||||
@Test | |||||
public void testTxRollback() { | |||||
final MemoryKVStorage STORAGE = new MemoryKVStorage(); | |||||
// 初始化账本到指定的存储库; | |||||
ledgerHash = initLedger(STORAGE, parti0, parti1, parti2, parti3); | |||||
// 加载账本; | |||||
LedgerManager ledgerManager = new LedgerManager(); | |||||
LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
// 验证参与方账户的存在; | |||||
LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | |||||
UserAccount user0 = previousBlockDataset.getUserAccountSet().getUser(parti0.getAddress()); | |||||
assertNotNull(user0); | |||||
boolean partiRegistered = previousBlockDataset.getUserAccountSet().contains(parti0.getAddress()); | |||||
assertTrue(partiRegistered); | |||||
// 生成新区块; | |||||
LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); | |||||
OperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration(); | |||||
TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, | |||||
opReg, ledgerManager); | |||||
// 注册新用户; | |||||
BlockchainKeypair userKeypair1 = BlockchainKeyGenerator.getInstance().generate(); | |||||
transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair1, parti0); | |||||
TransactionResponse txResp1 = txbatchProcessor.schedule(transactionRequest); | |||||
BlockchainKeypair userKeypair2 = BlockchainKeyGenerator.getInstance().generate(); | |||||
transactionRequest = LedgerTestUtils.createTxRequest_MultiOPs_WithError(ledgerHash, userKeypair2, parti0); | |||||
TransactionResponse txResp2 = txbatchProcessor.schedule(transactionRequest); | |||||
BlockchainKeypair userKeypair3 = BlockchainKeyGenerator.getInstance().generate(); | |||||
transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair3, parti0); | |||||
TransactionResponse txResp3 = txbatchProcessor.schedule(transactionRequest); | |||||
LedgerBlock newBlock = newBlockEditor.prepare(); | LedgerBlock newBlock = newBlockEditor.prepare(); | ||||
newBlockEditor.commit(); | newBlockEditor.commit(); | ||||
assertEquals(TransactionState.SUCCESS, txResp1.getExecutionState()); | |||||
assertEquals(TransactionState.LEDGER_ERROR, txResp2.getExecutionState()); | |||||
assertEquals(TransactionState.SUCCESS, txResp3.getExecutionState()); | |||||
// 验证正确性; | // 验证正确性; | ||||
ledgerManager = new LedgerManager(); | ledgerManager = new LedgerManager(); | ||||
ledgerRepo = ledgerManager.register(ledgerHash, storage); | |||||
ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); | |||||
LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); | LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); | ||||
assertEquals(newBlock.getHash(), latestBlock.getHash()); | assertEquals(newBlock.getHash(), latestBlock.getHash()); | ||||
assertEquals(1, newBlock.getHeight()); | assertEquals(1, newBlock.getHeight()); | ||||
LedgerDataSet ledgerDS = ledgerRepo.getDataSet(latestBlock); | |||||
boolean existUser1 = ledgerDS.getUserAccountSet().contains(userKeypair1.getAddress()); | |||||
boolean existUser2 = ledgerDS.getUserAccountSet().contains(userKeypair2.getAddress()); | |||||
boolean existUser3 = ledgerDS.getUserAccountSet().contains(userKeypair3.getAddress()); | |||||
assertTrue(existUser1); | |||||
assertFalse(existUser2); | |||||
assertTrue(existUser3); | |||||
} | } | ||||
private HashDigest initLedger(MemoryKVStorage storage, BlockchainKeypair... partiKeys) { | private HashDigest initLedger(MemoryKVStorage storage, BlockchainKeypair... partiKeys) { | ||||
@@ -106,7 +229,7 @@ public class TransactionBatchProcessorTest { | |||||
// 创建账本; | // 创建账本; | ||||
LedgerEditor ldgEdt = LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | LedgerEditor ldgEdt = LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | ||||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||||
TransactionRequest genesisTxReq = LedgerTestUtils.createLedgerInitTxRequest(partiKeys); | |||||
LedgerTransactionContext genisisTxCtx = ldgEdt.newTransaction(genesisTxReq); | LedgerTransactionContext genisisTxCtx = ldgEdt.newTransaction(genesisTxReq); | ||||
LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | ||||
@@ -102,8 +102,7 @@ public class TransactionSetTest { | |||||
txSnapshot.setContractAccountSetHash(contractAccountSetHash); | txSnapshot.setContractAccountSetHash(contractAccountSetHash); | ||||
long blockHeight = 8922L; | long blockHeight = 8922L; | ||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txReq, TransactionState.SUCCESS, txSnapshot, | |||||
null); | |||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txReq, TransactionState.SUCCESS, txSnapshot); | |||||
txset.add(tx); | txset.add(tx); | ||||
assertTrue(txset.isUpdated()); | assertTrue(txset.isUpdated()); | ||||
@@ -11,7 +11,7 @@ import com.jd.blockchain.consts.DataCodes; | |||||
* @author huanghaiquan | * @author huanghaiquan | ||||
* | * | ||||
*/ | */ | ||||
@EnumContract(code= DataCodes.ENUM_TYPE_TRANSACTION_STATE) | |||||
@EnumContract(code = DataCodes.ENUM_TYPE_TRANSACTION_STATE) | |||||
public enum TransactionState { | public enum TransactionState { | ||||
/** | /** | ||||
@@ -23,16 +23,21 @@ public enum TransactionState { | |||||
* 共识错误; | * 共识错误; | ||||
*/ | */ | ||||
CONSENSUS_ERROR((byte) 1), | CONSENSUS_ERROR((byte) 1), | ||||
/** | /** | ||||
* 账本错误; | * 账本错误; | ||||
*/ | */ | ||||
LEDGER_ERROR((byte) 2), | LEDGER_ERROR((byte) 2), | ||||
/** | /** | ||||
* 数据序列更新错误; | |||||
* 由于在错误的账本上执行交易而被丢弃; | |||||
*/ | |||||
DISCARD_BY_WRONG_LEDGER((byte) 3), | |||||
/** | |||||
* 由于交易内容的验签失败而丢弃; | |||||
*/ | */ | ||||
DATA_SEQUENCE_UPDATE_ERROR((byte) 3), | |||||
DISCARD_BY_WRONG_CONTENT_SIGNATURE((byte) 4), | |||||
/** | /** | ||||
* 系统错误; | * 系统错误; | ||||
@@ -44,7 +49,7 @@ public enum TransactionState { | |||||
*/ | */ | ||||
TIMEOUT((byte) 0x81); | TIMEOUT((byte) 0x81); | ||||
@EnumField(type= PrimitiveType.INT8) | |||||
@EnumField(type = PrimitiveType.INT8) | |||||
public final byte CODE; | public final byte CODE; | ||||
private TransactionState(byte code) { | private TransactionState(byte code) { | ||||
@@ -55,12 +55,22 @@ public class TxBuilder implements TransactionBuilder { | |||||
txContent.addOperations(opFactory.getOperations()); | txContent.addOperations(opFactory.getOperations()); | ||||
txContent.setTime(time); | txContent.setTime(time); | ||||
byte[] contentBodyBytes = BinaryProtocol.encode(txContent, TransactionContentBody.class); | |||||
HashDigest contentHash = Crypto.getHashFunction(DEFAULT_HASH_ALGORITHM).hash(contentBodyBytes); | |||||
HashDigest contentHash = computeTxContentHash(txContent); | |||||
txContent.setHash(contentHash); | txContent.setHash(contentHash); | ||||
return txContent; | return txContent; | ||||
} | } | ||||
public static HashDigest computeTxContentHash(TransactionContent txContent) { | |||||
byte[] contentBodyBytes = BinaryProtocol.encode(txContent, TransactionContentBody.class); | |||||
HashDigest contentHash = Crypto.getHashFunction(DEFAULT_HASH_ALGORITHM).hash(contentBodyBytes); | |||||
return contentHash; | |||||
} | |||||
public static boolean verifyTxContentHash(TransactionContent txContent, HashDigest verifiedHash) { | |||||
HashDigest hash = computeTxContentHash(txContent); | |||||
return hash.equals(verifiedHash); | |||||
} | |||||
public Collection<OperationResultHandle> getReturnValuehandlers() { | public Collection<OperationResultHandle> getReturnValuehandlers() { | ||||
return opFactory.getReturnValuetHandlers(); | return opFactory.getReturnValuetHandlers(); | ||||
@@ -75,8 +75,14 @@ public class TxRequestBuilder implements TransactionRequestBuilder { | |||||
} | } | ||||
public static boolean verifySignature(TransactionContent txContent, SignatureDigest signDigest, PubKey pubKey) { | public static boolean verifySignature(TransactionContent txContent, SignatureDigest signDigest, PubKey pubKey) { | ||||
return Crypto.getSignatureFunction(pubKey.getAlgorithm()).verify(signDigest, pubKey, | |||||
txContent.getHash().toBytes()); | |||||
if (!TxBuilder.verifyTxContentHash(txContent, txContent.getHash())) { | |||||
return false; | |||||
} | |||||
return verifyHashSignature(txContent.getHash(), signDigest, pubKey); | |||||
} | |||||
public static boolean verifyHashSignature(HashDigest hash, SignatureDigest signDigest, PubKey pubKey) { | |||||
return Crypto.getSignatureFunction(pubKey.getAlgorithm()).verify(signDigest, pubKey, hash.toBytes()); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -8,11 +8,11 @@ | |||||
<artifactId>spring-boot-starter-parent</artifactId> | <artifactId>spring-boot-starter-parent</artifactId> | ||||
<version>2.0.6.RELEASE</version> | <version>2.0.6.RELEASE</version> | ||||
</parent> | </parent> | ||||
<!--<parent>--> | |||||
<!--<groupId>org.sonatype.oss</groupId>--> | |||||
<!--<artifactId>oss-parent</artifactId>--> | |||||
<!--<version>7</version>--> | |||||
<!--</parent>--> | |||||
<!--<parent> --> | |||||
<!--<groupId>org.sonatype.oss</groupId> --> | |||||
<!--<artifactId>oss-parent</artifactId> --> | |||||
<!--<version>7</version> --> | |||||
<!--</parent> --> | |||||
<groupId>com.jd.blockchain</groupId> | <groupId>com.jd.blockchain</groupId> | ||||
<artifactId>jdchain-root</artifactId> | <artifactId>jdchain-root</artifactId> | ||||
@@ -92,6 +92,11 @@ | |||||
<artifactId>mockito-core</artifactId> | <artifactId>mockito-core</artifactId> | ||||
<scope>test</scope> | <scope>test</scope> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-log4j2</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
</dependencies> | </dependencies> | ||||
<dependencyManagement> | <dependencyManagement> | ||||
@@ -446,22 +451,22 @@ | |||||
</execution> | </execution> | ||||
</executions> | </executions> | ||||
</plugin> | </plugin> | ||||
<!--<!– 生成sources源码包的插件 –>--> | |||||
<!--<plugin>--> | |||||
<!--<artifactId>maven-source-plugin</artifactId>--> | |||||
<!--<version>2.4</version>--> | |||||
<!--<configuration>--> | |||||
<!--<attach>true</attach>--> | |||||
<!--</configuration>--> | |||||
<!--<executions>--> | |||||
<!--<execution>--> | |||||
<!--<phase>package</phase>--> | |||||
<!--<goals>--> | |||||
<!--<goal>jar-no-fork</goal>--> | |||||
<!--</goals>--> | |||||
<!--</execution>--> | |||||
<!--</executions>--> | |||||
<!--</plugin>--> | |||||
<!--<!– 生成sources源码包的插件 –> --> | |||||
<!--<plugin> --> | |||||
<!--<artifactId>maven-source-plugin</artifactId> --> | |||||
<!--<version>2.4</version> --> | |||||
<!--<configuration> --> | |||||
<!--<attach>true</attach> --> | |||||
<!--</configuration> --> | |||||
<!--<executions> --> | |||||
<!--<execution> --> | |||||
<!--<phase>package</phase> --> | |||||
<!--<goals> --> | |||||
<!--<goal>jar-no-fork</goal> --> | |||||
<!--</goals> --> | |||||
<!--</execution> --> | |||||
<!--</executions> --> | |||||
<!--</plugin> --> | |||||
</plugins> | </plugins> | ||||
</pluginManagement> | </pluginManagement> | ||||
@@ -46,6 +46,9 @@ public class BytesUtils { | |||||
if (bytes1 == bytes2) { | if (bytes1 == bytes2) { | ||||
return true; | return true; | ||||
} | } | ||||
if (bytes1 == null || bytes2 == null) { | |||||
return false; | |||||
} | |||||
if (bytes1.length != bytes2.length) { | if (bytes1.length != bytes2.length) { | ||||
return false; | return false; | ||||
} | } | ||||