Repository: terryjiao/BitcoinWallet Branch: master Commit: 56f3b10ca425 Files: 21 Total size: 61.7 KB Directory structure: gitextract_58x9ldua/ ├── .gitignore ├── .idea/ │ ├── .name │ ├── compiler.xml │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ ├── uiDesigner.xml │ └── vcs.xml ├── LICENSE ├── README.md ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src/ └── main/ └── java/ ├── GenerateKeyStore.java ├── Main.java ├── SendTransaction.java ├── TransactionManager.java └── english ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures /out .externalNativeBuild /keystore */.DS_Store ================================================ FILE: .idea/.name ================================================ BitcoinDemo ================================================ FILE: .idea/compiler.xml ================================================ ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/uiDesigner.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018 Weitong Jiao Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Ethereum Wallet Generating ETH key pairs and address from mnemonic. ---------- ### 1. From entropy to mnemonic #### 1. Generating 128 digits random entropy ```java UUID uuid = UUID.randomUUID(); String[] digits = uuid.toString().split("\\-"); StringBuilder randomDigits = new StringBuilder(); for (String digit : digits) { randomDigits.append(digit); } ``` #### 2. Doing SHA256 to entropy for checksum, append first 4 bits to the end of entropy ```java //generate sha-256 from entropy String encodeStr = ""; byte[] hash = Sha256.sha256(hexStringToByteArray(entropy)); encodeStr = String.valueOf(Hex.encodeHex(hash)); System.out.println(encodeStr); char firstSHA = encodeStr.charAt(0); String new_entropy = entropy + firstSHA; String bin_entropy = ""; for (int i = 0; i < new_entropy.length(); i++) { bin_entropy += dict[Integer.parseInt(new_entropy.substring(i, i + 1), 16)]; } ``` #### 3. Segment 132 bits entropy into 11 bits long parts ```java String[] segments = new String[12]; for (int i = 0; i <= 11; i++) { segments[i] = bin_entropy.substring(i * 11, (i + 1) * 11); } ``` #### 4. Generating mnemonic from dictionary ```java mnemonic += wordlist[Integer.valueOf(segments[0], 2)]; for (int j = 1; j < segments.length; j++) { mnemonic += " " + (wordlist[Integer.valueOf(segments[j], 2)]); } ``` ![enter image description here](https://upload-images.jianshu.io/upload_images/10931084-93b10c15c7277420.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700) ---------- ### 2. From mnemonic to seed Using PBKDF2 function to get 512 bits seed from mnemonic. In this part we need a salt string to generate the seed we needed. Normally the value of salt is "mnemonic" for universality ```java String seed; String salt = "mnemonic"; seed = getSeed(mnemonic, salt); ``` ![enter image description here](https://upload-images.jianshu.io/upload_images/10931084-eb8d30c6fc836a5b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700) ---------- ###3. From seed to master private key Divide 512 bits seed into two equal parts, the first 256 bits is master private key and the last 256 bits is chain code. We could use BIP32 library to do the jobs by APIs this library provided. ```java ExtendedPrivateKey rootKey = ExtendedPrivateKey.fromSeed(hexStringToByteArray(seed), Bitcoin.MAIN_NET); ``` ![enter image description here](https://upload-images.jianshu.io/upload_images/10931084-177ace609e88a5a3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700) ---------- ###4. From master private key to child private key Firstly, generate address index to get 0th private key generated from master private key and chain code. ```java AddressIndex ethAddressIndex = BIP44.m().purpose44().coinType(60).account(0).external().address(0); ``` 44.60.0.0.0 is eth address index. And then get key pair and address that we need. ```java ExtendedPrivateKey childPrivateKey = rootKey.derive(ethAddressIndex, AddressIndex.DERIVATION); byte[] privateKeyBytes = childPrivateKey.getKey(); ECKeyPair keyPair = ECKeyPair.create(privateKeyBytes); List returnList = EthAddress(childPrivateKey, keyPair); ``` ![enter image description here](https://upload-images.jianshu.io/upload_images/10931084-dfd91a5ee94953e7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700) ================================================ FILE: build.gradle ================================================ group 'com.terry' version '1.0-SNAPSHOT' apply plugin: 'java' sourceCompatibility = 1.8 repositories { jcenter() } dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' compile 'io.github.novacrypto:BIP39:0.1.9' compile group: 'commons-codec', name: 'commons-codec', version: '1.10' compile 'io.github.novacrypto:BIP44:0.0.3' compile 'com.lhalcyon:bip32:1.0.0' compile ('org.web3j:core:3.5.0') compile group: 'org.bitcoinj', name: 'bitcoinj-core', version: '0.14.6' } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Sun Jul 29 20:02:31 CST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-rc-2-all.zip ================================================ FILE: gradlew ================================================ #!/usr/bin/env sh ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save ( ) { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=$(save "$@") # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then cd "$(dirname "$0")" fi exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: settings.gradle ================================================ rootProject.name = 'BitcoinDemo' ================================================ FILE: src/main/java/GenerateKeyStore.java ================================================ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.security.*; import java.security.KeyStore.ProtectionParameter; import java.security.cert.CertificateException; import javax.crypto.spec.SecretKeySpec; public class GenerateKeyStore { private String fileName = ""; private void execCommand(String[] arstringCommand) { for (int i = 0; i < arstringCommand.length; i++) { System.out.print(arstringCommand[i] + " "); } try { Runtime.getRuntime().exec(arstringCommand); } catch (Exception e) { System.out.println(e.getMessage()); } } public void execCommand(String arstringCommand) { try { Runtime.getRuntime().exec(arstringCommand); } catch (Exception e) { System.out.println(e.getMessage()); } } /** * 生成密钥 */ public void genKeyByPrivateKey(String address, String password) { fileName = "/Users/terry/Documents/GitHub/BitcoinWallet/keystore/" + address + "_PrivateKey" + ".keystore"; String[] arstringCommand = new String[]{ "keytool", "-genkey", // -genkey表示生成密钥 "-validity", // -validity指定证书有效期(单位:天),这里是36000天 "36500", "-keysize",// 指定密钥长度 "1024", "-alias", // -alias指定别名,这里是ss "Mnemonic", "-keyalg", // -keyalg 指定密钥的算法 (如 RSA DSA(如果不指定默认采用DSA)) "RSA", "-keystore", // -keystore指定存储位置,这里是/Users/terry/Documents/GitHub/my/BitcoinDemo/keystore/demo.keystore fileName, "-dname",// CN=(名字与姓氏), OU=(组织单位名称), O=(组织名称), L=(城市或区域名称), // ST=(州或省份名称), C=(单位的两字母国家代码)" "CN=(user), OU=(imbuff), O=(imbuff), L=(SH), ST=(SH), C=(CN)", "-storepass", // 指定密钥库的密码(获取keystore信息所需的密码) password, "-keypass",// 指定别名条目的密码(私钥的密码) password, "-v"// -v 显示密钥库中的证书详细信息 }; execCommand(arstringCommand); } /** * */ public void genKeyByMnemonic(String address, String password) { fileName = "/Users/terry/Documents/GitHub/BitcoinWallet/keystore/" + address + "_Mnemonic" + ".keystore"; String[] arstringCommand = new String[]{ "keytool", "-genkey", // -genkey表示生成密钥 "-validity", // -validity指定证书有效期(单位:天),这里是36000天 "36500", "-keysize",// 指定密钥长度 "1024", "-alias", // -alias指定别名,这里是ss "Mnemonic", "-keyalg", // -keyalg 指定密钥的算法 (如 RSA DSA(如果不指定默认采用DSA)) "RSA", "-keystore", // -keystore指定存储位置,这里是/Users/terry/Documents/GitHub/my/BitcoinDemo/keystore/demo.keystore fileName, "-dname",// CN=(名字与姓氏), OU=(组织单位名称), O=(组织名称), L=(城市或区域名称), // ST=(州或省份名称), C=(单位的两字母国家代码)" "CN=(user), OU=(imbuff), O=(imbuff), L=(SH), ST=(SH), C=(CN)", "-storepass", // 指定密钥库的密码(获取keystore信息所需的密码) password, "-keypass",// 指定别名条目的密码(私钥的密码) password, "-v"// -v 显示密钥库中的证书详细信息 }; execCommand(arstringCommand); } public void protectContent(String privateKey, String password) { FileInputStream fis = null; OutputStream os = null; try { // 读取keystore文件转换为keystore密钥库对象 fis = new FileInputStream(fileName); // 生成证书的类型为jceks KeyStore keyStore = KeyStore.getInstance("jceks"); // 该密钥库的密码,storepass指定密钥库的密码(获取keystore信息所需的密码) String storepass = password; keyStore.load(fis, storepass.toCharArray()); fis.close(); // 一旦加载了 keystore,就能够从 keystore 读取现有条目,或向 keystore 写入新条目: String alias = "Mnemonic";// 别名 String keypass = password; // 别名密码 ,keypass 指定别名条目的密码(私钥的密码) ProtectionParameter param = new KeyStore.PasswordProtection(keypass.toCharArray()); KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, param); PrivateKey myPrivateKey = pkEntry.getPrivateKey(); System.out.println("获取的私钥是:" + myPrivateKey.toString()); // 根据给定的字节数组构造一个密钥 String desPwd = privateKey;// 用户要求保存于keystore文件中的密码 javax.crypto.SecretKey mySecretKey = new SecretKeySpec(desPwd.getBytes(), "JKS"); KeyStore.SecretKeyEntry skEntry = new KeyStore.SecretKeyEntry(mySecretKey); keyStore.setEntry(alias, skEntry, new KeyStore.PasswordProtection("decryp pwd".toCharArray())); //将此 keystore 存储到给定输出流,并用给定密码保护其完整性。 os = new FileOutputStream(fileName); keyStore.store(os, storepass.toCharArray()); os.close(); } catch (KeyStoreException | NoSuchAlgorithmException | IOException | CertificateException | UnrecoverableEntryException e) { e.printStackTrace(); } finally { try { if (os != null) os.close(); if (fis != null) fis.close(); } catch (IOException e) { e.printStackTrace(); } } } public void getContent(String password) { String storepass = password; try { FileInputStream fis = null; // 读取keystore文件转换为keystore密钥库对象 fis = new FileInputStream(fileName); // 因为生成证书的类型为JKS 也有其他的格式 KeyStore keyStore = KeyStore.getInstance("jceks"); // 该密钥库的密码"888999",storepass指定密钥库的密码(获取keystore信息所需的密码) keyStore.load(fis, storepass.toCharArray()); fis.close(); // 根据别名(alias=desPws)从证书(keyStore)获取密码并解密 //keyStore.getKey返回与给定别名关联的密钥,并用给定密码来恢复它。 Key key = keyStore.getKey("Mnemonic", "decryp pwd".toCharArray()); //key.getEncoded()返回基本编码格式的密钥,如果此密钥不支持编码,则返回 null。 byte[] bt = key.getEncoded(); StringBuilder privateKey = new StringBuilder(); System.out.println("从证书中获取的内容是:"); for (int i = 0; i < bt.length; i++) { char ch = (char) bt[i]; privateKey.append(ch); System.out.print(ch); } } catch (UnrecoverableKeyException | KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) { e.printStackTrace(); System.out.println("wrong password !"); } } } ================================================ FILE: src/main/java/Main.java ================================================ import io.github.novacrypto.base58.Base58; import io.github.novacrypto.hashing.Sha256; import io.github.novacrypto.toruntime.CheckedExceptionToRuntime; import org.apache.commons.codec.binary.*; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.text.Normalizer; import java.util.*; import io.github.novacrypto.bip32.ExtendedPrivateKey; import io.github.novacrypto.bip32.networks.Bitcoin; import io.github.novacrypto.bip44.AddressIndex; import io.github.novacrypto.bip44.BIP44; import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Utils; import org.bouncycastle.crypto.digests.RIPEMD160Digest; import org.web3j.crypto.*; import static io.github.novacrypto.toruntime.CheckedExceptionToRuntime.toRuntime; public class Main { private static final String[] dict = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"}; private static String[] wordlist = new String[2048]; private static GenerateKeyStore ks = new GenerateKeyStore(); private static String password = "password"; private static WalletFile walletFile; public static void main(String[] args) throws Exception { String entropy = createEntropy(); String mnemonic = generateMnemonic(entropy); System.out.println(mnemonic); List params = generateKeyPairs(mnemonic); //genKeyStoreByPrivateKey(params.get(0), params.get(2), password); try { Thread.sleep(1000); //1000 ms } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } //genKeyStoreByMnemonic(mnemonic, params.get(2), password); new SendTransaction().getBalance("0x915489e6a7caf14afc874d678879f18fd0e3a684"); new SendTransaction().getBalance("0x26857844be5fea27bd48aedced42bb8727501779"); BigDecimal amount = BigDecimal.valueOf(0.01); sendTransaction("0x" + params.get(2), "0x26857844be5fea27bd48aedced42bb8727501779", amount, password, walletFile); new SendTransaction().getBalance("0x915489e6a7caf14afc874d678879f18fd0e3a684"); new SendTransaction().getBalance("0x26857844be5fea27bd48aedced42bb8727501779"); } public static String createEntropy() { UUID uuid = UUID.randomUUID(); String[] digits = uuid.toString().split("\\-"); StringBuilder randomDigits = new StringBuilder(); for (String digit : digits) { randomDigits.append(digit); } return randomDigits.toString(); } public static String generateMnemonic(String entropy) { System.out.println(entropy); //generate sha-256 from entropy String encodeStr = ""; byte[] hash = Sha256.sha256(hexStringToByteArray(entropy)); encodeStr = String.valueOf(Hex.encodeHex(hash)); System.out.println(encodeStr); char firstSHA = encodeStr.charAt(0); String new_entropy = entropy + firstSHA; String bin_entropy = ""; for (int i = 0; i < new_entropy.length(); i++) { bin_entropy += dict[Integer.parseInt(new_entropy.substring(i, i + 1), 16)]; } String[] segments = new String[12]; //hardcode for (int i = 0; i <= 11; i++) { segments[i] = bin_entropy.substring(i * 11, (i + 1) * 11); } //请修改文件的绝对路径 String path = "/Users/terry/Documents/GitHub/my/BitcoinDemo/src/main/java/english"; readTextFile(path); String mnemonic = ""; //generate mnemonic mnemonic += wordlist[Integer.valueOf(segments[0], 2)]; for (int j = 1; j < segments.length; j++) { mnemonic += " " + (wordlist[Integer.valueOf(segments[j], 2)]); } return mnemonic; } public static void readTextFile(String filePath) { try { String encoding = "utf-8"; File file = new File(filePath); if (file.isFile() && file.exists()) { //判断文件是否存在 InputStreamReader read = new InputStreamReader( new FileInputStream(file), encoding);//考虑到编码格式 BufferedReader bufferedReader = new BufferedReader(read); String lineTxt = null; int index = 0; while ((lineTxt = bufferedReader.readLine()) != null) { wordlist[index++] = lineTxt; } read.close(); } else { System.out.println("找不到指定的文件"); } } catch (Exception e) { System.out.println("读取文件内容出错"); e.printStackTrace(); } } private static List generateKeyPairs(String mnemonic) throws InvalidKeySpecException, NoSuchAlgorithmException, CipherException { // 1. we just need eth wallet for now AddressIndex ethAddressIndex = BIP44.m().purpose44().coinType(60).account(0).external().address(0); AddressIndex btcAddressIndex = BIP44.m().purpose44().coinType(0).account(0).external().address(0); // 2. calculate seed from mnemonics , then get master/root key ; Note that the bip39 passphrase we set "" for common String seed; String salt = "mnemonic"; seed = getSeed(mnemonic, salt); System.out.println(seed); ExtendedPrivateKey rootKey = ExtendedPrivateKey.fromSeed(hexStringToByteArray(seed), Bitcoin.MAIN_NET); // 3. get child private key deriving from master/root key ExtendedPrivateKey childPrivateKey = rootKey.derive(ethAddressIndex, AddressIndex.DERIVATION); // 4. get key pair byte[] privateKeyBytes = childPrivateKey.getKey(); //child private key ECKeyPair keyPair = ECKeyPair.create(privateKeyBytes); walletFile = Wallet.createLight(password, keyPair); List returnList = EthAddress(childPrivateKey, keyPair); childPrivateKey = rootKey.derive(btcAddressIndex, AddressIndex.DERIVATION); bitcoinAddress(childPrivateKey); return returnList; } /** * generate ETH privatekey, publickey and address. * * @param childPrivateKey * @param keyPair */ private static List EthAddress(ExtendedPrivateKey childPrivateKey, ECKeyPair keyPair) { String privateKey = childPrivateKey.getPrivateKey(); String publicKey = childPrivateKey.neuter().getPublicKey(); String address = Keys.getAddress(keyPair); System.out.println("ETH privateKey:" + privateKey); System.out.println("ETH publicKey:" + publicKey); System.out.println("ETH address:" + address); List returnList = new ArrayList<>(); returnList.add(privateKey); returnList.add(publicKey); returnList.add(address); return returnList; } /** * generate bitcoin privatekey, publickey and address. * * @param childPrivateKey */ private static void bitcoinAddress(ExtendedPrivateKey childPrivateKey) { // 获取比特币私钥 String privateKey = childPrivateKey.getPrivateKey(); // 加80前缀和01后缀 String rk = "80" + privateKey + "01"; // 生成校验和 byte[] checksum = Sha256.sha256(hexStringToByteArray(rk)); checksum = Sha256.sha256(checksum); // 取校验和前4位(32bits) String end = String.valueOf(Hex.encodeHex(checksum)).substring(0, 8); rk = rk + end; // 进行base58编码 String privateK = Base58.base58Encode(hexStringToByteArray(rk)); // 获取比特币公钥 String publicKey = childPrivateKey.neuter().getPublicKey(); // 对公钥进行一次sha256 byte[] pk256 = hexStringToByteArray(publicKey); pk256 = Sha256.sha256(pk256); // 进行ripe160加密(20位) RIPEMD160Digest digest = new RIPEMD160Digest(); digest.update(pk256, 0, pk256.length); byte[] ripemd160Bytes = new byte[digest.getDigestSize()]; digest.doFinal(ripemd160Bytes, 0); // 加00前缀(比特币主网)变成21位 byte[] extendedRipemd160Bytes = hexStringToByteArray("00" + String.valueOf(Hex.encodeHex(ripemd160Bytes))); // 计算校验和 checksum = Sha256.sha256(extendedRipemd160Bytes); checksum = Sha256.sha256(checksum); // 加校验和前4位,变成25位 String pk = String.valueOf(Hex.encodeHex(extendedRipemd160Bytes)) + String.valueOf(Hex.encodeHex(checksum)).substring(0, 8); // base58加密 String address = Base58.base58Encode(hexStringToByteArray(pk)); System.out.println("bitcoin privateKey:" + privateK); System.out.println("bitcoin publicKey:" + publicKey); System.out.println("bitcoin address:" + address); generateSegwitAddress(address); } public static String getSeed(String mnemonic, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException { char[] chars = Normalizer.normalize(mnemonic, Normalizer.Form.NFKD).toCharArray(); byte[] salt_ = getUtf8Bytes(salt); KeySpec spec = new PBEKeySpec(chars, salt_, 2048, 512); SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512"); return String.valueOf(Hex.encodeHex(f.generateSecret(spec).getEncoded())); } private static void generateSegwitAddress(String address) { byte[] decoded = Utils.parseAsHexOrBase58(address); // We should throw off header byte that is 0 for Bitcoin (Main) byte[] pureBytes = new byte[20]; System.arraycopy(decoded, 1, pureBytes, 0, 20); // Than we should prepend the following bytes: byte[] scriptSig = new byte[pureBytes.length + 2]; scriptSig[0] = 0x00; scriptSig[1] = 0x14; System.arraycopy(pureBytes, 0, scriptSig, 2, pureBytes.length); byte[] addressBytes = org.bitcoinj.core.Utils.sha256hash160(scriptSig); // Here are the address bytes byte[] readyForAddress = new byte[addressBytes.length + 1 + 4]; // prepending p2sh header: readyForAddress[0] = (byte) 5; System.arraycopy(addressBytes, 0, readyForAddress, 1, addressBytes.length); // But we should also append check sum: byte[] checkSum = Sha256Hash.hashTwice(readyForAddress, 0, addressBytes.length + 1); System.arraycopy(checkSum, 0, readyForAddress, addressBytes.length + 1, 4); // To get the final address: String segwitAddress = Base58.base58Encode(readyForAddress); System.out.println("segwit address:" + segwitAddress); } /*private static byte[] fromHex(String hex) { byte[] binary = new byte[hex.length() / 2]; for (int i = 0; i < binary.length; i++) { binary[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16); } return binary; }*/ private static byte[] getUtf8Bytes(final String str) { return toRuntime(new CheckedExceptionToRuntime.Func() { @Override public byte[] run() throws Exception { return str.getBytes("UTF-8"); } }); } public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); } return data; } private static void genKeyStoreByPrivateKey(String ksContent, String ksName, String ksPwd) { ks.genKeyByPrivateKey(ksName, ksPwd); try { Thread.sleep(1000); //1000 毫秒,也就是1秒. } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } ks.protectContent(ksContent, ksPwd); ks.getContent(ksPwd); } private static void genKeyStoreByMnemonic(String ksContent, String ksName, String ksPwd) { ks.genKeyByMnemonic(ksName, ksPwd); try { Thread.sleep(1000); //1000 毫秒,也就是1秒. } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } ks.protectContent(ksContent, ksPwd); ks.getContent(ksPwd); } public static void sendTransaction(String fromAddress, String toAddress, BigDecimal amount, String password, WalletFile walletfile) throws Exception { new SendTransaction().sendTransaction(fromAddress, toAddress, amount, password, walletfile); } } ================================================ FILE: src/main/java/SendTransaction.java ================================================ import org.web3j.crypto.WalletFile; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.DefaultBlockParameter; import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.Request; import org.web3j.protocol.core.methods.request.Transaction; import org.web3j.protocol.core.methods.response.*; import org.web3j.protocol.http.HttpService; import org.web3j.utils.Convert; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; public class SendTransaction { private Web3j web3j = Web3j.build(new HttpService("https://test-eth.nshd.com")); public void sendTransaction(String fromAddress, String toAddress, BigDecimal amount, String password, WalletFile walletfile) throws Exception { BigInteger mNonce = getNonce(fromAddress); BigInteger gasPrice = getGasPrice(); BigInteger value = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger(); Transaction transaction = Transaction.createEtherTransaction(fromAddress, null, null, null, toAddress, value); BigInteger gasLimit = getGasLimit(transaction); String sign = new TransactionManager().signedEthTransactionData(toAddress, mNonce, gasPrice, gasLimit, amount, walletfile, password); sendTransaction(sign); } public BigInteger getBalance(String fromAddress){ BigInteger balance = null; try { EthGetBalance ethGetBalance = web3j.ethGetBalance(fromAddress, DefaultBlockParameterName.PENDING).send(); balance = ethGetBalance.getBalance(); } catch (IOException e) { e.printStackTrace(); } System.out.println("address " + fromAddress + " balance " + balance + " wei"); return balance; } private BigInteger getNonce(String userAddress) { BigInteger nonce = BigInteger.ONE; try { Request rs = web3j.ethGetTransactionCount( userAddress, DefaultBlockParameterName.PENDING); EthGetTransactionCount egtc = rs.sendAsync().get(); nonce = egtc.getTransactionCount(); } catch (Exception e) { System.out.println("" + e); } return nonce; } private BigInteger getGasPrice() { BigInteger gasPrice = BigInteger.ONE; try { Request rs = web3j.ethGasPrice(); EthGasPrice eGasPrice = rs.sendAsync().get(); gasPrice = eGasPrice.getGasPrice(); } catch (Exception e) { System.out.println("" + e); } return gasPrice; } private BigInteger getGasLimit(Transaction transaction) { BigInteger gasLimit = BigInteger.ONE; try { Request rs = web3j.ethEstimateGas(transaction); EthEstimateGas eGasLimit = rs.sendAsync().get(); gasLimit = eGasLimit.getAmountUsed(); } catch (Exception e) { System.out.println("" + e); } return gasLimit; } private void sendTransaction(String sign){ try { Request rs = web3j.ethSendRawTransaction(sign); EthSendTransaction eSendTransaction = rs.sendAsync().get(); String res = eSendTransaction.getTransactionHash(); System.out.println("send hash : " + res); } catch (Exception e) { System.out.println("" + e); } } } ================================================ FILE: src/main/java/TransactionManager.java ================================================ import io.github.novacrypto.bip32.networks.Bitcoin; import org.web3j.abi.FunctionEncoder; import org.web3j.abi.datatypes.Address; import org.web3j.abi.datatypes.Function; import org.web3j.abi.datatypes.generated.Uint256; import org.web3j.crypto.*; import org.web3j.tx.ChainId; import org.web3j.utils.Convert; import org.web3j.utils.Numeric; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; import java.util.Collections; public class TransactionManager { /** * ETH 转账离线签名 * * @param to 转入的钱包地址 * @param nonce 以太坊nonce * @param gasPrice gasPrice * @param gasLimit gasLimit * @param amount 转账的eth数量 * @param walletfile 钱包对象 * @param password 密码 * @return 签名data */ public String signedEthTransactionData(String to, BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, BigDecimal amount, WalletFile walletfile, String password) throws Exception { // 把十进制的转换成ETH的Wei, 1ETH = 10^18 Wei BigDecimal amountInWei = Convert.toWei(amount.toString(), Convert.Unit.ETHER); RawTransaction rawTransaction = RawTransaction.createEtherTransaction(nonce, gasPrice, gasLimit, to, amountInWei.toBigInteger()); return signData(rawTransaction, walletfile, password); } public String signData(RawTransaction rawTransaction, WalletFile walletfile, String password) throws Exception { Credentials credentials = Credentials.create(Wallet.decrypt(password, walletfile)); byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, ChainId.ROPSTEN, credentials); return Numeric.toHexString(signMessage); } public String signContractTransaction(String contractAddress, String to, BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, BigDecimal amount, BigDecimal decimal, WalletFile walletfile, String password) throws Exception { BigDecimal realValue = amount.multiply(decimal); Function function = new Function("transfer", Arrays.asList(new Address(to), new Uint256(realValue.toBigInteger())), Collections.emptyList()); String data = FunctionEncoder.encode(function); RawTransaction rawTransaction = RawTransaction.createTransaction( nonce, gasPrice, gasLimit, contractAddress, data); return signData(rawTransaction, walletfile, password); } // ---------------- singleton stuff -------------------------- public static TransactionManager shared() { return TransactionManager.Holder.singleton; } public TransactionManager() { } private static class Holder { private static final TransactionManager singleton = new TransactionManager(); } } ================================================ FILE: src/main/java/english ================================================ abandon ability able about above absent absorb abstract absurd abuse access accident account accuse achieve acid acoustic acquire across act action actor actress actual adapt add addict address adjust admit adult advance advice aerobic affair afford afraid again age agent agree ahead aim air airport aisle alarm album alcohol alert alien all alley allow almost alone alpha already also alter always amateur amazing among amount amused analyst anchor ancient anger angle angry animal ankle announce annual another answer antenna antique anxiety any apart apology appear apple approve april arch arctic area arena argue arm armed armor army around arrange arrest arrive arrow art artefact artist artwork ask aspect assault asset assist assume asthma athlete atom attack attend attitude attract auction audit august aunt author auto autumn average avocado avoid awake aware away awesome awful awkward axis baby bachelor bacon badge bag balance balcony ball bamboo banana banner bar barely bargain barrel base basic basket battle beach bean beauty because become beef before begin behave behind believe below belt bench benefit best betray better between beyond bicycle bid bike bind biology bird birth bitter black blade blame blanket blast bleak bless blind blood blossom blouse blue blur blush board boat body boil bomb bone bonus book boost border boring borrow boss bottom bounce box boy bracket brain brand brass brave bread breeze brick bridge brief bright bring brisk broccoli broken bronze broom brother brown brush bubble buddy budget buffalo build bulb bulk bullet bundle bunker burden burger burst bus business busy butter buyer buzz cabbage cabin cable cactus cage cake call calm camera camp can canal cancel candy cannon canoe canvas canyon capable capital captain car carbon card cargo carpet carry cart case cash casino castle casual cat catalog catch category cattle caught cause caution cave ceiling celery cement census century cereal certain chair chalk champion change chaos chapter charge chase chat cheap check cheese chef cherry chest chicken chief child chimney choice choose chronic chuckle chunk churn cigar cinnamon circle citizen city civil claim clap clarify claw clay clean clerk clever click client cliff climb clinic clip clock clog close cloth cloud clown club clump cluster clutch coach coast coconut code coffee coil coin collect color column combine come comfort comic common company concert conduct confirm congress connect consider control convince cook cool copper copy coral core corn correct cost cotton couch country couple course cousin cover coyote crack cradle craft cram crane crash crater crawl crazy cream credit creek crew cricket crime crisp critic crop cross crouch crowd crucial cruel cruise crumble crunch crush cry crystal cube culture cup cupboard curious current curtain curve cushion custom cute cycle dad damage damp dance danger daring dash daughter dawn day deal debate debris decade december decide decline decorate decrease deer defense define defy degree delay deliver demand demise denial dentist deny depart depend deposit depth deputy derive describe desert design desk despair destroy detail detect develop device devote diagram dial diamond diary dice diesel diet differ digital dignity dilemma dinner dinosaur direct dirt disagree discover disease dish dismiss disorder display distance divert divide divorce dizzy doctor document dog doll dolphin domain donate donkey donor door dose double dove draft dragon drama drastic draw dream dress drift drill drink drip drive drop drum dry duck dumb dune during dust dutch duty dwarf dynamic eager eagle early earn earth easily east easy echo ecology economy edge edit educate effort egg eight either elbow elder electric elegant element elephant elevator elite else embark embody embrace emerge emotion employ empower empty enable enact end endless endorse enemy energy enforce engage engine enhance enjoy enlist enough enrich enroll ensure enter entire entry envelope episode equal equip era erase erode erosion error erupt escape essay essence estate eternal ethics evidence evil evoke evolve exact example excess exchange excite exclude excuse execute exercise exhaust exhibit exile exist exit exotic expand expect expire explain expose express extend extra eye eyebrow fabric face faculty fade faint faith fall false fame family famous fan fancy fantasy farm fashion fat fatal father fatigue fault favorite feature february federal fee feed feel female fence festival fetch fever few fiber fiction field figure file film filter final find fine finger finish fire firm first fiscal fish fit fitness fix flag flame flash flat flavor flee flight flip float flock floor flower fluid flush fly foam focus fog foil fold follow food foot force forest forget fork fortune forum forward fossil foster found fox fragile frame frequent fresh friend fringe frog front frost frown frozen fruit fuel fun funny furnace fury future gadget gain galaxy gallery game gap garage garbage garden garlic garment gas gasp gate gather gauge gaze general genius genre gentle genuine gesture ghost giant gift giggle ginger giraffe girl give glad glance glare glass glide glimpse globe gloom glory glove glow glue goat goddess gold good goose gorilla gospel gossip govern gown grab grace grain grant grape grass gravity great green grid grief grit grocery group grow grunt guard guess guide guilt guitar gun gym habit hair half hammer hamster hand happy harbor hard harsh harvest hat have hawk hazard head health heart heavy hedgehog height hello helmet help hen hero hidden high hill hint hip hire history hobby hockey hold hole holiday hollow home honey hood hope horn horror horse hospital host hotel hour hover hub huge human humble humor hundred hungry hunt hurdle hurry hurt husband hybrid ice icon idea identify idle ignore ill illegal illness image imitate immense immune impact impose improve impulse inch include income increase index indicate indoor industry infant inflict inform inhale inherit initial inject injury inmate inner innocent input inquiry insane insect inside inspire install intact interest into invest invite involve iron island isolate issue item ivory jacket jaguar jar jazz jealous jeans jelly jewel job join joke journey joy judge juice jump jungle junior junk just kangaroo keen keep ketchup key kick kid kidney kind kingdom kiss kit kitchen kite kitten kiwi knee knife knock know lab label labor ladder lady lake lamp language laptop large later latin laugh laundry lava law lawn lawsuit layer lazy leader leaf learn leave lecture left leg legal legend leisure lemon lend length lens leopard lesson letter level liar liberty library license life lift light like limb limit link lion liquid list little live lizard load loan lobster local lock logic lonely long loop lottery loud lounge love loyal lucky luggage lumber lunar lunch luxury lyrics machine mad magic magnet maid mail main major make mammal man manage mandate mango mansion manual maple marble march margin marine market marriage mask mass master match material math matrix matter maximum maze meadow mean measure meat mechanic medal media melody melt member memory mention menu mercy merge merit merry mesh message metal method middle midnight milk million mimic mind minimum minor minute miracle mirror misery miss mistake mix mixed mixture mobile model modify mom moment monitor monkey monster month moon moral more morning mosquito mother motion motor mountain mouse move movie much muffin mule multiply muscle museum mushroom music must mutual myself mystery myth naive name napkin narrow nasty nation nature near neck need negative neglect neither nephew nerve nest net network neutral never news next nice night noble noise nominee noodle normal north nose notable note nothing notice novel now nuclear number nurse nut oak obey object oblige obscure observe obtain obvious occur ocean october odor off offer office often oil okay old olive olympic omit once one onion online only open opera opinion oppose option orange orbit orchard order ordinary organ orient original orphan ostrich other outdoor outer output outside oval oven over own owner oxygen oyster ozone pact paddle page pair palace palm panda panel panic panther paper parade parent park parrot party pass patch path patient patrol pattern pause pave payment peace peanut pear peasant pelican pen penalty pencil people pepper perfect permit person pet phone photo phrase physical piano picnic picture piece pig pigeon pill pilot pink pioneer pipe pistol pitch pizza place planet plastic plate play please pledge pluck plug plunge poem poet point polar pole police pond pony pool popular portion position possible post potato pottery poverty powder power practice praise predict prefer prepare present pretty prevent price pride primary print priority prison private prize problem process produce profit program project promote proof property prosper protect proud provide public pudding pull pulp pulse pumpkin punch pupil puppy purchase purity purpose purse push put puzzle pyramid quality quantum quarter question quick quit quiz quote rabbit raccoon race rack radar radio rail rain raise rally ramp ranch random range rapid rare rate rather raven raw razor ready real reason rebel rebuild recall receive recipe record recycle reduce reflect reform refuse region regret regular reject relax release relief rely remain remember remind remove render renew rent reopen repair repeat replace report require rescue resemble resist resource response result retire retreat return reunion reveal review reward rhythm rib ribbon rice rich ride ridge rifle right rigid ring riot ripple risk ritual rival river road roast robot robust rocket romance roof rookie room rose rotate rough round route royal rubber rude rug rule run runway rural sad saddle sadness safe sail salad salmon salon salt salute same sample sand satisfy satoshi sauce sausage save say scale scan scare scatter scene scheme school science scissors scorpion scout scrap screen script scrub sea search season seat second secret section security seed seek segment select sell seminar senior sense sentence series service session settle setup seven shadow shaft shallow share shed shell sheriff shield shift shine ship shiver shock shoe shoot shop short shoulder shove shrimp shrug shuffle shy sibling sick side siege sight sign silent silk silly silver similar simple since sing siren sister situate six size skate sketch ski skill skin skirt skull slab slam sleep slender slice slide slight slim slogan slot slow slush small smart smile smoke smooth snack snake snap sniff snow soap soccer social sock soda soft solar soldier solid solution solve someone song soon sorry sort soul sound soup source south space spare spatial spawn speak special speed spell spend sphere spice spider spike spin spirit split spoil sponsor spoon sport spot spray spread spring spy square squeeze squirrel stable stadium staff stage stairs stamp stand start state stay steak steel stem step stereo stick still sting stock stomach stone stool story stove strategy street strike strong struggle student stuff stumble style subject submit subway success such sudden suffer sugar suggest suit summer sun sunny sunset super supply supreme sure surface surge surprise surround survey suspect sustain swallow swamp swap swarm swear sweet swift swim swing switch sword symbol symptom syrup system table tackle tag tail talent talk tank tape target task taste tattoo taxi teach team tell ten tenant tennis tent term test text thank that theme then theory there they thing this thought three thrive throw thumb thunder ticket tide tiger tilt timber time tiny tip tired tissue title toast tobacco today toddler toe together toilet token tomato tomorrow tone tongue tonight tool tooth top topic topple torch tornado tortoise toss total tourist toward tower town toy track trade traffic tragic train transfer trap trash travel tray treat tree trend trial tribe trick trigger trim trip trophy trouble truck true truly trumpet trust truth try tube tuition tumble tuna tunnel turkey turn turtle twelve twenty twice twin twist two type typical ugly umbrella unable unaware uncle uncover under undo unfair unfold unhappy uniform unique unit universe unknown unlock until unusual unveil update upgrade uphold upon upper upset urban urge usage use used useful useless usual utility vacant vacuum vague valid valley valve van vanish vapor various vast vault vehicle velvet vendor venture venue verb verify version very vessel veteran viable vibrant vicious victory video view village vintage violin virtual virus visa visit visual vital vivid vocal voice void volcano volume vote voyage wage wagon wait walk wall walnut want warfare warm warrior wash wasp waste water wave way wealth weapon wear weasel weather web wedding weekend weird welcome west wet whale what wheat wheel when where whip whisper wide width wife wild will win window wine wing wink winner winter wire wisdom wise wish witness wolf woman wonder wood wool word work world worry worth wrap wreck wrestle wrist write wrong yard year yellow you young youth zebra zero zone zoo