Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Batch verification #150

Merged
merged 10 commits into from
Apr 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "src/main/native/crypto/libsodium"]
path = src/main/native/crypto/libsodium
url = https://github.com/jedisct1/libsodium.git
[submodule "src/main/native/crypto/ed25519-donna"]
path = src/main/native/crypto/ed25519-donna
url = https://github.com/cryptokat/ed25519-donna.git
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ matrix:
- env: NAME="Linux tests (Java 11)"
os: linux
sudo: false
jdk: openjdk11
script:
- wget https://github.com/sormuras/bach/raw/master/install-jdk.sh
- source ./install-jdk.sh -F 11 -L GPL
- mvn test

# macOS tests (Java 8 LTS)
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@
<plugin>
<groupId>com.akathist.maven.plugins.launch4j</groupId>
<artifactId>launch4j-maven-plugin</artifactId>
<version>1.7.21</version>
<version>1.7.25</version>
<executions>
<execution>
<id>semux</id>
Expand Down
17 changes: 15 additions & 2 deletions src/main/java/org/semux/consensus/SemuxSync.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -601,12 +602,24 @@ protected boolean validateBlockVotes(Block block) {
byte[] encoded = vote.getEncoded();

// check validity of votes
if (!block.getVotes().stream()
.allMatch(sig -> Key.verify(encoded, sig) && validators.contains(Hex.encode(sig.getAddress())))) {
if (block.getVotes().stream().anyMatch(sig -> !validators.contains(Hex.encode(sig.getAddress())))) {
logger.warn("Block votes are invalid");
return false;
}

if (!Key.isVerifyBatchSupported()) {
if (!block.getVotes().stream()
.allMatch(sig -> Key.verify(encoded, sig))) {
logger.warn("Block votes are invalid");
return false;
}
} else {
if (!Key.verifyBatch(Collections.nCopies(block.getVotes().size(), encoded), block.getVotes())) {
logger.warn("Block votes are invalid");
return false;
}
}

// at least two thirds voters
if (block.getVotes().stream()
.map(sig -> new ByteArray(sig.getA()))
Expand Down
22 changes: 18 additions & 4 deletions src/main/java/org/semux/core/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.lang3.tuple.Pair;
import org.semux.Network;
import org.semux.config.Config;
import org.semux.crypto.Hex;
import org.semux.crypto.Key;
import org.semux.crypto.Key.Signature;
import org.semux.util.MerkleUtil;
import org.semux.util.SimpleDecoder;
Expand Down Expand Up @@ -197,10 +199,22 @@ public boolean validateTransactions(BlockHeader header, List<Transaction> transa
*/
public boolean validateTransactions(BlockHeader header, Collection<Transaction> unvalidatedTransactions,
List<Transaction> allTransactions, Network network) {

// validate transactions
boolean valid = unvalidatedTransactions.parallelStream().allMatch(tx -> tx.validate(network));
if (!valid) {
return false;
if (!Key.isVerifyBatchSupported() || unvalidatedTransactions.size() < 3) {
if (!unvalidatedTransactions.parallelStream().allMatch(tx -> tx.validate(network))) {
return false;
}
} else {
if (!unvalidatedTransactions.parallelStream().allMatch(tx -> tx.validate(network, false))) {
return false;
}

if (!Key.verifyBatch(
unvalidatedTransactions.stream().map(Transaction::getHash).collect(Collectors.toList()),
unvalidatedTransactions.stream().map(Transaction::getSignature).collect(Collectors.toList()))) {
return false;
}
}

// validate transactions root
Expand Down Expand Up @@ -447,7 +461,7 @@ public Pair<byte[], List<Integer>> getEncodedResultsAndIndex() {
* @return
*/
public byte[] getEncodedVotes() {
SimpleEncoder enc = new SimpleEncoder();
SimpleEncoder enc = new SimpleEncoder(4 + 4 + votes.size() * Signature.LENGTH);

enc.writeInt(view);
enc.writeInt(votes.size());
Expand Down
12 changes: 10 additions & 2 deletions src/main/java/org/semux/core/Transaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,13 @@ public Transaction sign(Key key) {
* </p>
*
* @param network
* @param verifySignature
* Whether to verify the transaction signature or not. This is useful
* when there are multiple transaction signatures that can be
* verified in batch for performance reason.
* @return true if success, otherwise false
*/
public boolean validate(Network network) {
public boolean validate(Network network, boolean verifySignature) {
return hash != null && hash.length == Hash.HASH_LEN
&& networkId == network.id()
&& type != null
Expand All @@ -162,7 +166,7 @@ public boolean validate(Network network) {
&& signature != null && !Arrays.equals(signature.getAddress(), EMPTY_ADDRESS)

&& Arrays.equals(Hash.h256(encoded), hash)
&& Key.verify(hash, signature)
&& (!verifySignature || Key.verify(hash, signature))

// The coinbase key is publicly available. People can use it for transactions.
// It won't introduce any fundamental loss to the system but could potentially
Expand All @@ -172,6 +176,10 @@ public boolean validate(Network network) {
&& !Arrays.equals(to, Constants.COINBASE_ADDRESS)));
}

public boolean validate(Network network) {
return validate(network, true);
}

/**
* Returns the transaction network id.
*
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/org/semux/crypto/Key.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.Collection;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
Expand Down Expand Up @@ -201,6 +202,21 @@ public static boolean verify(byte[] message, Signature signature) {
return false;
}

public static boolean isVerifyBatchSupported() {
return Native.isEnabled();
}

public static boolean verifyBatch(Collection<byte[]> messages, Collection<Signature> signatures) {
if (!isVerifyBatchSupported()) {
throw new UnsupportedOperationException("Key#verifyBatch is only implemented in the native library.");
}

return Native.verifyBatch(
messages.toArray(new byte[messages.size()][]),
signatures.stream().map(Signature::getS).toArray(byte[][]::new),
signatures.stream().map(Signature::getA).toArray(byte[][]::new));
}

/**
* Verifies a signature.
*
Expand Down
19 changes: 14 additions & 5 deletions src/main/java/org/semux/crypto/Native.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,16 @@ protected static void init() {
switch (os) {
case LINUX:
if (SystemUtil.getOsArch().equals("aarch64")) {
enabled = loadLibrary("/native/aarch64/libsodium.so.23") && loadLibrary("/native/aarch64/libcrypto.so");
enabled = loadLibrary("/native/Linux-aarch64/libsemuxcrypto.so");
} else {
enabled = loadLibrary("/native/linux64/libsodium.so.23") && loadLibrary("/native/linux64/libcrypto.so");
enabled = loadLibrary("/native/Linux-x86_64/libsemuxcrypto.so");
}
break;
case MACOS:
enabled = loadLibrary("/native/macos64/libsodium.23.dylib")
&& loadLibrary("/native/macos64/libcrypto.dylib");
enabled = loadLibrary("/native/Darwin-x86_64/libsemuxcrypto.dylib");
break;
case WINDOWS:
enabled = loadLibrary("/native/win64/libsodium.dll") && loadLibrary("/native/win64/crypto.dll");
enabled = loadLibrary("/native/Windows-x86_64/libsemuxcrypto.dll");
break;
default:
break;
Expand Down Expand Up @@ -153,4 +152,14 @@ public static void enable() {
* @return
*/
public static native boolean verify(byte[] message, byte[] signature, byte[] publicKey);

/**
* Batch verifies Ed25519 signatures.
*
* @param messages
* @param signatures
* @param publicKeys
* @return
*/
public static native boolean verifyBatch(byte[][] messages, byte[][] signatures, byte[][] publicKeys);
}
4 changes: 4 additions & 0 deletions src/main/java/org/semux/util/SimpleEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public SimpleEncoder() {
this(Bytes.EMPTY_BYTES);
}

public SimpleEncoder(int size) {
out = new ByteArrayOutputStream(size);
}

public void writeBoolean(boolean b) {
out.write(b ? 1 : 0);
}
Expand Down
60 changes: 36 additions & 24 deletions src/main/native/README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,54 @@
# Semux Native Library

## Build on Linux
libsemuxcrypto is a JNI library that aims to increase the performance Semux wallet by implementing cryptography functions in C++. This library relies on thrid-parties including [libsodium](https://github.com/jedisct1/libsodium) and [ed25519-donna](https://github.com/floodyberry/ed25519-donna).

## Build on x86_64 Linux

Prerequisites:
1. Build and install libsodium 1.0.16
Build on linux supports cross-compiling to the following platforms:

Build:
```
mkdir build && cd build
cmake ..
make
```

## Build on macOS
1. Linux-aarch64
2. Linux-x86_64
3. Windows-x86_64

Prerequisites:
1. Build and install libsodium 1.0.16

Build:
- cmake
- automake
- autoconf
- gcc-x86_64-linux-gnu
- gcc-aarch64-linux-gnu
- gcc-mingw-w64
- binutils-x86_64-linux-gnu
- binutils-aarch64-linux-gnu
- binutils-mingw-w64

Steps to build on Debian/Ubuntu based distributions with a x86_64 machine:
```
sudo apt install cmake automake autoconf gcc gcc-aarch64-linux-gnu gcc-mingw-w64 binutils binutils-aarch64-linux-gnu binutils-mingw-w64

mkdir build && cd build
cmake ..
make
install_name_tool -change "/usr/local/lib/libsodium.23.dylib" "@loader_path/libsodium.23.dylib" crypto/libcrypto.dylib
cmake -vvv -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain-Linux-x86_64.cmake ../
make -j$(nproc)

cd .. && rm -rf build && mkdir build && cd build
cmake -vvv -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain-Linux-aarch64.cmake ../
make -j$(nproc)

cd .. && rm -rf build && mkdir build && cd build
cmake -vvv -DCMAKE_TOOLCHAIN_FILE=toolchain-Windows-x86_64.cmake ../
make -j$(nproc)
```

## Build on Windows
## Build on macOS

Prerequisites:
1. Visual Studio 2012 build tools
2. CMake 3.8+
3. Download and unpack libsodium 1.0.16 pre-built binaries
4. Set `sodiumDIR` environment variable
1. clang
2. cmake
3. autoconf
4. automake

Build:
```
mkdir build && cd build
cmake -G "Visual Studio 11 2012 x64" ..
# Build the solution with Visual Studio
cmake -vvv -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain-Darwin-x86_64.cmake ../
make -j$(nproc)
```
9 changes: 9 additions & 0 deletions src/main/native/cmake/toolchain-Darwin-x86_64.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
set(CMAKE_SYSTEM_NAME Darwin)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
SET(CMAKE_C_COMPILER clang)
SET(CMAKE_CXX_COMPILER clang++)
SET(MACOSX_VERSION_MIN 10.8)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=${MACOSX_VERSION_MIN} -arch ${CMAKE_SYSTEM_PROCESSOR} -march=core2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=${MACOSX_VERSION_MIN} -arch ${CMAKE_SYSTEM_PROCESSOR} -march=core2 -std=c++98 -stdlib=libc++")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -mmacosx-version-min=${MACOSX_VERSION_MIN} -arch ${CMAKE_SYSTEM_PROCESSOR} -march=core2 -Wl,-dead_strip -Wl,-undefined,error -stdlib=libc++ -lc++")
70 changes: 70 additions & 0 deletions src/main/native/cmake/toolchain-Linux-aarch64.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# **********************************************************
# Copyright (c) 2014-2017 Google, Inc. All rights reserved.
# **********************************************************

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of Google, Inc. nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.

# For cross-compiling on arm64 Linux using gcc-aarch64-linux-gnu package:
# - install AArch64 tool chain:
# $ sudo apt-get install g++-aarch64-linux-gnu
# - cross-compiling config
# $ cmake -DCMAKE_TOOLCHAIN_FILE=../dynamorio/make/toolchain-arm64.cmake ../dynamorio
# You may have to set CMAKE_FIND_ROOT_PATH to point to the target enviroment, e.g.
# by passing -DCMAKE_FIND_ROOT_PATH=/usr/aarch64-linux-gnu on Debian-like systems.
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(TARGET_ABI "linux-gnu")
# specify the cross compiler
SET(CMAKE_C_COMPILER aarch64-${TARGET_ABI}-gcc)
SET(CMAKE_CXX_COMPILER aarch64-${TARGET_ABI}-g++)

# To build the tests, we need to set where the target environment containing
# the required library is. On Debian-like systems, this is
# /usr/aarch64-linux-gnu.
SET(CMAKE_FIND_ROOT_PATH "/usr/aarch64-${TARGET_ABI}")
# search for programs in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# for libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

# Set additional variables.
# If we don't set some of these, CMake will end up using the host version.
# We want the full path, however, so we can pass EXISTS and other checks in
# the our CMake code.
find_program(GCC_FULL_PATH aarch64-${TARGET_ABI}-gcc)
if (NOT GCC_FULL_PATH)
message(FATAL_ERROR "Cross-compiler aarch64-${TARGET_ABI}-gcc not found")
endif ()
get_filename_component(GCC_DIR ${GCC_FULL_PATH} PATH)
SET(CMAKE_LINKER ${GCC_DIR}/aarch64-${TARGET_ABI}-ld CACHE FILEPATH "linker")
SET(CMAKE_ASM_COMPILER ${GCC_DIR}/aarch64-${TARGET_ABI}-as CACHE FILEPATH "assembler")
SET(CMAKE_OBJCOPY ${GCC_DIR}/aarch64-${TARGET_ABI}-objcopy CACHE FILEPATH "objcopy")
SET(CMAKE_STRIP ${GCC_DIR}/aarch64-${TARGET_ABI}-strip CACHE FILEPATH "strip")
SET(CMAKE_CPP ${GCC_DIR}/aarch64-${TARGET_ABI}-cpp CACHE FILEPATH "cpp")

set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--gc-sections -Wl,--no-undefined")
Loading